fix(nodes): agent 主控地址预填 origin+基础路径,地址为空时不生成安装命令并提示

This commit is contained in:
Muse
2026-10-21 22:05:09 +00:00
parent dcfaf6ace6
commit 0ed6d8a1cf
16 changed files with 103 additions and 18 deletions
+24 -5
View File
@@ -20,6 +20,7 @@ import type { Msg } from '@/utils';
import { NodeFormSchema, type NodeFormValues, type ProbeResult } from '@/schemas/node';
import { FormField, rhfZodValidate } from '@/components/form/rhf';
import { useOutboundTagGroups } from '@/api/queries/useOutboundTags';
import { currentPanelMasterUrl } from './agentMasterUrl';
import './NodeFormModal.css';
type Mode = 'add' | 'edit';
@@ -248,8 +249,10 @@ export default function NodeFormModal({
if (agentMode && mode === 'add' && obj?.agentToken) {
setAgentToken(obj.agentToken);
setAgentNodeName(result.data.name.trim());
// Default master URL to the current panel origin; user can edit it.
setAgentMasterUrl(window.location.origin);
// Default the master URL to the address this panel is being
// viewed through — origin AND base path. Dropping the base
// path made the agent's registration 404. User can edit it.
setAgentMasterUrl(currentPanelMasterUrl());
// Keep the modal open state; the token dialog shows on top.
} else {
onOpenChange(false);
@@ -264,6 +267,13 @@ export default function NodeFormModal({
if (!submitting) onOpenChange(false);
}
// The install command is only meaningful with a master URL; an empty
// one would produce a command that can never register, so withhold it
// (and say why) instead of letting the user copy a dead command.
const installCmd = agentMasterUrl
? `bash <(curl -fL https://git.881218.xyz/rogue/3x-ui-agent/raw/branch/main/install-agent.sh) --master ${agentMasterUrl} --token ${agentToken ?? ''} --name ${agentNodeName}`
: '';
return (
<>
{messageContextHolder}
@@ -560,9 +570,9 @@ export default function NodeFormModal({
<Button
key="copy"
type="primary"
disabled={!installCmd}
onClick={() => {
const cmd = `bash <(curl -fL https://git.881218.xyz/rogue/3x-ui-agent/raw/branch/main/install-agent.sh) --master ${agentMasterUrl} --token ${agentToken} --name ${agentNodeName}`;
navigator.clipboard.writeText(cmd).then(
navigator.clipboard.writeText(installCmd).then(
() => messageApi.success(t('pages.nodes.agentTokenCopied')),
() => messageApi.error(t('pages.nodes.agentTokenCopyFailed')),
);
@@ -597,9 +607,18 @@ export default function NodeFormModal({
onChange={(e) => setAgentMasterUrl(e.target.value.trim())}
style={{ marginBottom: 12, fontFamily: 'monospace' }}
/>
{!agentMasterUrl && (
<Alert
type="warning"
showIcon
style={{ marginBottom: 12 }}
title={t('pages.nodes.agentMasterUrlRequired')}
/>
)}
<p>{t('pages.nodes.agentTokenHint')}</p>
<Input.TextArea
value={`bash <(curl -fL https://git.881218.xyz/rogue/3x-ui-agent/raw/branch/main/install-agent.sh) --master ${agentMasterUrl} --token ${agentToken} --name ${agentNodeName}`}
value={installCmd}
placeholder={t('pages.nodes.agentMasterUrlRequired')}
readOnly
rows={4}
style={{ fontFamily: 'monospace' }}
@@ -0,0 +1,20 @@
// Default master URL offered in the agent install command.
//
// The agent registers against {master_url}/panel/api/agent/register, and
// the panel itself is usually served under a base path (webBasePath, e.g.
// "/rogue"). Prefilling the bare origin — as the form used to — produced
// an install command whose registration 404'd, leaving the node stuck at
// "unknown" on the panel. The address the operator is viewing the panel
// through already contains the base path, so derive the default from it:
// origin + window.X_UI_BASE_PATH (injected by the panel server). The
// operator can still override it (e.g. a public address instead of a LAN
// or tunnel one).
export function computeDefaultMasterUrl(origin: string, basePath: string | undefined): string {
const trimmed = (basePath || '/').replace(/\/+$/, '');
return origin + trimmed;
}
export function currentPanelMasterUrl(): string {
if (typeof window === 'undefined') return '';
return computeDefaultMasterUrl(window.location.origin, window.X_UI_BASE_PATH);
}
@@ -0,0 +1,33 @@
import { describe, expect, it } from 'vitest';
import { computeDefaultMasterUrl } from '@/pages/nodes/agentMasterUrl';
// The install command's --master must be the address agents can register
// against: origin + panel base path. Dropping the base path was the
// production bug — registration 404'd and the node stayed "unknown".
describe('computeDefaultMasterUrl', () => {
it('keeps a base path with trailing slash', () => {
expect(computeDefaultMasterUrl('https://amb.example.com', '/rogue/')).toBe(
'https://amb.example.com/rogue',
);
});
it('keeps a base path without trailing slash', () => {
expect(computeDefaultMasterUrl('https://amb.example.com', '/rogue')).toBe(
'https://amb.example.com/rogue',
);
});
it('root base path yields the bare origin', () => {
expect(computeDefaultMasterUrl('http://192.168.0.1:55889', '/')).toBe('http://192.168.0.1:55889');
});
it('undefined or empty base path yields the bare origin', () => {
expect(computeDefaultMasterUrl('http://10.0.0.2:2053', undefined)).toBe('http://10.0.0.2:2053');
expect(computeDefaultMasterUrl('http://10.0.0.2:2053', '')).toBe('http://10.0.0.2:2053');
});
it('nested base paths survive', () => {
expect(computeDefaultMasterUrl('https://example.com', '/a/b/')).toBe('https://example.com/a/b');
});
});
+2 -1
View File
@@ -921,7 +921,8 @@
"agentTokenCopied": "Install command copied",
"agentTokenCopyFailed": "Copy failed, please copy manually",
"copyToken": "Copy Install Command",
"agentMasterUrlHint": "Master URL (used by agent to reach the master; change to public address if behind proxy):"
"agentMasterUrlHint": "Master URL (used by agent to reach the master; change to public address if behind proxy):",
"agentMasterUrlRequired": "Master URL is empty — enter the master address first; the install command is generated only then."
},
"settings": {
"defaultTag": "افتراضي",
+2 -1
View File
@@ -1061,7 +1061,8 @@
"agentTokenCopied": "Install command copied",
"agentTokenCopyFailed": "Copy failed, please copy manually",
"copyToken": "Copy Install Command",
"agentMasterUrlHint": "Master URL (used by agent to reach the master; change to public address if behind proxy):"
"agentMasterUrlHint": "Master URL (used by agent to reach the master; change to public address if behind proxy):",
"agentMasterUrlRequired": "Master URL is empty — enter the master address first; the install command is generated only then."
},
"settings": {
"defaultTag": "Default",
+2 -1
View File
@@ -921,7 +921,8 @@
"agentTokenCopied": "Install command copied",
"agentTokenCopyFailed": "Copy failed, please copy manually",
"copyToken": "Copy Install Command",
"agentMasterUrlHint": "Master URL (used by agent to reach the master; change to public address if behind proxy):"
"agentMasterUrlHint": "Master URL (used by agent to reach the master; change to public address if behind proxy):",
"agentMasterUrlRequired": "Master URL is empty — enter the master address first; the install command is generated only then."
},
"settings": {
"defaultTag": "Predeterminado",
+2 -1
View File
@@ -921,7 +921,8 @@
"agentTokenCopied": "Install command copied",
"agentTokenCopyFailed": "Copy failed, please copy manually",
"copyToken": "Copy Install Command",
"agentMasterUrlHint": "Master URL (used by agent to reach the master; change to public address if behind proxy):"
"agentMasterUrlHint": "Master URL (used by agent to reach the master; change to public address if behind proxy):",
"agentMasterUrlRequired": "Master URL is empty — enter the master address first; the install command is generated only then."
},
"settings": {
"defaultTag": "پیش‌فرض",
+2 -1
View File
@@ -921,7 +921,8 @@
"agentTokenCopied": "Install command copied",
"agentTokenCopyFailed": "Copy failed, please copy manually",
"copyToken": "Copy Install Command",
"agentMasterUrlHint": "Master URL (used by agent to reach the master; change to public address if behind proxy):"
"agentMasterUrlHint": "Master URL (used by agent to reach the master; change to public address if behind proxy):",
"agentMasterUrlRequired": "Master URL is empty — enter the master address first; the install command is generated only then."
},
"settings": {
"defaultTag": "Bawaan",
+2 -1
View File
@@ -921,7 +921,8 @@
"agentTokenCopied": "Install command copied",
"agentTokenCopyFailed": "Copy failed, please copy manually",
"copyToken": "Copy Install Command",
"agentMasterUrlHint": "Master URL (used by agent to reach the master; change to public address if behind proxy):"
"agentMasterUrlHint": "Master URL (used by agent to reach the master; change to public address if behind proxy):",
"agentMasterUrlRequired": "Master URL is empty — enter the master address first; the install command is generated only then."
},
"settings": {
"defaultTag": "デフォルト",
+2 -1
View File
@@ -921,7 +921,8 @@
"agentTokenCopied": "Install command copied",
"agentTokenCopyFailed": "Copy failed, please copy manually",
"copyToken": "Copy Install Command",
"agentMasterUrlHint": "Master URL (used by agent to reach the master; change to public address if behind proxy):"
"agentMasterUrlHint": "Master URL (used by agent to reach the master; change to public address if behind proxy):",
"agentMasterUrlRequired": "Master URL is empty — enter the master address first; the install command is generated only then."
},
"settings": {
"defaultTag": "Padrão",
+2 -1
View File
@@ -921,7 +921,8 @@
"agentTokenCopied": "Install command copied",
"agentTokenCopyFailed": "Copy failed, please copy manually",
"copyToken": "Copy Install Command",
"agentMasterUrlHint": "Master URL (used by agent to reach the master; change to public address if behind proxy):"
"agentMasterUrlHint": "Master URL (used by agent to reach the master; change to public address if behind proxy):",
"agentMasterUrlRequired": "Master URL is empty — enter the master address first; the install command is generated only then."
},
"settings": {
"defaultTag": "По умолчанию",
+2 -1
View File
@@ -921,7 +921,8 @@
"agentTokenCopied": "Install command copied",
"agentTokenCopyFailed": "Copy failed, please copy manually",
"copyToken": "Copy Install Command",
"agentMasterUrlHint": "Master URL (used by agent to reach the master; change to public address if behind proxy):"
"agentMasterUrlHint": "Master URL (used by agent to reach the master; change to public address if behind proxy):",
"agentMasterUrlRequired": "Master URL is empty — enter the master address first; the install command is generated only then."
},
"settings": {
"defaultTag": "Varsayılan",
+2 -1
View File
@@ -921,7 +921,8 @@
"agentTokenCopied": "Install command copied",
"agentTokenCopyFailed": "Copy failed, please copy manually",
"copyToken": "Copy Install Command",
"agentMasterUrlHint": "Master URL (used by agent to reach the master; change to public address if behind proxy):"
"agentMasterUrlHint": "Master URL (used by agent to reach the master; change to public address if behind proxy):",
"agentMasterUrlRequired": "Master URL is empty — enter the master address first; the install command is generated only then."
},
"settings": {
"defaultTag": "Типово",
+2 -1
View File
@@ -921,7 +921,8 @@
"agentTokenCopied": "Install command copied",
"agentTokenCopyFailed": "Copy failed, please copy manually",
"copyToken": "Copy Install Command",
"agentMasterUrlHint": "Master URL (used by agent to reach the master; change to public address if behind proxy):"
"agentMasterUrlHint": "Master URL (used by agent to reach the master; change to public address if behind proxy):",
"agentMasterUrlRequired": "Master URL is empty — enter the master address first; the install command is generated only then."
},
"settings": {
"defaultTag": "Mặc định",
+2 -1
View File
@@ -943,7 +943,8 @@
"agentTokenCopied": "安装命令已复制",
"agentTokenCopyFailed": "复制失败,请手动复制",
"copyToken": "复制安装命令",
"agentMasterUrlHint": "主控地址(agent 用来连接主控,如有域名或反代请改成对外地址):"
"agentMasterUrlHint": "主控地址(agent 用来连接主控,如有域名或反代请改成对外地址):",
"agentMasterUrlRequired": "主控地址为空——请先填入主控地址,之后才会生成安装命令。"
},
"settings": {
"defaultTag": "默认",
+2 -1
View File
@@ -921,7 +921,8 @@
"agentTokenCopied": "Install command copied",
"agentTokenCopyFailed": "Copy failed, please copy manually",
"copyToken": "Copy Install Command",
"agentMasterUrlHint": "Master URL (used by agent to reach the master; change to public address if behind proxy):"
"agentMasterUrlHint": "Master URL (used by agent to reach the master; change to public address if behind proxy):",
"agentMasterUrlRequired": "Master URL is empty — enter the master address first; the install command is generated only then."
},
"settings": {
"defaultTag": "預設",