Controller.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  1. <?php
  2. namespace App\Http\Controllers;
  3. use App\Models\EmailFilter;
  4. use App\Models\Node;
  5. use App\Models\NodeDailyDataFlow;
  6. use App\Models\NodeHourlyDataFlow;
  7. use App\Models\User;
  8. use App\Models\UserDailyDataFlow;
  9. use App\Models\UserDataFlowLog;
  10. use App\Models\UserHourlyDataFlow;
  11. use DB;
  12. use Exception;
  13. use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
  14. use Illuminate\Foundation\Bus\DispatchesJobs;
  15. use Illuminate\Foundation\Validation\ValidatesRequests;
  16. use Illuminate\Http\UploadedFile;
  17. use Illuminate\Routing\Controller as BaseController;
  18. use RuntimeException;
  19. use Str;
  20. class Controller extends BaseController
  21. {
  22. use AuthorizesRequests;
  23. use DispatchesJobs;
  24. use ValidatesRequests;
  25. // 生成随机密码
  26. public function makePasswd(): string
  27. {
  28. return Str::random();
  29. }
  30. // 生成UUID
  31. public function makeUUID()
  32. {
  33. return Str::uuid();
  34. }
  35. // 生成网站安全码
  36. public function makeSecurityCode(): string
  37. {
  38. return strtolower(Str::random(8));
  39. }
  40. // 类似Linux中的tail命令
  41. public function tail($file, $n, $base = 5)
  42. {
  43. $fileLines = $this->countLine($file);
  44. if ($fileLines < 15000) {
  45. return false;
  46. }
  47. $fp = fopen($file, 'rb+');
  48. assert($n > 0);
  49. $pos = $n + 1;
  50. $lines = [];
  51. while (count($lines) <= $n) {
  52. try {
  53. fseek($fp, -$pos, SEEK_END);
  54. } catch (Exception $e) {
  55. break;
  56. }
  57. $pos *= $base;
  58. while ( ! feof($fp)) {
  59. array_unshift($lines, fgets($fp));
  60. }
  61. }
  62. return array_slice($lines, 0, $n);
  63. }
  64. /**
  65. * 计算文件行数
  66. *
  67. * @param $file
  68. *
  69. * @return int
  70. */
  71. public function countLine($file): int
  72. {
  73. $fp = fopen($file, 'rb');
  74. $i = 0;
  75. while ( ! feof($fp)) {
  76. //每次读取2M
  77. if ($data = fread($fp, 1024 * 1024 * 2)) {
  78. //计算读取到的行数
  79. $num = substr_count($data, "\n");
  80. $i += $num;
  81. }
  82. }
  83. fclose($fp);
  84. return $i;
  85. }
  86. // 获取邮箱后缀
  87. public function emailFilterList($type): array
  88. {
  89. return EmailFilter::whereType($type)->pluck('words')->toArray();
  90. }
  91. // 将Base64图片转换为本地图片并保存
  92. public function base64ImageSaver($base64_image_content): ?string
  93. {
  94. // 匹配出图片的格式
  95. if (preg_match(
  96. '/^(data:\s*image\/(\w+);base64,)/',
  97. $base64_image_content,
  98. $result
  99. )) {
  100. $type = $result[2];
  101. $directory = date('Ymd');
  102. $path = '/assets/images/qrcode/' . $directory . '/';
  103. // 检查是否有该文件夹,如果没有就创建,并给予最高权限
  104. if ( ! file_exists(public_path($path))
  105. && ! mkdir(
  106. $concurrentDirectory = public_path($path),
  107. 0755,
  108. true
  109. )
  110. && ! is_dir($concurrentDirectory)) {
  111. throw new RuntimeException(
  112. sprintf(
  113. 'Directory "%s" was not created',
  114. $concurrentDirectory
  115. )
  116. );
  117. }
  118. $fileName = Str::random(18) . ".{$type}";
  119. if (file_put_contents(
  120. public_path($path . $fileName),
  121. base64_decode(
  122. str_replace($result[1], '', $base64_image_content)
  123. )
  124. )) {
  125. chmod(public_path($path . $fileName), 0744);
  126. return $path . $fileName;
  127. }
  128. }
  129. return '';
  130. }
  131. // 上传文件处理
  132. public function uploadFile(UploadedFile $file): string
  133. {
  134. $fileType = $file->getClientOriginalExtension();
  135. // 验证文件合法性
  136. if ( ! in_array($fileType, ['jpg', 'png', 'jpeg', 'bmp'])) {
  137. return false;
  138. }
  139. $name = date('YmdHis') . random_int(1000, 2000) . '.' . $fileType;
  140. $move = $file->move(base_path() . '/public/upload/image/', $name);
  141. return $move ? '/upload/image/' . $name : '';
  142. }
  143. /**
  144. * 节点信息
  145. *
  146. * @param int $uid 用户ID
  147. * @param int $nodeId 节点ID
  148. * @param int $infoType 信息类型:0为链接,1为文字
  149. *
  150. * @return string
  151. */
  152. public function getUserNodeInfo(
  153. int $uid,
  154. int $nodeId,
  155. int $infoType
  156. ): string {
  157. $user = User::find($uid);
  158. $node = Node::find($nodeId);
  159. $scheme = null;
  160. $group = sysConfig('website_name');// 分组名称
  161. $host = $node->is_relay ? $node->relay_server : ($node->server ?: $node->ip);
  162. $data = null;
  163. switch ($node->type) {
  164. case 2:
  165. // 生成v2ray scheme
  166. if ($infoType !== 1) {
  167. // 生成v2ray scheme
  168. $data = $this->v2raySubUrl(
  169. $node->name,
  170. $host,
  171. $node->v2_port,
  172. $user->vmess_id,
  173. $node->v2_alter_id,
  174. $node->v2_net,
  175. $node->v2_type,
  176. $node->v2_host,
  177. $node->v2_path,
  178. $node->v2_tls ? "tls" : ""
  179. );
  180. } else {
  181. $data = "服务器:" . $host . PHP_EOL . "IPv6:" . ($node->ipv6 ?: "") . PHP_EOL . "端口:" . $node->v2_port . PHP_EOL . "加密方式:" . $node->v2_method . PHP_EOL . "用户ID:" . $user->vmess_id . PHP_EOL . "额外ID:" . $node->v2_alter_id . PHP_EOL . "传输协议:" . $node->v2_net . PHP_EOL . "伪装类型:" . $node->v2_type . PHP_EOL . "伪装域名:" . ($node->v2_host ?: "") . PHP_EOL . "路径:" . ($node->v2_path ?: "") . PHP_EOL . "TLS:" . ($node->v2_tls ? "tls" : "") . PHP_EOL;
  182. }
  183. break;
  184. case 3:
  185. if ($infoType !== 1) {
  186. $data = $this->trojanSubUrl(
  187. $user->passwd,
  188. $host,
  189. $node->port,
  190. $node->name
  191. );
  192. } else {
  193. $data = "备注:" . $node->name . PHP_EOL . "服务器:" . $host . PHP_EOL . "密码:" . $user->passwd . PHP_EOL . "端口:" . $node->port . PHP_EOL;
  194. }
  195. break;
  196. case 1:
  197. case 4:
  198. $protocol = $node->protocol;
  199. $method = $node->method;
  200. $obfs = $node->obfs;
  201. if ($node->single) {
  202. //单端口使用中转的端口
  203. $port = $node->is_relay ? $node->relay_port : $node->port;
  204. $passwd = $node->passwd;
  205. $protocol_param = $user->port . ':' . $user->passwd;
  206. } else {
  207. $port = $user->port;
  208. $passwd = $user->passwd;
  209. $protocol_param = $node->protocol_param;
  210. if ($node->type === 1) {
  211. $protocol = $user->protocol;
  212. $method = $user->method;
  213. $obfs = $user->obfs;
  214. }
  215. }
  216. if ($infoType !== 1) {
  217. // 生成ss/ssr scheme
  218. $data = $node->compatible ? $this->ssSubUrl(
  219. $host,
  220. $port,
  221. $method,
  222. $passwd,
  223. $group
  224. ) : $this->ssrSubUrl(
  225. $host,
  226. $port,
  227. $protocol,
  228. $method,
  229. $obfs,
  230. $passwd,
  231. $node->obfs_param,
  232. $protocol_param,
  233. $node->name,
  234. $group,
  235. $node->is_udp
  236. );
  237. } else {
  238. // 生成文本配置信息
  239. $data = "服务器:" . $host . PHP_EOL . "IPv6:" . $node->ipv6 . PHP_EOL . "服务器端口:" . $port . PHP_EOL . "密码:" . $passwd . PHP_EOL . "加密:" . $method . PHP_EOL . ($node->compatible ? '' : "协议:" . $protocol . PHP_EOL . "协议参数:" . $protocol_param . PHP_EOL . "混淆:" . $obfs . PHP_EOL . "混淆参数:" . $node->obfs_param . PHP_EOL);
  240. }
  241. break;
  242. default:
  243. }
  244. return $data;
  245. }
  246. public function v2raySubUrl(
  247. $name,
  248. $host,
  249. $port,
  250. $uuid,
  251. $alter_id,
  252. $net,
  253. $type,
  254. $domain,
  255. $path,
  256. $tls
  257. ): string {
  258. return 'vmess://' . base64url_encode(
  259. json_encode(
  260. [
  261. "v" => "2",
  262. "ps" => $name,
  263. "add" => $host,
  264. "port" => $port,
  265. "id" => $uuid,
  266. "aid" => $alter_id,
  267. "net" => $net,
  268. "type" => $type,
  269. "host" => $domain,
  270. "path" => $path,
  271. "tls" => $tls ? "tls" : "",
  272. ],
  273. JSON_PRETTY_PRINT
  274. )
  275. );
  276. }
  277. public function trojanSubUrl($password, $domain, $port, $remark): string
  278. {
  279. return 'trojan://' . urlencode(
  280. $password
  281. ) . '@' . $domain . ':' . $port . '#' . urlencode($remark);
  282. }
  283. public function ssSubUrl($host, $port, $method, $passwd, $group): string
  284. {
  285. return 'ss://' . base64url_encode(
  286. $method . ':' . $passwd . '@' . $host . ':' . $port
  287. ) . '#' . $group;
  288. }
  289. public function ssrSubUrl(
  290. $host,
  291. $port,
  292. $protocol,
  293. $method,
  294. $obfs,
  295. $passwd,
  296. $obfs_param,
  297. $protocol_param,
  298. $name,
  299. $group,
  300. $is_udp
  301. ): string {
  302. return 'ssr://' . base64url_encode(
  303. $host . ':' . $port . ':' . $protocol . ':' . $method . ':' . $obfs . ':' . base64url_encode(
  304. $passwd
  305. ) . '/?obfsparam=' . base64url_encode(
  306. $obfs_param
  307. ) . '&protoparam=' . base64url_encode(
  308. $protocol_param
  309. ) . '&remarks=' . base64url_encode(
  310. $name
  311. ) . '&group=' . base64url_encode(
  312. $group
  313. ) . '&udpport=' . $is_udp . '&uot=0'
  314. );
  315. }
  316. // 流量使用图表
  317. public function dataFlowChart($id, $is_node = 0): array
  318. {
  319. if ($is_node) {
  320. $currentFlow = UserDataFlowLog::whereNodeId($id);
  321. $hourlyFlow = NodeHourlyDataFlow::whereNodeId($id);
  322. $dailyFlow = NodeDailyDataFlow::whereNodeId($id);
  323. } else {
  324. $currentFlow = UserDataFlowLog::whereUserId($id);
  325. $hourlyFlow = UserHourlyDataFlow::userHourly($id);
  326. $dailyFlow = UserDailyDataFlow::userDaily($id);
  327. }
  328. $currentFlow = $currentFlow->where(
  329. 'log_time',
  330. '>=',
  331. strtotime(date('Y-m-d H:00'))
  332. )->sum(DB::raw('u + d'));
  333. $hourlyFlow = $hourlyFlow->whereDate('created_at', date('Y-m-d'))
  334. ->pluck('total', 'created_at')
  335. ->toArray();
  336. $dailyFlow = $dailyFlow->whereMonth('created_at', date('n'))->pluck(
  337. 'total',
  338. 'created_at'
  339. )->toArray();
  340. // 节点一天内的流量
  341. $hourlyData = array_fill(0, date('G') + 1, 0);
  342. foreach ($hourlyFlow as $date => $dataFlow) {
  343. $hourlyData[date('G', strtotime($date))] = round($dataFlow / GB, 3);
  344. }
  345. $hourlyData[date('G') + 1] = round($currentFlow / GB, 3);
  346. // 节点一个月内的流量
  347. $dailyData = array_fill(0, date('j'), 0);
  348. foreach ($dailyFlow as $date => $dataFlow) {
  349. $dailyData[date('j', strtotime($date)) - 1] = round(
  350. $dataFlow / GB,
  351. 3
  352. );
  353. }
  354. $dailyData[date('j', strtotime(now())) - 1] = round(
  355. (array_sum($hourlyFlow) + $currentFlow) / GB,
  356. 3
  357. );
  358. return [
  359. 'trafficDaily' => json_encode($dailyData),
  360. 'trafficHourly' => json_encode($hourlyData),
  361. 'monthDays' => json_encode(range(1, date("j"), 1)),// 本月天数
  362. 'dayHours' => json_encode(range(0, date("G") + 1, 1))// 本日小时
  363. ];
  364. }
  365. }