feat(server): 管理面与成员视图 API + Scalar 在线文档

契约 19 路径全量:引导/会话/改密、engine 桩、keys、overview/logs、payments、
qpu、members、audit、成员视图。鉴权优先、422 信封、登录节流、上游验证接线。
This commit is contained in:
2026-10-06 01:30:52 +08:00
parent 6980d09750
commit 6454f4acfa
18 changed files with 2358 additions and 0 deletions
+72
View File
@@ -0,0 +1,72 @@
import { OpenAPIHono, createRoute } from "@hono/zod-openapi";
import { unixToIso, type AuditLogItem } from "@new-api-key-usage/core";
import {
requireAdminSession,
type AppVars,
} from "../middleware.ts";
import { auditLogItemSchema, err401, err422 } from "../api-schemas.ts";
import { z } from "zod";
const pageSchema = z.object({
items: z.array(auditLogItemSchema),
total: z.number().int(),
});
export function adminAuditRoutes() {
const r = new OpenAPIHono<{ Variables: AppVars }>();
r.use("*", requireAdminSession);
r.openapi(
createRoute({
method: "get",
path: "/audit-logs",
summary: "审计日志",
description: "分页、时间倒序;可按 domain 过滤(key/payment/member/account)",
tags: ["审计"],
security: [{ adminSession: [] }],
request: {
query: z.object({
page: z.string().optional(),
page_size: z.string().optional(),
domain: z
.enum(["key", "payment", "member", "account"])
.optional(),
}),
},
responses: {
200: {
description: "items/total",
content: { "application/json": { schema: pageSchema } },
},
401: err401,
422: err422,
},
}),
async (c) => {
const guard = await requireAdminSession(c, async () => {});
if (guard) return guard;
const q = c.req.query();
const page = Math.max(1, Number(q.page ?? 1) || 1);
const page_size = Math.min(
200,
Math.max(1, Number(q.page_size ?? 50) || 50),
);
const { rows, total } = await c.get("repos").audit.listPage({
domain: q.domain,
page,
page_size,
});
const items: AuditLogItem[] = rows.map((row) => ({
id: row.id,
created_at: unixToIso(row.created_at)!,
operator: row.operator,
domain: row.domain as AuditLogItem["domain"],
action: row.action,
summary: row.summary,
}));
return c.json({ items, total }, 200);
},
);
return r;
}