diff --git a/app/Http/Controllers/V1/Client/ClientController.php b/app/Http/Controllers/V1/Client/ClientController.php index fa01243..f0d16c8 100644 --- a/app/Http/Controllers/V1/Client/ClientController.php +++ b/app/Http/Controllers/V1/Client/ClientController.php @@ -44,7 +44,8 @@ class ClientController extends Controller 'ClashX Meta' => '1.3.5', 'Hiddify' => '0.1.0', 'loon' => '637', - 'v2rayN' => '6.31' + 'v2rayN' => '6.31', + 'surge' => '2398' ]; foreach($minSupportHy2ClinetVersionMap as $client => $minVersion){ if (stripos($flag, $client) !== false && $this->versionCompare($version, $minVersion)) { diff --git a/app/Protocols/ClashMeta.php b/app/Protocols/ClashMeta.php index a560f30..57fac82 100644 --- a/app/Protocols/ClashMeta.php +++ b/app/Protocols/ClashMeta.php @@ -24,10 +24,13 @@ class ClashMeta $user = $this->user; $appName = admin_setting('app_name', 'XBoard'); $defaultConfig = base_path() . '/resources/rules/default.clash.yaml'; - $customConfig = base_path() . '/resources/rules/custom.clash.yaml'; + $customClashConfig = base_path() . '/resources/rules/custom.clash.yaml'; + $customConfig = base_path() . '/resources/rules/custom.clashmeta.yaml'; if (\File::exists($customConfig)) { $config = Yaml::parseFile($customConfig); - } else { + } elseif(\File::exists($customClashConfig)) { + $config = Yaml::parseFile($customClashConfig); + } else{ $config = Yaml::parseFile($defaultConfig); } $proxy = []; @@ -310,6 +313,7 @@ class ClashMeta $array['obfs'] = 'salamander'; $array['obfs-password'] = $server['server_key']; } + if(isset($server['ports'])) $array['ports'] = $server['ports']; break; } diff --git a/app/Protocols/Shadowrocket.php b/app/Protocols/Shadowrocket.php index 8be81f1..ecc7ff2 100644 --- a/app/Protocols/Shadowrocket.php +++ b/app/Protocols/Shadowrocket.php @@ -272,6 +272,7 @@ class Shadowrocket $params["obfsParam"] =$server['server_key']; } if($server['insecure']) $params['insecure'] = $server['insecure']; + if($server['ports']) $params['mport'] = $server['ports']; $query = http_build_query($params); $uri = "hysteria://{$server['host']}:{$server['port']}?{$query}#{$server['name']}"; $uri .= "\r\n"; @@ -284,6 +285,7 @@ class Shadowrocket ]; if($server['is_obfs']) $params['obfs-password'] = $server['server_key']; if($server['insecure']) $params['insecure'] = $server['insecure']; + if($server['ports']) $params['mport'] = $server['ports']; $query = http_build_query($params); $uri = "hysteria2://{$password}@{$server['host']}:{$server['port']}?{$query}#{$server['name']}"; $uri .= "\r\n"; @@ -291,4 +293,4 @@ class Shadowrocket } return $uri; } -} +} \ No newline at end of file diff --git a/app/Protocols/SingBox.php b/app/Protocols/SingBox.php index 55d8e6d..fb7304a 100644 --- a/app/Protocols/SingBox.php +++ b/app/Protocols/SingBox.php @@ -8,6 +8,7 @@ class SingBox public $flag = 'sing-box,hiddify'; private $servers; private $user; + private $config; public function __construct($user, $servers, array $options = null) { @@ -17,13 +18,12 @@ class SingBox public function handle() { - $appName = config('app_name', 'XBoard'); - $config = $this->loadConfig(); - $outbounds = $this->buildOutbounds(); - $config['outbounds'] = $outbounds; + $appName = admin_setting('app_name', 'XBoard'); + $this->config = $this->loadConfig(); + $this->buildOutbounds(); $user = $this->user; - return response($config, 200) + return response($this->config, 200) ->header('subscription-userinfo', "upload={$user['u']}; download={$user['d']}; total={$user['transfer_enable']}; expire={$user['expired_at']}") ->header('profile-update-interval', '24') ->header('content-disposition', 'attachment;filename*=UTF-8\'\'' . rawurlencode($appName)); @@ -40,61 +40,38 @@ class SingBox protected function buildOutbounds() { - $outbounds = []; - - $selector = [ - "tag" => "节点选择", - "type" => "selector", - "default" => "自动选择", - "outbounds" => ["自动选择"] - ]; - - $urltest = [ - "tag" => "自动选择", - "type" => "urltest", - "outbounds" => [] - ]; - - $outbounds[] = &$selector; - + $outbounds = $this->config['outbounds']; + $proxies = []; foreach ($this->servers as $item) { if ($item['type'] === 'shadowsocks') { $ssConfig = $this->buildShadowsocks($this->user['uuid'], $item); - $outbounds[] = $ssConfig; - $selector['outbounds'][] = $item['name']; - $urltest['outbounds'][] = $item['name']; + $proxies[] = $ssConfig; } if ($item['type'] === 'trojan') { $trojanConfig = $this->buildTrojan($this->user['uuid'], $item); - $outbounds[] = $trojanConfig; - $selector['outbounds'][] = $item['name']; - $urltest['outbounds'][] = $item['name']; + $proxies[] = $trojanConfig; } if ($item['type'] === 'vmess') { $vmessConfig = $this->buildVmess($this->user['uuid'], $item); - $outbounds[] = $vmessConfig; - $selector['outbounds'][] = $item['name']; - $urltest['outbounds'][] = $item['name']; + $proxies[] = $vmessConfig; } if ($item['type'] === 'vless') { $vlessConfig = $this->buildVless($this->user['uuid'], $item); - $outbounds[] = $vlessConfig; - $selector['outbounds'][] = $item['name']; - $urltest['outbounds'][] = $item['name']; + $proxies[] = $vlessConfig; } if ($item['type'] === 'hysteria') { $hysteriaConfig = $this->buildHysteria($this->user['uuid'], $item, $this->user); - $outbounds[] = $hysteriaConfig; - $selector['outbounds'][] = $item['name']; - $urltest['outbounds'][] = $item['name']; + $proxies[] = $hysteriaConfig; + } + } + foreach ($outbounds as &$outbound) { + if (in_array($outbound['type'], ['urltest', 'selector'])) { + array_push($outbound['outbounds'], ...array_column($proxies, 'tag')); } } - $outbounds[] = [ "tag" => "direct", "type" => "direct" ]; - $outbounds[] = [ "tag" => "block", "type" => "block" ]; - $outbounds[] = [ "tag" => "dns-out", "type" => "dns" ]; - $outbounds[] = $urltest; - + $outbounds = array_merge($outbounds, $proxies); + $this->config['outbounds'] = $outbounds; return $outbounds; } @@ -132,7 +109,7 @@ class SingBox $array['uuid'] = $uuid; $array['security'] = 'auto'; $array['alter_id'] = 0; - $array['transport']= []; + $array['transport'] = []; if ($server['tls']) { $tlsConfig = []; @@ -146,31 +123,35 @@ class SingBox } if ($server['network'] === 'tcp') { $tcpSettings = $server['networkSettings']; - if (isset($tcpSettings['header']['type']) && $tcpSettings['header']['type'] == 'http') $array['transport']['type'] = $tcpSettings['header']['type']; - if (isset($tcpSettings['header']['request']['path'][0])){ + if (isset($tcpSettings['header']['type']) && $tcpSettings['header']['type'] == 'http') + $array['transport']['type'] = $tcpSettings['header']['type']; + if (isset($tcpSettings['header']['request']['path'][0])) { $paths = $tcpSettings['header']['request']['path']; $array['transport']['path'] = $paths[array_rand($paths)]; } - if (isset($tcpSettings['header']['request']['headers']['Host'][0])){ + if (isset($tcpSettings['header']['request']['headers']['Host'][0])) { $hosts = $tcpSettings['header']['request']['headers']['Host']; $array['transport']['host'] = $hosts; } } if ($server['network'] === 'ws') { - $array['transport']['type'] ='ws'; + $array['transport']['type'] = 'ws'; if ($server['networkSettings']) { $wsSettings = $server['networkSettings']; - if (isset($wsSettings['path']) && !empty($wsSettings['path'])) $array['transport']['path'] = $wsSettings['path']; - if (isset($wsSettings['headers']['Host']) && !empty($wsSettings['headers']['Host'])) $array['transport']['headers'] = ['Host' => array($wsSettings['headers']['Host'])]; + if (isset($wsSettings['path']) && !empty($wsSettings['path'])) + $array['transport']['path'] = $wsSettings['path']; + if (isset($wsSettings['headers']['Host']) && !empty($wsSettings['headers']['Host'])) + $array['transport']['headers'] = ['Host' => array($wsSettings['headers']['Host'])]; $array['transport']['max_early_data'] = 2048; $array['transport']['early_data_header_name'] = 'Sec-WebSocket-Protocol'; } } if ($server['network'] === 'grpc') { - $array['transport']['type'] ='grpc'; + $array['transport']['type'] = 'grpc'; if ($server['networkSettings']) { $grpcSettings = $server['networkSettings']; - if (isset($grpcSettings['serviceName'])) $array['transport']['service_name'] = $grpcSettings['serviceName']; + if (isset($grpcSettings['serviceName'])) + $array['transport']['service_name'] = $grpcSettings['serviceName']; } } @@ -216,39 +197,46 @@ class SingBox if ($server['network'] === 'tcp') { $tcpSettings = $server['network_settings']; - if (isset($tcpSettings['header']['type']) && $tcpSettings['header']['type'] == 'http') $array['transport']['type'] = $tcpSettings['header']['type']; - if (isset($tcpSettings['header']['request']['path'])) $array['transport']['path'] = $tcpSettings['header']['request']['path']; + if (isset($tcpSettings['header']['type']) && $tcpSettings['header']['type'] == 'http') + $array['transport']['type'] = $tcpSettings['header']['type']; + if (isset($tcpSettings['header']['request']['path'])) + $array['transport']['path'] = $tcpSettings['header']['request']['path']; } if ($server['network'] === 'ws') { - $array['transport']['type'] ='ws'; + $array['transport']['type'] = 'ws'; if ($server['network_settings']) { $wsSettings = $server['network_settings']; - if (isset($wsSettings['path']) && !empty($wsSettings['path'])) $array['transport']['path'] = $wsSettings['path']; - if (isset($wsSettings['headers']['Host']) && !empty($wsSettings['headers']['Host'])) $array['transport']['headers'] = ['Host' => array($wsSettings['headers']['Host'])]; + if (isset($wsSettings['path']) && !empty($wsSettings['path'])) + $array['transport']['path'] = $wsSettings['path']; + if (isset($wsSettings['headers']['Host']) && !empty($wsSettings['headers']['Host'])) + $array['transport']['headers'] = ['Host' => array($wsSettings['headers']['Host'])]; $array['transport']['max_early_data'] = 2048; $array['transport']['early_data_header_name'] = 'Sec-WebSocket-Protocol'; } } if ($server['network'] === 'grpc') { - $array['transport']['type'] ='grpc'; + $array['transport']['type'] = 'grpc'; if ($server['network_settings']) { $grpcSettings = $server['network_settings']; - if (isset($grpcSettings['serviceName'])) $array['transport']['service_name'] = $grpcSettings['serviceName']; + if (isset($grpcSettings['serviceName'])) + $array['transport']['service_name'] = $grpcSettings['serviceName']; } } if ($server['network'] === 'h2') { $array['transport']['type'] = 'http'; if ($server['network_settings']) { $h2Settings = $server['network_settings']; - if (isset($h2Settings['host'])) $array['transport']['host'] = array($h2Settings['host']); - if (isset($h2Settings['path'])) $array['transport']['path'] = $h2Settings['path']; + if (isset($h2Settings['host'])) + $array['transport']['host'] = array($h2Settings['host']); + if (isset($h2Settings['path'])) + $array['transport']['path'] = $h2Settings['path']; } } return $array; } - protected function buildTrojan($password, $server) + protected function buildTrojan($password, $server) { $array = []; $array['tag'] = $server['name']; @@ -263,24 +251,25 @@ class SingBox 'server_name' => $server['server_name'] ]; - if(isset($server['network']) && in_array($server['network'], ["grpc", "ws"])){ + if (isset($server['network']) && in_array($server['network'], ["grpc", "ws"])) { $array['transport']['type'] = $server['network']; // grpc配置 - if($server['network'] === "grpc" && isset($server['network_settings']['serviceName'])) { + if ($server['network'] === "grpc" && isset($server['network_settings']['serviceName'])) { $array['transport']['service_name'] = $server['network_settings']['serviceName']; } // ws配置 - if($server['network'] === "ws") { - if(isset($server['network_settings']['path'])) { + if ($server['network'] === "ws") { + if (isset($server['network_settings']['path'])) { $array['transport']['path'] = $server['network_settings']['path']; } - if(isset($server['network_settings']['headers']['Host'])){ + if (isset($server['network_settings']['headers']['Host'])) { $array['transport']['headers'] = ['Host' => array($server['network_settings']['headers']['Host'])]; } $array['transport']['max_early_data'] = 2048; $array['transport']['early_data_header_name'] = 'Sec-WebSocket-Protocol'; } - }; + } + ; return $array; } diff --git a/app/Protocols/Stash.php b/app/Protocols/Stash.php index a2b0ea6..aba157e 100644 --- a/app/Protocols/Stash.php +++ b/app/Protocols/Stash.php @@ -24,9 +24,12 @@ class Stash $appName = admin_setting('app_name', 'XBoard'); // 暂时使用clash配置文件,后续根据Stash更新情况更新 $defaultConfig = base_path() . '/resources/rules/default.clash.yaml'; - $customConfig = base_path() . '/resources/rules/custom.clash.yaml'; - if (\File::exists($customConfig)) { - $config = Yaml::parseFile($customConfig); + $customClashConfig = base_path() . '/resources/rules/custom.clash.yaml'; + $customStashConfig = base_path() . '/resources/rules/custom.stash.yaml'; + if (\File::exists($customStashConfig)) { + $config = Yaml::parseFile($customStashConfig); + } elseif (\File::exists($customClashConfig)) { + $config = Yaml::parseFile($customClashConfig); } else { $config = Yaml::parseFile($defaultConfig); } @@ -296,6 +299,7 @@ class Stash $array['type'] = 'hysteria2'; $array['auth'] = $password; $array['fast-open'] = true; + if(isset($server['ports'])) $array['ports'] = $server['ports']; break; } return $array; diff --git a/app/Protocols/Surge.php b/app/Protocols/Surge.php index 587e5ce..8e74029 100644 --- a/app/Protocols/Surge.php +++ b/app/Protocols/Surge.php @@ -52,6 +52,12 @@ class Surge // [Proxy Group] $proxyGroup .= $item['name'] . ', '; } + if ($item['type'] === 'hysteria') { + // [Proxy] + $proxies .= self::buildHysteria($user['uuid'], $item); + // [Proxy Group] + $proxyGroup .= $item['name'] . ', '; + } } $defaultConfig = base_path() . '/resources/rules/default.surge.conf'; @@ -160,4 +166,27 @@ class Surge $uri .= "\r\n"; return $uri; } + + //参考文档: https://manual.nssurge.com/policy/proxy.html + public static function buildHysteria($password, $server) + { + if($server['version'] != 2) return ''; + $config = [ + "{$server['name']}=hysteria2", + "{$server['host']}", + "{$server['port']}", + "password={$password}", + "download-bandwidth={$server['up_mbps']}", + $server['server_name'] ? "sni={$server['server_name']}" : "", + // 'tfo=true', + 'udp-relay=true' + ]; + if ($server['insecure']) { + $config[] = $server['insecure'] ? 'skip-cert-verify=true' : 'skip-cert-verify=false'; + } + $config = array_filter($config); + $uri = implode(',', $config); + $uri .= "\r\n"; + return $uri; + } } diff --git a/app/Protocols/V2rayN.php b/app/Protocols/V2rayN.php index 4220b98..e498e48 100644 --- a/app/Protocols/V2rayN.php +++ b/app/Protocols/V2rayN.php @@ -208,6 +208,10 @@ class V2rayN $params = []; if ($server['server_name']) $params['sni'] = $server['server_name']; $params['insecure'] = $server['insecure'] ? 1 : 0; + if($server['is_obfs']) { + $params['obfs'] = 'salamander'; + $params['obfs-password'] = $server['server_key']; + } $query = http_build_query($params); if ($server['version'] == 2) { $uri = "hysteria2://{$password}@{$server['host']}:{$server['port']}?{$query}#{$name}";