mirror of
https://github.com/lkddi/Xboard.git
synced 2026-04-23 19:37:35 +08:00
feat: add xhttp subscriptions, network monitoring, chart legend toggle and ticket sender labels
This commit is contained in:
@@ -168,12 +168,19 @@ class MachineController extends Controller
|
||||
$params = $request->validate([
|
||||
'machine_id' => 'required|integer|exists:v2_server_machine,id',
|
||||
'limit' => 'nullable|integer|min:10|max:1440',
|
||||
'range_hours' => 'nullable|integer|min:1|max:24',
|
||||
]);
|
||||
|
||||
$query = ServerMachineLoadHistory::query()
|
||||
->where('machine_id', $params['machine_id']);
|
||||
|
||||
if (!empty($params['range_hours'])) {
|
||||
$query->where('recorded_at', '>=', now()->subHours((int) $params['range_hours'])->timestamp);
|
||||
}
|
||||
|
||||
$limit = (int) ($params['limit'] ?? 60);
|
||||
|
||||
$history = ServerMachineLoadHistory::query()
|
||||
->where('machine_id', $params['machine_id'])
|
||||
$history = $query
|
||||
->orderByDesc('recorded_at')
|
||||
->limit($limit)
|
||||
->get([
|
||||
@@ -182,6 +189,8 @@ class MachineController extends Controller
|
||||
'mem_used',
|
||||
'disk_total',
|
||||
'disk_used',
|
||||
'net_in_speed',
|
||||
'net_out_speed',
|
||||
'recorded_at',
|
||||
])
|
||||
->reverse()
|
||||
|
||||
@@ -225,6 +225,7 @@ class ManageController extends Controller
|
||||
'ids' => 'required|array',
|
||||
'ids.*' => 'integer',
|
||||
'show' => 'nullable|integer|in:0,1',
|
||||
'enabled' => 'nullable|boolean',
|
||||
]);
|
||||
|
||||
$ids = $params['ids'];
|
||||
@@ -236,6 +237,9 @@ class ManageController extends Controller
|
||||
if (array_key_exists('show', $params) && $params['show'] !== null) {
|
||||
$update['show'] = (int) $params['show'];
|
||||
}
|
||||
if (array_key_exists('enabled', $params) && $params['enabled'] !== null) {
|
||||
$update['enabled'] = (bool) $params['enabled'];
|
||||
}
|
||||
|
||||
if (empty($update)) {
|
||||
return $this->fail([400, '没有可更新的字段']);
|
||||
|
||||
@@ -50,32 +50,46 @@ class MachineController extends Controller
|
||||
'swap.used' => 'nullable|integer|min:0',
|
||||
'disk.total' => 'nullable|integer|min:0',
|
||||
'disk.used' => 'nullable|integer|min:0',
|
||||
'net.in_speed' => 'nullable|numeric|min:0',
|
||||
'net.out_speed' => 'nullable|numeric|min:0',
|
||||
]);
|
||||
|
||||
$machine = $this->authenticateMachine($request);
|
||||
$recordedAt = now()->timestamp;
|
||||
|
||||
$machine->forceFill([
|
||||
'load_status' => [
|
||||
'cpu' => (float) $request->input('cpu'),
|
||||
'mem' => [
|
||||
'total' => (int) $request->input('mem.total'),
|
||||
'used' => (int) $request->input('mem.used'),
|
||||
],
|
||||
'swap' => [
|
||||
'total' => (int) $request->input('swap.total', 0),
|
||||
'used' => (int) $request->input('swap.used', 0),
|
||||
],
|
||||
'disk' => [
|
||||
'total' => (int) $request->input('disk.total', 0),
|
||||
'used' => (int) $request->input('disk.used', 0),
|
||||
],
|
||||
'updated_at' => $recordedAt,
|
||||
$loadStatus = [
|
||||
'cpu' => (float) $request->input('cpu'),
|
||||
'mem' => [
|
||||
'total' => (int) $request->input('mem.total'),
|
||||
'used' => (int) $request->input('mem.used'),
|
||||
],
|
||||
'swap' => [
|
||||
'total' => (int) $request->input('swap.total', 0),
|
||||
'used' => (int) $request->input('swap.used', 0),
|
||||
],
|
||||
'disk' => [
|
||||
'total' => (int) $request->input('disk.total', 0),
|
||||
'used' => (int) $request->input('disk.used', 0),
|
||||
],
|
||||
'updated_at' => $recordedAt,
|
||||
];
|
||||
|
||||
$netInSpeed = $request->input('net.in_speed');
|
||||
$netOutSpeed = $request->input('net.out_speed');
|
||||
|
||||
if ($netInSpeed !== null && $netOutSpeed !== null) {
|
||||
$loadStatus['net'] = [
|
||||
'in_speed' => (float) $netInSpeed,
|
||||
'out_speed' => (float) $netOutSpeed,
|
||||
];
|
||||
}
|
||||
|
||||
$machine->forceFill([
|
||||
'load_status' => $loadStatus,
|
||||
'last_seen_at' => $recordedAt,
|
||||
])->save();
|
||||
|
||||
ServerMachineLoadHistory::create([
|
||||
$historyData = [
|
||||
'machine_id' => $machine->id,
|
||||
'cpu' => (float) $request->input('cpu'),
|
||||
'mem_total' => (int) $request->input('mem.total'),
|
||||
@@ -83,7 +97,14 @@ class MachineController extends Controller
|
||||
'disk_total' => (int) $request->input('disk.total', 0),
|
||||
'disk_used' => (int) $request->input('disk.used', 0),
|
||||
'recorded_at' => $recordedAt,
|
||||
]);
|
||||
];
|
||||
|
||||
if ($netInSpeed !== null && $netOutSpeed !== null) {
|
||||
$historyData['net_in_speed'] = (float) $netInSpeed;
|
||||
$historyData['net_out_speed'] = (float) $netOutSpeed;
|
||||
}
|
||||
|
||||
ServerMachineLoadHistory::create($historyData);
|
||||
|
||||
// Time-based cleanup: keep 24h of data, runs on ~5% of requests
|
||||
if (random_int(1, 20) === 1) {
|
||||
|
||||
@@ -17,6 +17,8 @@ class ServerMachineLoadHistory extends Model
|
||||
'mem_used' => 'integer',
|
||||
'disk_total' => 'integer',
|
||||
'disk_used' => 'integer',
|
||||
'net_in_speed' => 'float',
|
||||
'net_out_speed' => 'float',
|
||||
'recorded_at' => 'integer',
|
||||
'created_at' => 'timestamp',
|
||||
'updated_at' => 'timestamp',
|
||||
|
||||
@@ -36,6 +36,27 @@ class ClashMeta extends AbstractProtocol
|
||||
'http' => '0.0.0',
|
||||
'h2' => '0.0.0',
|
||||
'httpupgrade' => '0.0.0',
|
||||
'xhttp' => '0.0.0',
|
||||
],
|
||||
'strict' => true,
|
||||
],
|
||||
'*.vmess.protocol_settings.network' => [
|
||||
'whitelist' => [
|
||||
'tcp' => '0.0.0',
|
||||
'ws' => '0.0.0',
|
||||
'grpc' => '0.0.0',
|
||||
'http' => '0.0.0',
|
||||
'h2' => '0.0.0',
|
||||
'httpupgrade' => '0.0.0',
|
||||
],
|
||||
'strict' => true,
|
||||
],
|
||||
'*.trojan.protocol_settings.network' => [
|
||||
'whitelist' => [
|
||||
'tcp' => '0.0.0',
|
||||
'ws' => '0.0.0',
|
||||
'grpc' => '0.0.0',
|
||||
'httpupgrade' => '0.0.0',
|
||||
],
|
||||
'strict' => true,
|
||||
],
|
||||
@@ -468,6 +489,18 @@ class ClashMeta extends AbstractProtocol
|
||||
if ($host = data_get($protocol_settings, 'network_settings.host'))
|
||||
$array['ws-opts']['headers'] = ['Host' => $host];
|
||||
break;
|
||||
case 'xhttp':
|
||||
$array['network'] = 'xhttp';
|
||||
$xhttpOpts = [];
|
||||
if ($path = data_get($protocol_settings, 'network_settings.path'))
|
||||
$xhttpOpts['path'] = $path;
|
||||
if ($host = data_get($protocol_settings, 'network_settings.host'))
|
||||
$xhttpOpts['host'] = $host;
|
||||
if ($mode = data_get($protocol_settings, 'network_settings.mode'))
|
||||
$xhttpOpts['mode'] = $mode;
|
||||
if (!empty($xhttpOpts))
|
||||
$array['xhttp-opts'] = $xhttpOpts;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -135,6 +135,17 @@ class General extends AbstractProtocol
|
||||
$config['path'] = $path;
|
||||
$config['host'] = data_get($protocol_settings, 'network_settings.host', $server['host']);
|
||||
break;
|
||||
case 'xhttp':
|
||||
$config['net'] = 'xhttp';
|
||||
$config['type'] = 'xhttp';
|
||||
if ($path = data_get($protocol_settings, 'network_settings.path'))
|
||||
$config['path'] = $path;
|
||||
$config['host'] = data_get($protocol_settings, 'network_settings.host', $server['host']);
|
||||
if ($mode = data_get($protocol_settings, 'network_settings.mode', 'auto'))
|
||||
$config['mode'] = $mode;
|
||||
if ($extra = data_get($protocol_settings, 'network_settings.extra'))
|
||||
$config['extra'] = is_array($extra) && !empty($extra) ? json_encode($extra) : null;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -216,10 +227,13 @@ class General extends AbstractProtocol
|
||||
$config['host'] = data_get($protocol_settings, 'network_settings.host', $server['host']);
|
||||
break;
|
||||
case 'xhttp':
|
||||
$config['path'] = data_get($protocol_settings, 'network_settings.path');
|
||||
if ($path = data_get($protocol_settings, 'network_settings.path'))
|
||||
$config['path'] = $path;
|
||||
$config['host'] = data_get($protocol_settings, 'network_settings.host', $server['host']);
|
||||
$config['mode'] = data_get($protocol_settings, 'network_settings.mode', 'auto');
|
||||
$config['extra'] = json_encode(data_get($protocol_settings, 'network_settings.extra'));
|
||||
if ($mode = data_get($protocol_settings, 'network_settings.mode', 'auto'))
|
||||
$config['mode'] = $mode;
|
||||
if ($extra = data_get($protocol_settings, 'network_settings.extra'))
|
||||
$config['extra'] = is_array($extra) && !empty($extra) ? json_encode($extra) : null;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -286,6 +300,16 @@ class General extends AbstractProtocol
|
||||
$array['path'] = $path;
|
||||
$array['host'] = data_get($protocol_settings, 'network_settings.host', $server['host']);
|
||||
break;
|
||||
case 'xhttp':
|
||||
$array['type'] = 'xhttp';
|
||||
if ($path = data_get($protocol_settings, 'network_settings.path'))
|
||||
$array['path'] = $path;
|
||||
$array['host'] = data_get($protocol_settings, 'network_settings.host', $server['host']);
|
||||
if ($mode = data_get($protocol_settings, 'network_settings.mode', 'auto'))
|
||||
$array['mode'] = $mode;
|
||||
if ($extra = data_get($protocol_settings, 'network_settings.extra'))
|
||||
$array['extra'] = is_array($extra) && !empty($extra) ? json_encode($extra) : null;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -225,6 +225,20 @@ class Loon extends AbstractProtocol
|
||||
if ($serviceName = data_get($protocol_settings, 'network_settings.serviceName'))
|
||||
$config[] = "grpc-service-name={$serviceName}";
|
||||
break;
|
||||
case 'h2':
|
||||
$config[] = 'transport=h2';
|
||||
if ($path = data_get($protocol_settings, 'network_settings.path'))
|
||||
$config[] = "path={$path}";
|
||||
if ($host = data_get($protocol_settings, 'network_settings.host'))
|
||||
$config[] = "host=" . (is_array($host) ? $host[0] : $host);
|
||||
break;
|
||||
case 'httpupgrade':
|
||||
$config[] = 'transport=httpupgrade';
|
||||
if ($path = data_get($protocol_settings, 'network_settings.path'))
|
||||
$config[] = "path={$path}";
|
||||
if ($host = data_get($protocol_settings, 'network_settings.host', $server['host']))
|
||||
$config[] = "host={$host}";
|
||||
break;
|
||||
}
|
||||
|
||||
$config = array_filter($config);
|
||||
@@ -295,6 +309,24 @@ class Loon extends AbstractProtocol
|
||||
$config[] = "grpc-service-name={$serviceName}";
|
||||
}
|
||||
break;
|
||||
case 'h2':
|
||||
$config[] = "transport=h2";
|
||||
if ($path = data_get($protocol_settings, 'network_settings.path')) {
|
||||
$config[] = "path={$path}";
|
||||
}
|
||||
if ($host = data_get($protocol_settings, 'network_settings.host')) {
|
||||
$config[] = "host=" . (is_array($host) ? $host[0] : $host);
|
||||
}
|
||||
break;
|
||||
case 'httpupgrade':
|
||||
$config[] = "transport=httpupgrade";
|
||||
if ($path = data_get($protocol_settings, 'network_settings.path')) {
|
||||
$config[] = "path={$path}";
|
||||
}
|
||||
if ($host = data_get($protocol_settings, 'network_settings.host', $server['host'])) {
|
||||
$config[] = "host={$host}";
|
||||
}
|
||||
break;
|
||||
default:
|
||||
$config[] = "transport=tcp";
|
||||
break;
|
||||
|
||||
@@ -23,6 +23,10 @@ class Shadowrocket extends AbstractProtocol
|
||||
protected $protocolRequirements = [
|
||||
'shadowrocket.hysteria.protocol_settings.version' => [2 => '1993'],
|
||||
'shadowrocket.anytls.base_version' => '2592',
|
||||
'shadowrocket.trojan.protocol_settings.network' => [
|
||||
'whitelist' => ['tcp', 'ws', 'grpc', 'h2', 'httpupgrade'],
|
||||
'strict' => true,
|
||||
],
|
||||
];
|
||||
|
||||
public function handle()
|
||||
@@ -147,6 +151,18 @@ class Shadowrocket extends AbstractProtocol
|
||||
$config['peer'] = $host [0] ?? $server['host'];
|
||||
}
|
||||
break;
|
||||
case 'xhttp':
|
||||
$config['obfs'] = "xhttp";
|
||||
if ($path = data_get($protocol_settings, 'network_settings.path')) {
|
||||
$config['path'] = $path;
|
||||
}
|
||||
if ($host = data_get($protocol_settings, 'network_settings.host', $server['host'])) {
|
||||
$config['obfsParam'] = $host;
|
||||
}
|
||||
if ($mode = data_get($protocol_settings, 'network_settings.mode', 'auto')) {
|
||||
$config['mode'] = $mode;
|
||||
}
|
||||
break;
|
||||
}
|
||||
$query = http_build_query($config, '', '&', PHP_QUERY_RFC3986);
|
||||
$uri = "vmess://{$userinfo}?{$query}";
|
||||
@@ -282,8 +298,8 @@ class Shadowrocket extends AbstractProtocol
|
||||
$params['sni'] = data_get($protocol_settings, 'reality_settings.server_name');
|
||||
break;
|
||||
default: // Standard TLS
|
||||
$params['allowInsecure'] = data_get($protocol_settings, 'allow_insecure');
|
||||
if ($serverName = data_get($protocol_settings, 'server_name')) {
|
||||
$params['allowInsecure'] = (int) data_get($protocol_settings, 'tls_settings.allow_insecure');
|
||||
if ($serverName = data_get($protocol_settings, 'tls_settings.server_name')) {
|
||||
$params['peer'] = $serverName;
|
||||
}
|
||||
break;
|
||||
@@ -299,6 +315,29 @@ class Shadowrocket extends AbstractProtocol
|
||||
$path = data_get($protocol_settings, 'network_settings.path');
|
||||
$params['plugin'] = "obfs-local;obfs=websocket;obfs-host={$host};obfs-uri={$path}";
|
||||
break;
|
||||
case 'h2':
|
||||
$params['obfs'] = 'h2';
|
||||
if ($path = data_get($protocol_settings, 'network_settings.path'))
|
||||
$params['path'] = $path;
|
||||
if ($host = data_get($protocol_settings, 'network_settings.host', $server['host']))
|
||||
$params['obfsParam'] = is_array($host) ? $host[0] : $host;
|
||||
break;
|
||||
case 'httpupgrade':
|
||||
$params['obfs'] = 'httpupgrade';
|
||||
if ($path = data_get($protocol_settings, 'network_settings.path'))
|
||||
$params['path'] = $path;
|
||||
if ($host = data_get($protocol_settings, 'network_settings.host', $server['host']))
|
||||
$params['obfsParam'] = $host;
|
||||
break;
|
||||
case 'xhttp':
|
||||
$params['obfs'] = 'xhttp';
|
||||
if ($path = data_get($protocol_settings, 'network_settings.path'))
|
||||
$params['path'] = $path;
|
||||
if ($host = data_get($protocol_settings, 'network_settings.host', $server['host']))
|
||||
$params['obfsParam'] = $host;
|
||||
if ($mode = data_get($protocol_settings, 'network_settings.mode', 'auto'))
|
||||
$params['mode'] = $mode;
|
||||
break;
|
||||
}
|
||||
$query = http_build_query($params);
|
||||
$addr = Helper::wrapIPv6($server['host']);
|
||||
|
||||
@@ -40,16 +40,25 @@ class SingBox extends AbstractProtocol
|
||||
],
|
||||
'protocol_settings.tls_settings.ech.enabled' => [
|
||||
1 => '1.5.0'
|
||||
],
|
||||
'protocol_settings.network' => [
|
||||
'xhttp' => '9999.0.0'
|
||||
]
|
||||
],
|
||||
'vmess' => [
|
||||
'protocol_settings.tls_settings.ech.enabled' => [
|
||||
1 => '1.5.0'
|
||||
],
|
||||
'protocol_settings.network' => [
|
||||
'xhttp' => '9999.0.0'
|
||||
]
|
||||
],
|
||||
'trojan' => [
|
||||
'protocol_settings.tls_settings.ech.enabled' => [
|
||||
1 => '1.5.0'
|
||||
],
|
||||
'protocol_settings.network' => [
|
||||
'xhttp' => '9999.0.0'
|
||||
]
|
||||
],
|
||||
'hysteria' => [
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('v2_server_machine_load_history', function (Blueprint $table) {
|
||||
$table->double('net_in_speed')->nullable()->after('disk_used');
|
||||
$table->double('net_out_speed')->nullable()->after('net_in_speed');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('v2_server_machine_load_history', function (Blueprint $table) {
|
||||
$table->dropColumn(['net_in_speed', 'net_out_speed']);
|
||||
});
|
||||
}
|
||||
};
|
||||
+1
-1
Submodule public/assets/admin updated: 30dcb220eb...37c135bdb8
Reference in New Issue
Block a user