/* ===================================================================== * 认证模块(auth.js) * 登录 / 注册 / 会话保存 / 当前用户刷新。 * localStorage 只保存 token 与 username(绝不保存密码)。 * ===================================================================== */ import { state } from "./state.js"; import { $, escapeHtml, openModal, showToast } from "./utils.js"; import { api, uploadFile } from "./api.js"; import { render, renderShell } from "./router.js"; export function isLoggedIn() { return !!localStorage.getItem("token"); } export function saveSession(token, username) { localStorage.setItem("token", token); localStorage.setItem("username", username); } export function logout() { localStorage.removeItem("token"); localStorage.removeItem("username"); state.user = null; state.friendStatus = "none"; renderShell(); render(); showToast("已退出登录"); } export async function refreshUser() { if (!isLoggedIn()) { state.user = null; state.friendStatus = "none"; return; } try { const data = await api("/api/user/level"); state.user = { id: data.user_id, username: localStorage.getItem("username") || "", role: data.role, }; if (data.role === "blogger") state.friendStatus = "blogger"; else if (data.role === "friend") state.friendStatus = "accepted"; else { try { const fs = await api("/api/friend/status"); state.friendStatus = fs.status; } catch (err) { state.friendStatus = "none"; } } } catch (err) { if (err.status === 401) logout(); else showToast(err.message, "error"); } } export async function afterAuthSuccess(username) { await refreshUser(); // 拉取真实角色与好友状态 renderShell(); render(); } export function openLoginModal() { const form = document.createElement("form"); form.className = "form"; form.innerHTML = `

还没有账号?立即注册

`; const modal = openModal({ title: "登录", content: form }); $("#goto-register", form).addEventListener("click", (e) => { e.preventDefault(); modal.close(); openRegisterModal(); }); form.addEventListener("submit", async (e) => { e.preventDefault(); const email = form.email.value.trim(); const password = form.password.value; if (!email || !password) { showToast("请填写邮箱和密码", "error"); return; } const btn = $('button[type="submit"]', form); btn.disabled = true; btn.textContent = "登录中…"; try { const data = await api("/api/login", { method: "POST", body: { email, password } }); saveSession(data.token, data.username); modal.close(); await afterAuthSuccess(data.username); showToast("登录成功", "success"); } catch (err) { showToast(err.message, "error"); } finally { btn.disabled = false; btn.textContent = "登录"; } }); } /** 好友个人设置:上传自己的头像(访客禁止上传,后端有角色校验) */ export function openProfileModal() { const content = document.createElement("div"); content.innerHTML = `

支持 jpg / png / webp,最大 4MB

`; const modal = openModal({ title: "个人设置", content }); const avatarBox = $("#profile-avatar", content); const renderPreview = (url) => { avatarBox.innerHTML = url ? `头像预览` : ''; }; api("/api/user/me").then((me) => renderPreview(me.avatar || "")).catch(() => {}); $("#btn-avatar-upload", content).addEventListener("click", () => $("#avatar-file", content).click()); $("#avatar-file", content).addEventListener("change", async () => { const file = $("#avatar-file", content).files && $("#avatar-file", content).files[0]; if (!file) return; try { const data = await uploadFile("/api/upload/avatar", file); renderPreview(data.url); showToast("头像上传成功", "success"); } catch (err) { showToast(err.message, "error"); } $("#avatar-file", content).value = ""; }); } export function openRegisterModal() { const form = document.createElement("form"); form.className = "form"; form.innerHTML = `

已有账号?直接登录

`; const modal = openModal({ title: "注册", content: form }); $("#goto-login", form).addEventListener("click", (e) => { e.preventDefault(); modal.close(); openLoginModal(); }); // 发送邮箱验证码(60 秒倒计时防重复点击;后端另有按邮箱/IP 的小时限流) const sendCodeBtn = $("#btn-send-code", form); sendCodeBtn.addEventListener("click", async () => { const email = form.email.value.trim(); if (!email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) { showToast("请先填写正确的邮箱", "error"); return; } sendCodeBtn.disabled = true; sendCodeBtn.textContent = "发送中…"; try { await api("/api/email/send-code", { method: "POST", body: { email, purpose: "register" } }); showToast("验证码已发送,请查收邮件", "success"); let countdown = 60; const timer = setInterval(() => { countdown -= 1; if (countdown <= 0) { clearInterval(timer); sendCodeBtn.disabled = false; sendCodeBtn.textContent = "发送验证码"; } else { sendCodeBtn.textContent = `${countdown}s 后重发`; } }, 1000); } catch (err) { showToast(err.message, "error"); sendCodeBtn.disabled = false; sendCodeBtn.textContent = "发送验证码"; } }); form.addEventListener("submit", async (e) => { e.preventDefault(); const email = form.email.value.trim(); const username = form.username.value.trim(); const password = form.password.value; const code = form.code.value.trim(); if (!email || !username || !password || !code) { showToast("请填写完整信息", "error"); return; } if (password.length < 6) { showToast("密码至少 6 位", "error"); return; } const btn = $('button[type="submit"]', form); btn.disabled = true; btn.textContent = "注册中…"; try { await api("/api/register", { method: "POST", body: { email, username, password, code } }); // 注册成功后自动登录 const loginData = await api("/api/login", { method: "POST", body: { email, password } }); saveSession(loginData.token, username); modal.close(); await afterAuthSuccess(username); showToast("注册成功", "success"); } catch (err) { showToast(err.message, "error"); } finally { btn.disabled = false; btn.textContent = "注册"; } }); }