SubController.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  1. <?php
  2. declare(strict_types=1);
  3. namespace App\Controllers;
  4. use App\Models\Link;
  5. use App\Models\Node;
  6. use App\Models\Setting;
  7. use App\Models\UserSubscribeLog;
  8. use App\Utils\ResponseHelper;
  9. use App\Utils\Tools;
  10. use Psr\Http\Message\ResponseInterface;
  11. use Symfony\Component\Yaml\Yaml;
  12. use function array_key_exists;
  13. use function array_merge;
  14. use function in_array;
  15. use function json_decode;
  16. /**
  17. * SubController
  18. */
  19. final class SubController extends BaseController
  20. {
  21. public static function getContent($request, $response, $args): ResponseInterface
  22. {
  23. $err_msg = '订阅链接无效';
  24. $token = $args['token'];
  25. $subtype = $args['subtype'];
  26. $subtype_list = ['json', 'clash', 'sip008'];
  27. if (! $_ENV['Subscribe'] || ! in_array($subtype, $subtype_list)) {
  28. return ResponseHelper::error($response, $err_msg);
  29. }
  30. $link = Link::where('token', $token)->first();
  31. if (! $link->isValid()) {
  32. return ResponseHelper::error($response, $err_msg);
  33. }
  34. $user = $link->user();
  35. match ($subtype) {
  36. 'json' => $sub_info = self::getJson($user),
  37. 'clash' => $sub_info = self::getClash($user),
  38. 'sip008' => $sub_info = self::getSIP008($user),
  39. };
  40. if ($_ENV['subscribeLog'] === true) {
  41. UserSubscribeLog::addSubscribeLog($user, $subtype, $request->getHeaderLine('User-Agent'));
  42. }
  43. if (in_array($subtype, ['json', 'sip008'])) {
  44. return $response->withJson([
  45. $sub_info,
  46. ]);
  47. }
  48. $sub_details = ' upload=' . $user->u
  49. . '; download=' . $user->d
  50. . '; total=' . $user->transfer_enable
  51. . '; expire=' . strtotime($user->class_expire);
  52. return $response->withHeader('Subscription-Userinfo', $sub_details)->write(
  53. $sub_info
  54. );
  55. }
  56. public static function getJson($user): array
  57. {
  58. $nodes = [];
  59. //篩選出用戶能連接的節點
  60. $nodes_raw = Node::where('type', 1)
  61. ->where('node_class', '<=', $user->class)
  62. ->whereIn('node_group', [0, $user->node_group])
  63. ->where(static function ($query): void {
  64. $query->where('node_bandwidth_limit', '=', 0)->orWhereRaw('node_bandwidth < node_bandwidth_limit');
  65. })
  66. ->get();
  67. foreach ($nodes_raw as $node_raw) {
  68. $node_custom_config = json_decode($node_raw->custom_config, true);
  69. //檢查是否配置“前端/订阅中下发的服务器地址”
  70. if (! array_key_exists('server_user', $node_custom_config)) {
  71. $server = $node_raw->server;
  72. } else {
  73. $server = $node_custom_config['server_user'];
  74. }
  75. switch ((int) $node_raw->sort) {
  76. case 0:
  77. $plugin = $node_custom_config['plugin'] ?? '';
  78. $plugin_option = $node_custom_config['plugin_option'] ?? '';
  79. $node = [
  80. 'name' => $node_raw->name,
  81. 'id' => $node_raw->id,
  82. 'type' => 'ss',
  83. 'address' => $server,
  84. 'port' => (int) $user->port,
  85. 'password' => $user->passwd,
  86. 'encryption' => $user->method,
  87. 'plugin' => $plugin,
  88. 'plugin_option' => $plugin_option,
  89. 'remark' => $node_raw->info,
  90. ];
  91. break;
  92. case 11:
  93. $v2_port = $node_custom_config['v2_port'] ?? ($node_custom_config['offset_port_user']
  94. ?? ($node_custom_config['offset_port_node'] ?? 443));
  95. //默認值有問題的請懂 V2 怎麽用的人來改一改。
  96. $alter_id = $node_custom_config['alter_id'] ?? '0';
  97. $security = $node_custom_config['security'] ?? 'none';
  98. $flow = $node_custom_config['flow'] ?? '';
  99. $encryption = $node_custom_config['encryption'] ?? '';
  100. $network = $node_custom_config['network'] ?? '';
  101. $header = $node_custom_config['header'] ?? ['type' => 'none'];
  102. $header_type = $header['type'] ?? '';
  103. $host = $node_custom_config['host'] ?? '';
  104. $servicename = $node_custom_config['servicename'] ?? '';
  105. $path = $node_custom_config['path'] ?? '/';
  106. $tls = in_array($security, ['tls', 'xtls']) ? '1' : '0';
  107. $enable_vless = $node_custom_config['enable_vless'] ?? '0';
  108. $node = [
  109. 'name' => $node_raw->name,
  110. 'id' => $node_raw->id,
  111. 'type' => 'vmess',
  112. 'address' => $server,
  113. 'port' => (int) $v2_port,
  114. 'uuid' => $user->uuid,
  115. 'alterid' => (int) $alter_id,
  116. 'security' => $security,
  117. 'flow' => $flow,
  118. 'encryption' => $encryption,
  119. 'network' => $network,
  120. 'header' => $header,
  121. 'header_type' => $header_type,
  122. 'host' => $host,
  123. 'path' => $path,
  124. 'servicename' => $servicename,
  125. 'tls' => (int) $tls,
  126. 'enable_vless' => (int) $enable_vless,
  127. 'remark' => $node_raw->info,
  128. ];
  129. break;
  130. case 14:
  131. $trojan_port = $node_custom_config['trojan_port'] ?? ($node_custom_config['offset_port_user']
  132. ?? ($node_custom_config['offset_port_node'] ?? 443));
  133. $host = $node_custom_config['host'] ?? '';
  134. $allow_insecure = $node_custom_config['allow_insecure'] ?? '0';
  135. $security = $node_custom_config['security']
  136. ?? array_key_exists('enable_xtls', $node_custom_config)
  137. && $node_custom_config['enable_xtls'] === '1' ? 'xtls' : 'tls';
  138. $mux = $node_custom_config['mux'] ?? '';
  139. $transport = $node_custom_config['transport']
  140. ?? array_key_exists('grpc', $node_custom_config)
  141. && $node_custom_config['grpc'] === '1' ? 'grpc' : 'tcp';
  142. $transport_plugin = $node_custom_config['transport_plugin'] ?? '';
  143. $transport_method = $node_custom_config['transport_method'] ?? '';
  144. $servicename = $node_custom_config['servicename'] ?? '';
  145. $path = $node_custom_config['path'] ?? '';
  146. $node = [
  147. 'name' => $node_raw->name,
  148. 'id' => $node_raw->id,
  149. 'type' => 'trojan',
  150. 'address' => $server,
  151. 'host' => $host,
  152. 'port' => (int) $trojan_port,
  153. 'uuid' => $user->uuid,
  154. 'security' => $security,
  155. 'mux' => $mux,
  156. 'transport' => $transport,
  157. 'transport_plugin' => $transport_plugin,
  158. 'transport_method' => $transport_method,
  159. 'allow_insecure' => (int) $allow_insecure,
  160. 'servicename' => $servicename,
  161. 'path' => $path,
  162. 'remark' => $node_raw->info,
  163. ];
  164. break;
  165. default:
  166. $node = [];
  167. break;
  168. }
  169. if ($node === []) {
  170. continue;
  171. }
  172. $nodes[] = $node;
  173. }
  174. return [
  175. 'version' => 2,
  176. 'sub_name' => $_ENV['appName'],
  177. 'user_email' => $user->email,
  178. 'user_name' => $user->user_name,
  179. 'user_class' => $user->class,
  180. 'user_class_expire_date' => $user->class_expire,
  181. 'user_total_traffic' => $user->transfer_enable,
  182. 'user_used_traffic' => $user->u + $user->d,
  183. 'nodes' => $nodes,
  184. ];
  185. }
  186. public static function getClash($user): string
  187. {
  188. $nodes = [];
  189. $clash_config = $_ENV['Clash_Config'];
  190. $clash_group_indexes = $_ENV['Clash_Group_Indexes'];
  191. $clash_group_config = $_ENV['Clash_Group_Config'];
  192. //篩選出用戶能連接的節點
  193. $nodes_raw = Node::where('type', 1)
  194. ->where('node_class', '<=', $user->class)
  195. ->whereIn('node_group', [0, $user->node_group])
  196. ->where(static function ($query): void {
  197. $query->where('node_bandwidth_limit', '=', 0)->orWhereRaw('node_bandwidth < node_bandwidth_limit');
  198. })
  199. ->get();
  200. foreach ($nodes_raw as $node_raw) {
  201. $node_custom_config = json_decode($node_raw->custom_config, true);
  202. //檢查是否配置“前端/订阅中下发的服务器地址”
  203. if (! array_key_exists('server_user', $node_custom_config)) {
  204. $server = $node_raw->server;
  205. } else {
  206. $server = $node_custom_config['server_user'];
  207. }
  208. switch ((int) $node_raw->sort) {
  209. case 0:
  210. $plugin = $node_custom_config['plugin'] ?? '';
  211. $plugin_option = $node_custom_config['plugin_option'] ?? null;
  212. // Clash 特定配置
  213. $udp = $node_custom_config['udp'] ?? true;
  214. $node = [
  215. 'name' => $node_raw->name,
  216. 'type' => 'ss',
  217. 'server' => $server,
  218. 'port' => (int) $user->port,
  219. 'password' => $user->passwd,
  220. 'cipher' => $user->method,
  221. 'udp' => $udp,
  222. 'plugin' => $plugin,
  223. 'plugin-opts' => $plugin_option,
  224. ];
  225. break;
  226. case 11:
  227. $v2_port = $node_custom_config['v2_port'] ?? ($node_custom_config['offset_port_user']
  228. ?? ($node_custom_config['offset_port_node'] ?? 443));
  229. $alter_id = $node_custom_config['alter_id'] ?? '0';
  230. $security = $node_custom_config['security'] ?? 'none';
  231. $encryption = $node_custom_config['encryption'] ?? 'auto';
  232. $network = $node_custom_config['network'] ?? '';
  233. $host = $node_custom_config['host'] ?? '';
  234. $allow_insecure = $node_custom_config['allow_insecure'] ?? false;
  235. $tls = in_array($security, ['tls', 'xtls']);
  236. // Clash 特定配置
  237. $udp = $node_custom_config['udp'] ?? true;
  238. $ws_opts = $node_custom_config['ws-opts'] ?? $node_custom_config['ws_opts'] ?? null;
  239. $h2_opts = $node_custom_config['h2-opts'] ?? $node_custom_config['h2_opts'] ?? null;
  240. $http_opts = $node_custom_config['http-opts'] ?? $node_custom_config['http_opts'] ?? null;
  241. $grpc_opts = $node_custom_config['grpc-opts'] ?? $node_custom_config['grpc_opts'] ?? null;
  242. $node = [
  243. 'name' => $node_raw->name,
  244. 'type' => 'vmess',
  245. 'server' => $server,
  246. 'port' => (int) $v2_port,
  247. 'uuid' => $user->uuid,
  248. 'alterId' => (int) $alter_id,
  249. 'cipher' => $encryption,
  250. 'udp' => $udp,
  251. 'tls' => $tls,
  252. 'skip-cert-verify' => $allow_insecure,
  253. 'servername' => $host,
  254. 'network' => $network,
  255. 'ws-opts' => $ws_opts,
  256. 'h2-opts' => $h2_opts,
  257. 'http-opts' => $http_opts,
  258. 'grpc-opts' => $grpc_opts,
  259. ];
  260. break;
  261. case 14:
  262. $trojan_port = $node_custom_config['trojan_port'] ?? ($node_custom_config['offset_port_user']
  263. ?? ($node_custom_config['offset_port_node'] ?? 443));
  264. $network = $node_custom_config['network']
  265. ?? array_key_exists('grpc', $node_custom_config)
  266. && $node_custom_config['grpc'] === '1' ? 'grpc' : 'tcp';
  267. $host = $node_custom_config['host'] ?? '';
  268. $allow_insecure = $node_custom_config['allow_insecure'] ?? false;
  269. // Clash 特定配置
  270. $udp = $node_custom_config['udp'] ?? true;
  271. $ws_opts = $node_custom_config['ws-opts'] ?? $node_custom_config['ws_opts'] ?? null;
  272. $grpc_opts = $node_custom_config['grpc-opts'] ?? $node_custom_config['grpc_opts'] ?? null;
  273. $node = [
  274. 'name' => $node_raw->name,
  275. 'type' => 'trojan',
  276. 'server' => $server,
  277. 'sni' => $host,
  278. 'port' => (int) $trojan_port,
  279. 'password' => $user->uuid,
  280. 'network' => $network,
  281. 'udp' => $udp,
  282. 'skip-cert-verify' => $allow_insecure,
  283. 'ws-opts' => $ws_opts,
  284. 'grpc-opts' => $grpc_opts,
  285. ];
  286. break;
  287. default:
  288. $node = [];
  289. break;
  290. }
  291. if ($node === []) {
  292. continue;
  293. }
  294. $nodes[] = $node;
  295. foreach ($clash_group_indexes as $index) {
  296. $clash_group_config['proxy-groups'][$index]['proxies'][] = $node_raw->name;
  297. }
  298. }
  299. $clash_nodes = [
  300. 'proxies' => $nodes,
  301. ];
  302. return Yaml::dump(array_merge($clash_config, $clash_nodes, $clash_group_config), 3, 1);
  303. }
  304. // SIP008 SS 订阅
  305. public static function getSIP008($user): array
  306. {
  307. $nodes = [];
  308. //判断是否开启SS订阅
  309. if (! Setting::obtain('enable_ss_sub')) {
  310. return $nodes;
  311. }
  312. //篩選出用戶能連接的節點
  313. $nodes_raw = Node::where('type', 1)
  314. ->where('node_class', '<=', $user->class)
  315. ->whereIn('node_group', [0, $user->node_group])
  316. ->where(static function ($query): void {
  317. $query->where('node_bandwidth_limit', '=', 0)->orWhereRaw('node_bandwidth < node_bandwidth_limit');
  318. })
  319. ->get();
  320. foreach ($nodes_raw as $node_raw) {
  321. $node_custom_config = json_decode($node_raw->custom_config, true);
  322. //檢查是否配置“前端/订阅中下发的服务器地址”
  323. if (! array_key_exists('server_user', $node_custom_config)) {
  324. $server = $node_raw->server;
  325. } else {
  326. $server = $node_custom_config['server_user'];
  327. }
  328. switch ((int) $node_raw->sort) {
  329. case 0:
  330. $plugin = $node_custom_config['plugin'] ?? '';
  331. $plugin_option = $node_custom_config['plugin_option'] ?? '';
  332. $node = [
  333. 'id' => $node_raw->id,
  334. 'remarks' => $node_raw->name,
  335. 'server' => $server,
  336. 'server_port' => (int) $user->port,
  337. 'password' => $user->passwd,
  338. 'method' => $user->method,
  339. 'plugin' => $plugin,
  340. 'plugin_opts' => $plugin_option,
  341. ];
  342. break;
  343. default:
  344. $node = [];
  345. break;
  346. }
  347. if ($node === []) {
  348. continue;
  349. }
  350. $nodes[] = $node;
  351. }
  352. return [
  353. 'version' => 1,
  354. 'servers' => $nodes,
  355. 'bytes_used' => $user->u + $user->d,
  356. 'bytes_remaining' => $user->transfer_enable - $user->u - $user->d,
  357. ];
  358. }
  359. public static function getUniversalSub($user): string
  360. {
  361. $userid = $user->id;
  362. $token = Link::where('userid', $userid)->first();
  363. if ($token === null) {
  364. $token = new Link();
  365. $token->userid = $userid;
  366. $token->token = Tools::genSubToken();
  367. $token->save();
  368. }
  369. return $_ENV['subUrl'] . '/sub/' . $token->token;
  370. }
  371. }