// 外部系统连接管理:凭据字段由系统定义声明,只提交、不回显。 import { $ } from "./dom.js"; import { api } from "./api.js"; import { escapeHtml, fmtTime } from "./format.js"; import { dialogConfirm, message } from "./dialog.js"; let editingId = ""; let providerReady = false; let definitions = []; let systemsById = new Map(); function selectedDefinition() { return definitions.find(x => x.definition_id === $("ext-definition").value); } function renderCredentialFields(fields = []) { $("ext-credential-fields").innerHTML = fields.map(field => ` `).join(""); } function resetForm() { editingId = ""; $("ext-form-title").textContent = "连接外部系统"; $("ext-name").disabled = false; $("ext-definition").disabled = false; $("ext-err").textContent = ""; $("ext-save").textContent = "连接并验证"; $("ext-form-cancel").hidden = true; const selected = selectedDefinition() || definitions[0]; $("ext-name").value = selected ? selected.name : ""; renderCredentialFields((selected || {}).credential_fields || []); } export function closeExternalSystemsModal() { $("external-modal").classList.remove("show"); resetForm(); } async function openExternalSystemsModal() { $("external-modal").classList.add("show"); resetForm(); await loadExternalSystems(); } function cardHtml(item) { const good = item.status === "active"; const badge = good ? "已连接" : (item.status === "disabled" ? "系统已停用" : "需更新凭据"); const checked = item.last_verified_at ? fmtTime(item.last_verified_at) : "尚未验证"; return `
${escapeHtml(item.name)}${badge}
${escapeHtml(item.system_name || "外部系统")} · ${escapeHtml(item.username_masked || "***")} · 最近验证 ${escapeHtml(checked)}
`; } async function loadExternalSystems() { const provider = $("ext-provider"); const list = $("ext-list"); provider.textContent = "加载中…"; list.innerHTML = '
加载中…
'; try { const [catalog, response] = await Promise.all([ api("GET", "/v1/external-system-providers"), api("GET", "/v1/external-systems"), ]); const providers = catalog.providers || []; definitions = providers.flatMap(item => item.definitions || []); providerReady = definitions.length > 0 && providers.some(item => item.configured); $("ext-definition").innerHTML = definitions.map(item => `` ).join(""); const rows = response.results || []; systemsById = new Map(rows.map(item => [item.external_system_id, item])); provider.textContent = providerReady ? `管理员已配置 ${definitions.length} 个外部系统,请选择后填写自己的访问凭据` : `暂没有可连接的外部系统${providers.find(item => item.reason)?.reason ? ":" + providers.find(item => item.reason).reason : ",请联系管理员配置"}`; $("ext-save").disabled = !providerReady; list.innerHTML = rows.length ? rows.map(cardHtml).join("") : '
还没有外部系统连接。填写下方凭据后,助手即可按你的权限查询。
'; resetForm(); } catch (error) { providerReady = false; provider.textContent = "加载失败"; list.innerHTML = `
${escapeHtml(error.message)}
`; $("ext-save").disabled = true; } } $("hd-external").onclick = openExternalSystemsModal; $("ext-close").onclick = closeExternalSystemsModal; $("external-modal").addEventListener("click", event => { if (event.target.id === "external-modal") closeExternalSystemsModal(); }); $("ext-form-cancel").onclick = resetForm; $("ext-definition").onchange = () => { if (editingId) return; const selected = selectedDefinition(); if (selected) $("ext-name").value = selected.name; renderCredentialFields((selected || {}).credential_fields || []); }; $("ext-form").addEventListener("submit", async event => { event.preventDefault(); if (!providerReady) return; const name = $("ext-name").value.trim(); const definitionId = $("ext-definition").value; const credentials = Object.fromEntries( Array.from($("ext-credential-fields").querySelectorAll("[data-credential-name]")) .map(input => [input.dataset.credentialName, input.value]), ); if (Object.values(credentials).some(value => !value.trim()) || (!editingId && (!name || !definitionId))) { $("ext-err").textContent = "请填写连接名称和全部访问凭据"; return; } const button = $("ext-save"); button.disabled = true; button.textContent = "正在验证…"; $("ext-err").textContent = ""; try { if (editingId) { await api("PUT", `/v1/external-systems/${editingId}/credentials`, { credentials }); message("外部系统凭据已更新", "success"); } else { await api("POST", "/v1/external-systems", { definition_id: definitionId, name, credentials }); message("外部系统已连接;下一轮对话即可使用", "success"); } resetForm(); await loadExternalSystems(); } catch (error) { $("ext-err").textContent = error.message; } finally { button.disabled = !providerReady; button.textContent = editingId ? "验证并更新" : "连接并验证"; } }); $("ext-list").addEventListener("click", async event => { const card = event.target.closest(".ext-card"); if (!card) return; const id = card.dataset.id; if (event.target.closest("[data-ext-edit]")) { const system = systemsById.get(id); editingId = id; $("ext-form-title").textContent = "更新外部系统凭据"; $("ext-name").value = system.name; $("ext-name").disabled = true; $("ext-definition").value = system.definition_id; $("ext-definition").disabled = true; renderCredentialFields(system.credential_fields || []); $("ext-save").textContent = "验证并更新"; $("ext-form-cancel").hidden = false; $("ext-credential-fields").querySelector("input")?.focus(); return; } if (event.target.closest("[data-ext-test]")) { const button = event.target.closest("button"); button.disabled = true; try { const result = await api("POST", `/v1/external-systems/${id}/test`); message(result.ok ? `连接正常,可用接口 ${result.operation_count || 0} 个` : `连接失败:${result.error}`, result.ok ? "success" : "error"); await loadExternalSystems(); } catch (error) { message("测试失败:" + error.message, "error"); } finally { button.disabled = false; } return; } if (event.target.closest("[data-ext-delete]")) { const name = card.querySelector(".ext-card-name").textContent; if (!await dialogConfirm({ title:"断开外部系统", message:`断开「${name}」?保存的密文凭据和连接配置将被清除。`, okText:"断开", danger:true })) return; try { await api("DELETE", `/v1/external-systems/${id}`); message("外部系统已断开", "success"); resetForm(); await loadExternalSystems(); } catch (error) { message("断开失败:" + error.message, "error"); } } });