Controller.php 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  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\Routing\Controller as BaseController;
  17. use RuntimeException;
  18. use Str;
  19. class Controller extends BaseController
  20. {
  21. use AuthorizesRequests;
  22. use DispatchesJobs;
  23. use ValidatesRequests;
  24. // 类似Linux中的tail命令
  25. public function tail($file, $n, $base = 5)
  26. {
  27. $fileLines = $this->countLine($file);
  28. if ($fileLines < 15000) {
  29. return false;
  30. }
  31. $fp = fopen($file, 'rb+');
  32. assert($n > 0);
  33. $pos = $n + 1;
  34. $lines = [];
  35. while (count($lines) <= $n) {
  36. try {
  37. fseek($fp, -$pos, SEEK_END);
  38. } catch (Exception $e) {
  39. break;
  40. }
  41. $pos *= $base;
  42. while (! feof($fp)) {
  43. array_unshift($lines, fgets($fp));
  44. }
  45. }
  46. return array_slice($lines, 0, $n);
  47. }
  48. /**
  49. * 计算文件行数.
  50. *
  51. * @param $file
  52. *
  53. * @return int
  54. */
  55. public function countLine($file): int
  56. {
  57. $fp = fopen($file, 'rb');
  58. $i = 0;
  59. while (! feof($fp)) {
  60. //每次读取2M
  61. if ($data = fread($fp, 1024 * 1024 * 2)) {
  62. //计算读取到的行数
  63. $num = substr_count($data, "\n");
  64. $i += $num;
  65. }
  66. }
  67. fclose($fp);
  68. return $i;
  69. }
  70. // 获取邮箱后缀
  71. public function emailFilterList($type): array
  72. {
  73. return EmailFilter::whereType($type)->pluck('words')->toArray();
  74. }
  75. // 将Base64图片转换为本地图片并保存
  76. public function base64ImageSaver($base64_image_content): ?string
  77. {
  78. // 匹配出图片的格式
  79. if (preg_match('/^(data:\s*image\/(\w+);base64,)/', $base64_image_content, $result)) {
  80. $type = $result[2];
  81. $directory = date('Ymd');
  82. $path = '/assets/images/qrcode/'.$directory.'/';
  83. // 检查是否有该文件夹,如果没有就创建,并给予最高权限
  84. if (! file_exists(public_path($path))
  85. && ! mkdir($concurrentDirectory = public_path($path), 0755, true)
  86. && ! is_dir($concurrentDirectory)) {
  87. throw new RuntimeException(sprintf('Directory "%s" was not created', $concurrentDirectory));
  88. }
  89. $fileName = Str::random(18).".{$type}";
  90. if (file_put_contents(public_path($path.$fileName), base64_decode(str_replace($result[1], '', $base64_image_content)))) {
  91. chmod(public_path($path.$fileName), 0744);
  92. return $path.$fileName;
  93. }
  94. }
  95. return '';
  96. }
  97. /**
  98. * 节点信息.
  99. *
  100. * @param int $uid 用户ID
  101. * @param int $nodeId 节点ID
  102. * @param int $infoType 信息类型:0为链接,1为文字
  103. *
  104. * @return string
  105. */
  106. public function getUserNodeInfo(int $uid, int $nodeId, int $infoType): string
  107. {
  108. $user = User::find($uid);
  109. $node = Node::find($nodeId);
  110. $scheme = null;
  111. $group = sysConfig('website_name'); // 分组名称
  112. $host = $node->is_relay ? $node->relay_server : ($node->server ?: $node->ip);
  113. $data = null;
  114. switch ($node->type) {
  115. case 2:
  116. // 生成v2ray scheme
  117. if ($infoType !== 1) {
  118. // 生成v2ray scheme
  119. $data = $this->v2raySubUrl(
  120. $node->name,
  121. $host,
  122. $node->v2_port,
  123. $user->vmess_id,
  124. $node->v2_alter_id,
  125. $node->v2_net,
  126. $node->v2_type,
  127. $node->v2_host,
  128. $node->v2_path,
  129. $node->v2_tls ? 'tls' : ''
  130. );
  131. } else {
  132. $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;
  133. }
  134. break;
  135. case 3:
  136. if ($infoType !== 1) {
  137. $data = $this->trojanSubUrl($user->passwd, $host, $node->port, $node->name);
  138. } else {
  139. $data = '备注:'.$node->name.PHP_EOL.'服务器:'.$host.PHP_EOL.'密码:'.$user->passwd.PHP_EOL.'端口:'.$node->port.PHP_EOL;
  140. }
  141. break;
  142. case 1:
  143. case 4:
  144. $protocol = $node->protocol;
  145. $method = $node->method;
  146. $obfs = $node->obfs;
  147. if ($node->single) {
  148. //单端口使用中转的端口
  149. $port = $node->is_relay ? $node->relay_port : $node->port;
  150. $passwd = $node->passwd;
  151. $protocol_param = $user->port.':'.$user->passwd;
  152. } else {
  153. $port = $user->port;
  154. $passwd = $user->passwd;
  155. $protocol_param = $node->protocol_param;
  156. if ($node->type === 1) {
  157. $protocol = $user->protocol;
  158. $method = $user->method;
  159. $obfs = $user->obfs;
  160. }
  161. }
  162. if ($infoType !== 1) {
  163. // 生成ss/ssr scheme
  164. $data = $node->compatible ? $this->ssSubUrl($host, $port, $method, $passwd, $group) :
  165. $this->ssrSubUrl($host, $port, $protocol, $method, $obfs, $passwd, $node->obfs_param, $protocol_param, $node->name, $group, $node->is_udp);
  166. } else {
  167. // 生成文本配置信息
  168. $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);
  169. }
  170. break;
  171. default:
  172. }
  173. return $data;
  174. }
  175. public function v2raySubUrl($name, $host, $port, $uuid, $alter_id, $net, $type, $domain, $path, $tls): string
  176. {
  177. return 'vmess://'.base64url_encode(json_encode([
  178. 'v' => '2', 'ps' => $name, 'add' => $host, 'port' => $port, 'id' => $uuid, 'aid' => $alter_id, 'net' => $net,
  179. 'type' => $type, 'host' => $domain, 'path' => $path, 'tls' => $tls ? 'tls' : '',
  180. ], JSON_PRETTY_PRINT));
  181. }
  182. public function trojanSubUrl($password, $domain, $port, $remark): string
  183. {
  184. return 'trojan://'.urlencode($password).'@'.$domain.':'.$port.'#'.urlencode($remark);
  185. }
  186. public function ssSubUrl($host, $port, $method, $passwd, $group): string
  187. {
  188. return 'ss://'.base64url_encode($method.':'.$passwd.'@'.$host.':'.$port).'#'.$group;
  189. }
  190. public function ssrSubUrl($host, $port, $protocol, $method, $obfs, $passwd, $obfs_param, $protocol_param, $name, $group, $is_udp): string
  191. {
  192. return 'ssr://'.base64url_encode($host.':'.$port.':'.$protocol.':'.$method.':'.$obfs.':'.base64url_encode($passwd).'/?obfsparam='.base64url_encode($obfs_param).'&protoparam='.base64url_encode($protocol_param).'&remarks='.base64url_encode($name).'&group='.base64url_encode($group).'&udpport='.$is_udp.'&uot=0');
  193. }
  194. // 流量使用图表
  195. public function dataFlowChart($id, $is_node = false): array
  196. {
  197. if ($is_node) {
  198. $currentFlow = UserDataFlowLog::whereNodeId($id);
  199. $hourlyFlow = NodeHourlyDataFlow::whereNodeId($id);
  200. $dailyFlow = NodeDailyDataFlow::whereNodeId($id);
  201. } else {
  202. $currentFlow = UserDataFlowLog::whereUserId($id);
  203. $hourlyFlow = UserHourlyDataFlow::userHourly($id);
  204. $dailyFlow = UserDailyDataFlow::userDaily($id);
  205. }
  206. $currentFlow = $currentFlow->where('log_time', '>=', strtotime(date('Y-m-d H:00')))->sum(DB::raw('u + d'));
  207. $hourlyFlow = $hourlyFlow->whereDate('created_at', date('Y-m-d'))->pluck('total', 'created_at')->toArray();
  208. $dailyFlow = $dailyFlow->whereMonth('created_at', date('n'))->pluck('total', 'created_at')->toArray();
  209. // 节点一天内的流量
  210. $hourlyData = array_fill(0, date('G') + 1, 0);
  211. foreach ($hourlyFlow as $date => $dataFlow) {
  212. $hourlyData[date('G', strtotime($date))] = round($dataFlow / GB, 3);
  213. }
  214. $hourlyData[date('G') + 1] = round($currentFlow / GB, 3);
  215. // 节点一个月内的流量
  216. $dailyData = array_fill(0, date('j'), 0);
  217. foreach ($dailyFlow as $date => $dataFlow) {
  218. $dailyData[date('j', strtotime($date)) - 1] = round($dataFlow / GB, 3);
  219. }
  220. $dailyData[date('j', strtotime(now())) - 1] = round((array_sum($hourlyFlow) + $currentFlow) / GB, 3);
  221. return [
  222. 'trafficDaily' => json_encode($dailyData),
  223. 'trafficHourly' => json_encode($hourlyData),
  224. 'monthDays' => json_encode(range(1, date('j'), 1)), // 本月天数
  225. 'dayHours' => json_encode(range(0, date('G') + 1, 1)), // 本日小时
  226. ];
  227. }
  228. }