fix: server 审查修复——member_id 校验、访问日志 finally、静默初始化并发

非法 member_id 不再以 NaN 入库;HTTP 访问日志覆盖异常路径;setup-state 静默建管理员遇唯一冲突视为已初始化。
This commit is contained in:
2026-10-06 12:55:18 +08:00
parent fa5b3a8942
commit 54ea686f44
4 changed files with 41 additions and 17 deletions
+3
View File
@@ -40,8 +40,11 @@ export function createApp(opts: CreateAppOptions) {
app.use("*", async (c, next) => { app.use("*", async (c, next) => {
const start = Date.now(); const start = Date.now();
try {
await next(); await next();
} finally {
appLogger("http").info`${c.req.method} ${c.req.path} → ${c.res.status} · ${Date.now() - start}ms`; appLogger("http").info`${c.req.method} ${c.req.path} → ${c.res.status} · ${Date.now() - start}ms`;
}
}); });
app.use("*", async (c, next) => { app.use("*", async (c, next) => {
c.set("repos", repos); c.set("repos", repos);
+24 -6
View File
@@ -133,10 +133,18 @@ export function adminKeyRoutes() {
if (guard) return guard; if (guard) return guard;
const body = await c.req.json().catch(() => null); const body = await c.req.json().catch(() => null);
const sk_key = typeof body?.sk_key === "string" ? body.sk_key.trim() : ""; const sk_key = typeof body?.sk_key === "string" ? body.sk_key.trim() : "";
const member_id = let member_id: number | null = null;
body?.member_id === undefined || body?.member_id === null if (body?.member_id !== undefined && body?.member_id !== null) {
? null const n = Number(body.member_id);
: Number(body.member_id); if (!Number.isInteger(n) || n <= 0) {
return jsonError(
c,
422,
ERR.validation_failed({ member_id: "成员 id 无效" }),
);
}
member_id = n;
}
if (!sk_key || !sk_key.startsWith("sk-")) { if (!sk_key || !sk_key.startsWith("sk-")) {
return jsonError( return jsonError(
c, c,
@@ -276,8 +284,18 @@ export function adminKeyRoutes() {
} }
} }
if (body && "member_id" in body) { if (body && "member_id" in body) {
const nextMember = let nextMember: number | null = null;
body.member_id == null ? null : Number(body.member_id); if (body.member_id != null) {
const n = Number(body.member_id);
if (!Number.isInteger(n) || n <= 0) {
return jsonError(
c,
422,
ERR.validation_failed({ member_id: "成员 id 无效" }),
);
}
nextMember = n;
}
if (nextMember !== before.member_id) { if (nextMember !== before.member_id) {
patch.member_id = nextMember; patch.member_id = nextMember;
actions.push({ actions.push({
+4
View File
@@ -74,6 +74,7 @@ export function adminSessionRoutes() {
const initUser = c.get("adminInitUsername"); const initUser = c.get("adminInitUsername");
const initPass = c.get("adminInitPassword"); const initPass = c.get("adminInitPassword");
if (initUser && initPass) { if (initUser && initPass) {
try {
const password_hash = await hashPassword(initPass); const password_hash = await hashPassword(initPass);
await repos.users.insert(initUser, password_hash); await repos.users.insert(initUser, password_hash);
await repos.audit.insert({ await repos.audit.insert({
@@ -82,6 +83,9 @@ export function adminSessionRoutes() {
action: "account.init", action: "account.init",
summary: "空库 env 静默重建管理员", summary: "空库 env 静默重建管理员",
}); });
} catch {
// 并发/重复初始化:唯一约束冲突时视为已初始化
}
return c.json({ initialized: true }, 200); return c.json({ initialized: true }, 200);
} }
return c.json({ initialized: false }, 200); return c.json({ initialized: false }, 200);
-1
View File
@@ -15,7 +15,6 @@ import {
err401, err401,
err422, err422,
logPageSchema, logPageSchema,
keyItemSchema,
} from "../api-schemas.ts"; } from "../api-schemas.ts";
import { z } from "zod"; import { z } from "zod";