213 lines
4.7 KiB
HTML
213 lines
4.7 KiB
HTML
<!DOCTYPE html>
|
||
|
||
<html lang="zh-CN">
|
||
|
||
<head>
|
||
|
||
<meta charset="utf-8">
|
||
|
||
<title>互医配置</title>
|
||
|
||
<link rel="stylesheet" href="/static/static.css">
|
||
|
||
</head>
|
||
|
||
<body>
|
||
|
||
<div class="wrap">
|
||
|
||
<header>
|
||
|
||
<h1>互医配置</h1>
|
||
|
||
<nav class="nav" id="main-nav"></nav>
|
||
|
||
</header>
|
||
|
||
<div class="toolbar">
|
||
|
||
<button class="primary" id="refresh">从 xk-api 重新拉取</button>
|
||
|
||
<button type="button" id="copy-diag">复制 diagnostics</button>
|
||
|
||
<a href="/fileauth">上传凭证</a>
|
||
<a href="/upload">文件上传</a>
|
||
|
||
</div>
|
||
|
||
<div id="summary" class="panel" style="margin-bottom:1rem"></div>
|
||
|
||
<div class="panel">
|
||
|
||
<h3>三栏对比</h3>
|
||
|
||
<div style="overflow:auto">
|
||
|
||
<table id="compare"><thead><tr>
|
||
|
||
<th>字段</th><th>云端 xk-api</th><th>本地 .env</th><th>Runner 生效</th>
|
||
|
||
</tr></thead><tbody></tbody></table>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="panel">
|
||
|
||
<h3>运行环境</h3>
|
||
|
||
<pre id="runtime"></pre>
|
||
|
||
</div>
|
||
|
||
<div class="panel">
|
||
|
||
<h3>诊断 diagnostics</h3>
|
||
|
||
<pre id="diagnostics"></pre>
|
||
|
||
</div>
|
||
|
||
<details class="panel">
|
||
|
||
<summary>展开云端原始响应(脱敏)</summary>
|
||
|
||
<pre id="cloud-raw"></pre>
|
||
|
||
</details>
|
||
|
||
</div>
|
||
|
||
<script src="/static/app.js"></script>
|
||
|
||
<script>
|
||
|
||
if (typeof renderMainNav === 'function') renderMainNav('config');
|
||
|
||
|
||
|
||
function row(label, cloud, local, eff) {
|
||
|
||
return `<tr><td>${esc(label)}</td><td>${esc(cloud)}</td><td>${esc(local)}</td><td>${esc(eff)}</td></tr>`;
|
||
|
||
}
|
||
|
||
|
||
|
||
function render(v) {
|
||
|
||
const sum = document.getElementById('summary');
|
||
|
||
const okCloud = !v.cloud.rawError;
|
||
|
||
const okToken = v.effective && v.effective.uploadTokenSet;
|
||
|
||
const mismatch = (v.diagnostics || []).some(d => d.includes('不一致'));
|
||
|
||
let cls = 'badge ok';
|
||
|
||
let msg = 'xk-api 已连通';
|
||
|
||
if (!okCloud) { cls = 'badge err'; msg = 'xk-api 未连通'; }
|
||
|
||
else if (!okToken) { cls = 'badge warn'; msg = '本地 uploadToken 未生成'; }
|
||
|
||
else if (mismatch) { cls = 'badge warn'; msg = '配置存在不一致项'; }
|
||
|
||
sum.innerHTML = `<span class="${cls}">${esc(msg)}</span> · 刷新于 ${esc(v.effective.configRefreshedAt)} · ${esc(v.xkApiBaseUrl)}`;
|
||
|
||
|
||
|
||
const c = v.cloud, l = v.localEnv, e = v.effective;
|
||
|
||
document.querySelector('#compare tbody').innerHTML =
|
||
|
||
row('organID', c.organID, '-', e.organID) +
|
||
|
||
row('unitID', c.unitID, '-', e.unitID) +
|
||
|
||
row('organName', c.organName, '-', e.organName) +
|
||
|
||
row('appKey', c.appKeyPreview, l.hyAppKeyPreview, e.appKeyPreview) +
|
||
|
||
row('appSecret', c.appSecretSet ? '已配置' : '未配置', l.hyAppSecretSet ? '已配置' : '未配置', e.appSecretSet ? '已配置' : '未配置') +
|
||
|
||
row('aesKey', c.aesKeySet ? '已配置' : '未配置', l.hyAesKeySet ? '已配置' : '未配置', e.aesKeySet ? '已配置' : '未配置') +
|
||
|
||
row('fileBucket', c.fileBucket, l.hyFileBucket || '-', e.fileBucket) +
|
||
|
||
row('uploadToken(云端预生成)', c.uploadTokenSet ? c.uploadTokenPreview : '空', '-', '-') +
|
||
|
||
row('uploadToken(Runner 本地 FileAuth)', '-', '-', e.uploadTokenSet ? e.uploadTokenPreview + ' [' + e.uploadTokenSource + ']' : '空') +
|
||
|
||
row('fileUploadURL', c.fileUploadUrl, l.forwardBaseUrl + (l.fileUploadViaForward ? ' (经 forward)' : ''), e.fileUploadURL) +
|
||
|
||
row('FORWARD_SHARED_SECRET', '-', l.forwardSharedSecretSet ? '已配置' : '未配置', l.forwardSharedSecretSet ? '已配置' : '未配置');
|
||
|
||
|
||
|
||
document.getElementById('runtime').textContent =
|
||
|
||
`FILE_UPLOAD_VIA_FORWARD: ${l.fileUploadViaForward}\n` +
|
||
|
||
`FORWARD_BASE_URL: ${l.forwardBaseUrl}\n` +
|
||
|
||
`FORWARD_SHARED_SECRET: ${l.forwardSharedSecretSet ? '已配置' : '未配置'}\n` +
|
||
|
||
`XK_API_TOKEN: ${l.xkApiTokenSet ? '已配置' : '未配置'}\n` +
|
||
|
||
`生效说明: ${e.sourceNote}`;
|
||
|
||
|
||
|
||
document.getElementById('diagnostics').textContent =
|
||
|
||
(v.diagnostics && v.diagnostics.length) ? v.diagnostics.join('\n') : '(无)';
|
||
|
||
document.getElementById('cloud-raw').textContent = JSON.stringify(v.cloudRaw || {}, null, 2);
|
||
|
||
window._lastConfigView = v;
|
||
|
||
}
|
||
|
||
|
||
|
||
async function load(refresh) {
|
||
|
||
const path = refresh ? '/api/config/refresh' : '/api/config/view';
|
||
|
||
const opts = refresh ? { method: 'POST' } : {};
|
||
|
||
const v = await api(path, opts);
|
||
|
||
render(v);
|
||
|
||
if (refresh) showToast('配置已重新拉取', 'ok');
|
||
|
||
}
|
||
|
||
|
||
|
||
document.getElementById('refresh').onclick = () => load(true).catch(e => showToast(e.message, 'err'));
|
||
|
||
document.getElementById('copy-diag').onclick = () => {
|
||
|
||
const t = document.getElementById('diagnostics').textContent;
|
||
|
||
navigator.clipboard.writeText(t).then(() => showToast('已复制 diagnostics', 'ok')).catch(() => showToast(t, 'info'));
|
||
|
||
};
|
||
|
||
|
||
|
||
load(false).catch(e => showToast(e.message, 'err'));
|
||
|
||
</script>
|
||
|
||
</body>
|
||
|
||
</html>
|
||
|
||
|