feat: show server Reality keypair on node edit form (item 17)
New read-only GET /api/nodes/:id/reality-keys returns the panel-hosted keypair for the node's server from keyStore (display only — it never mints a pair; a missing entry reports nulls). The node edit form shows public and private key rows with copy buttons for Reality protocols.
This commit is contained in:
@@ -903,6 +903,7 @@ function nodeForm(item = null) {
|
||||
<label>入口端口<div class="inline-fields"><select name="portMode" data-node-sync><option value="random"${selected('random', node.portMode)}>范围内随机</option><option value="fixed"${selected('fixed', node.portMode)}>固定端口</option></select><input name="port" type="number" value="${esc(node.port || '')}" placeholder="固定时填写" min="1024" max="65535"></div></label>
|
||||
<label data-reality-only>Reality 目标预设<select name="realityPreset" data-reality-preset>${state.realityPresets.map((entry) => `<option value="${esc(entry.id)}" data-sni="${esc(entry.serverName)}" data-port="${entry.destPort}"${selected(entry.id, preset.id)}>${esc(entry.label)} · ${esc(entry.serverName)}</option>`).join('')}<option value="custom"${selected('custom', preset.id)}>自定义</option></select></label>
|
||||
<label data-reality-only>Reality SNI / 目标端口<div class="inline-fields"><input name="realityServerName" value="${esc(node.realityServerName || 'www.tesla.com')}" placeholder="www.tesla.com"><input name="realityDestPort" type="number" value="${Number(node.realityDestPort || 443)}" min="1" max="65535"></div></label>
|
||||
${item ? '<div class="wide cred-group" data-reality-only id="reality-keys-box"><div class="g-head">服务器 Reality 密钥对(主控托管)</div><div class="empty"><b>加载中…</b></div></div>' : ''}
|
||||
<label data-socks-only>SOCKS5 固定用户名<div class="inline-fields"><input name="socksUser" value="${esc(node.socksUser || '')}" placeholder="留空自动生成"><button type="button" class="icon-btn" data-tip="复制" data-action="copy-field" data-field="socksUser">${ICONS.copy}</button></div></label>
|
||||
<label data-socks-only>SOCKS5 固定密码<div class="inline-fields"><input name="socksPassword" value="${esc(node.socksPassword || '')}" placeholder="留空自动生成"><button type="button" class="icon-btn" data-tip="复制" data-action="copy-field" data-field="socksPassword">${ICONS.copy}</button></div></label>
|
||||
<label>拓扑<select name="topology" data-node-sync><option value="forward"${selected('forward', node.topology || 'forward')}>转发(入口 → 出口)</option><option value="direct"${selected('direct', node.topology)}>单机直连(无需出口)</option></select></label>
|
||||
@@ -918,6 +919,15 @@ function nodeForm(item = null) {
|
||||
<label data-forward-only data-manual-only data-exit-cred="password">落地密码<input name="exitPassword" placeholder="${item ? '留空保持不变' : ''}"></label>
|
||||
<div class="form-actions wide"><button type="button" class="btn" data-close>取消</button><button class="btn primary" type="submit">${item ? '保存修改' : '创建节点'}</button></div></form>`, true);
|
||||
syncNodeForm();
|
||||
// Round-3 item 17: show the server's panel-hosted Reality keypair on
|
||||
// existing Reality nodes (display only — no rotation action here).
|
||||
if (item && ['vless-reality-vision', 'vless-reality'].includes(node.protocol)) {
|
||||
api(`/api/nodes/${item.id}/reality-keys`).then((data) => {
|
||||
const box = $('#reality-keys-box'); if (!box) return;
|
||||
box.innerHTML = `<div class="g-head">服务器 Reality 密钥对(主控托管,同机 Reality 节点共用)</div>
|
||||
${data.publicKey ? credRowHTML('公钥', data.publicKey) + credRowHTML('私钥', data.privateKey) : '<div class="empty"><b>尚未生成(首次部署时由主控生成)</b></div>'}`;
|
||||
}).catch(() => { const box = $('#reality-keys-box'); if (box) box.innerHTML = '<div class="empty"><b>密钥读取失败</b></div>'; });
|
||||
}
|
||||
}
|
||||
function syncNodeForm() {
|
||||
const form = $('#node-form'); if (!form) return;
|
||||
|
||||
Reference in New Issue
Block a user