feat: 架构升级 + 设计升级(node:sqlite 迁移 / P0 安全修复 / 游客通道 / SSE stream-ticket / 墨蓝主题 / ResultView 拆分 / 全站无障碍)+ RainID 接线整合

This commit is contained in:
2026-08-12 02:52:12 +08:00
parent 0b0144877e
commit ed6bc69ca3
82 changed files with 1974 additions and 2007 deletions
+16 -1
View File
@@ -3,8 +3,12 @@ import type { ApiResponse, Scale, ScaleSummary, SubmitResult, ValidationResult }
const api = axios.create({ baseURL: "/api", timeout: 30000 });
function getAuthToken(): string | null {
return localStorage.getItem("token") || sessionStorage.getItem("guest_token");
}
api.interceptors.request.use((config) => {
const token = localStorage.getItem("token");
const token = getAuthToken();
if (token) config.headers.Authorization = `Bearer ${token}`;
return config;
});
@@ -14,6 +18,7 @@ api.interceptors.response.use(
(err) => {
if (err.response?.status === 401 && !window.location.pathname.includes("/login")) {
localStorage.removeItem("token");
sessionStorage.removeItem("guest_token");
window.location.href = "/login";
}
return Promise.reject(err);
@@ -26,6 +31,14 @@ export const authApi = {
sendCode: (email: string) => api.post("/auth/send-code", { email }).then((r) => r.data),
register: (name: string, email: string, code: string, password: string, turnstileToken?: string) => api.post("/auth/register", { name, email, code, password, turnstileToken }).then((r) => r.data),
me: () => api.get("/auth/me").then((r) => r.data),
oidcExchange: (ticket: string) => api.post("/auth/oidc/exchange", { ticket }).then((r) => r.data),
// 游客通道:POST /api/auth/guest -> { success, data: { token, user: { isGuest: true } } }guest JWT 2h
guest: () => api.post("/auth/guest").then((r) => r.data),
};
// Public settings (non-sensitive)
export const settingsApi = {
getPublic: () => api.get("/settings/public").then((r) => r.data.data),
};
// Captcha
@@ -114,6 +127,8 @@ export const adminApi = {
deleteRecord: (id: string) => api.delete(`/admin/records/${id}`),
getSettings: () => api.get("/admin/settings").then((r) => r.data.data),
saveSettings: (settings: Record<string, string>) => api.post("/admin/settings", settings),
testSmtp: (to?: string) => api.post("/admin/test-smtp", { to }).then((r) => r.data),
testAi: () => api.post("/ai/test").then((r) => r.data),
getStats: () => api.get("/admin/stats").then((r) => r.data.data),
reloadScales: () => api.post("/scales/reload"),
getPendingReviews: () => api.get("/admin/reviews").then((r) => r.data.data),