From 9bb99e55097b7c708a170e7c9e117fd9495a59bc Mon Sep 17 00:00:00 2001 From: Haitao Pan Date: Fri, 6 Feb 2026 12:06:35 +0800 Subject: [PATCH] refactor: Refine VLESS node selection logic to prioritize bound nodes, filter internal agents, and remove explicit sandbox fallback node generation. --- .../user-center/components/VlessQrCard.tsx | 48 +++++++++---------- .../builtin/user-center/lib/vless.ts | 4 +- .../builtin/user-center/routes/agent.tsx | 21 ++++++-- 3 files changed, 41 insertions(+), 32 deletions(-) diff --git a/src/modules/extensions/builtin/user-center/components/VlessQrCard.tsx b/src/modules/extensions/builtin/user-center/components/VlessQrCard.tsx index c686b8c..b3ebe79 100644 --- a/src/modules/extensions/builtin/user-center/components/VlessQrCard.tsx +++ b/src/modules/extensions/builtin/user-center/components/VlessQrCard.tsx @@ -58,24 +58,6 @@ interface VlessQrCardProps { boundNodeAddress?: string | null } -function buildSandboxFallbackNode(): VlessNode { - return { - name: 'Sandbox Node', - address: 'ha-proxy-jp.svc.plus', - port: 1443, - server_name: 'ha-proxy-jp.svc.plus', - transport: 'tcp', - tcp_port: 1443, - xhttp_port: 443, - path: '/split', - mode: 'auto', - flow: 'xtls-rprx-vision', - uri_scheme_tcp: - 'vless://${UUID}@${DOMAIN}:1443?encryption=none&flow=${FLOW}&security=tls&sni=${SNI}&fp=${FP}&type=tcp#${TAG}', - uri_scheme_xhttp: - 'vless://${UUID}@${DOMAIN}:443?encryption=none&security=tls&sni=${SNI}&fp=${FP}&type=xhttp&path=${PATH}&mode=${MODE}#${TAG}', - } -} export default function VlessQrCard({ uuid, @@ -83,7 +65,15 @@ export default function VlessQrCard({ allowSandboxFallbackNode = false, boundNodeAddress, }: VlessQrCardProps) { - const { data: nodes, error: nodesError } = useSWR('/api/agent-server/v1/nodes', fetcher) + const { data: allNodes, error: nodesError } = useSWR('/api/agent-server/v1/nodes', fetcher) + + const nodes = useMemo(() => { + return (allNodes ?? []).filter((node) => { + const name = (node.name || '').toLowerCase() + // Skip the redundant Internal Agents (Shared Token) node + return !(name.includes('internal agents') && name.includes('shared token')) + }) + }, [allNodes]) const [selectedNode, setSelectedNode] = useState(null) const [preferredTransport, setPreferredTransport] = useState('tcp') const [isSelectorOpen, setIsSelectorOpen] = useState(false) @@ -95,16 +85,21 @@ export default function VlessQrCard({ const rawNode = useMemo(() => { if (selectedNode) return selectedNode - if (boundNodeAddress && nodes?.length) { - const matched = nodes.find((node) => node.address === boundNodeAddress) + + // 1. Try to use the node bound by root management (search in all nodes, including filtered ones) + if (boundNodeAddress && allNodes?.length) { + const matched = allNodes.find((node) => node.address === boundNodeAddress) if (matched) { return matched } } + + // 2. Default to the first visible (non-filtered) node if (nodes && nodes[0]) return nodes[0] - if (allowSandboxFallbackNode && uuid) return buildSandboxFallbackNode() + + // 3. No fallback node return undefined - }, [allowSandboxFallbackNode, boundNodeAddress, nodes, selectedNode, uuid]) + }, [allNodes, allowSandboxFallbackNode, boundNodeAddress, nodes, selectedNode]) const effectiveNode = useMemo((): VlessNode | undefined => { if (!rawNode) return undefined @@ -296,9 +291,12 @@ export default function VlessQrCard({ ) : (!nodes || nodes.length === 0) && !rawNode ? (
-

❌ 节点数据缺失

+

❌ {allowSandboxFallbackNode ? '由于演示模式限制,请联系管理员或 Root 用户完成节点绑定' : '节点数据缺失'}

- 无法从服务器获取代理节点列表{nodesError ? `(${nodesError.message})` : ''}。请检查 /api/agent-server/v1/nodes 接口是否正常。 + {allowSandboxFallbackNode + ? '演示模式账号未在本地存储发现有效节点绑定,请使用 root 账号在此浏览器完成一次 Sandbox 节点绑定逻辑。' + : `无法从服务器获取代理节点列表${nodesError ? `(${nodesError.message})` : ''}。请检查 /api/agent-server/v1/nodes 接口是否正常。` + }

) : !effectiveNode ? ( diff --git a/src/modules/extensions/builtin/user-center/lib/vless.ts b/src/modules/extensions/builtin/user-center/lib/vless.ts index e056a8b..2b753d1 100644 --- a/src/modules/extensions/builtin/user-center/lib/vless.ts +++ b/src/modules/extensions/builtin/user-center/lib/vless.ts @@ -57,7 +57,7 @@ const DEFAULT_XRAY_CONFIG = { settings: { vnext: [ { - address: 'ha-proxy-jp.svc.plus', + address: 'your-node.svc.plus', port: 1443, users: [ { @@ -73,7 +73,7 @@ const DEFAULT_XRAY_CONFIG = { network: 'tcp', security: 'tls', tlsSettings: { - serverName: 'ha-proxy-jp.svc.plus', + serverName: 'your-node.svc.plus', allowInsecure: false, fingerprint: 'chrome', }, diff --git a/src/modules/extensions/builtin/user-center/routes/agent.tsx b/src/modules/extensions/builtin/user-center/routes/agent.tsx index 6278698..cd44404 100644 --- a/src/modules/extensions/builtin/user-center/routes/agent.tsx +++ b/src/modules/extensions/builtin/user-center/routes/agent.tsx @@ -84,18 +84,29 @@ export default function UserCenterAgentRoute() { port: 443, transport: 'tcp', security: 'tls', - }) + } as any) }, [isGuestSandboxReadOnly]) const effectiveNodes = useMemo(() => { + // 1. If we have a bound node address (from root management), try to find it in the full list + if (isGuestSandboxReadOnly && normalizedEmail) { + const binding = getSandboxNodeBinding() + if (binding?.address && nodes?.length) { + const matched = nodes.find((n) => n.address === binding.address) + if (matched) { + return [matched] + } + } + } + + // 2. Otherwise, use all displayable nodes if (visibleNodes.length > 0) { return visibleNodes } - if (isGuestSandboxReadOnly && boundNode) { - return [boundNode] - } + + // 3. No fallback logic return [] - }, [boundNode, isGuestSandboxReadOnly, visibleNodes]) + }, [isGuestSandboxReadOnly, nodes, visibleNodes, normalizedEmail]) const groupedNodes = useMemo(() => { const groups: Record = {