feat: add local hardware approval leases
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
const activeLease = document.querySelector("#activeLease");
|
||||
const pendingRequests = document.querySelector("#pendingRequests");
|
||||
const pendingCount = document.querySelector("#pendingCount");
|
||||
const revokeButton = document.querySelector("#revokeButton");
|
||||
const message = document.querySelector("#message");
|
||||
let snapshot = null;
|
||||
|
||||
function escapeHtml(value) {
|
||||
const node = document.createElement("span");
|
||||
node.textContent = String(value ?? "");
|
||||
return node.innerHTML;
|
||||
}
|
||||
|
||||
function showMessage(value, error = false) {
|
||||
message.textContent = value;
|
||||
message.style.color = error ? "#ff8797" : "#7fbbff";
|
||||
}
|
||||
|
||||
async function api(path, body) {
|
||||
const response = await fetch(path, {
|
||||
method: body ? "POST" : "GET",
|
||||
credentials: "same-origin",
|
||||
headers: body ? { "Content-Type": "application/json" } : {},
|
||||
body: body ? JSON.stringify(body) : undefined,
|
||||
});
|
||||
if (response.status === 401) {
|
||||
location.replace(`/login?next=${encodeURIComponent(location.pathname)}`);
|
||||
throw new Error("请先登录本机服务");
|
||||
}
|
||||
const payload = await response.json();
|
||||
if (!response.ok) throw new Error(payload.message || payload.error);
|
||||
return payload;
|
||||
}
|
||||
|
||||
function render(data) {
|
||||
snapshot = data;
|
||||
pendingCount.textContent = String(data.pending.length);
|
||||
if (data.active) {
|
||||
const lease = data.active;
|
||||
activeLease.className = "lease-details";
|
||||
activeLease.innerHTML = `
|
||||
<div class="identity"><strong>${escapeHtml(lease.label)}</strong><code>${escapeHtml(lease.client_id)}</code><span>来源 ${escapeHtml(lease.client_address)} · 最近执行 ${escapeHtml(lease.last_activity_at)}</span></div>
|
||||
<div class="timer">${Number(lease.idle_remaining_seconds)}s</div>`;
|
||||
revokeButton.hidden = false;
|
||||
revokeButton.dataset.clientId = lease.client_id;
|
||||
} else {
|
||||
activeLease.className = "empty";
|
||||
activeLease.textContent = "当前无人获批使用真实台架。";
|
||||
revokeButton.hidden = true;
|
||||
}
|
||||
if (!data.pending.length) {
|
||||
pendingRequests.className = "request-list empty";
|
||||
pendingRequests.textContent = "暂时没有申请。";
|
||||
return;
|
||||
}
|
||||
pendingRequests.className = "request-list";
|
||||
pendingRequests.innerHTML = data.pending.map((request) => `
|
||||
<article class="request">
|
||||
<div class="identity"><strong>${escapeHtml(request.label)}</strong><code>${escapeHtml(request.client_id)}</code><span>来源 ${escapeHtml(request.client_address)} · ${escapeHtml(request.requested_at)}</span></div>
|
||||
<div class="actions">
|
||||
<button type="button" data-action="approve" data-client-id="${escapeHtml(request.client_id)}">同意</button>
|
||||
<button type="button" class="secondary" data-action="deny" data-client-id="${escapeHtml(request.client_id)}">拒绝</button>
|
||||
</div>
|
||||
</article>`).join("");
|
||||
}
|
||||
|
||||
async function refresh() {
|
||||
try {
|
||||
render(await api("/api/local/hardware-access"));
|
||||
document.querySelector("#clock").textContent = new Date().toLocaleTimeString("zh-CN", { hour12: false });
|
||||
} catch (error) {
|
||||
showMessage(error.message, true);
|
||||
}
|
||||
}
|
||||
|
||||
pendingRequests.addEventListener("click", async (event) => {
|
||||
const button = event.target.closest("button[data-action]");
|
||||
if (!button) return;
|
||||
button.disabled = true;
|
||||
try {
|
||||
const action = button.dataset.action;
|
||||
render(await api(`/api/local/hardware-access/${action}`, { client_id: button.dataset.clientId }));
|
||||
showMessage(action === "approve" ? "已授予该终端真实台架使用权。" : "已拒绝该终端的申请。");
|
||||
} catch (error) {
|
||||
showMessage(error.message, true);
|
||||
} finally {
|
||||
button.disabled = false;
|
||||
}
|
||||
});
|
||||
|
||||
revokeButton.addEventListener("click", async () => {
|
||||
revokeButton.disabled = true;
|
||||
try {
|
||||
render(await api("/api/local/hardware-access/revoke", { client_id: revokeButton.dataset.clientId }));
|
||||
showMessage("已停止该终端的真实台架使用权;再次使用必须重新申请。");
|
||||
} catch (error) {
|
||||
showMessage(error.message, true);
|
||||
} finally {
|
||||
revokeButton.disabled = false;
|
||||
}
|
||||
});
|
||||
|
||||
refresh();
|
||||
setInterval(refresh, 2000);
|
||||
Reference in New Issue
Block a user