(() => { const form = document.getElementById("client-auth"); const message = document.getElementById("client-auth-status"); const loginButton = form.querySelector('button[type="submit"]'); const checkButton = document.getElementById("client-auth-check"); const refreshButton = document.getElementById("client-auth-refresh"); let accessToken; let refreshToken; let accessExpiry = 0; async function requestToken(operation, data) { const response = await fetch("/api/v1/auth/" + operation, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data), }); const result = await response.json(); if (!response.ok || result.code !== 0) throw new Error(result.message || "鉴权失败"); accessToken = result.data.access_token; accessExpiry = Date.now() + result.data.expires_in * 1000; return result.data; } async function runAction(action) { loginButton.disabled = checkButton.disabled = refreshButton.disabled = true; try { await action(); } catch (error) { message.textContent = error.message; } finally { loginButton.disabled = checkButton.disabled = false; refreshButton.disabled = !refreshToken; } } form.addEventListener("submit", (event) => { event.preventDefault(); const data = new FormData(form); form.elements.namedItem("password").value = ""; runAction(async () => { const result = await requestToken("login", { username: data.get("username"), password: data.get("password"), }); refreshToken = result.refresh_token; message.textContent = "已登录。访问令牌过期时会自动刷新。"; }); }); async function refresh() { if (!refreshToken) throw new Error("请先登录,再提交图片。"); try { await requestToken("refresh", { refresh_token: refreshToken }); } catch (error) { accessToken = refreshToken = undefined; refreshButton.disabled = true; message.textContent = "请重新登录。"; throw error; } } checkButton.addEventListener("click", () => runAction(async () => { const path = "/api/v1/auth/check"; const anonymous = await fetch(path); if (anonymous.status !== 401) throw new Error("无令牌检查返回 HTTP " + anonymous.status); if (!accessToken) { message.textContent = "无令牌已拒绝(HTTP 401)。登录后可验证访问令牌。"; return; } const response = await fetch(path, { headers: { Authorization: "Bearer " + accessToken } }); const result = await response.json(); if (!response.ok || result.code !== 0) throw new Error(result.message || "访问令牌检查失败"); message.textContent = "鉴权通过:无令牌返回 HTTP 401,有效令牌返回 HTTP 200。"; })); refreshButton.addEventListener("click", () => runAction(async () => { await refresh(); message.textContent = "访问令牌已刷新,有效期 15 分钟。"; })); window.hairClientFetch = async (path, options) => { if (Date.now() >= accessExpiry || !accessToken) await refresh(); const send = () => { const headers = new Headers(options.headers); headers.set("Authorization", "Bearer " + accessToken); return fetch(path, { ...options, headers }); }; const response = await send(); if (response.status !== 401) return response; await refresh(); return send(); }; })();