135 lines
4.1 KiB
JavaScript
135 lines
4.1 KiB
JavaScript
const $ = (id) => document.getElementById(id);
|
|
let challengeId = "";
|
|
|
|
function setStatus(msg, danger = false) {
|
|
const el = $("status");
|
|
if (!el) return;
|
|
el.style.color = danger ? "#b42318" : "#0f5f3d";
|
|
el.textContent = msg;
|
|
}
|
|
|
|
function setLoading(button, loading, idleText, loadingText) {
|
|
if (!button) return;
|
|
button.disabled = loading;
|
|
button.textContent = loading ? loadingText : idleText;
|
|
}
|
|
|
|
async function postJSON(url, body) {
|
|
const res = await fetch(url, {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify(body),
|
|
});
|
|
const data = await res.json();
|
|
if (!res.ok) throw new Error(data.detail || "请求失败");
|
|
return data;
|
|
}
|
|
|
|
async function refreshChallenge() {
|
|
try {
|
|
const res = await fetch("/api/auth/challenge");
|
|
const data = await res.json();
|
|
if (!res.ok || !data.ok) throw new Error(data.detail || "校验题加载失败");
|
|
challengeId = (data.challenge_id || "").trim();
|
|
const label = $("challengeLabel");
|
|
if (label) label.textContent = `人机校验:${data.question || ""}`;
|
|
const ans = $("challengeAnswer");
|
|
if (ans) ans.value = "";
|
|
} catch {
|
|
setStatus("校验题加载失败,请刷新页面重试", true);
|
|
}
|
|
}
|
|
|
|
function nextPath() {
|
|
const nxt = (window.__NEXT_PATH__ || "/").trim();
|
|
if (!nxt.startsWith("/")) return "/";
|
|
return nxt;
|
|
}
|
|
|
|
function fields() {
|
|
return {
|
|
username: ($("username") && $("username").value.trim()) || "",
|
|
password: ($("password") && $("password").value) || "",
|
|
remember_me: Boolean($("rememberMe") && $("rememberMe").checked),
|
|
challenge_id: challengeId,
|
|
challenge_answer: ($("challengeAnswer") && $("challengeAnswer").value.trim()) || "",
|
|
honeypot: ($("botTrap") && $("botTrap").value) || "",
|
|
};
|
|
}
|
|
|
|
async function authAction(url, button, idleText, loadingText, okMessage) {
|
|
setLoading(button, true, idleText, loadingText);
|
|
try {
|
|
const data = await postJSON(url, fields());
|
|
if (!data.ok) {
|
|
setStatus(data.detail || "操作失败", true);
|
|
return;
|
|
}
|
|
setStatus(okMessage);
|
|
window.location.href = nextPath();
|
|
} catch (e) {
|
|
setStatus(e.message || "请求异常", true);
|
|
} finally {
|
|
setLoading(button, false, idleText, loadingText);
|
|
}
|
|
}
|
|
|
|
const loginBtn = $("loginBtn");
|
|
const registerBtn = $("registerBtn");
|
|
const refreshChallengeBtn = $("refreshChallengeBtn");
|
|
|
|
if (loginBtn) {
|
|
loginBtn.addEventListener("click", async () => {
|
|
await authAction("/api/auth/login", loginBtn, "登录", "登录中...", "登录成功,正在跳转...");
|
|
});
|
|
}
|
|
|
|
if (registerBtn) {
|
|
registerBtn.addEventListener("click", async () => {
|
|
setLoading(registerBtn, true, "注册", "注册中...");
|
|
try {
|
|
const data = await postJSON("/api/auth/register", fields());
|
|
if (!data.ok) {
|
|
setStatus(data.detail || "注册失败", true);
|
|
return;
|
|
}
|
|
const code = (data.reset_code || "").trim();
|
|
if (code) {
|
|
const msg =
|
|
`注册成功!请务必保存你的重置码(找回密码唯一凭证):\n\n${code}\n\n` +
|
|
"请立即复制并妥善保管,点击“确定”后继续进入系统。";
|
|
await window.uiAlert(msg, "注册成功");
|
|
try {
|
|
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
await navigator.clipboard.writeText(code);
|
|
}
|
|
} catch {
|
|
// 忽略复制失败
|
|
}
|
|
}
|
|
setStatus("注册成功,正在跳转...");
|
|
const redirectTo = (data.redirect_to || "").trim();
|
|
if (redirectTo && redirectTo.startsWith("/")) {
|
|
window.location.href = redirectTo;
|
|
} else {
|
|
window.location.href = nextPath();
|
|
}
|
|
} catch (e) {
|
|
setStatus(e.message || "请求异常", true);
|
|
await refreshChallenge();
|
|
} finally {
|
|
setLoading(registerBtn, false, "注册", "注册中...");
|
|
}
|
|
});
|
|
}
|
|
|
|
if (refreshChallengeBtn) {
|
|
refreshChallengeBtn.addEventListener("click", async () => {
|
|
setLoading(refreshChallengeBtn, true, "刷新题目", "刷新中...");
|
|
await refreshChallenge();
|
|
setLoading(refreshChallengeBtn, false, "刷新题目", "刷新中...");
|
|
});
|
|
}
|
|
|
|
refreshChallenge();
|