feat: 增加surge的hy2下发、添加clash meta、shadowrocket、stash订阅hy2端口跳跃的下发

This commit is contained in:
xboard 2024-05-14 21:57:36 +08:00
parent 227f50b9d1
commit 5a0e59b103
7 changed files with 107 additions and 74 deletions

View File

@ -44,7 +44,8 @@ class ClientController extends Controller
'ClashX Meta' => '1.3.5', 'ClashX Meta' => '1.3.5',
'Hiddify' => '0.1.0', 'Hiddify' => '0.1.0',
'loon' => '637', 'loon' => '637',
'v2rayN' => '6.31' 'v2rayN' => '6.31',
'surge' => '2398'
]; ];
foreach($minSupportHy2ClinetVersionMap as $client => $minVersion){ foreach($minSupportHy2ClinetVersionMap as $client => $minVersion){
if (stripos($flag, $client) !== false && $this->versionCompare($version, $minVersion)) { if (stripos($flag, $client) !== false && $this->versionCompare($version, $minVersion)) {

View File

@ -24,9 +24,12 @@ class ClashMeta
$user = $this->user; $user = $this->user;
$appName = admin_setting('app_name', 'XBoard'); $appName = admin_setting('app_name', 'XBoard');
$defaultConfig = base_path() . '/resources/rules/default.clash.yaml'; $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)) { if (\File::exists($customConfig)) {
$config = Yaml::parseFile($customConfig); $config = Yaml::parseFile($customConfig);
} elseif(\File::exists($customClashConfig)) {
$config = Yaml::parseFile($customClashConfig);
} else{ } else{
$config = Yaml::parseFile($defaultConfig); $config = Yaml::parseFile($defaultConfig);
} }
@ -310,6 +313,7 @@ class ClashMeta
$array['obfs'] = 'salamander'; $array['obfs'] = 'salamander';
$array['obfs-password'] = $server['server_key']; $array['obfs-password'] = $server['server_key'];
} }
if(isset($server['ports'])) $array['ports'] = $server['ports'];
break; break;
} }

View File

@ -272,6 +272,7 @@ class Shadowrocket
$params["obfsParam"] =$server['server_key']; $params["obfsParam"] =$server['server_key'];
} }
if($server['insecure']) $params['insecure'] = $server['insecure']; if($server['insecure']) $params['insecure'] = $server['insecure'];
if($server['ports']) $params['mport'] = $server['ports'];
$query = http_build_query($params); $query = http_build_query($params);
$uri = "hysteria://{$server['host']}:{$server['port']}?{$query}#{$server['name']}"; $uri = "hysteria://{$server['host']}:{$server['port']}?{$query}#{$server['name']}";
$uri .= "\r\n"; $uri .= "\r\n";
@ -284,6 +285,7 @@ class Shadowrocket
]; ];
if($server['is_obfs']) $params['obfs-password'] = $server['server_key']; if($server['is_obfs']) $params['obfs-password'] = $server['server_key'];
if($server['insecure']) $params['insecure'] = $server['insecure']; if($server['insecure']) $params['insecure'] = $server['insecure'];
if($server['ports']) $params['mport'] = $server['ports'];
$query = http_build_query($params); $query = http_build_query($params);
$uri = "hysteria2://{$password}@{$server['host']}:{$server['port']}?{$query}#{$server['name']}"; $uri = "hysteria2://{$password}@{$server['host']}:{$server['port']}?{$query}#{$server['name']}";
$uri .= "\r\n"; $uri .= "\r\n";

View File

