import { createRoute, type RouteConfig } from "@hono/zod-openapi"; import { type ApiScope, apiResponse } from "@umt/contracts"; import type { Context } from "hono"; import type { z } from "zod"; import { type AuthEnv, requireScope } from "../../http/auth.ts"; import type { ModuleApp } from "../types.ts"; import type { Actor } from "./service/common.ts"; // 本模块路由封装:createRoute(进 OpenAPI)+ requireScope + 入参校验 + 出参按合同 parse。 export interface RouteDef { method: "get" | "post" | "patch" | "put" | "delete"; path: string; scope: ApiScope; summary: string; tags: string[]; params?: z.ZodObject; query?: z.ZodType; body?: z.ZodType; response: z.ZodType; } type Infer = T extends z.ZodType ? z.infer : Record; export interface Input { params: Infer; query: Infer; body: Infer; actor: Actor; c: Context; } /** 调用者(W2-A 的 c.var.actor:admin 会话 / API Key)→ 审计/修订用的操作者;管理员记为 user */ export function actorOf(c: Context): Actor { const a = c.get("actor") as { type?: string; id?: string } | undefined; if (!a) return { type: "system", id: null }; return { type: a.type === "api_key" ? "api_key" : "user", id: a.id ?? null }; } export function route(app: ModuleApp, def: D, handler: (input: Input) => Promise) { const request: RouteConfig["request"] = {}; if (def.params) request.params = def.params as never; if (def.query) request.query = def.query as never; if (def.body) request.body = { content: { "application/json": { schema: def.body as never } }, required: true }; const r = createRoute({ method: def.method, path: def.path, summary: def.summary, tags: def.tags, middleware: [requireScope(def.scope)] as never, request, responses: { 200: { description: def.summary, content: { "application/json": { schema: apiResponse(def.response) } } }, }, }); // biome-ignore lint/suspicious/noExplicitAny: 路由泛型在封装里擦除,入参已由 zod-openapi 校验 app.openapi(r, (async (c: any) => { const input = { params: def.params ? c.req.valid("param") : {}, query: def.query ? c.req.valid("query") : {}, body: def.body ? c.req.valid("json") : {}, actor: actorOf(c), c, } as Input; const data = def.response.parse(await handler(input)); return c.json({ ok: true, data }, 200); // biome-ignore lint/suspicious/noExplicitAny: 同上 }) as any); }