(() => { const form = document.getElementById("client-auth"); const message = document.getElementById("client-auth-status"); const button = form.querySelector("button"); 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; } form.addEventListener("submit", async (event) => { event.preventDefault(); button.disabled = true; const data = new FormData(form); try { const result = await requestToken("login", { username: data.get("username"), password: data.get("password"), }); refreshToken = result.refresh_token; message.textContent = "已登录。访问令牌过期时会自动刷新。"; } catch (error) { message.textContent = error.message; } finally { form.elements.namedItem("password").value = ""; button.disabled = false; } }); async function refresh() { if (!refreshToken) throw new Error("请先登录,再提交图片。"); try { await requestToken("refresh", { refresh_token: refreshToken }); } catch (error) { accessToken = refreshToken = undefined; message.textContent = "请重新登录。"; throw error; } } 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(); }; })();