@ -8,6 +8,7 @@ class SingBox
public $flag = 'sing-box,hiddify'; public $flag = 'sing-box,hiddify';
private $servers; private $servers;
private $user; private $user;
private $config;
public function __construct($user, $servers, array $options = null) public function __construct($user, $servers, array $options = null)
{ {
@ -17,13 +18,12 @@ class SingBox
public function handle() public function handle()
{ {
$appName = config('app_name', 'XBoard'); $appName = admin_setting('app_name', 'XBoard');
$config = $this->loadConfig(); $this->config = $this->loadConfig();
$outbounds = $this->buildOutbounds(); $this->buildOutbounds();
$config['outbounds'] = $outbounds;
$user = $this->user; $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('subscription-userinfo', "upload={$user['u']}; download={$user['d']}; total={$user['transfer_enable']}; expire={$user['expired_at']}")
->header('profile-update-interval', '24') ->header('profile-update-interval', '24')
->header('content-disposition', 'attachment;filename*=UTF-8\'\'' . rawurlencode($appName)); ->header('content-disposition', 'attachment;filename*=UTF-8\'\'' . rawurlencode($appName));
@ -40,61 +40,38 @@ class SingBox
protected function buildOutbounds() protected function buildOutbounds()
{ {
$outbounds = []; $outbounds = $this->config['outbounds'];
$proxies = [];
$selector = [
"tag" => "节点选择",
"type" => "selector",
"default" => "自动选择",
"outbounds" => ["自动选择"]
];
$urltest = [
"tag" => "自动选择",
"type" => "urltest",
"outbounds" => []
];
$outbounds[] = &$selector;
foreach ($this->servers as $item) { foreach ($this->servers as $item) {
if ($item['type'] === 'shadowsocks') { if ($item['type'] === 'shadowsocks') {
$ssConfig = $this->buildShadowsocks($this->user['uuid'], $item); $ssConfig = $this->buildShadowsocks($this->user['uuid'], $item);
$outbounds[] = $ssConfig; $proxies[] = $ssConfig;
$selector['outbounds'][] = $item['name'];
$urltest['outbounds'][] = $item['name'];
} }
if ($item['type'] === 'trojan') { if ($item['type'] === 'trojan') {
$trojanConfig = $this->buildTrojan($this->user['uuid'], $item); $trojanConfig = $this->buildTrojan($this->user['uuid'], $item);
$outbounds[] = $trojanConfig; $proxies[] = $trojanConfig;
$selector['outbounds'][] = $item['name'];
$urltest['outbounds'][] = $item['name'];
} }
if ($item['type'] === 'vmess') { if ($item['type'] === 'vmess') {
$vmessConfig = $this->buildVmess($this->user['uuid'], $item); $vmessConfig = $this->buildVmess($this->user['uuid'], $item);
$outbounds[] = $vmessConfig; $proxies[] = $vmessConfig;
$selector['outbounds'][] = $item['name'];
$urltest['outbounds'][] = $item['name'];
} }
if ($item['type'] === 'vless') { if ($item['type'] === 'vless') {
$vlessConfig = $this->buildVless($this->user['uuid'], $item); $vlessConfig = $this->buildVless($this->user['uuid'], $item);
$outbounds[] = $vlessConfig; $proxies[] = $vlessConfig;
$selector['outbounds'][] = $item['name'];
$urltest['outbounds'][] = $item['name'];
} }
if ($item['type'] === 'hysteria') { if ($item['type'] === 'hysteria') {
$hysteriaConfig = $this->buildHysteria($this->user['uuid'], $item, $this->user); $hysteriaConfig = $this->buildHysteria($this->user['uuid'], $item, $this->user);
$outbounds[] = $hysteriaConfig; $proxies[] = $hysteriaConfig;
$selector['outbounds'][] = $item['name']; }
$urltest['outbounds'][] = $item['name']; }
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 = array_merge($outbounds, $proxies);
$outbounds[] = [ "tag" => "block", "type" => "block" ]; $this->config['outbounds'] = $outbounds;
$outbounds[] = [ "tag" => "dns-out", "type" => "dns" ];
$outbounds[] = $urltest;
return $outbounds; return $outbounds;
} }
@ -146,7 +123,8 @@ class SingBox
} }
if ($server['network'] === 'tcp') { if ($server['network'] === 'tcp') {
$tcpSettings = $server['networkSettings']; $tcpSettings = $server['networkSettings'];
if (isset($tcpSettings['header']['type']) && $tcpSettings['header']['type'] == 'http') $array['transport']['type'] = $tcpSettings['header']['type']; 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']['request']['path'][0])) {
$paths = $tcpSettings['header']['request']['path']; $paths = $tcpSettings['header']['request']['path'];
$array['transport']['path'] = $paths[array_rand($paths)]; $array['transport']['path'] = $paths[array_rand($paths)];
@ -160,8 +138,10 @@ class SingBox
$array['transport']['type'] = 'ws'; $array['transport']['type'] = 'ws';
if ($server['networkSettings']) { if ($server['networkSettings']) {
$wsSettings = $server['networkSettings']; $wsSettings = $server['networkSettings'];
if (isset($wsSettings['path']) && !empty($wsSettings['path'])) $array['transport']['path'] = $wsSettings['path']; if (isset($wsSettings['path']) && !empty($wsSettings['path']))
if (isset($wsSettings['headers']['Host']) && !empty($wsSettings['headers']['Host'])) $array['transport']['headers'] = ['Host' => array($wsSettings['headers']['Host'])]; $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']['max_early_data'] = 2048;
$array['transport']['early_data_header_name'] = 'Sec-WebSocket-Protocol'; $array['transport']['early_data_header_name'] = 'Sec-WebSocket-Protocol';
} }
@ -170,7 +150,8 @@ class SingBox
$array['transport']['type'] = 'grpc'; $array['transport']['type'] = 'grpc';
if ($server['networkSettings']) { if ($server['networkSettings']) {
$grpcSettings = $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,15 +197,19 @@ class SingBox
if ($server['network'] === 'tcp') { if ($server['network'] === 'tcp') {
$tcpSettings = $server['network_settings']; $tcpSettings = $server['network_settings'];
if (isset($tcpSettings['header']['type']) && $tcpSettings['header']['type'] == 'http') $array['transport']['type'] = $tcpSettings['header']['type']; if (isset($tcpSettings['header']['type']) && $tcpSettings['header']['type'] == 'http')
if (isset($tcpSettings['header']['request']['path'])) $array['transport']['path'] = $tcpSettings['header']['request']['path']; $array['transport']['type'] = $tcpSettings['header']['type'];
if (isset($tcpSettings['header']['request']['path']))
$array['transport']['path'] = $tcpSettings['header']['request']['path'];
} }
if ($server['network'] === 'ws') { if ($server['network'] === 'ws') {
$array['transport']['type'] = 'ws'; $array['transport']['type'] = 'ws';
if ($server['network_settings']) { if ($server['network_settings']) {
$wsSettings = $server['network_settings']; $wsSettings = $server['network_settings'];
if (isset($wsSettings['path']) && !empty($wsSettings['path'])) $array['transport']['path'] = $wsSettings['path']; if (isset($wsSettings['path']) && !empty($wsSettings['path']))
if (isset($wsSettings['headers']['Host']) && !empty($wsSettings['headers']['Host'])) $array['transport']['headers'] = ['Host' => array($wsSettings['headers']['Host'])]; $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']['max_early_data'] = 2048;
$array['transport']['early_data_header_name'] = 'Sec-WebSocket-Protocol'; $array['transport']['early_data_header_name'] = 'Sec-WebSocket-Protocol';
} }
@ -233,15 +218,18 @@ class SingBox
$array['transport']['type'] = 'grpc'; $array['transport']['type'] = 'grpc';
if ($server['network_settings']) { if ($server['network_settings']) {
$grpcSettings = $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') { if ($server['network'] === 'h2') {
$array['transport']['type'] = 'http'; $array['transport']['type'] = 'http';
if ($server['network_settings']) { if ($server['network_settings']) {
$h2Settings = $server['network_settings']; $h2Settings = $server['network_settings'];
if (isset($h2Settings['host'])) $array['transport']['host'] = array($h2Settings['host']); if (isset($h2Settings['host']))
if (isset($h2Settings['path'])) $array['transport']['path'] = $h2Settings['path']; $array['transport']['host'] = array($h2Settings['host']);
if (isset($h2Settings['path']))
$array['transport']['path'] = $h2Settings['path'];
} }
} }
@ -280,7 +268,8 @@ class SingBox
$array['transport']['max_early_data'] = 2048; $array['transport']['max_early_data'] = 2048;
$array['transport']['early_data_header_name'] = 'Sec-WebSocket-Protocol'; $array['transport']['early_data_header_name'] = 'Sec-WebSocket-Protocol';
} }
}; }
;
return $array; return $array;
} }

View File

@ -24,9 +24,12 @@ class Stash
$appName = admin_setting('app_name', 'XBoard'); $appName = admin_setting('app_name', 'XBoard');
// 暂时使用clash配置文件后续根据Stash更新情况更新 // 暂时使用clash配置文件后续根据Stash更新情况更新
$defaultConfig = base_path() . '/resources/rules/default.clash.yaml'; $defaultConfig = base_path() . '/resources/rules/default.clash.yaml';
$customConfig = base_path() . '/resources/rules/custom.clash.yaml'; $customClashConfig = base_path() . '/resources/rules/custom.clash.yaml';
if (\File::exists($customConfig)) { $customStashConfig = base_path() . '/resources/rules/custom.stash.yaml';
$config = Yaml::parseFile($customConfig); if (\File::exists($customStashConfig)) {
$config = Yaml::parseFile($customStashConfig);
} elseif (\File::exists($customClashConfig)) {
$config = Yaml::parseFile($customClashConfig);
} else { } else {
$config = Yaml::parseFile($defaultConfig); $config = Yaml::parseFile($defaultConfig);
} }
@ -296,6 +299,7 @@ class Stash
$array['type'] = 'hysteria2'; $array['type'] = 'hysteria2';
$array['auth'] = $password; $array['auth'] = $password;
$array['fast-open'] = true; $array['fast-open'] = true;
if(isset($server['ports'])) $array['ports'] = $server['ports'];
break; break;
} }
return $array; return $array;

View File

@ -52,6 +52,12 @@ class Surge
// [Proxy Group] // [Proxy Group]
$proxyGroup .= $item['name'] . ', '; $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'; $defaultConfig = base_path() . '/resources/rules/default.surge.conf';
@ -160,4 +166,27 @@ class Surge
$uri .= "\r\n"; $uri .= "\r\n";
return $uri; 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;
}
} }

View File

@ -208,6 +208,10 @@ class V2rayN
$params = []; $params = [];
if ($server['server_name']) $params['sni'] = $server['server_name']; if ($server['server_name']) $params['sni'] = $server['server_name'];
$params['insecure'] = $server['insecure'] ? 1 : 0; $params['insecure'] = $server['insecure'] ? 1 : 0;
if($server['is_obfs']) {
$params['obfs'] = 'salamander';
$params['obfs-password'] = $server['server_key'];
}
$query = http_build_query($params); $query = http_build_query($params);
if ($server['version'] == 2) { if ($server['version'] == 2) {
$uri = "hysteria2://{$password}@{$server['host']}:{$server['port']}?{$query}#{$name}"; $uri = "hysteria2://{$password}@{$server['host']}:{$server['port']}?{$query}#{$name}";