diff --git a/apps/backend/src/app.ts b/apps/backend/src/app.ts index 13ed43eb..1053e348 100644 --- a/apps/backend/src/app.ts +++ b/apps/backend/src/app.ts @@ -6,7 +6,7 @@ import claimsRouter from "./routes/claims.js"; import open91Router from "./routes/open-91.js"; import tencentRouter from "./routes/tencent.js"; import webhooksRouter from "./routes/webhooks.js"; -import { buildSuccessPayload } from "./utils/http.js"; +import { buildSuccessPayload, sendRouteError } from "./utils/http.js"; import { accessLogMiddleware } from "./middleware/access-log.js"; import { corsMiddleware } from "./middleware/cors.js"; import { buildHealthPayload, type StartupState } from "./startup/state.js"; @@ -16,7 +16,10 @@ type CreateAppOptions = { isShutdownStarted: () => boolean; }; -export function createApp({ startupState, isShutdownStarted }: CreateAppOptions) { +export function createApp({ + startupState, + isShutdownStarted, +}: CreateAppOptions) { const app = express(); app.use(accessLogMiddleware); @@ -44,7 +47,12 @@ export function createApp({ startupState, isShutdownStarted }: CreateAppOptions) app.get("/health/ready", (_req, res) => { if (startupState.core.ready) { - res.json(buildSuccessPayload(buildHealthPayload(startupState, isShutdownStarted()), "ready")); + res.json( + buildSuccessPayload( + buildHealthPayload(startupState, isShutdownStarted()), + "ready" + ) + ); return; } @@ -84,5 +92,9 @@ export function createApp({ startupState, isShutdownStarted }: CreateAppOptions) app.use("/api/v1/claim", claimsRouter); app.use("/api/v1/admin", adminRouter); + app.use((err, _req, res, _next) => { + sendRouteError(res, err, "服务内部错误", "[global]"); + }); + return app; } diff --git a/apps/backend/src/utils/http.ts b/apps/backend/src/utils/http.ts index 99d1f5bc..344e6ec4 100644 --- a/apps/backend/src/utils/http.ts +++ b/apps/backend/src/utils/http.ts @@ -207,3 +207,42 @@ function formatResponseDateTime(value) { return `${tokens.year}-${tokens.month}-${tokens.day} ${tokens.hour}:${tokens.minute}:${tokens.second}` } + +type RouteHandlerOptions = { + successMessage?: string + errorMessage?: string + scope?: string +} + +type RouteFileHandlerOptions = { + errorMessage?: string + scope?: string +} + +export function createRouteHandler( + action: (req: any, res: any) => unknown | Promise, + { successMessage = 'ok', errorMessage, scope }: RouteHandlerOptions = {}, +) { + return async (req, res) => { + try { + const data = await action(req, res) + res.json(buildSuccessPayload(data, successMessage)) + } catch (error) { + sendRouteError(res, error, errorMessage, scope) + } + } +} + +export function createRouteFileHandler( + action: (req: any, res: any) => string | Promise, + { errorMessage, scope }: RouteFileHandlerOptions = {}, +) { + return async (req, res) => { + try { + const filePath = await action(req, res) + res.sendFile(filePath) + } catch (error) { + sendRouteError(res, error, errorMessage, scope) + } + } +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..ff955191 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,6 @@ +{ + "name": "core-infra", + "lockfileVersion": 3, + "requires": true, + "packages": {} +}