mirror of
https://github.com/v2board/v2board.git
synced 2025-03-13 22:24:46 +08:00
增加后台用户管理面板在线设备数显示
This commit is contained in:
parent
692ad4cf54
commit
9db84a4a4a
@ -14,6 +14,7 @@ use App\Services\AuthService;
|
|||||||
use App\Utils\Helper;
|
use App\Utils\Helper;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
|
||||||
class UserController extends Controller
|
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']);
|
$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([
|
return response([
|
||||||
'data' => $res,
|
'data' => $res,
|
||||||
|
@ -44,6 +44,9 @@ class UniProxyController extends Controller
|
|||||||
$users = $users->toArray();
|
$users = $users->toArray();
|
||||||
|
|
||||||
$users = array_map(function ($user) {
|
$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']);
|
$ips_array = Cache::get('ALIVE_IP_USER_'. $user['id']);
|
||||||
$count = 0;
|
$count = 0;
|
||||||
if ($ips_array) {
|
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
|
return e
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
title: "\u8bbe\u5907\u6570\u9650\u5236",
|
title: "\u8bbe\u5907\u6570",
|
||||||
dataIndex: "device_limit",
|
dataIndex: "device_limit",
|
||||||
key: "device_limit",
|
key: "device_limit",
|
||||||
sorter: !0,
|
sorter: (e,t) => e.alive_ip - t.alive_ip,
|
||||||
render: (e,t)=>{
|
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",
|
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}"
|
id: "\u5728\u7ebf\u8bbe\u5907 {alive_ip}/{device_limit}"
|
||||||
}, {
|
}, {
|
||||||
alive_ip: d.alive_ip,
|
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", {
|
})))), 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"
|
className: "mb-4"
|
||||||
}, l.a.createElement(i["a"], {
|
}, 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