feat: MCP 管理进后台 UI (v2.1) — 开关/Token 即时生效无需重启
- settings 表新增 mcp_enabled / mcp_token,鉴权中间件动态读库,后台改动即时生效(实测验证:关->503、开->200、换 token 旧401新200,均不重启) - 首次启动自动把 TONAV_MCP_TOKEN 环境变量迁移进数据库(兼容旧部署,systemd 现有 token 无感切换) - 后台新增 API:GET /admin/api/mcp/status(token 掩码)、POST /admin/api/mcp/token(重新生成+自动启用,仅一次明文展示)、POST /admin/api/mcp/enabled - 后台设置弹窗新增 MCP Server 区块:开关 checkbox、token 掩码展示、一键重新生成 - README 同步
This commit is contained in:
@@ -129,6 +129,24 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom:20px; padding:15px; background:#f9f9f9; border-radius:10px">
|
||||
<h3 style="font-size:14px; margin-bottom:10px">🤖 MCP Server</h3>
|
||||
<div style="display:flex; align-items:center; gap:8px; margin-bottom:10px">
|
||||
<input type="checkbox" id="cfg_mcp_enabled" style="width:16px; height:16px" onchange="toggleMCP()">
|
||||
<label for="cfg_mcp_enabled" style="margin:0; font-size:13px">启用 MCP 接口(AI 可通过它管理导航页)</label>
|
||||
</div>
|
||||
<div id="mcpTokenRow" style="display:none">
|
||||
<label style="font-size:12px; color:#666">Token</label>
|
||||
<div style="display:flex; gap:8px; align-items:center">
|
||||
<input type="text" id="cfg_mcp_token" class="input" readonly style="flex:1; background:#fff; color:#555">
|
||||
<button class="btn btn-primary" onclick="regenMCPToken()" style="padding:8px 14px; white-space:nowrap">🔄 重新生成</button>
|
||||
</div>
|
||||
<div style="font-size:11px; color:#999; margin-top:6px">
|
||||
端点: <code>POST/GET /mcp</code>(Streamable HTTP + SSE)· 改动即时生效,无需重启
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary" onclick="saveSettings()" style="width:100%; margin-bottom:15px">保存设置</button>
|
||||
|
||||
<div style="padding:15px; border:1px solid #eee; border-radius:10px">
|
||||
@@ -150,9 +168,52 @@
|
||||
document.getElementById('cfg_pass').value = data.webdav_password || '';
|
||||
document.getElementById('cfg_auto_backup').checked = data.auto_backup === 'true';
|
||||
document.getElementById('settingsModal').style.display = 'flex';
|
||||
loadMCPStatus();
|
||||
loadCloudBackups();
|
||||
}
|
||||
|
||||
async function loadMCPStatus() {
|
||||
try {
|
||||
const resp = await fetch('/admin/api/mcp/status');
|
||||
const d = await resp.json();
|
||||
const cb = document.getElementById('cfg_mcp_enabled');
|
||||
const row = document.getElementById('mcpTokenRow');
|
||||
cb.checked = d.enabled;
|
||||
row.style.display = d.enabled ? 'block' : 'none';
|
||||
document.getElementById('cfg_mcp_token').value = d.token_masked || '(未生成,点重新生成创建)';
|
||||
} catch(e) {}
|
||||
}
|
||||
|
||||
function toggleMCP() {
|
||||
const enabled = document.getElementById('cfg_mcp_enabled').checked;
|
||||
document.getElementById('mcpTokenRow').style.display = enabled ? 'block' : 'none';
|
||||
fetch('/admin/api/mcp/enabled', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({enabled: enabled})
|
||||
}).then(r => r.json()).then(res => {
|
||||
if (!res.success) {
|
||||
alert(res.message);
|
||||
document.getElementById('cfg_mcp_enabled').checked = !enabled;
|
||||
document.getElementById('mcpTokenRow').style.display = enabled ? 'none' : 'block';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function regenMCPToken() {
|
||||
if (!confirm('确定重新生成 Token?\n\n旧 Token 将立即失效,已接入的 AI 客户端需要更新为新 Token。')) return;
|
||||
try {
|
||||
const resp = await fetch('/admin/api/mcp/token', {method: 'POST'});
|
||||
const res = await resp.json();
|
||||
if (res.success) {
|
||||
alert('✅ 新 Token 已生成(仅此一次明文展示,请立即复制):\n\n' + res.token);
|
||||
loadMCPStatus();
|
||||
} else {
|
||||
alert('❌ ' + res.message);
|
||||
}
|
||||
} catch(e) { alert('生成失败'); }
|
||||
}
|
||||
|
||||
async function loadCloudBackups() {
|
||||
const listDiv = document.getElementById('backupList');
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user