From 5b8e42dfc16405ddcdf98e2848ea4e907d1dff08 Mon Sep 17 00:00:00 2001 From: Z User Date: Mon, 31 Aug 2026 05:31:24 +0000 Subject: [PATCH] feat(ui): per-protocol required/optional/auto field guide; XHTTP VLESS-only Show a unified field guide alert for each protocol. Keep XHTTP out of VMess/Trojan transport options and document that on the transport control. feat(ui): per-protocol required/optional/auto field guide; XHTTP VLESS-only Show a unified field guide alert for each protocol. Keep XHTTP out of VMess/Trojan transport options and document that on the transport control. feat(ui): per-protocol required/optional/auto field guide; XHTTP VLESS-only Show a unified field guide alert for each protocol. Keep XHTTP out of VMess/Trojan transport options and document that on the transport control. feat(ui): per-protocol required/optional/auto field guide; XHTTP VLESS-only Show a unified field guide alert for each protocol. Keep XHTTP out of VMess/Trojan transport options and document that on the transport control. feat(ui): per-protocol required/optional/auto field guide; XHTTP VLESS-only Show a unified field guide alert for each protocol. Keep XHTTP out of VMess/Trojan transport options and document that on the transport control. feat(ui): per-protocol required/optional/auto field guide; XHTTP VLESS-only Show a unified field guide alert for each protocol. Keep XHTTP out of VMess/Trojan transport options and document that on the transport control. feat(ui): per-protocol required/optional/auto field guide; XHTTP VLESS-only Show a unified field guide alert for each protocol. Keep XHTTP out of VMess/Trojan transport options and document that on the transport control. feat(ui): per-protocol required/optional/auto field guide; XHTTP VLESS-only Show a unified field guide alert for each protocol. Keep XHTTP out of VMess/Trojan transport options and document that on the transport control. feat(ui): per-protocol required/optional/auto field guide; XHTTP VLESS-only Show a unified field guide alert for each protocol. Keep XHTTP out of VMess/Trojan transport options and document that on the transport control. feat(ui): per-protocol required/optional/auto field guide; XHTTP VLESS-only Show a unified field guide alert for each protocol. Keep XHTTP out of VMess/Trojan transport options and document that on the transport control. feat(ui): per-protocol required/optional/auto field guide; XHTTP VLESS-only Show a unified field guide alert for each protocol. Keep XHTTP out of VMess/Trojan transport options and document that on the transport control. feat(ui): per-protocol required/optional/auto field guide; XHTTP VLESS-only Show a unified field guide alert for each protocol. Keep XHTTP out of VMess/Trojan transport options and document that on the transport control. --- .../src/components/ListenerConfigFields.tsx | 49 ++++++++++++++++++- frontend/src/i18n/locales/en.ts | 2 +- frontend/src/i18n/locales/zh.ts | 2 +- 3 files changed, 50 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/ListenerConfigFields.tsx b/frontend/src/components/ListenerConfigFields.tsx index e5a1ea7..87acf2e 100644 --- a/frontend/src/components/ListenerConfigFields.tsx +++ b/frontend/src/components/ListenerConfigFields.tsx @@ -34,6 +34,7 @@ const WRAPPER_TLS_PROTOCOLS = new Set([ const MUX_PROTOCOLS = new Set(['shadowsocks', 'vmess', 'vless', 'trojan']); const SIMPLE_OBFS_PROTOCOLS = new Set(['shadowsocks']); const KCP_TUN_PROTOCOLS = new Set(['shadowsocks']); +// XHTTP is VLESS-only (MetaCubeX listener schema). Do NOT add vmess/trojan here. const XHTTP_PROTOCOLS = new Set(['vless']); const MKCP_PROTOCOLS = new Set(['vmess']); const MEKYA_PROTOCOLS = new Set(['vmess']); @@ -693,6 +694,42 @@ export function formValuesToConfig( return cfg; } + +/** Per-protocol required / optional / auto-generate field guide. */ +const ProtocolFieldGuide: React.FC<{ protocol: string }> = ({ protocol }) => { + const { t } = useI18n(); + const key = protocol.toLowerCase(); + const required = t(`listeners.guide.${key}.required`); + const optional = t(`listeners.guide.${key}.optional`); + const auto = t(`listeners.guide.${key}.auto`); + // If i18n missing, t() may return the key path — skip empty-looking guides. + const missing = (s: string) => !s || s.startsWith('listeners.guide.'); + if (missing(required) && missing(optional) && missing(auto)) { + return ( + + ); + } + return ( + +
{t('listeners.fieldRequired') || 'Required'}: {missing(required) ? '—' : required}
+
{t('listeners.fieldOptional') || 'Optional'}: {missing(optional) ? '—' : optional}
+
{t('listeners.fieldAuto') || 'Auto-generate if empty'}: {missing(auto) ? '—' : auto}
+ + } + /> + ); +}; + /** Collapsible-style section with enable switch driving nested fields. */ const EnableSection: React.FC<{ name: string; @@ -749,11 +786,21 @@ const ListenerConfigFields: React.FC = ({ protocol }) => { + {TRANSPORT_PROTOCOLS.has(protocol) && ( <> {t('listeners.sectionTransport')} - + TCP WebSocket diff --git a/frontend/src/i18n/locales/en.ts b/frontend/src/i18n/locales/en.ts index 7ad13c1..9960a45 100644 --- a/frontend/src/i18n/locales/en.ts +++ b/frontend/src/i18n/locales/en.ts @@ -10,7 +10,7 @@ export default { login: { title: '3M-UI', subtitle: 'Mihomo Core Management Console', username: 'Username', password: 'Password', button: 'Login', welcomeBack: 'Welcome back', failed: 'Login failed' }, password: { title: 'Change Password', current: 'Current Password', new: 'New Password', confirm: 'Confirm New Password', button: 'Change Password', success: 'Password updated', failed: 'Failed to change password', mismatch: 'Passwords do not match' }, dashboard: { title: 'Dashboard', subtitle: 'System & Core Overview', status: 'Core Status', running: 'Running', stoppedStatus: 'Stopped', version: 'Version', pid: 'PID', uptime: 'Uptime', listeners: 'Node Stats', total: 'Total', enabled: 'Enabled', disabled: 'Disabled', system: 'System Resources', cpu: 'CPU', memory: 'Memory', disk: 'Disk', network: 'Network', upload: 'Upload', download: 'Download', traffic: 'Traffic Stats', uploadRate: 'Upload rate', downloadRate: 'Download rate', totalUpload: 'Total upload', totalDownload: 'Total download', onlineUsers: 'Online users', activeConnections: 'Active connections', start: 'Start', stop: 'Stop', restart: 'Restart', started: 'Core started', stopped: 'Core stopped', restarted: 'Core restarted', operationFailed: 'Operation failed', unavailable: 'Status unavailable' , users: 'Users', totalUsers: 'Total users', enabledUsers: 'Enabled users'}, - listeners: { quickSetup: 'Quick setup', quickSetupHint: 'One-click inbound (VLESS+REALITY by default). Override port, SNI, keys below if needed.', preset: 'Preset', portAuto: 'Leave empty to auto-pick a port', hy2QuickHint: 'Skeleton only — add cert/key in node edit before production.', quickHints: 'Generated', bind: 'Bind address', publicHost: 'Public host', title: 'Nodes', create: 'Create Node', edit: 'Edit Node', name: 'Name', protocol: 'Protocol', port: 'Port', bindAddress: 'Bind Address', status: 'Status', actions: 'Actions', config: 'Protocol Config', udp: 'UDP', tls: 'TLS', deleteConfirm: 'Delete this node?', created: 'Created', updated: 'Updated', deleted: 'Deleted', reloaded: 'Reloaded', copyURI: 'Copy subscription URI', urisTitle: 'Node subscription links', invalidJSON: 'Invalid JSON', selectProtocolFirst: 'Select a protocol first', autoGenerateHint: 'Auto on save if empty: REALITY private-key/short-id/dest/server-names, SS password+cipher, Snell PSK+version, Sudoku key, password-protocol users (trojan/hy2/anytls/mieru/tuic/shadowquic/trusttunnel), Hy2 self-signed cert. Share-URI pbk is derived (not stored). Click Generate next to a field to fill now.', usersHint: 'Create credentials under Users and bind them to this node. Only official Listener fields are configured here.', portHint: 'Single port (e.g. 443) or official ports syntax (e.g. 200,204,401-429)', sectionProtocol: 'Protocol options', transportLayer: 'Transport', + listeners: { quickSetup: 'Quick setup', quickSetupHint: 'One-click inbound (VLESS+REALITY by default). Override port, SNI, keys below if needed.', preset: 'Preset', portAuto: 'Leave empty to auto-pick a port', hy2QuickHint: 'Skeleton only — add cert/key in node edit before production.', quickHints: 'Generated', bind: 'Bind address', publicHost: 'Public host', title: 'Nodes', create: 'Create Node', edit: 'Edit Node', name: 'Name', protocol: 'Protocol', port: 'Port', bindAddress: 'Bind Address', status: 'Status', actions: 'Actions', config: 'Protocol Config', udp: 'UDP', tls: 'TLS', deleteConfirm: 'Delete this node?', created: 'Created', updated: 'Updated', deleted: 'Deleted', reloaded: 'Reloaded', copyURI: 'Copy subscription URI', urisTitle: 'Node subscription links', invalidJSON: 'Invalid JSON', selectProtocolFirst: 'Select a protocol first', autoGenerateHint: 'Auto on save if empty: REALITY private-key/short-id/dest/server-names, SS password+cipher, Snell PSK+version, Sudoku key, password-protocol users (trojan/hy2/anytls/mieru/tuic/shadowquic/trusttunnel), Hy2 self-signed cert. Share-URI pbk is derived (not stored). Click Generate next to a field to fill now.', fieldGuideTitle: 'Field guide (this protocol)', fieldGuideFallback: 'Per-field tooltips apply. Bind users under Users.', fieldRequired: 'Required', fieldOptional: 'Optional', fieldAuto: 'Auto-generate if empty on save', transportXhttpHint: 'XHTTP is available for VLESS only.', transportNoXhttpHint: 'XHTTP is not available for this protocol (VLESS only).', guide: { vless: { required: 'Port; bind users (UUID) under Users.', optional: 'Transport (TCP/WS/gRPC/XHTTP), security (TLS/Reality), flow, encryption/decryption.', auto: 'Reality private-key/short-id/dest; user UUID. Share pbk is derived from private-key.' }, vmess: { required: 'Port; bind users (UUID).', optional: 'alterId (prefer 0), transport (TCP/WS/gRPC, no XHTTP), TLS/Reality.', auto: 'Reality material; user UUID. No XHTTP.' }, trojan: { required: 'Port; TLS cert+key (unless Reality) or Reality; bind users (password).', optional: 'Transport (TCP/WS/gRPC, no XHTTP), optional SS layer.', auto: 'Self-signed cert if missing; user password; Reality material. No XHTTP.' }, shadowsocks: { required: 'Port, cipher.', optional: 'Plugin options.', auto: 'Password (by cipher).' }, snell: { required: 'Port; version (default 4).', optional: 'obfs mode/host (http or tls).', auto: 'PSK if left empty on save.' }, hysteria2: { required: 'Port; user passwords.', optional: 'obfs, up/down, ALPN, SNI.', auto: 'Self-signed cert; passwords.' }, tuic: { required: 'Port; token (v4) or users (v5).', optional: 'Congestion, ALPN, SNI.', auto: 'Cert; default user password.' }, anytls: { required: 'Port; cert (or alternate TLS mode); users.', optional: 'Idle session settings.', auto: 'Cert; passwords.' }, mieru: { required: 'Port; users.', optional: 'Mux-related.', auto: 'Cert; passwords.' }, shadowquic: { required: 'Port; users.', optional: 'JLS upstream and advanced.', auto: 'Passwords.' }, sudoku: { required: 'Port; key.', optional: 'AEAD, HTTPMask, tables.', auto: 'Key if empty.' }, trusttunnel: { required: 'Port; cert; users.', optional: 'QUIC/congestion.', auto: 'Cert; passwords.' } }, usersHint: 'Create credentials under Users and bind them to this node. Only official Listener fields are configured here.', portHint: 'Single port (e.g. 443) or official ports syntax (e.g. 200,204,401-429)', sectionProtocol: 'Protocol options', transportLayer: 'Transport', securityLayer: 'Security', sectionSecurity: 'Security', sectionTransport: 'Transport', sectionTLS: 'TLS certificate', sectionReality: 'Reality', sectionSSOption: 'Trojan SS option', cipher: 'Cipher', password: 'Password', passwordOptional: 'Password (optional)', uuid: 'UUID', privateKey: 'Private key', certificate: 'Certificate', alpn: 'ALPN', sni: 'SNI', clientFingerprint: 'Client fingerprint', allowInsecure: 'Allow insecure', muxOption: 'Mux', realityPrivateKey: 'Reality private key', realityShortID: 'Short ID', realityServerNames: 'Server names', realityDest: 'Dest', transport: 'Transport', path: 'Path', host: 'Host', serviceName: 'Service name', up: 'Up', down: 'Down', ignoreClientBandwidth: 'Ignore client bandwidth', masquerade: 'Masquerade', congestionControl: 'Congestion control', clone: 'Clone', saveTemplate: 'Save as template', versions: 'Versions', cloned: 'Cloned', templateCreated: 'Template created', instantiated: 'Listener created from template', batchDone: 'Batch operation completed', templates: 'Listener Templates', templateName: 'Template name', createdAt: 'Created at', instantiate: 'Instantiate', deleteTemplateConfirm: 'Delete this template?', templateDeleted: 'Template deleted', enableSelected: 'Enable selected', disableSelected: 'Disable selected', newPort: 'New port', version: 'Version', reason: 'Reason', diff: 'Diff', rollback: 'Rollback', rollbackConfirm: 'Rollback to this version?', rollbackDone: 'Rollback completed' , addUser: 'Add user', aeadMethod: 'AEAD method', allowInsecureHint: 'Allow insecure (debug only)', alterId: 'Alter ID', alterIdHint: 'VMess alterId, prefer 0', authenticationTimeout: 'Auth timeout', bandwidthHint: 'Bandwidth limit, e.g. 100 mbps', bbrProfile: 'BBR profile', clientAuthCert: 'Client cert', clientAuthType: 'Client auth type', congestionController: 'Congestion controller', createWizard: 'Create listener wizard', cwnd: 'CWND', decryption: 'Decryption', diff --git a/frontend/src/i18n/locales/zh.ts b/frontend/src/i18n/locales/zh.ts index c95670b..b37fa8e 100644 --- a/frontend/src/i18n/locales/zh.ts +++ b/frontend/src/i18n/locales/zh.ts @@ -10,7 +10,7 @@ export default { login: { title: '3M-UI', subtitle: 'Mihomo 核心管理控制台', username: '用户名', password: '密码', button: '登录', welcomeBack: '欢迎回来', failed: '登录失败' }, password: { title: '修改密码', current: '当前密码', new: '新密码', confirm: '确认新密码', button: '修改密码', success: '密码已更新', failed: '修改密码失败', mismatch: '两次输入的密码不一致' }, dashboard: { title: '仪表盘', subtitle: '系统与核心状态概览', status: '核心状态', running: '运行中', stoppedStatus: '已停止', version: '版本', pid: '进程 ID', uptime: '运行时间', listeners: '节点统计', total: '总数', enabled: '已启用', disabled: '已禁用', system: '系统资源', cpu: 'CPU', memory: '内存', disk: '磁盘', network: '网络', upload: '上传', download: '下载', traffic: '流量统计', uploadRate: '上传速率', downloadRate: '下载速率', totalUpload: '总上传', totalDownload: '总下载', onlineUsers: '在线用户', activeConnections: '活跃连接', start: '启动', stop: '停止', restart: '重启', started: '核心已启动', stopped: '核心已停止', restarted: '核心已重启', operationFailed: '操作失败', unavailable: '无法获取状态' , users: '用户', totalUsers: '用户总数', enabledUsers: '启用用户'}, - listeners: { quickSetup: '一键搭建', quickSetupHint: '一键创建入站(默认 VLESS+REALITY)。可在下方覆盖端口、SNI、密钥等。', preset: '预设方案', portAuto: '留空自动分配端口', hy2QuickHint: '仅生成骨架,生产前请在节点编辑中补充证书/私钥', quickHints: '生成结果', bind: '绑定地址', publicHost: '公网地址', title: '节点管理', create: '创建节点', edit: '编辑节点', name: '名称', protocol: '协议', port: '端口', bindAddress: '绑定地址', status: '状态', actions: '操作', config: '协议配置', udp: 'UDP', tls: 'TLS', deleteConfirm: '确定要删除此节点吗?', created: '创建成功', updated: '更新成功', deleted: '删除成功', reloaded: '重载成功', copyURI: '复制订阅链接', urisTitle: '节点订阅链接', invalidJSON: '无效的 JSON', selectProtocolFirst: '请先选择协议', autoGenerateHint: '可留空,保存时自动生成:REALITY私钥/ShortID/dest/server-names、SS密码与cipher、Snell PSK与version、Sudoku key、密码类协议用户(trojan/hy2/anytls/mieru/tuic/shadowquic/trusttunnel)、Hy2自签证书。分享链接中的pbk由私钥推导(不写入服务端)。可点各字段旁的「生成」按钮立即填入。', usersHint: '用户凭证请在「用户管理」中创建并绑定到本节点;此处仅配置官方 Listener 协议字段。', portHint: '支持单端口(如 443)或官方 ports 语法(如 200,204,401-429)', sectionProtocol: '协议选项', transportLayer: '传输层', + listeners: { quickSetup: '一键搭建', quickSetupHint: '一键创建入站(默认 VLESS+REALITY)。可在下方覆盖端口、SNI、密钥等。', preset: '预设方案', portAuto: '留空自动分配端口', hy2QuickHint: '仅生成骨架,生产前请在节点编辑中补充证书/私钥', quickHints: '生成结果', bind: '绑定地址', publicHost: '公网地址', title: '节点管理', create: '创建节点', edit: '编辑节点', name: '名称', protocol: '协议', port: '端口', bindAddress: '绑定地址', status: '状态', actions: '操作', config: '协议配置', udp: 'UDP', tls: 'TLS', deleteConfirm: '确定要删除此节点吗?', created: '创建成功', updated: '更新成功', deleted: '删除成功', reloaded: '重载成功', copyURI: '复制订阅链接', urisTitle: '节点订阅链接', invalidJSON: '无效的 JSON', selectProtocolFirst: '请先选择协议', autoGenerateHint: '可留空,保存时自动生成:REALITY私钥/ShortID/dest/server-names、SS密码与cipher、Snell PSK与version、Sudoku key、密码类协议用户(trojan/hy2/anytls/mieru/tuic/shadowquic/trusttunnel)、Hy2自签证书。分享链接中的pbk由私钥推导(不写入服务端)。可点各字段旁的「生成」按钮立即填入。', fieldGuideTitle: '字段说明(当前协议)', fieldGuideFallback: '各字段旁有提示。用户在「用户管理」中绑定到节点。', fieldRequired: '必填', fieldOptional: '可选', fieldAuto: '可自动生成(留空保存时)', transportXhttpHint: 'XHTTP 仅 VLESS 可用。', transportNoXhttpHint: '本协议不支持 XHTTP(仅 VLESS)。', guide: { vless: { required: '端口;启用后需在用户管理绑定用户(UUID)。', optional: '传输(TCP/WS/gRPC/XHTTP)、安全(TLS/Reality)、flow、encryption/decryption。', auto: 'Reality 私钥/short-id/dest;绑定用户的 UUID。分享链接中的 pbk 由私钥推导。' }, vmess: { required: '端口;绑定用户(UUID)。', optional: 'alterId(建议 0)、传输(TCP/WS/gRPC,无 XHTTP)、TLS/Reality。', auto: 'Reality 材料;用户 UUID。不提供 XHTTP。' }, trojan: { required: '端口;TLS 证书+私钥(非 Reality 时)或 Reality;绑定用户(密码)。', optional: '传输(TCP/WS/gRPC,无 XHTTP)、SS 二次加密等。', auto: '缺证书时可自签;用户密码;Reality 材料。不提供 XHTTP。' }, shadowsocks: { required: '端口、cipher。', optional: 'plugin / 插件选项。', auto: 'password(按 cipher 生成)。' }, snell: { required: '端口;version(默认 4)。', optional: 'obfs mode/host(http 或 tls 混淆)。', auto: 'PSK 留空则保存时生成。' }, hysteria2: { required: '端口;用户密码(映射或绑定用户)。', optional: 'obfs、带宽 up/down、ALPN、SNI。', auto: '证书/私钥(自签);用户密码。' }, tuic: { required: '端口;token(v4)或 users(v5)。', optional: 'congestion、ALPN、SNI。', auto: '证书;缺省用户密码。' }, anytls: { required: '端口;证书(或 allow-insecure 等模式);用户。', optional: 'idle session 等。', auto: '证书;用户密码。' }, mieru: { required: '端口;用户。', optional: 'mux 相关。', auto: '证书;用户密码。' }, shadowquic: { required: '端口;用户。', optional: 'JLS upstream 等高级项。', auto: '用户密码。' }, sudoku: { required: '端口;key。', optional: 'AEAD、HTTPMask、表类型。', auto: 'key 留空可生成。' }, trusttunnel: { required: '端口;证书;用户。', optional: 'QUIC/拥塞等。', auto: '证书;用户密码。' } }, usersHint: '用户凭证请在「用户管理」中创建并绑定到本节点;此处仅配置官方 Listener 协议字段。', portHint: '支持单端口(如 443)或官方 ports 语法(如 200,204,401-429)', sectionProtocol: '协议选项', transportLayer: '传输层', securityLayer: '安全层', sectionSecurity: '安全层', sectionTransport: '传输层', sectionTLS: 'TLS 证书', sectionReality: 'Reality', sectionSSOption: 'Trojan SS 混淆', cipher: '加密方法', password: '密码', passwordOptional: '密码(可选)', uuid: 'UUID', privateKey: '私钥', certificate: '证书', alpn: 'ALPN', sni: 'SNI', clientFingerprint: '客户端指纹', allowInsecure: '允许不安全', muxOption: '多路复用', realityPrivateKey: 'Reality 私钥', realityShortID: 'Short ID', realityServerNames: 'Server Names', realityDest: 'Dest', transport: '传输', path: '路径', host: 'Host', serviceName: 'Service Name', up: '上行', down: '下行', ignoreClientBandwidth: '忽略客户端带宽', masquerade: 'Masquerade', congestionControl: '拥塞控制', clone: '克隆', saveTemplate: '保存为模板', versions: '配置版本', cloned: '克隆成功', templateCreated: '模板创建成功', instantiated: '已从模板创建节点', batchDone: '批量操作完成', templates: '节点模板', templateName: '模板名称', createdAt: '创建时间', instantiate: '实例化', deleteTemplateConfirm: '确定删除此模板?', templateDeleted: '模板已删除', enableSelected: '启用选中', disableSelected: '禁用选中', newPort: '新端口', version: '版本', reason: '原因', diff: '配置差异', rollback: '回滚', rollbackConfirm: '确定回滚到此版本?', rollbackDone: '回滚完成' , addUser: '添加用户', aeadMethod: 'AEAD 加密方式', allowInsecureHint: '允许不安全连接(仅调试)', alterId: 'Alter ID', alterIdHint: 'VMess 额外 ID,建议 0', authenticationTimeout: '认证超时', bandwidthHint: '带宽限制,如 100 mbps', bbrProfile: 'BBR 配置', clientAuthCert: '客户端证书', clientAuthType: '客户端认证类型', congestionController: '拥塞控制', createWizard: '向导创建节点', cwnd: 'CWND', decryption: '解密',