123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278 |
- <?php
- declare(strict_types=1);
- namespace App\Controllers;
- use App\Models\Link;
- use App\Models\Node;
- use App\Models\UserSubscribeLog;
- use Slim\Http\Response;
- use Slim\Http\ServerRequest;
- use function array_key_exists;
- use function base64_encode;
- use function json_decode;
- use function json_encode;
- /**
- * LinkController
- */
- final class LinkController extends BaseController
- {
- public static function getContent(ServerRequest $request, Response $response, array $args)
- {
- if (! $_ENV['Subscribe']) {
- return $response->withJson([
- 'ret' => 0,
- ]);
- }
- $token = $args['token'];
- $params = $request->getQueryParams();
- $Elink = Link::where('token', $token)->first();
- if ($Elink === null) {
- return $response->withJson([
- 'ret' => 0,
- ]);
- }
- $user = $Elink->getUser();
- if ($user === null) {
- return $response->withJson([
- 'ret' => 0,
- ]);
- }
- if ((int) $user->is_banned === 1) {
- return $response->withJson([
- 'ret' => 0,
- ]);
- }
- $sub_type = '';
- $sub_info = [];
- if (isset($params['clash']) && $params['clash'] === '1') {
- $sub_type = 'clash';
- $sub_info = SubController::getClash($user);
- }
- if (isset($params['sip002']) && $params['sip002'] === '1') {
- $sub_type = 'sip002';
- $sub_info = self::getSIP002($user);
- }
- if (isset($params['ss']) && $params['ss'] === '1') {
- $sub_type = 'ss';
- $sub_info = self::getSS($user);
- }
- if (isset($params['v2ray']) && $params['v2ray'] === '1') {
- $sub_type = 'v2ray';
- $sub_info = self::getV2Ray($user);
- }
- if (isset($params['trojan']) && $params['trojan'] === '1') {
- $sub_type = 'trojan';
- $sub_info = self::getTrojan($user);
- }
- if (isset($params['sub'])) {
- switch ($params['sub']) {
- case '3':
- $sub_type = 'v2ray';
- $sub_info = self::getV2Ray($user);
- break;
- case '4':
- $sub_type = 'trojan';
- $sub_info = self::getTrojan($user);
- break;
- default:
- $sub_type = 'ss';
- $sub_info = self::getSS($user);
- break;
- }
- }
- // 记录订阅日志
- if ($_ENV['subscribeLog'] === true) {
- UserSubscribeLog::addSubscribeLog($user, $sub_type, $request->getHeaderLine('User-Agent'));
- }
- $sub_details = ' upload=' . $user->u
- . '; download=' . $user->d
- . '; total=' . $user->transfer_enable
- . '; expire=' . strtotime($user->class_expire);
- return $response->withHeader('Subscription-Userinfo', $sub_details)->write(
- $sub_info
- );
- }
- // 传统 SS 订阅
- public static function getSS($user): string
- {
- $links = '';
- //篩選出用戶能連接的節點
- $nodes_raw = Node::where('type', 1)
- ->where('node_class', '<=', $user->class)
- ->whereIn('node_group', [0, $user->node_group])
- ->where(static function ($query): void {
- $query->where('node_bandwidth_limit', '=', 0)->orWhereRaw('node_bandwidth < node_bandwidth_limit');
- })
- ->get();
- foreach ($nodes_raw as $node_raw) {
- $node_custom_config = json_decode($node_raw->custom_config, true);
- //檢查是否配置“前端/订阅中下发的服务器地址”
- if (! array_key_exists('server_user', $node_custom_config)) {
- $server = $node_raw->server;
- } else {
- $server = $node_custom_config['server_user'];
- }
- if ((int) $node_raw->sort === 0) {
- $links .= base64_encode($user->method . ':' . $user->passwd . '@' . $server . ':' . $user->port) . '#' .
- $node_raw->name . PHP_EOL;
- }
- }
- return $links;
- }
- // SIP002 SS 订阅
- public static function getSIP002($user): string
- {
- $links = '';
- //篩選出用戶能連接的節點
- $nodes_raw = Node::where('type', 1)
- ->where('node_class', '<=', $user->class)
- ->whereIn('node_group', [0, $user->node_group])
- ->where(static function ($query): void {
- $query->where('node_bandwidth_limit', '=', 0)->orWhereRaw('node_bandwidth < node_bandwidth_limit');
- })
- ->get();
- foreach ($nodes_raw as $node_raw) {
- $node_custom_config = json_decode($node_raw->custom_config, true);
- //檢查是否配置“前端/订阅中下发的服务器地址”
- if (! array_key_exists('server_user', $node_custom_config)) {
- $server = $node_raw->server;
- } else {
- $server = $node_custom_config['server_user'];
- }
- if ((int) $node_raw->sort === 0) {
- $plugin = $node_custom_config['plugin'] ?? '';
- $plugin_option = $node_custom_config['plugin_option'] ?? '';
- $links .= $user->method . ':' . $user->passwd . '@' . $server . ':' .
- $user->port . '/?plugin=' . $plugin . '&' . $plugin_option . '#' .
- $node_raw->name . PHP_EOL;
- }
- }
- return $links;
- }
- public static function getV2Ray($user): string
- {
- $links = '';
- //篩選出用戶能連接的節點
- $nodes_raw = Node::where('type', 1)
- ->where('node_class', '<=', $user->class)
- ->whereIn('node_group', [0, $user->node_group])
- ->where(static function ($query): void {
- $query->where('node_bandwidth_limit', '=', 0)->orWhereRaw('node_bandwidth < node_bandwidth_limit');
- })
- ->get();
- foreach ($nodes_raw as $node_raw) {
- $node_custom_config = json_decode($node_raw->custom_config, true);
- //檢查是否配置“前端/订阅中下发的服务器地址”
- if (! array_key_exists('server_user', $node_custom_config)) {
- $server = $node_raw->server;
- } else {
- $server = $node_custom_config['server_user'];
- }
- if ((int) $node_raw->sort === 11) {
- $v2_port = $node_custom_config['v2_port'] ?? ($node_custom_config['offset_port_user'] ?? ($node_custom_config['offset_port_node'] ?? 443));
- //默認值有問題的請懂 V2 怎麽用的人來改一改。
- $alter_id = $node_custom_config['alter_id'] ?? '0';
- $security = $node_custom_config['security'] ?? 'none';
- $network = $node_custom_config['network'] ?? '';
- $header = $node_custom_config['header'] ?? ['type' => 'none'];
- $header_type = $header['type'] ?? '';
- $host = $node_custom_config['host'] ?? '';
- $path = $node_custom_config['path'] ?? '/';
- $v2rayn_array = [
- 'v' => '2',
- 'ps' => $node_raw->name,
- 'add' => $server,
- 'port' => $v2_port,
- 'id' => $user->uuid,
- 'aid' => $alter_id,
- 'net' => $network,
- 'type' => $header_type,
- 'host' => $host,
- 'path' => $path,
- 'tls' => $security,
- ];
- $links .= 'vmess://' . base64_encode(json_encode($v2rayn_array)) . PHP_EOL;
- }
- }
- return $links;
- }
- public static function getTrojan($user): string
- {
- $links = '';
- //篩選出用戶能連接的節點
- $nodes_raw = Node::where('type', 1)
- ->where('node_class', '<=', $user->class)
- ->whereIn('node_group', [0, $user->node_group])
- ->where(static function ($query): void {
- $query->where('node_bandwidth_limit', '=', 0)->orWhereRaw('node_bandwidth < node_bandwidth_limit');
- })
- ->get();
- foreach ($nodes_raw as $node_raw) {
- $node_custom_config = json_decode($node_raw->custom_config, true);
- //檢查是否配置“前端/订阅中下发的服务器地址”
- if (! array_key_exists('server_user', $node_custom_config)) {
- $server = $node_raw->server;
- } else {
- $server = $node_custom_config['server_user'];
- }
- if ((int) $node_raw->sort === 14) {
- $trojan_port = $node_custom_config['trojan_port'] ?? ($node_custom_config['offset_port_user'] ?? ($node_custom_config['offset_port_node'] ?? 443));
- $host = $node_custom_config['host'] ?? '';
- $allow_insecure = $node_custom_config['allow_insecure'] ?? '0';
- $security = $node_custom_config['security'] ?? array_key_exists('enable_xtls', $node_custom_config) && $node_custom_config['enable_xtls'] === '1' ? 'xtls' : 'tls';
- $mux = $node_custom_config['mux'] ?? '';
- $transport = $node_custom_config['transport'] ?? array_key_exists('grpc', $node_custom_config) && $node_custom_config['grpc'] === '1' ? 'grpc' : 'tcp';
- $transport_plugin = $node_custom_config['transport_plugin'] ?? '';
- $transport_method = $node_custom_config['transport_method'] ?? '';
- $servicename = $node_custom_config['servicename'] ?? '';
- $path = $node_custom_config['path'] ?? '';
- $links .= 'trojan://' . $user->uuid . '@' . $server . ':' . $trojan_port . '?peer=' . $host . '&sni=' . $host .
- '&obfs=' . $transport_plugin . '&path=' . $path . '&mux=' . $mux . '&allowInsecure=' . $allow_insecure .
- '&obfsParam=' . $transport_method . '&type=' . $transport . '&security=' . $security . '&serviceName=' . $servicename . '#' .
- $node_raw->name . PHP_EOL;
- }
- }
- return $links;
- }
- public static function getTraditionalSub($user): string
- {
- $userid = $user->id;
- $token = Link::where('userid', $userid)->first();
- return $_ENV['subUrl'] . '/link/' . $token->token;
- }
- }
|