Files
xk-hy-transit-go/internal/logweb/web/fileauth.html
2026-05-28 16:39:51 +08:00

404 lines
8.4 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<title>上传凭证 §2.3.3</title>
<link rel="stylesheet" href="/static/static.css">
<style>
.step { margin-bottom: 1rem; }
.step h4 { margin: 0 0 0.35rem; color: var(--accent); font-size: 0.95rem; }
.step pre { margin: 0; max-height: 12rem; overflow: auto; }
.formula { color: var(--muted); font-size: 0.9rem; margin-bottom: 1rem; }
.check-ok { color: var(--ok); }
.check-fail { color: var(--err); }
label.chk { display: inline-flex; align-items: center; gap: 0.35rem; font-size: 0.9rem; }
.diff { border-left: 3px solid var(--warn); padding-left: 0.75rem; margin-top: 0.5rem; }
</style>
</head>
<body>
<div class="wrap">
<header>
<h1>上传凭证Ver2.0 §2.3.3</h1>
<nav class="nav" id="main-nav"></nav>
</header>
<p class="formula">
五步:<code>putPolicy</code><code>encodedPutPolicy</code>URL-safe Base64
<code>HMAC-SHA1(secret, encodedPutPolicy)</code><code>encodedSign</code>
<code>uploadToken = AccessKey:encodedSign:encodedPutPolicy</code>
</p>
<div class="toolbar">
<label>deadlineUnix 秒)<input type="text" id="deadline" placeholder="默认 now+3600" style="width:11rem"></label>
<label>bucket <input type="text" id="bucket" placeholder="Runner fileBucket" style="width:10rem"></label>
<button class="primary" id="btn-gen">生成upload profile</button>
<button id="btn-doc">文档黄金样例</button>
<button id="btn-refresh">刷新 Runner 并对比</button>
<label class="chk"><input type="checkbox" id="show-full"> 显示完整 token</label>
<button type="button" id="btn-copy">复制完整 token</button>
</div>
<div id="err-box"></div>
<div class="panel" id="panel-ver233">
<h3>A. §2.3.3 生产算法transit / xk-api</h3>
<div class="step">
<h4>1. putPolicyscope=仅 bucket无 returnBody</h4>
<pre id="policy-json"></pre>
</div>
<div class="step">
<h4>2. encodedPutPolicy = urlsafe_base64(putPolicy)</h4>
<pre id="encoded-policy"></pre>
</div>
<div class="step">
<h4>3. sign = HMAC-SHA1(secret, <strong>encodedPutPolicy</strong> UTF-8)</h4>
<p class="formula" style="margin:0 0 0.35rem">signInput签名字节串<code id="sign-input"></code> · hex<code id="sign-hex"></code></p>
<pre id="encoded-sign"></pre>
</div>
<div class="step">
<h4>45. uploadToken = AccessKey + ':' + encodedSign + ':' + encodedPutPolicy</h4>
<pre id="upload-token"></pre>
</div>
<p>算法:<code id="algorithm"></code> · B64<code id="b64-mode"></code> · AccessKey<code id="access-key"></code> · scope<code id="scope-val"></code> · deadline<code id="deadline-val"></code>
· <span id="doc-golden"></span></p>
</div>
<div class="panel">
<h3>B. 格式校验</h3>
<p id="format-summary"></p>
<ul id="format-checks"></ul>
<p>与 Runner 当前 token默认 prescription.pdf<span id="runner-match"></span><code id="runner-preview"></code></p>
</div>
<div class="panel">
<h3>C. 监管文档教学示例doc profile只读</h3>
<p id="doc-note" class="formula"></p>
<div class="step">
<h4>示例 token 三段</h4>
<pre id="doc-token-parts"></pre>
</div>
<div class="step">
<h4>encodedPutPolicy 解码</h4>
<pre id="doc-policy"></pre>
</div>
</div>
</div>
<script src="/static/app.js"></script>
<script>
if (typeof renderMainNav === 'function') renderMainNav('fileauth');
let lastData = null;
function tokenDisplay(data) {
if (!data) return '—';
const show = document.getElementById('show-full').checked;
return show ? data.uploadToken : (data.uploadTokenMasked || data.uploadToken);
}
function renderChecks(checks) {
const ul = document.getElementById('format-checks');
ul.innerHTML = (checks || []).map(c => {
const cls = c.ok ? 'check-ok' : 'check-fail';
const mark = c.ok ? '✓' : '✗';
return `<li class="${cls}">${mark} <strong>${esc(c.id)}</strong> — ${esc(c.detail)}</li>`;
}).join('');
}
function render(data) {
lastData = data;
document.getElementById('err-box').innerHTML = '';
document.getElementById('policy-json').textContent = data.policyJson || '—';
document.getElementById('encoded-policy').textContent = data.encodedPutPolicy || '—';
document.getElementById('sign-input').textContent = data.signInput || '—';
document.getElementById('sign-hex').textContent = data.signHex || '—';
document.getElementById('encoded-sign').textContent = data.encodedSign || '—';
document.getElementById('upload-token').textContent = tokenDisplay(data);
document.getElementById('access-key').textContent = data.accessKey || '—';
document.getElementById('scope-val').textContent = data.scope || data.bucket || '—';
document.getElementById('b64-mode').textContent = data.b64Mode || '—';
document.getElementById('deadline-val').textContent = data.deadlineUnix != null ? String(data.deadlineUnix) : '—';
document.getElementById('algorithm').textContent = data.algorithm || '—';
document.getElementById('doc-golden').innerHTML = data.docGoldenMatch
? '<span class="badge ok">与文档黄金样例一致</span>'
: '<span class="badge info">非文档固定向量(点「文档黄金样例」可对照)</span>';
const sum = document.getElementById('format-summary');
sum.innerHTML = data.formatOk
? '<span class="badge ok">格式符合 AccessKey:encodedSign:encodedPutPolicy</span>'
: '<span class="badge err">格式校验未全部通过</span>';
renderChecks(data.formatChecks);
const matchEl = document.getElementById('runner-match');
if (data.runnerTokenMatch) {
matchEl.innerHTML = '<span class="badge ok">一致</span>';
} else {
matchEl.innerHTML = '<span class="badge warn">不一致</span>(自定义 deadline 或需重新拉取配置)';
}
document.getElementById('runner-preview').textContent = data.runnerTokenPreview || '—';
const doc = data.docExample || {};
document.getElementById('doc-note').textContent = doc.note || '—';
document.getElementById('doc-token-parts').textContent =
'AccessKey: ' + (doc.accessKeyPart || '') + '\n' +
'encodedSign: ' + (doc.encodedSignPart || '') + '\n' +
'encodedPutPolicy: ' + (doc.encodedPolicyPart || '');
document.getElementById('doc-policy').textContent = doc.policyJsonDecoded || '—';
}
async function load(refresh) {
const errBox = document.getElementById('err-box');
errBox.innerHTML = '';
const params = new URLSearchParams();
const dl = document.getElementById('deadline').value.trim();
const bk = document.getElementById('bucket').value.trim();
if (dl) params.set('deadline', dl);
if (bk) params.set('bucket', bk);
if (refresh) params.set('refresh', '1');
try {
const data = await api('/api/fileauth/generate?' + params.toString());
render(data);
showToast('已生成', 'ok');
} catch (e) {
showInlineError(errBox, e.message || String(e));
showToast(firstLine(e.message) || '生成失败', 'err');
}
}
async function loadDocGolden() {
document.getElementById('deadline').value = '1451491200';
document.getElementById('bucket').value = 'my-bucket';
try {
const data = await api('/api/fileauth/generate?doc=1');
render(data);
showToast('文档黄金样例', 'ok');
} catch (e) {
showInlineError(document.getElementById('err-box'), e.message || String(e));
}
}
document.getElementById('btn-gen').onclick = () => load(false);
document.getElementById('btn-doc').onclick = () => loadDocGolden();
document.getElementById('btn-refresh').onclick = () => load(true);
document.getElementById('show-full').onchange = () => {
if (lastData) document.getElementById('upload-token').textContent = tokenDisplay(lastData);
};
document.getElementById('btn-copy').onclick = async () => {
if (!lastData || !lastData.uploadToken) {
showToast('请先生成 token', 'warn');
return;
}
try {
await navigator.clipboard.writeText(lastData.uploadToken);
showToast('已复制完整 uploadToken', 'ok');
} catch {
showToast('复制失败', 'err');
}
};
load(false);
</script>
</body>
</html>