From 9db84a4a4a38c58e39286f221049a04a256ddd64 Mon Sep 17 00:00:00 2001 From: wyx2685 Date: Sun, 29 Oct 2023 17:45:17 +0900 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=90=8E=E5=8F=B0=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E7=AE=A1=E7=90=86=E9=9D=A2=E6=9D=BF=E5=9C=A8=E7=BA=BF?= =?UTF-8?q?=E8=AE=BE=E5=A4=87=E6=95=B0=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/V1/Admin/UserController.php | 8 + .../V1/Server/UniProxyController.php | 3 + app/Protocols/Singbox.php | 175 ++++++++++++++++++ public/assets/admin/umi.js | 8 +- public/theme/v2board/assets/umi.js | 2 +- resources/rules/default.sing-box.json | 124 +++++++++++++ 6 files changed, 316 insertions(+), 4 deletions(-) create mode 100644 app/Protocols/Singbox.php create mode 100644 resources/rules/default.sing-box.json diff --git a/app/Http/Controllers/V1/Admin/UserController.php b/app/Http/Controllers/V1/Admin/UserController.php index 1081aaf3..cdecf46b 100644 --- a/app/Http/Controllers/V1/Admin/UserController.php +++ b/app/Http/Controllers/V1/Admin/UserController.php @@ -14,6 +14,7 @@ use App\Services\AuthService; use App\Utils\Helper; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\Cache; class UserController extends Controller { @@ -75,6 +76,13 @@ class UserController extends Controller } } $res[$i]['subscribe_url'] = Helper::getSubscribeUrl('/api/v1/client/subscribe?token=' . $res[$i]['token']); + //统计在线设备 + $countalive = 0; + $ips_array = Cache::get('ALIVE_IP_USER_'. $res[$i]['id']); + if ($ips_array) { + $countalive = $ips_array['alive_ip']; + } + $res[$i]['alive_ip'] = $countalive; } return response([ 'data' => $res, diff --git a/app/Http/Controllers/V1/Server/UniProxyController.php b/app/Http/Controllers/V1/Server/UniProxyController.php index 2cce34fe..b12a5e5b 100644 --- a/app/Http/Controllers/V1/Server/UniProxyController.php +++ b/app/Http/Controllers/V1/Server/UniProxyController.php @@ -44,6 +44,9 @@ class UniProxyController extends Controller $users = $users->toArray(); $users = array_map(function ($user) { + if ($user['device_limit'] == null || $user['device_limit'] <= 0) { + return $user; + } $ips_array = Cache::get('ALIVE_IP_USER_'. $user['id']); $count = 0; if ($ips_array) { diff --git a/app/Protocols/Singbox.php b/app/Protocols/Singbox.php new file mode 100644 index 00000000..12fc6f95 --- /dev/null +++ b/app/Protocols/Singbox.php @@ -0,0 +1,175 @@ +user = $user; + $this->servers = $servers; + } + + public function handle() + { + $appName = config('app_name', 'V2Board'); + $config = $this->loadConfig(); + $outbounds = $this->buildOutbounds(); + $config['outbounds'] = $outbounds; + + return json_encode($config); + //return response($config, 200); + } + + protected function loadConfig() + { + $defaultConfig = base_path('resources/rules/default.sing-box.json'); + $customConfig = base_path('resources/rules/custom.sing-box.json'); + $jsonData = file_exists($customConfig) ? file_get_contents($customConfig) : file_get_contents($defaultConfig); + + return json_decode($jsonData, true); + } + + protected function buildOutbounds() + { + $outbounds = []; + + $selector = [ + "tag" => "节点选择", + "type" => "selector", + "default" => "自动选择", + "outbounds" => ["自动选择"] + ]; + + $urltest = [ + "tag" => "自动选择", + "type" => "urltest", + "outbounds" => [] + ]; + + $outbounds[] = &$selector; + + foreach ($this->servers as $item) { + if ($item['type'] === 'vless') { + $vlessConfig = $this->buildVless($this->user['uuid'], $item); + $outbounds[] = $vlessConfig; + $selector['outbounds'][] = $item['name']; + $urltest['outbounds'][] = $item['name']; + } elseif ($item['type'] === 'hysteria') { + $hysteriaConfig = $this->buildHysteria($this->user['uuid'], $item, $this->user); + $outbounds[] = $hysteriaConfig; + $tag = $item['version'] == 2 ? "Hy2" : "Hy"; + $selector['outbounds'][] = "[$tag]{$item['name']}"; + $urltest['outbounds'][] = "[$tag]{$item['name']}"; + } + } + + $outbounds[] = [ "tag" => "direct", "type" => "direct" ]; + $outbounds[] = [ "tag" => "block", "type" => "block" ]; + $outbounds[] = [ "tag" => "dns-out", "type" => "dns" ]; + $outbounds[] = $urltest; + + return $outbounds; + } + + /** + * Vless订阅 + */ + + protected function buildVless($password, $server) + { + $tlsSettings = $server['tls_settings'] ?? []; + $tlsConfig = []; + + if ($server['tls']) { + $tlsConfig['enabled'] = true; + + switch ($server['tls']) { + case 1: + $tlsConfig['insecure'] = (bool) ($tlsSettings['allowInsecure'] ?? false); + $tlsConfig['server_name'] = $tlsSettings['serverName'] ?? null; + break; + + case 2: + $tlsConfig['insecure'] = (bool) ($tlsSettings['allowInsecure'] ?? false); + $tlsConfig['server_name'] = $tlsSettings['server_name'] ?? null; + + if ( + isset($tlsSettings['public_key'], $tlsSettings['short_id']) && + !empty($tlsSettings['server_name']) + ) { + $tlsConfig['reality'] = [ + 'enabled' => true, + 'public_key' => $tlsSettings['public_key'], + 'short_id' => $tlsSettings['short_id'] + ]; + + $fingerprints = ['chrome', 'firefox', 'safari', 'ios', 'edge', 'qq']; + $tlsConfig['utls'] = [ + "enabled" => true, + "fingerprint" => $fingerprints[array_rand($fingerprints)] + ]; + } + break; + } + } + + return [ + "type" => "vless", + "tag" => $server['name'], + "server" => $server['host'], + "server_port" => $server['port'], + "uuid" => $password, + "flow" => $server['flow'], + "packet_encoding" => "xudp", + "tls" => $tlsConfig + ]; + } + + protected function buildHysteria($password, $server, $user) + { + $array = [ + 'server' => $server['host'], + 'server_port' => $server['port'], + //'up_mbps' => $user->speed_limit ? min($server['up_mbps'], $user->speed_limit) : $server['up_mbps'], + //'down_mbps' => $user->speed_limit ? min($server['down_mbps'], $user->speed_limit) : $server['down_mbps'], + 'tls' => [ + 'enabled' => true, + 'insecure' => $server['insecure'] ? true : false, + 'server_name' => $server['server_name'] + ] + ]; + + if ($server['version'] == 1) { + $array['auth_str'] = $password; + $array['tag'] = "[Hy]" . $server['name']; + $array['type'] = 'hysteria'; + $array['up_mbps'] = $user->speed_limit ? min($server['down_mbps'], $user->speed_limit) : $server['down_mbps']; + $array['down_mbps'] = $user->speed_limit ? min($server['up_mbps'], $user->speed_limit) : $server['up_mbps']; + if ($server['is_obfs']) { + $array['obfs'] = $server['server_key']; + } + + $array['disable_mtu_discovery'] = true; + $array['tls']['alpn'] = [ServerHysteria::$alpnMap[$server['alpn']]]; + } elseif ($server['version'] == 2) { + $array['password'] = $password; + $array['tag'] = "[Hy2]" . $server['name']; + $array['type'] = 'hysteria2'; + $array['password'] = $password; + + if (isset($server['obfs'])) { + $array['obfs']['type'] = $server['obfs']; + $array['obfs']['password'] = $server['obfs_password']; + } + } + + return $array; + } +} diff --git a/public/assets/admin/umi.js b/public/assets/admin/umi.js index 57b8f577..976ce403 100644 --- a/public/assets/admin/umi.js +++ b/public/assets/admin/umi.js @@ -70673,12 +70673,14 @@ return e } }, { - title: "\u8bbe\u5907\u6570\u9650\u5236", + title: "\u8bbe\u5907\u6570", dataIndex: "device_limit", key: "device_limit", - sorter: !0, + sorter: (e,t) => e.alive_ip - t.alive_ip, render: (e,t)=>{ - return null !== e ? e : "-" + var deviceCount = t.alive_ip !== null ? t.alive_ip : 0 ; + var deviceLimit = t.device_limit !== null ? t.device_limit : "∞"; + return `${deviceCount} / ${deviceLimit}`; } }, { title: "\u5230\u671f\u65f6\u95f4", diff --git a/public/theme/v2board/assets/umi.js b/public/theme/v2board/assets/umi.js index 144f9930..f9d1341c 100644 --- a/public/theme/v2board/assets/umi.js +++ b/public/theme/v2board/assets/umi.js @@ -30880,7 +30880,7 @@ id: "\u5728\u7ebf\u8bbe\u5907 {alive_ip}/{device_limit}" }, { alive_ip: d.alive_ip, - device_limit: d.device_limit == null ? "-" : d.device_limit + device_limit: d.device_limit == null ? "∞" : d.device_limit })))), y >= 80 && !Object(p["h"])(d.expired_at) && (null === d || void 0 === d ? void 0 : null === (e = d.plan) || void 0 === e ? void 0 : e.reset_price) && l.a.createElement("div", { className: "mb-4" }, l.a.createElement(i["a"], { diff --git a/resources/rules/default.sing-box.json b/resources/rules/default.sing-box.json new file mode 100644 index 00000000..3ab059e9 --- /dev/null +++ b/resources/rules/default.sing-box.json @@ -0,0 +1,124 @@ +{ + "dns": { + "rules": [ + { + "outbound": [ + "any" + ], + "server": "local" + }, + { + "disable_cache": true, + "geosite": [ + "category-ads-all" + ], + "server": "block" + }, + { + "clash_mode": "global", + "server": "remote" + }, + { + "clash_mode": "direct", + "server": "local" + }, + { + "geosite": "cn", + "server": "local" + } + ], + "servers": [ + { + "address": "https://1.1.1.1/dns-query", + "detour": "节点选择", + "tag": "remote" + }, + { + "address": "https://223.5.5.5/dns-query", + "detour": "direct", + "tag": "local" + }, + { + "address": "rcode://success", + "tag": "block" + } + ], + "strategy": "prefer_ipv4" + }, + "experimental": { + "clash_api": { + "external_controller": "127.0.0.1:9090", + "secret": "", + "store_selected": true + } + }, + "inbounds": [ + { + "auto_route": true, + "domain_strategy": "prefer_ipv4", + "endpoint_independent_nat": true, + "inet4_address": "172.19.0.1/30", + "inet6_address": "2001:0470:f9da:fdfa::1/64", + "mtu": 9000, + "sniff": true, + "sniff_override_destination": true, + "stack": "system", + "strict_route": true, + "type": "tun" + }, + { + "domain_strategy": "prefer_ipv4", + "listen": "127.0.0.1", + "listen_port": 2333, + "sniff": true, + "sniff_override_destination": true, + "tag": "socks-in", + "type": "socks", + "users": [] + }, + { + "domain_strategy": "prefer_ipv4", + "listen": "127.0.0.1", + "listen_port": 2334, + "sniff": true, + "sniff_override_destination": true, + "tag": "mixed-in", + "type": "mixed", + "users": [] + } + ], + "outbounds": [ + ], + "route": { + "auto_detect_interface": true, + "rules": [ + { + "geosite": "category-ads-all", + "outbound": "block" + }, + { + "outbound": "dns-out", + "protocol": "dns" + }, + { + "clash_mode": "direct", + "outbound": "direct" + }, + { + "clash_mode": "global", + "outbound": "节点选择" + }, + { + "geoip": [ + "cn", + "private" + ], + "outbound": "direct" + }, + { + "geosite": "cn", + "outbound": "direct" + } + ] + } +}