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

162 lines
6.4 KiB
HTML
Raw Permalink 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>上传凭证 FileAuth</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; }
</style>
</head>
<body>
<div class="wrap">
<header>
<h1>上传凭证FileAuth.java</h1>
<nav class="nav" id="main-nav"></nav>
</header>
<p class="formula">
<code>docs/FileAuth.java</code> 一致:
<code>sign = HMAC-SHA1(secret, policyJSON)</code>
<code>uploadToken = AccessKey:encodedSign:policyB64</code>policyB64 = urlsafe_base64(policyJSON)
</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">生成</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-upload">
<h3>生产 upload profilescope=仅 bucket</h3>
<div class="step">
<h4>1. putPolicy</h4>
<pre id="policy-json"></pre>
</div>
<div class="step">
<h4>2. sign = HMAC-SHA1(secret, <strong>policyJSON</strong> UTF-8) → encodedSign</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>3. policyB64 = urlsafe_base64(putPolicy)</h4>
<pre id="encoded-policy"></pre>
</div>
<div class="step">
<h4>4. uploadToken = AccessKey + ':' + encodedSign + ':' + policyB64</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></p>
</div>
<div class="panel">
<h3>格式校验</h3>
<p id="format-summary"></p>
<ul id="format-checks"></ul>
<p>与 Runner 当前 token<span id="runner-match"></span><code id="runner-preview"></code></p>
</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 || '—';
const sum = document.getElementById('format-summary');
sum.innerHTML = data.formatOk
? '<span class="badge ok">格式符合 AccessKey:encodedSign:policyB64</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 || '—';
}
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');
}
}
document.getElementById('btn-gen').onclick = () => load(false);
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>