mirror of
https://github.com/v2board/v2board.git
synced 2025-03-14 06:34:43 +08:00
增加后台用户管理面板在线设备数显示
This commit is contained in:
parent
692ad4cf54
commit
9db84a4a4a
@ -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,
|
||||
|
@ -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) {
|
||||
|
175
app/Protocols/Singbox.php
Normal file
175
app/Protocols/Singbox.php
Normal file
@ -0,0 +1,175 @@
|
||||
<?php
|
||||
namespace App\Protocols;
|
||||
|
||||
use App\Models\ServerHysteria;
|
||||
use App\Models\User;
|
||||
|
||||
class SingBox
|
||||
{
|
||||
public $flag = 'sing-box';
|
||||
private $servers;
|
||||
private $user;
|
||||
|
||||
public function __construct($user, $servers, array $options = null)
|
||||
{
|
||||
$this->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;
|
||||
}
|
||||
}
|
8
public/assets/admin/umi.js
vendored
8
public/assets/admin/umi.js
vendored
@ -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",
|
||||
|
2
public/theme/v2board/assets/umi.js
vendored
2
public/theme/v2board/assets/umi.js
vendored
@ -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"], {
|
||||
|
124
resources/rules/default.sing-box.json
Normal file
124
resources/rules/default.sing-box.json
Normal file
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user