mirror of
https://github.com/lkddi/Xboard.git
synced 2026-04-03 10:30:51 +08:00
feat: Refactor uTLS & Multiplex Support, Node Status Push Optimization
- Server/ServerSave/Server.php: Unified utls and multiplex schema, validation, and defaults for vmess/vless/trojan/mieru protocols, enabling more flexible protocol configuration. - Protocols (SingBox/ClashMeta/Shadowrocket/Stash/General): All protocol generators now support utls (client-fingerprint/fp) and multiplex options. Removed getRandFingerprint, replaced with getTlsFingerprint supporting random/custom fingerprints. - Helper.php: Refactored TLS fingerprint utility to support object/string/random input. - ServerService: Abstracted updateMetrics method to unify HTTP/WS node status caching logic. - NodeWebSocketServer: Improved node connection, status push, and full sync logic; adjusted log levels; clarified push logic. - ServerController: Reused ServerService for node metrics handling, reducing code duplication. - Docs: Improved aapanel installation docs, added fix for empty admin dashboard.
This commit is contained in:
@@ -126,7 +126,7 @@ class NodeWebSocketServer extends Command
|
|||||||
NodeRegistry::add($nodeId, $conn);
|
NodeRegistry::add($nodeId, $conn);
|
||||||
Cache::put("node_ws_alive:{$nodeId}", true, 86400);
|
Cache::put("node_ws_alive:{$nodeId}", true, 86400);
|
||||||
|
|
||||||
Log::info("[WS] Node#{$nodeId} connected", [
|
Log::debug("[WS] Node#{$nodeId} connected", [
|
||||||
'remote' => $conn->getRemoteIp(),
|
'remote' => $conn->getRemoteIp(),
|
||||||
'total' => NodeRegistry::count(),
|
'total' => NodeRegistry::count(),
|
||||||
]);
|
]);
|
||||||
@@ -137,8 +137,8 @@ class NodeWebSocketServer extends Command
|
|||||||
'data' => ['node_id' => $nodeId],
|
'data' => ['node_id' => $nodeId],
|
||||||
]));
|
]));
|
||||||
|
|
||||||
// Push full sync (config + users) immediately
|
// Push full sync (config + users) immediately to this specific connection
|
||||||
$this->pushFullSync($nodeId, $node);
|
$this->pushFullSync($conn, $node);
|
||||||
};
|
};
|
||||||
|
|
||||||
$worker->onMessage = function (TcpConnection $conn, $data) {
|
$worker->onMessage = function (TcpConnection $conn, $data) {
|
||||||
@@ -148,12 +148,18 @@ class NodeWebSocketServer extends Command
|
|||||||
}
|
}
|
||||||
|
|
||||||
$event = $msg['event'] ?? '';
|
$event = $msg['event'] ?? '';
|
||||||
|
$nodeId = $conn->nodeId ?? null;
|
||||||
|
|
||||||
switch ($event) {
|
switch ($event) {
|
||||||
case 'pong':
|
case 'pong':
|
||||||
// Heartbeat response — node is alive
|
// Heartbeat response — node is alive
|
||||||
if (!empty($conn->nodeId)) {
|
if ($nodeId) {
|
||||||
Cache::put("node_ws_alive:{$conn->nodeId}", true, 86400);
|
Cache::put("node_ws_alive:{$nodeId}", true, 86400);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'node.status':
|
||||||
|
if ($nodeId && isset($msg['data'])) {
|
||||||
|
$this->handleNodeStatus($nodeId, $msg['data']);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -167,7 +173,7 @@ class NodeWebSocketServer extends Command
|
|||||||
$nodeId = $conn->nodeId;
|
$nodeId = $conn->nodeId;
|
||||||
NodeRegistry::remove($nodeId);
|
NodeRegistry::remove($nodeId);
|
||||||
Cache::forget("node_ws_alive:{$nodeId}");
|
Cache::forget("node_ws_alive:{$nodeId}");
|
||||||
Log::info("[WS] Node#{$nodeId} disconnected", [
|
Log::debug("[WS] Node#{$nodeId} disconnected", [
|
||||||
'total' => NodeRegistry::count(),
|
'total' => NodeRegistry::count(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@@ -176,6 +182,25 @@ class NodeWebSocketServer extends Command
|
|||||||
Worker::runAll();
|
Worker::runAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle status data pushed from node via WebSocket
|
||||||
|
*/
|
||||||
|
private function handleNodeStatus(int $nodeId, array $data): void
|
||||||
|
{
|
||||||
|
$node = Server::find($nodeId);
|
||||||
|
if (!$node) return;
|
||||||
|
|
||||||
|
$nodeType = strtoupper($node->type);
|
||||||
|
|
||||||
|
// Update last check-in cache
|
||||||
|
Cache::put(\App\Utils\CacheKey::get('SERVER_' . $nodeType . '_LAST_CHECK_AT', $nodeId), time(), 3600);
|
||||||
|
|
||||||
|
// Update metrics cache via Service
|
||||||
|
ServerService::updateMetrics($node, $data);
|
||||||
|
|
||||||
|
Log::debug("[WS] Node#{$nodeId} status updated via WebSocket");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subscribe to Redis pub/sub channel for receiving push commands from Laravel.
|
* Subscribe to Redis pub/sub channel for receiving push commands from Laravel.
|
||||||
* Laravel app publishes to "node:push" channel, Workerman picks it up and forwards to the right node.
|
* Laravel app publishes to "node:push" channel, Workerman picks it up and forwards to the right node.
|
||||||
@@ -229,15 +254,23 @@ class NodeWebSocketServer extends Command
|
|||||||
/**
|
/**
|
||||||
* Push full config + users to a newly connected node.
|
* Push full config + users to a newly connected node.
|
||||||
*/
|
*/
|
||||||
private function pushFullSync(int $nodeId, Server $node): void
|
private function pushFullSync(TcpConnection $conn, Server $node): void
|
||||||
{
|
{
|
||||||
|
$nodeId = $conn->nodeId;
|
||||||
// Push config
|
// Push config
|
||||||
$config = ServerService::buildNodeConfig($node);
|
$config = ServerService::buildNodeConfig($node);
|
||||||
NodeRegistry::send($nodeId, 'sync.config', ['config' => $config]);
|
Log::debug("[WS] Node#{$nodeId} config: ", $config);
|
||||||
|
$conn->send(json_encode([
|
||||||
|
'event' => 'sync.config',
|
||||||
|
'data' => ['config' => $config]
|
||||||
|
]));
|
||||||
|
|
||||||
// Push users
|
// Push users
|
||||||
$users = ServerService::getAvailableUsers($node)->toArray();
|
$users = ServerService::getAvailableUsers($node)->toArray();
|
||||||
NodeRegistry::send($nodeId, 'sync.users', ['users' => $users]);
|
$conn->send(json_encode([
|
||||||
|
'event' => 'sync.users',
|
||||||
|
'data' => ['users' => $users]
|
||||||
|
]));
|
||||||
|
|
||||||
Log::info("[WS] Full sync pushed to node#{$nodeId}", [
|
Log::info("[WS] Full sync pushed to node#{$nodeId}", [
|
||||||
'users' => count($users),
|
'users' => count($users),
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ namespace App\Http\Controllers\V2\Server;
|
|||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Jobs\UserAliveSyncJob;
|
use App\Jobs\UserAliveSyncJob;
|
||||||
|
use App\Services\ServerService;
|
||||||
use App\Services\UserService;
|
use App\Services\UserService;
|
||||||
use App\Utils\CacheKey;
|
use App\Utils\CacheKey;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
@@ -113,6 +114,7 @@ class ServerController extends Controller
|
|||||||
'used' => (int) ($status['disk']['used'] ?? 0),
|
'used' => (int) ($status['disk']['used'] ?? 0),
|
||||||
],
|
],
|
||||||
'updated_at' => now()->timestamp,
|
'updated_at' => now()->timestamp,
|
||||||
|
'kernel_status' => $status['kernel_status'] ?? null,
|
||||||
];
|
];
|
||||||
|
|
||||||
$cacheTime = max(300, (int) admin_setting('server_push_interval', 60) * 3);
|
$cacheTime = max(300, (int) admin_setting('server_push_interval', 60) * 3);
|
||||||
@@ -125,24 +127,7 @@ class ServerController extends Controller
|
|||||||
// handle node metrics (Metrics)
|
// handle node metrics (Metrics)
|
||||||
$metrics = $request->input('metrics');
|
$metrics = $request->input('metrics');
|
||||||
if (is_array($metrics) && !empty($metrics)) {
|
if (is_array($metrics) && !empty($metrics)) {
|
||||||
$metricsData = [
|
ServerService::updateMetrics($node, $metrics);
|
||||||
'uptime' => (int) ($metrics['uptime'] ?? 0),
|
|
||||||
'inbound_speed' => (int) ($metrics['inbound_speed'] ?? 0),
|
|
||||||
'outbound_speed' => (int) ($metrics['outbound_speed'] ?? 0),
|
|
||||||
'active_connections' => (int) ($metrics['active_connections'] ?? 0),
|
|
||||||
'total_connections' => (int) ($metrics['total_connections'] ?? 0),
|
|
||||||
'speed_limiter' => $metrics['speed_limiter'] ?? [],
|
|
||||||
'cpu_per_core' => $metrics['cpu_per_core'] ?? [],
|
|
||||||
'gc' => $metrics['gc'] ?? [],
|
|
||||||
'api' => $metrics['api'] ?? [],
|
|
||||||
'updated_at' => now()->timestamp,
|
|
||||||
];
|
|
||||||
$cacheTime = max(300, (int) admin_setting('server_push_interval', 60) * 3);
|
|
||||||
Cache::put(
|
|
||||||
CacheKey::get('SERVER_' . strtoupper($nodeType) . '_METRICS', $nodeId),
|
|
||||||
$metricsData,
|
|
||||||
$cacheTime
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return response()->json(['data' => true]);
|
return response()->json(['data' => true]);
|
||||||
|
|||||||
@@ -8,6 +8,23 @@ use Illuminate\Foundation\Http\FormRequest;
|
|||||||
|
|
||||||
class ServerSave extends FormRequest
|
class ServerSave extends FormRequest
|
||||||
{
|
{
|
||||||
|
private const UTLS_RULES = [
|
||||||
|
'utls.enabled' => 'nullable|boolean',
|
||||||
|
'utls.fingerprint' => 'nullable|string',
|
||||||
|
];
|
||||||
|
|
||||||
|
private const MULTIPLEX_RULES = [
|
||||||
|
'multiplex.enabled' => 'nullable|boolean',
|
||||||
|
'multiplex.protocol' => 'nullable|string',
|
||||||
|
'multiplex.max_connections' => 'nullable|integer',
|
||||||
|
'multiplex.min_streams' => 'nullable|integer',
|
||||||
|
'multiplex.max_streams' => 'nullable|integer',
|
||||||
|
'multiplex.padding' => 'nullable|boolean',
|
||||||
|
'multiplex.brutal.enabled' => 'nullable|boolean',
|
||||||
|
'multiplex.brutal.up_mbps' => 'nullable|integer',
|
||||||
|
'multiplex.brutal.down_mbps' => 'nullable|integer',
|
||||||
|
];
|
||||||
|
|
||||||
private const PROTOCOL_RULES = [
|
private const PROTOCOL_RULES = [
|
||||||
'shadowsocks' => [
|
'shadowsocks' => [
|
||||||
'cipher' => 'required|string',
|
'cipher' => 'required|string',
|
||||||
@@ -67,8 +84,8 @@ class ServerSave extends FormRequest
|
|||||||
'tls_settings' => 'nullable|array',
|
'tls_settings' => 'nullable|array',
|
||||||
],
|
],
|
||||||
'mieru' => [
|
'mieru' => [
|
||||||
'transport' => 'required|string',
|
'transport' => 'required|string|in:TCP,UDP',
|
||||||
'multiplexing' => 'required|string',
|
'traffic_pattern' => 'string'
|
||||||
],
|
],
|
||||||
'anytls' => [
|
'anytls' => [
|
||||||
'tls' => 'nullable|array',
|
'tls' => 'nullable|array',
|
||||||
@@ -112,13 +129,45 @@ class ServerSave extends FormRequest
|
|||||||
$type = $this->input('type');
|
$type = $this->input('type');
|
||||||
$rules = $this->getBaseRules();
|
$rules = $this->getBaseRules();
|
||||||
|
|
||||||
foreach (self::PROTOCOL_RULES[$type] ?? [] as $field => $rule) {
|
$protocolRules = self::PROTOCOL_RULES[$type] ?? [];
|
||||||
|
if (in_array($type, ['vmess', 'vless', 'trojan', 'mieru'])) {
|
||||||
|
$protocolRules = array_merge($protocolRules, self::MULTIPLEX_RULES, self::UTLS_RULES);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($protocolRules as $field => $rule) {
|
||||||
$rules['protocol_settings.' . $field] = $rule;
|
$rules['protocol_settings.' . $field] = $rule;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $rules;
|
return $rules;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function attributes(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'protocol_settings.cipher' => '加密方式',
|
||||||
|
'protocol_settings.obfs' => '混淆类型',
|
||||||
|
'protocol_settings.network' => '传输协议',
|
||||||
|
'protocol_settings.port_range' => '端口范围',
|
||||||
|
'protocol_settings.traffic_pattern' => 'Traffic Pattern',
|
||||||
|
'protocol_settings.transport' => '传输方式',
|
||||||
|
'protocol_settings.version' => '协议版本',
|
||||||
|
'protocol_settings.password' => '密码',
|
||||||
|
'protocol_settings.handshake.server' => '握手服务器',
|
||||||
|
'protocol_settings.handshake.server_port' => '握手端口',
|
||||||
|
'protocol_settings.multiplex.enabled' => '多路复用',
|
||||||
|
'protocol_settings.multiplex.protocol' => '复用协议',
|
||||||
|
'protocol_settings.multiplex.max_connections' => '最大连接数',
|
||||||
|
'protocol_settings.multiplex.min_streams' => '最小流数',
|
||||||
|
'protocol_settings.multiplex.max_streams' => '最大流数',
|
||||||
|
'protocol_settings.multiplex.padding' => '复用填充',
|
||||||
|
'protocol_settings.multiplex.brutal.enabled' => 'Brutal加速',
|
||||||
|
'protocol_settings.multiplex.brutal.up_mbps' => 'Brutal上行速率',
|
||||||
|
'protocol_settings.multiplex.brutal.down_mbps' => 'Brutal下行速率',
|
||||||
|
'protocol_settings.utls.enabled' => 'uTLS',
|
||||||
|
'protocol_settings.utls.fingerprint' => 'uTLS指纹',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
public function messages()
|
public function messages()
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
@@ -139,7 +188,11 @@ class ServerSave extends FormRequest
|
|||||||
'networkSettings.array' => '传输协议配置有误',
|
'networkSettings.array' => '传输协议配置有误',
|
||||||
'ruleSettings.array' => '规则配置有误',
|
'ruleSettings.array' => '规则配置有误',
|
||||||
'tlsSettings.array' => 'tls配置有误',
|
'tlsSettings.array' => 'tls配置有误',
|
||||||
'dnsSettings.array' => 'dns配置有误'
|
'dnsSettings.array' => 'dns配置有误',
|
||||||
|
'protocol_settings.*.required' => ':attribute 不能为空',
|
||||||
|
'protocol_settings.*.string' => ':attribute 必须是字符串',
|
||||||
|
'protocol_settings.*.integer' => ':attribute 必须是整数',
|
||||||
|
'protocol_settings.*.in' => ':attribute 的值不合法',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -126,19 +126,55 @@ class Server extends Model
|
|||||||
'rate_time_enable' => 'boolean',
|
'rate_time_enable' => 'boolean',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
private const MULTIPLEX_CONFIGURATION = [
|
||||||
|
'multiplex' => [
|
||||||
|
'type' => 'object',
|
||||||
|
'fields' => [
|
||||||
|
'enabled' => ['type' => 'boolean', 'default' => false],
|
||||||
|
'protocol' => ['type' => 'string', 'default' => 'yamux'],
|
||||||
|
'max_connections' => ['type' => 'integer', 'default' => null],
|
||||||
|
// 'min_streams' => ['type' => 'integer', 'default' => null],
|
||||||
|
// 'max_streams' => ['type' => 'integer', 'default' => null],
|
||||||
|
'padding' => ['type' => 'boolean', 'default' => false],
|
||||||
|
'brutal' => [
|
||||||
|
'type' => 'object',
|
||||||
|
'fields' => [
|
||||||
|
'enabled' => ['type' => 'boolean', 'default' => false],
|
||||||
|
'up_mbps' => ['type' => 'integer', 'default' => null],
|
||||||
|
'down_mbps' => ['type' => 'integer', 'default' => null],
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
private const UTLS_CONFIGURATION = [
|
||||||
|
'utls' => [
|
||||||
|
'type' => 'object',
|
||||||
|
'fields' => [
|
||||||
|
'enabled' => ['type' => 'boolean', 'default' => false],
|
||||||
|
'fingerprint' => ['type' => 'string', 'default' => 'chrome'],
|
||||||
|
]
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
private const PROTOCOL_CONFIGURATIONS = [
|
private const PROTOCOL_CONFIGURATIONS = [
|
||||||
self::TYPE_TROJAN => [
|
self::TYPE_TROJAN => [
|
||||||
'allow_insecure' => ['type' => 'boolean', 'default' => false],
|
|
||||||
'server_name' => ['type' => 'string', 'default' => null],
|
|
||||||
'network' => ['type' => 'string', 'default' => null],
|
'network' => ['type' => 'string', 'default' => null],
|
||||||
'network_settings' => ['type' => 'array', 'default' => null]
|
'network_settings' => ['type' => 'array', 'default' => null],
|
||||||
|
'server_name' => ['type' => 'string', 'default' => null],
|
||||||
|
'allow_insecure' => ['type' => 'boolean', 'default' => false],
|
||||||
|
...self::MULTIPLEX_CONFIGURATION,
|
||||||
|
...self::UTLS_CONFIGURATION
|
||||||
],
|
],
|
||||||
self::TYPE_VMESS => [
|
self::TYPE_VMESS => [
|
||||||
'tls' => ['type' => 'integer', 'default' => 0],
|
'tls' => ['type' => 'integer', 'default' => 0],
|
||||||
'network' => ['type' => 'string', 'default' => null],
|
'network' => ['type' => 'string', 'default' => null],
|
||||||
'rules' => ['type' => 'array', 'default' => null],
|
'rules' => ['type' => 'array', 'default' => null],
|
||||||
'network_settings' => ['type' => 'array', 'default' => null],
|
'network_settings' => ['type' => 'array', 'default' => null],
|
||||||
'tls_settings' => ['type' => 'array', 'default' => null]
|
'tls_settings' => ['type' => 'array', 'default' => null],
|
||||||
|
...self::MULTIPLEX_CONFIGURATION,
|
||||||
|
...self::UTLS_CONFIGURATION
|
||||||
],
|
],
|
||||||
self::TYPE_VLESS => [
|
self::TYPE_VLESS => [
|
||||||
'tls' => ['type' => 'integer', 'default' => 0],
|
'tls' => ['type' => 'integer', 'default' => 0],
|
||||||
@@ -156,7 +192,9 @@ class Server extends Model
|
|||||||
'private_key' => ['type' => 'string', 'default' => null],
|
'private_key' => ['type' => 'string', 'default' => null],
|
||||||
'short_id' => ['type' => 'string', 'default' => null]
|
'short_id' => ['type' => 'string', 'default' => null]
|
||||||
]
|
]
|
||||||
]
|
],
|
||||||
|
...self::MULTIPLEX_CONFIGURATION,
|
||||||
|
...self::UTLS_CONFIGURATION
|
||||||
],
|
],
|
||||||
self::TYPE_SHADOWSOCKS => [
|
self::TYPE_SHADOWSOCKS => [
|
||||||
'cipher' => ['type' => 'string', 'default' => null],
|
'cipher' => ['type' => 'string', 'default' => null],
|
||||||
@@ -251,8 +289,9 @@ class Server extends Model
|
|||||||
]
|
]
|
||||||
],
|
],
|
||||||
self::TYPE_MIERU => [
|
self::TYPE_MIERU => [
|
||||||
'transport' => ['type' => 'string', 'default' => 'tcp'],
|
'transport' => ['type' => 'string', 'default' => 'TCP'],
|
||||||
'multiplexing' => ['type' => 'string', 'default' => 'MULTIPLEXING_LOW']
|
'traffic_pattern' => ['type' => 'string', 'default' => ''],
|
||||||
|
...self::MULTIPLEX_CONFIGURATION,
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -227,7 +227,7 @@ class ClashMeta extends AbstractProtocol
|
|||||||
$array['plugin-opts'] = array_filter([
|
$array['plugin-opts'] = array_filter([
|
||||||
'host' => $parsedOpts['host'] ?? null,
|
'host' => $parsedOpts['host'] ?? null,
|
||||||
'password' => $parsedOpts['password'] ?? null,
|
'password' => $parsedOpts['password'] ?? null,
|
||||||
'version' => isset($parsedOpts['version']) ? (int)$parsedOpts['version'] : 2
|
'version' => isset($parsedOpts['version']) ? (int) $parsedOpts['version'] : 2
|
||||||
], fn($v) => $v !== null);
|
], fn($v) => $v !== null);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -266,14 +266,19 @@ class ClashMeta extends AbstractProtocol
|
|||||||
$array['servername'] = data_get($protocol_settings, 'tls_settings.server_name');
|
$array['servername'] = data_get($protocol_settings, 'tls_settings.server_name');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self::appendUtls($array, $protocol_settings);
|
||||||
|
self::appendMultiplex($array, $protocol_settings);
|
||||||
|
|
||||||
switch (data_get($protocol_settings, 'network')) {
|
switch (data_get($protocol_settings, 'network')) {
|
||||||
case 'tcp':
|
case 'tcp':
|
||||||
$array['network'] = data_get($protocol_settings, 'network_settings.header.type', 'tcp');
|
$array['network'] = data_get($protocol_settings, 'network_settings.header.type', 'tcp');
|
||||||
if (data_get($protocol_settings, 'network_settings.header.type', 'none') !== 'none') {
|
if (data_get($protocol_settings, 'network_settings.header.type', 'none') !== 'none') {
|
||||||
if ($httpOpts = array_filter([
|
if (
|
||||||
'headers' => data_get($protocol_settings, 'network_settings.header.request.headers'),
|
$httpOpts = array_filter([
|
||||||
'path' => data_get($protocol_settings, 'network_settings.header.request.path', ['/'])
|
'headers' => data_get($protocol_settings, 'network_settings.header.request.headers'),
|
||||||
])) {
|
'path' => data_get($protocol_settings, 'network_settings.header.request.path', ['/'])
|
||||||
|
])
|
||||||
|
) {
|
||||||
$array['http-opts'] = $httpOpts;
|
$array['http-opts'] = $httpOpts;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -336,7 +341,7 @@ class ClashMeta extends AbstractProtocol
|
|||||||
if ($serverName = data_get($protocol_settings, 'tls_settings.server_name')) {
|
if ($serverName = data_get($protocol_settings, 'tls_settings.server_name')) {
|
||||||
$array['servername'] = $serverName;
|
$array['servername'] = $serverName;
|
||||||
}
|
}
|
||||||
$array['client-fingerprint'] = Helper::getRandFingerprint();
|
self::appendUtls($array, $protocol_settings);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
$array['tls'] = true;
|
$array['tls'] = true;
|
||||||
@@ -346,7 +351,7 @@ class ClashMeta extends AbstractProtocol
|
|||||||
'public-key' => data_get($protocol_settings, 'reality_settings.public_key'),
|
'public-key' => data_get($protocol_settings, 'reality_settings.public_key'),
|
||||||
'short-id' => data_get($protocol_settings, 'reality_settings.short_id')
|
'short-id' => data_get($protocol_settings, 'reality_settings.short_id')
|
||||||
];
|
];
|
||||||
$array['client-fingerprint'] = Helper::getRandFingerprint();
|
self::appendUtls($array, $protocol_settings);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@@ -358,10 +363,12 @@ class ClashMeta extends AbstractProtocol
|
|||||||
$headerType = data_get($protocol_settings, 'network_settings.header.type', 'none');
|
$headerType = data_get($protocol_settings, 'network_settings.header.type', 'none');
|
||||||
if ($headerType === 'http') {
|
if ($headerType === 'http') {
|
||||||
$array['network'] = 'http';
|
$array['network'] = 'http';
|
||||||
if ($httpOpts = array_filter([
|
if (
|
||||||
'headers' => data_get($protocol_settings, 'network_settings.header.request.headers'),
|
$httpOpts = array_filter([
|
||||||
'path' => data_get($protocol_settings, 'network_settings.header.request.path', ['/'])
|
'headers' => data_get($protocol_settings, 'network_settings.header.request.headers'),
|
||||||
])) {
|
'path' => data_get($protocol_settings, 'network_settings.header.request.path', ['/'])
|
||||||
|
])
|
||||||
|
) {
|
||||||
$array['http-opts'] = $httpOpts;
|
$array['http-opts'] = $httpOpts;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -398,6 +405,8 @@ class ClashMeta extends AbstractProtocol
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self::appendMultiplex($array, $protocol_settings);
|
||||||
|
|
||||||
return $array;
|
return $array;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -417,6 +426,9 @@ class ClashMeta extends AbstractProtocol
|
|||||||
$array['sni'] = $serverName;
|
$array['sni'] = $serverName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self::appendUtls($array, $protocol_settings);
|
||||||
|
self::appendMultiplex($array, $protocol_settings);
|
||||||
|
|
||||||
switch (data_get($protocol_settings, 'network')) {
|
switch (data_get($protocol_settings, 'network')) {
|
||||||
case 'tcp':
|
case 'tcp':
|
||||||
$array['network'] = 'tcp';
|
$array['network'] = 'tcp';
|
||||||
@@ -565,8 +577,7 @@ class ClashMeta extends AbstractProtocol
|
|||||||
'port' => $server['port'],
|
'port' => $server['port'],
|
||||||
'username' => $password,
|
'username' => $password,
|
||||||
'password' => $password,
|
'password' => $password,
|
||||||
'transport' => strtoupper(data_get($protocol_settings, 'transport', 'TCP')),
|
'transport' => strtoupper(data_get($protocol_settings, 'transport', 'TCP'))
|
||||||
'multiplexing' => data_get($protocol_settings, 'multiplexing', 'MULTIPLEXING_LOW')
|
|
||||||
];
|
];
|
||||||
|
|
||||||
// 如果配置了端口范围
|
// 如果配置了端口范围
|
||||||
@@ -640,4 +651,37 @@ class ClashMeta extends AbstractProtocol
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
protected static function appendMultiplex(&$array, $protocol_settings)
|
||||||
|
{
|
||||||
|
if ($multiplex = data_get($protocol_settings, 'multiplex')) {
|
||||||
|
if (data_get($multiplex, 'enabled')) {
|
||||||
|
$array['smux'] = array_filter([
|
||||||
|
'enabled' => true,
|
||||||
|
'protocol' => data_get($multiplex, 'protocol', 'yamux'),
|
||||||
|
'max-connections' => data_get($multiplex, 'max_connections'),
|
||||||
|
// 'min-streams' => data_get($multiplex, 'min_streams'),
|
||||||
|
// 'max-streams' => data_get($multiplex, 'max_streams'),
|
||||||
|
'padding' => data_get($multiplex, 'padding') ? true : null,
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (data_get($multiplex, 'brutal.enabled')) {
|
||||||
|
$array['smux']['brutal'] = [
|
||||||
|
'enabled' => true,
|
||||||
|
'up' => data_get($multiplex, 'brutal.up_mbps'),
|
||||||
|
'down' => data_get($multiplex, 'brutal.down_mbps'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static function appendUtls(&$array, $protocol_settings)
|
||||||
|
{
|
||||||
|
if ($utls = data_get($protocol_settings, 'utls')) {
|
||||||
|
if (data_get($utls, 'enabled')) {
|
||||||
|
$array['client-fingerprint'] = Helper::getTlsFingerprint($utls);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -154,7 +154,9 @@ class General extends AbstractProtocol
|
|||||||
switch ($server['protocol_settings']['tls']) {
|
switch ($server['protocol_settings']['tls']) {
|
||||||
case 1:
|
case 1:
|
||||||
$config['security'] = "tls";
|
$config['security'] = "tls";
|
||||||
$config['fp'] = Helper::getRandFingerprint();
|
if ($fp = Helper::getTlsFingerprint(data_get($protocol_settings, 'utls'))) {
|
||||||
|
$config['fp'] = $fp;
|
||||||
|
}
|
||||||
if ($serverName = data_get($protocol_settings, 'tls_settings.server_name')) {
|
if ($serverName = data_get($protocol_settings, 'tls_settings.server_name')) {
|
||||||
$config['sni'] = $serverName;
|
$config['sni'] = $serverName;
|
||||||
}
|
}
|
||||||
@@ -166,7 +168,9 @@ class General extends AbstractProtocol
|
|||||||
$config['sni'] = data_get($protocol_settings, 'reality_settings.server_name');
|
$config['sni'] = data_get($protocol_settings, 'reality_settings.server_name');
|
||||||
$config['servername'] = data_get($protocol_settings, 'reality_settings.server_name');
|
$config['servername'] = data_get($protocol_settings, 'reality_settings.server_name');
|
||||||
$config['spx'] = "/";
|
$config['spx'] = "/";
|
||||||
$config['fp'] = Helper::getRandFingerprint();
|
if ($fp = Helper::getTlsFingerprint(data_get($protocol_settings, 'utls'))) {
|
||||||
|
$config['fp'] = $fp;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -165,14 +165,18 @@ class Shadowrocket extends AbstractProtocol
|
|||||||
if ($serverName = data_get($protocol_settings, 'tls_settings.server_name')) {
|
if ($serverName = data_get($protocol_settings, 'tls_settings.server_name')) {
|
||||||
$config['peer'] = $serverName;
|
$config['peer'] = $serverName;
|
||||||
}
|
}
|
||||||
$config['fp'] = Helper::getRandFingerprint();
|
if ($fp = Helper::getTlsFingerprint(data_get($protocol_settings, 'utls'))) {
|
||||||
|
$config['fp'] = $fp;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
$config['tls'] = 1;
|
$config['tls'] = 1;
|
||||||
$config['sni'] = data_get($protocol_settings, 'reality_settings.server_name');
|
$config['sni'] = data_get($protocol_settings, 'reality_settings.server_name');
|
||||||
$config['pbk'] = data_get($protocol_settings, 'reality_settings.public_key');
|
$config['pbk'] = data_get($protocol_settings, 'reality_settings.public_key');
|
||||||
$config['sid'] = data_get($protocol_settings, 'reality_settings.short_id');
|
$config['sid'] = data_get($protocol_settings, 'reality_settings.short_id');
|
||||||
$config['fp'] = Helper::getRandFingerprint();
|
if ($fp = Helper::getTlsFingerprint(data_get($protocol_settings, 'utls'))) {
|
||||||
|
$config['fp'] = $fp;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ namespace App\Protocols;
|
|||||||
|
|
||||||
use App\Utils\Helper;
|
use App\Utils\Helper;
|
||||||
use Illuminate\Support\Arr;
|
use Illuminate\Support\Arr;
|
||||||
use Illuminate\Support\Facades\File;
|
|
||||||
use App\Support\AbstractProtocol;
|
use App\Support\AbstractProtocol;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
|
|
||||||
@@ -20,7 +19,6 @@ class SingBox extends AbstractProtocol
|
|||||||
Server::TYPE_ANYTLS,
|
Server::TYPE_ANYTLS,
|
||||||
Server::TYPE_SOCKS,
|
Server::TYPE_SOCKS,
|
||||||
Server::TYPE_HTTP,
|
Server::TYPE_HTTP,
|
||||||
Server::TYPE_MIERU,
|
|
||||||
];
|
];
|
||||||
private $config;
|
private $config;
|
||||||
const CUSTOM_TEMPLATE_FILE = 'resources/rules/custom.sing-box.json';
|
const CUSTOM_TEMPLATE_FILE = 'resources/rules/custom.sing-box.json';
|
||||||
@@ -55,9 +53,6 @@ class SingBox extends AbstractProtocol
|
|||||||
'juicity' => [
|
'juicity' => [
|
||||||
'base_version' => '1.7.0'
|
'base_version' => '1.7.0'
|
||||||
],
|
],
|
||||||
'shadowtls' => [
|
|
||||||
'base_version' => '1.6.0'
|
|
||||||
],
|
|
||||||
'wireguard' => [
|
'wireguard' => [
|
||||||
'base_version' => '1.5.0'
|
'base_version' => '1.5.0'
|
||||||
],
|
],
|
||||||
@@ -292,11 +287,16 @@ class SingBox extends AbstractProtocol
|
|||||||
'enabled' => true,
|
'enabled' => true,
|
||||||
'insecure' => (bool) data_get($protocol_settings, 'tls_settings.allow_insecure'),
|
'insecure' => (bool) data_get($protocol_settings, 'tls_settings.allow_insecure'),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
$this->appendUtls($array['tls'], $protocol_settings);
|
||||||
|
|
||||||
if ($serverName = data_get($protocol_settings, 'tls_settings.server_name')) {
|
if ($serverName = data_get($protocol_settings, 'tls_settings.server_name')) {
|
||||||
$array['tls']['server_name'] = $serverName;
|
$array['tls']['server_name'] = $serverName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->appendMultiplex($array, $protocol_settings);
|
||||||
|
|
||||||
$transport = match ($protocol_settings['network']) {
|
$transport = match ($protocol_settings['network']) {
|
||||||
'tcp' => data_get($protocol_settings, 'network_settings.header.type', 'none') !== 'none' ? [
|
'tcp' => data_get($protocol_settings, 'network_settings.header.type', 'none') !== 'none' ? [
|
||||||
'type' => 'http',
|
'type' => 'http',
|
||||||
@@ -354,12 +354,10 @@ class SingBox extends AbstractProtocol
|
|||||||
$tlsConfig = [
|
$tlsConfig = [
|
||||||
'enabled' => true,
|
'enabled' => true,
|
||||||
'insecure' => (bool) data_get($protocol_settings, 'tls_settings.allow_insecure'),
|
'insecure' => (bool) data_get($protocol_settings, 'tls_settings.allow_insecure'),
|
||||||
'utls' => [
|
|
||||||
'enabled' => true,
|
|
||||||
'fingerprint' => Helper::getRandFingerprint()
|
|
||||||
]
|
|
||||||
];
|
];
|
||||||
|
|
||||||
|
$this->appendUtls($tlsConfig, $protocol_settings);
|
||||||
|
|
||||||
switch ($protocol_settings['tls']) {
|
switch ($protocol_settings['tls']) {
|
||||||
case 1:
|
case 1:
|
||||||
if ($serverName = data_get($protocol_settings, 'tls_settings.server_name')) {
|
if ($serverName = data_get($protocol_settings, 'tls_settings.server_name')) {
|
||||||
@@ -379,6 +377,8 @@ class SingBox extends AbstractProtocol
|
|||||||
$array['tls'] = $tlsConfig;
|
$array['tls'] = $tlsConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->appendMultiplex($array, $protocol_settings);
|
||||||
|
|
||||||
$transport = match ($protocol_settings['network']) {
|
$transport = match ($protocol_settings['network']) {
|
||||||
'tcp' => data_get($protocol_settings, 'network_settings.header.type') == 'http' ? [
|
'tcp' => data_get($protocol_settings, 'network_settings.header.type') == 'http' ? [
|
||||||
'type' => 'http',
|
'type' => 'http',
|
||||||
@@ -433,9 +433,15 @@ class SingBox extends AbstractProtocol
|
|||||||
'insecure' => (bool) data_get($protocol_settings, 'allow_insecure', false),
|
'insecure' => (bool) data_get($protocol_settings, 'allow_insecure', false),
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
|
$this->appendUtls($array['tls'], $protocol_settings);
|
||||||
|
|
||||||
if ($serverName = data_get($protocol_settings, 'server_name')) {
|
if ($serverName = data_get($protocol_settings, 'server_name')) {
|
||||||
$array['tls']['server_name'] = $serverName;
|
$array['tls']['server_name'] = $serverName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->appendMultiplex($array, $protocol_settings);
|
||||||
|
|
||||||
$transport = match (data_get($protocol_settings, 'network')) {
|
$transport = match (data_get($protocol_settings, 'network')) {
|
||||||
'grpc' => [
|
'grpc' => [
|
||||||
'type' => 'grpc',
|
'type' => 'grpc',
|
||||||
@@ -619,27 +625,39 @@ class SingBox extends AbstractProtocol
|
|||||||
return $array;
|
return $array;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function buildMieru($password, $server): array
|
protected function appendMultiplex(&$array, $protocol_settings)
|
||||||
{
|
{
|
||||||
$protocol_settings = data_get($server, 'protocol_settings', []);
|
if ($multiplex = data_get($protocol_settings, 'multiplex')) {
|
||||||
$array = [
|
if (data_get($multiplex, 'enabled')) {
|
||||||
'type' => 'mieru',
|
$array['multiplex'] = [
|
||||||
'tag' => $server['name'],
|
'enabled' => true,
|
||||||
'server' => $server['host'],
|
'protocol' => data_get($multiplex, 'protocol', 'yamux'),
|
||||||
'server_port' => $server['port'],
|
'max_connections' => data_get($multiplex, 'max_connections'),
|
||||||
'username' => $password,
|
'min_streams' => data_get($multiplex, 'min_streams'),
|
||||||
'password' => $password,
|
'max_streams' => data_get($multiplex, 'max_streams'),
|
||||||
'transport' => strtolower(data_get($protocol_settings, 'transport', 'tcp')),
|
'padding' => (bool) data_get($multiplex, 'padding', false),
|
||||||
];
|
];
|
||||||
|
if (data_get($multiplex, 'brutal.enabled')) {
|
||||||
if (isset($server['ports'])) {
|
$array['multiplex']['brutal'] = [
|
||||||
$array['server_port_range'] = [$server['ports']];
|
'enabled' => true,
|
||||||
|
'up_mbps' => data_get($multiplex, 'brutal.up_mbps'),
|
||||||
|
'down_mbps' => data_get($multiplex, 'brutal.down_mbps'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
$array['multiplex'] = array_filter($array['multiplex'], fn($v) => !is_null($v));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ($multiplexing = data_get($protocol_settings, 'multiplexing')) {
|
protected function appendUtls(&$tlsConfig, $protocol_settings)
|
||||||
$array['multiplexing'] = $multiplexing;
|
{
|
||||||
|
if ($utls = data_get($protocol_settings, 'utls')) {
|
||||||
|
if (data_get($utls, 'enabled')) {
|
||||||
|
$tlsConfig['utls'] = [
|
||||||
|
'enabled' => true,
|
||||||
|
'fingerprint' => Helper::getTlsFingerprint($utls)
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $array;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -283,7 +283,9 @@ class Stash extends AbstractProtocol
|
|||||||
$array['uuid'] = $uuid;
|
$array['uuid'] = $uuid;
|
||||||
$array['udp'] = true;
|
$array['udp'] = true;
|
||||||
|
|
||||||
$array['client-fingerprint'] = Helper::getRandFingerprint();
|
if ($fingerprint = Helper::getTlsFingerprint(data_get($protocol_settings, 'utls'))) {
|
||||||
|
$array['client-fingerprint'] = $fingerprint;
|
||||||
|
}
|
||||||
|
|
||||||
switch (data_get($protocol_settings, 'tls')) {
|
switch (data_get($protocol_settings, 'tls')) {
|
||||||
case 1:
|
case 1:
|
||||||
@@ -312,10 +314,12 @@ class Stash extends AbstractProtocol
|
|||||||
$headerType = data_get($protocol_settings, 'network_settings.header.type', 'tcp');
|
$headerType = data_get($protocol_settings, 'network_settings.header.type', 'tcp');
|
||||||
$array['network'] = ($headerType === 'http') ? 'http' : 'tcp';
|
$array['network'] = ($headerType === 'http') ? 'http' : 'tcp';
|
||||||
if ($headerType === 'http') {
|
if ($headerType === 'http') {
|
||||||
if ($httpOpts = array_filter([
|
if (
|
||||||
'headers' => data_get($protocol_settings, 'network_settings.header.request.headers'),
|
$httpOpts = array_filter([
|
||||||
'path' => data_get($protocol_settings, 'network_settings.header.request.path', ['/'])
|
'headers' => data_get($protocol_settings, 'network_settings.header.request.headers'),
|
||||||
])) {
|
'path' => data_get($protocol_settings, 'network_settings.header.request.path', ['/'])
|
||||||
|
])
|
||||||
|
) {
|
||||||
$array['http-opts'] = $httpOpts;
|
$array['http-opts'] = $httpOpts;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -331,11 +335,11 @@ class Stash extends AbstractProtocol
|
|||||||
$array['network'] = 'grpc';
|
$array['network'] = 'grpc';
|
||||||
$array['grpc-opts']['grpc-service-name'] = data_get($protocol_settings, 'network_settings.serviceName');
|
$array['grpc-opts']['grpc-service-name'] = data_get($protocol_settings, 'network_settings.serviceName');
|
||||||
break;
|
break;
|
||||||
// case 'h2':
|
// case 'h2':
|
||||||
// $array['network'] = 'h2';
|
// $array['network'] = 'h2';
|
||||||
// $array['h2-opts']['host'] = data_get($protocol_settings, 'network_settings.host');
|
// $array['h2-opts']['host'] = data_get($protocol_settings, 'network_settings.host');
|
||||||
// $array['h2-opts']['path'] = data_get($protocol_settings, 'network_settings.path');
|
// $array['h2-opts']['path'] = data_get($protocol_settings, 'network_settings.path');
|
||||||
// break;
|
// break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $array;
|
return $array;
|
||||||
|
|||||||
@@ -96,8 +96,41 @@ class ServerService
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build node config data
|
* Update node metrics and load status
|
||||||
*/
|
*/
|
||||||
|
public static function updateMetrics(Server $node, array $metrics): void
|
||||||
|
{
|
||||||
|
$nodeType = strtoupper($node->type);
|
||||||
|
$nodeId = $node->id;
|
||||||
|
$cacheTime = max(300, (int) admin_setting('server_push_interval', 60) * 3);
|
||||||
|
|
||||||
|
$metricsData = [
|
||||||
|
'uptime' => (int) ($metrics['uptime'] ?? 0),
|
||||||
|
'goroutines' => (int) ($metrics['goroutines'] ?? 0),
|
||||||
|
'active_connections' => (int) ($metrics['active_connections'] ?? 0),
|
||||||
|
'total_connections' => (int) ($metrics['total_connections'] ?? 0),
|
||||||
|
'total_users' => (int) ($metrics['total_users'] ?? 0),
|
||||||
|
'active_users' => (int) ($metrics['active_users'] ?? 0),
|
||||||
|
'inbound_speed' => (int) ($metrics['inbound_speed'] ?? 0),
|
||||||
|
'outbound_speed' => (int) ($metrics['outbound_speed'] ?? 0),
|
||||||
|
'cpu_per_core' => $metrics['cpu_per_core'] ?? [],
|
||||||
|
'load' => $metrics['load'] ?? [],
|
||||||
|
'speed_limiter' => $metrics['speed_limiter'] ?? [],
|
||||||
|
'gc' => $metrics['gc'] ?? [],
|
||||||
|
'api' => $metrics['api'] ?? [],
|
||||||
|
'ws' => $metrics['ws'] ?? [],
|
||||||
|
'limits' => $metrics['limits'] ?? [],
|
||||||
|
'updated_at' => now()->timestamp,
|
||||||
|
'kernel_status' => (bool) ($metrics['kernel_status'] ?? false),
|
||||||
|
];
|
||||||
|
|
||||||
|
\Illuminate\Support\Facades\Cache::put(
|
||||||
|
\App\Utils\CacheKey::get('SERVER_' . $nodeType . '_METRICS', $nodeId),
|
||||||
|
$metricsData,
|
||||||
|
$cacheTime
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public static function buildNodeConfig(Server $node): array
|
public static function buildNodeConfig(Server $node): array
|
||||||
{
|
{
|
||||||
$nodeType = $node->type;
|
$nodeType = $node->type;
|
||||||
@@ -120,28 +153,31 @@ class ServerService
|
|||||||
'plugin' => $protocolSettings['plugin'],
|
'plugin' => $protocolSettings['plugin'],
|
||||||
'plugin_opts' => $protocolSettings['plugin_opts'],
|
'plugin_opts' => $protocolSettings['plugin_opts'],
|
||||||
'server_key' => match ($protocolSettings['cipher']) {
|
'server_key' => match ($protocolSettings['cipher']) {
|
||||||
'2022-blake3-aes-128-gcm' => Helper::getServerKey($node->created_at, 16),
|
'2022-blake3-aes-128-gcm' => Helper::getServerKey($node->created_at, 16),
|
||||||
'2022-blake3-aes-256-gcm' => Helper::getServerKey($node->created_at, 32),
|
'2022-blake3-aes-256-gcm' => Helper::getServerKey($node->created_at, 32),
|
||||||
default => null,
|
default => null,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
'vmess' => [
|
'vmess' => [
|
||||||
...$baseConfig,
|
...$baseConfig,
|
||||||
'tls' => (int) $protocolSettings['tls'],
|
'tls' => (int) $protocolSettings['tls'],
|
||||||
|
'multiplex' => data_get($protocolSettings, 'multiplex'),
|
||||||
],
|
],
|
||||||
'trojan' => [
|
'trojan' => [
|
||||||
...$baseConfig,
|
...$baseConfig,
|
||||||
'host' => $host,
|
'host' => $host,
|
||||||
'server_name' => $protocolSettings['server_name'],
|
'server_name' => $protocolSettings['server_name'],
|
||||||
|
'multiplex' => data_get($protocolSettings, 'multiplex'),
|
||||||
],
|
],
|
||||||
'vless' => [
|
'vless' => [
|
||||||
...$baseConfig,
|
...$baseConfig,
|
||||||
'tls' => (int) $protocolSettings['tls'],
|
'tls' => (int) $protocolSettings['tls'],
|
||||||
'flow' => $protocolSettings['flow'],
|
'flow' => $protocolSettings['flow'],
|
||||||
'tls_settings' => match ((int) $protocolSettings['tls']) {
|
'tls_settings' => match ((int) $protocolSettings['tls']) {
|
||||||
2 => $protocolSettings['reality_settings'],
|
2 => $protocolSettings['reality_settings'],
|
||||||
default => $protocolSettings['tls_settings'],
|
default => $protocolSettings['tls_settings'],
|
||||||
},
|
},
|
||||||
|
'multiplex' => data_get($protocolSettings, 'multiplex'),
|
||||||
],
|
],
|
||||||
'hysteria' => [
|
'hysteria' => [
|
||||||
...$baseConfig,
|
...$baseConfig,
|
||||||
@@ -152,13 +188,13 @@ class ServerService
|
|||||||
'up_mbps' => (int) $protocolSettings['bandwidth']['up'],
|
'up_mbps' => (int) $protocolSettings['bandwidth']['up'],
|
||||||
'down_mbps' => (int) $protocolSettings['bandwidth']['down'],
|
'down_mbps' => (int) $protocolSettings['bandwidth']['down'],
|
||||||
...match ((int) $protocolSettings['version']) {
|
...match ((int) $protocolSettings['version']) {
|
||||||
1 => ['obfs' => $protocolSettings['obfs']['password'] ?? null],
|
1 => ['obfs' => $protocolSettings['obfs']['password'] ?? null],
|
||||||
2 => [
|
2 => [
|
||||||
'obfs' => $protocolSettings['obfs']['open'] ? $protocolSettings['obfs']['type'] : null,
|
'obfs' => $protocolSettings['obfs']['open'] ? $protocolSettings['obfs']['type'] : null,
|
||||||
'obfs-password' => $protocolSettings['obfs']['password'] ?? null,
|
'obfs-password' => $protocolSettings['obfs']['password'] ?? null,
|
||||||
],
|
],
|
||||||
default => [],
|
default => [],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
'tuic' => [
|
'tuic' => [
|
||||||
...$baseConfig,
|
...$baseConfig,
|
||||||
@@ -195,8 +231,10 @@ class ServerService
|
|||||||
],
|
],
|
||||||
'mieru' => [
|
'mieru' => [
|
||||||
...$baseConfig,
|
...$baseConfig,
|
||||||
'server_port' => (string) $serverPort,
|
'server_port' => (int) $serverPort,
|
||||||
'protocol' => (int) $protocolSettings['protocol'],
|
'transport' => data_get($protocolSettings, 'transport', 'TCP'),
|
||||||
|
'traffic_pattern' => $protocolSettings['traffic_pattern'],
|
||||||
|
// 'multiplex' => data_get($protocolSettings, 'multiplex'),
|
||||||
],
|
],
|
||||||
default => [],
|
default => [],
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -188,8 +188,20 @@ class Helper
|
|||||||
public static function getIpByDomainName($domain) {
|
public static function getIpByDomainName($domain) {
|
||||||
return gethostbynamel($domain) ?: [];
|
return gethostbynamel($domain) ?: [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function getTlsFingerprint($utls = null)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (is_array($utls) || is_object($utls)) {
|
||||||
|
if (!data_get($utls, 'enabled')) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
$fingerprint = data_get($utls, 'fingerprint', 'chrome');
|
||||||
|
if ($fingerprint !== 'random') {
|
||||||
|
return $fingerprint;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static function getRandFingerprint() {
|
|
||||||
$fingerprints = ['chrome', 'firefox', 'safari', 'ios', 'edge', 'qq'];
|
$fingerprints = ['chrome', 'firefox', 'safari', 'ios', 'edge', 'qq'];
|
||||||
return Arr::random($fingerprints);
|
return Arr::random($fingerprints);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -141,8 +141,9 @@ docker compose up -d
|
|||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
If you encounter any issues during installation or operation, please check:
|
If you encounter any issues during installation or operation, please check:
|
||||||
1. System requirements are met
|
1. **Empty Admin Dashboard**: If the admin panel is blank, run `git submodule update --init --recursive --force` to restore the theme files.
|
||||||
2. All required ports are available
|
2. System requirements are met
|
||||||
|
3. All required ports are available
|
||||||
3. Docker services are running properly
|
3. Docker services are running properly
|
||||||
4. Nginx configuration is correct
|
4. Nginx configuration is correct
|
||||||
5. Check logs for detailed error messages
|
5. Check logs for detailed error messages
|
||||||
|
|||||||
@@ -169,8 +169,9 @@ sh update.sh
|
|||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
### Common Issues
|
### Common Issues
|
||||||
1. Changes to admin path require service restart to take effect
|
1. **Empty Admin Dashboard**: If the admin panel is blank, run `git submodule update --init --recursive --force` to restore the theme files.
|
||||||
2. Any code changes after enabling Octane require restart to take effect
|
2. Changes to admin path require service restart to take effect
|
||||||
|
3. Any code changes after enabling Octane require restart to take effect
|
||||||
3. When PHP extension installation fails, check if PHP version is correct
|
3. When PHP extension installation fails, check if PHP version is correct
|
||||||
4. For database connection failures, check database configuration and permissions
|
4. For database connection failures, check database configuration and permissions
|
||||||
|
|
||||||
|
|||||||
Submodule public/assets/admin updated: 155f00a9d7...5296f66eb9
Reference in New Issue
Block a user