修复排行榜图鉴排序500:LB_COLS映射codex_n与SQL别名codexN不一致(上一轮测试漏测sort=codex分支,本轮三排序全回归);/api/me未登录改返200{user:null}消除启动401控制台噪音,会话识别对所有路由生效、仅受保护路由强制401

This commit is contained in:
JOJO 2026-08-06 10:16:49 +08:00
parent ab403f4cc7
commit 548314e22b
5 changed files with 13 additions and 13 deletions

View File

@ -54,7 +54,7 @@
UI = "auth"; UI = "auth";
show("o-auth"); show("o-auth");
const r = await NET.api("/api/me", "GET"); const r = await NET.api("/api/me", "GET");
if (r.ok) { NET._apply(r.data); enterHome(); } if (r.ok && r.data.user) { NET._apply(r.data); enterHome(); }
else { else {
if (r.status === 0) err("无法连接服务器,请稍后刷新重试"); if (r.status === 0) err("无法连接服务器,请稍后刷新重试");
show("o-auth"); show("o-auth");

View File

@ -18,8 +18,9 @@ async function enterHome() {
show("o-home"); show("o-home");
/* 刷新账号数据stats/codex再渲染面板与排行榜 */ /* 刷新账号数据stats/codex再渲染面板与排行榜 */
const r = await NET.api("/api/me", "GET"); const r = await NET.api("/api/me", "GET");
if (r.ok) NET._apply(r.data); if (r.ok && r.data.user) NET._apply(r.data);
else if (r.status === 401) { NET.user = null; UI = "auth"; show("o-auth"); return; } else if (r.ok) { NET.user = null; UI = "auth"; show("o-auth"); return; } // 会话失效
/* status===0 网络抖动:沿用现有数据,不踢出 */
buildHomeStats(); buildHomeStats();
buildLeaderboard(LB_SORT); buildLeaderboard(LB_SORT);
} }

View File

@ -23,10 +23,10 @@ const NET = {
return { ok: res.ok, status: res.status, data }; return { ok: res.ok, status: res.status, data };
}, },
/* 启动尝试恢复会话。true=已登录 */ /* 启动尝试恢复会话。true=已登录(未登录时服务端返回 200 {user:null} */
async boot() { async boot() {
const r = await this.api("/api/me", "GET"); const r = await this.api("/api/me", "GET");
if (r.ok) { this._apply(r.data); return true; } if (r.ok && r.data.user) { this._apply(r.data); return true; }
return false; return false;
}, },

View File

@ -82,14 +82,15 @@ function logout(req, res, ctx) {
return ctx.ok({ bye: true }); return ctx.ok({ bye: true });
} }
/* ---------- GET /api/me ---------- */ /* ---------- GET /api/me(未登录返回 user:null不产生 401 控制台噪音) ---------- */
function me(req, res, ctx) { function me(req, res, ctx) {
const u = ctx.user; const u = ctx.user;
if (!u) return ctx.ok({ user: null });
return ctx.ok({ user: { username: u.username, email: u.email, createdAt: u.created_at }, stats: statsOf(u.id), codex: codexOf(u.id), codexTotal: CODEX_TOTAL }); return ctx.ok({ user: { username: u.username, email: u.email, createdAt: u.created_at }, stats: statsOf(u.id), codex: codexOf(u.id), codexTotal: CODEX_TOTAL });
} }
/* ---------- GET /api/leaderboard?sort=endless|boss|codex ---------- */ /* ---------- GET /api/leaderboard?sort=endless|boss|codex ---------- */
const LB_COLS = { endless: "endless_high", boss: "boss_kills", codex: "codex_n" }; const LB_COLS = { endless: "endless_high", boss: "boss_kills", codex: "codexN" };
function leaderboard(req, res, ctx, _body, query) { function leaderboard(req, res, ctx, _body, query) {
const col = LB_COLS[(query && query.get("sort")) || ""] || "endless_high"; const col = LB_COLS[(query && query.get("sort")) || ""] || "endless_high";
/* col 来自上方白名单映射,非用户输入直接拼接 */ /* col 来自上方白名单映射,非用户输入直接拼接 */
@ -134,7 +135,7 @@ const ROUTES = {
"POST /api/register": { fn: register, auth: false }, "POST /api/register": { fn: register, auth: false },
"POST /api/login": { fn: login, auth: false }, "POST /api/login": { fn: login, auth: false },
"POST /api/logout": { fn: logout, auth: true }, "POST /api/logout": { fn: logout, auth: true },
"GET /api/me": { fn: me, auth: true }, "GET /api/me": { fn: me, auth: false },
"GET /api/leaderboard": { fn: leaderboard, auth: true }, "GET /api/leaderboard": { fn: leaderboard, auth: true },
"POST /api/run": { fn: run, auth: true }, "POST /api/run": { fn: run, auth: true },
}; };

View File

@ -52,11 +52,9 @@ async function handleApi(req, res, pathname, query) {
const ctx = makeCtx(req, res); const ctx = makeCtx(req, res);
const route = ROUTES[req.method + " " + pathname]; const route = ROUTES[req.method + " " + pathname];
if (!route) return ctx.fail(404, "not found"); if (!route) return ctx.fail(404, "not found");
if (route.auth) { /* 有 session 就识别身份route.auth 才强制要求登录 */
const u = auth.getSessionUser(db, ctx.token); if (ctx.token) ctx.user = auth.getSessionUser(db, ctx.token);
if (!u) return ctx.fail(401, "未登录或会话已过期"); if (route.auth && !ctx.user) return ctx.fail(401, "未登录或会话已过期");
ctx.user = u;
}
try { try {
let body = null; let body = null;
/* logout 等无 body 的 POST 不强制 JSON有 body 的必须 application/json */ /* logout 等无 body 的 POST 不强制 JSON有 body 的必须 application/json */