get()->append([ 'last_check_at', 'last_push_at', 'online', 'is_online', 'available_status', 'cache_key', 'load_status', 'metrics', 'online_conn' ]); } /** * 获取指定用户可用的服务器列表 * @param User $user * @return array */ public static function getAvailableServers(User $user): array { $servers = Server::whereJsonContains('group_ids', (string) $user->group_id) ->where('show', true) ->orderBy('sort', 'ASC') ->get() ->append(['last_check_at', 'last_push_at', 'online', 'is_online', 'available_status', 'cache_key', 'server_key']); $servers = collect($servers)->map(function ($server) use ($user) { // 判断动态端口 if (str_contains($server->port, '-')) { $port = $server->port; $server->port = (int) Helper::randomPort($port); $server->ports = $port; } else { $server->port = (int) $server->port; } $server->password = $server->generateServerPassword($user); $server->rate = $server->getCurrentRate(); return $server; })->toArray(); return $servers; } /** * 根据权限组获取可用的用户列表 * @param array $groupIds * @return Collection */ public static function getAvailableUsers(Server $node) { $users = User::toBase() ->whereIn('group_id', $node->group_ids) ->whereRaw('u + d < transfer_enable') ->where(function ($query) { $query->where('expired_at', '>=', time()) ->orWhere('expired_at', NULL); }) ->where('banned', 0) ->select([ 'id', 'uuid', 'speed_limit', 'device_limit' ]) ->get(); return HookManager::filter('server.users.get', $users, $node); } // 获取路由规则 public static function getRoutes(array $routeIds) { $routes = ServerRoute::select(['id', 'match', 'action', 'action_value'])->whereIn('id', $routeIds)->get(); return $routes; } /** * Build node config data */ public static function buildNodeConfig(Server $node): array { $nodeType = $node->type; $protocolSettings = $node->protocol_settings; $serverPort = $node->server_port; $host = $node->host; $baseConfig = [ 'protocol' => $nodeType, 'listen_ip' => '0.0.0.0', 'server_port' => (int) $serverPort, 'network' => data_get($protocolSettings, 'network'), 'networkSettings' => data_get($protocolSettings, 'network_settings') ?: null, ]; $response = match ($nodeType) { 'shadowsocks' => [ ...$baseConfig, 'cipher' => $protocolSettings['cipher'], 'plugin' => $protocolSettings['plugin'], 'plugin_opts' => $protocolSettings['plugin_opts'], 'server_key' => match ($protocolSettings['cipher']) { '2022-blake3-aes-128-gcm' => Helper::getServerKey($node->created_at, 16), '2022-blake3-aes-256-gcm' => Helper::getServerKey($node->created_at, 32), default => null, }, ], 'vmess' => [ ...$baseConfig, 'tls' => (int) $protocolSettings['tls'], ], 'trojan' => [ ...$baseConfig, 'host' => $host, 'server_name' => $protocolSettings['server_name'], ], 'vless' => [ ...$baseConfig, 'tls' => (int) $protocolSettings['tls'], 'flow' => $protocolSettings['flow'], 'tls_settings' => match ((int) $protocolSettings['tls']) { 2 => $protocolSettings['reality_settings'], default => $protocolSettings['tls_settings'], }, ], 'hysteria' => [ ...$baseConfig, 'server_port' => (int) $serverPort, 'version' => (int) $protocolSettings['version'], 'host' => $host, 'server_name' => $protocolSettings['tls']['server_name'], 'up_mbps' => (int) $protocolSettings['bandwidth']['up'], 'down_mbps' => (int) $protocolSettings['bandwidth']['down'], ...match ((int) $protocolSettings['version']) { 1 => ['obfs' => $protocolSettings['obfs']['password'] ?? null], 2 => [ 'obfs' => $protocolSettings['obfs']['open'] ? $protocolSettings['obfs']['type'] : null, 'obfs-password' => $protocolSettings['obfs']['password'] ?? null, ], default => [], }, ], 'tuic' => [ ...$baseConfig, 'version' => (int) $protocolSettings['version'], 'server_port' => (int) $serverPort, 'server_name' => $protocolSettings['tls']['server_name'], 'congestion_control' => $protocolSettings['congestion_control'], 'tls_settings' => data_get($protocolSettings, 'tls_settings'), 'auth_timeout' => '3s', 'zero_rtt_handshake' => false, 'heartbeat' => '3s', ], 'anytls' => [ ...$baseConfig, 'server_port' => (int) $serverPort, 'server_name' => $protocolSettings['tls']['server_name'], 'padding_scheme' => $protocolSettings['padding_scheme'], ], 'socks' => [ ...$baseConfig, 'server_port' => (int) $serverPort, ], 'naive' => [ ...$baseConfig, 'server_port' => (int) $serverPort, 'tls' => (int) $protocolSettings['tls'], 'tls_settings' => $protocolSettings['tls_settings'], ], 'http' => [ ...$baseConfig, 'server_port' => (int) $serverPort, 'tls' => (int) $protocolSettings['tls'], 'tls_settings' => $protocolSettings['tls_settings'], ], 'mieru' => [ ...$baseConfig, 'server_port' => (string) $serverPort, 'protocol' => (int) $protocolSettings['protocol'], ], default => [], }; if (!empty($node['route_ids'])) { $response['routes'] = self::getRoutes($node['route_ids']); } if (!empty($node['custom_outbounds'])) { $response['custom_outbounds'] = $node['custom_outbounds']; } if (!empty($node['custom_routes'])) { $response['custom_routes'] = $node['custom_routes']; } if (!empty($node['cert_config'])) { $response['cert_config'] = $node['cert_config']; } return $response; } /** * 根据协议类型和标识获取服务器 * @param int $serverId * @param string $serverType * @return Server|null */ public static function getServer($serverId, ?string $serverType) { return Server::query() ->when($serverType, function ($query) use ($serverType) { $query->where('type', Server::normalizeType($serverType)); }) ->where(function ($query) use ($serverId) { $query->where('code', $serverId) ->orWhere('id', $serverId); }) ->orderByRaw('CASE WHEN code = ? THEN 0 ELSE 1 END', [$serverId]) ->first(); } }