Files
order_site/apps/backend/src/app.ts
T

102 lines
2.8 KiB
TypeScript

import type { Request, Response, NextFunction } from "express";
import express from "express";
import process from "node:process";
import adminRouter from "./routes/admin.js";
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, 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";
type CreateAppOptions = {
startupState: StartupState;
isShutdownStarted: () => boolean;
};
export function createApp({
startupState,
isShutdownStarted,
}: CreateAppOptions) {
const app = express();
app.use(accessLogMiddleware);
app.use(corsMiddleware);
app.use(express.json({ limit: "2mb" }));
app.use(express.urlencoded({ extended: true }));
app.get("/health", (_req, res) => {
res.json(
buildSuccessPayload(
buildHealthPayload(startupState, isShutdownStarted()),
startupState.core.ready ? "ready" : "starting"
)
);
});
app.get("/health/live", (_req, res) => {
res.json(
buildSuccessPayload({
status: isShutdownStarted() ? "shutting_down" : "alive",
pid: process.pid,
})
);
});
app.get("/health/ready", (_req, res) => {
if (startupState.core.ready) {
res.json(
buildSuccessPayload(
buildHealthPayload(startupState, isShutdownStarted()),
"ready"
)
);
return;
}
res.status(503).json({
code: 1,
msg: startupState.core.lastError || "服务启动中,请稍后重试",
errorCode: "service_not_ready",
time: Math.floor(Date.now() / 1000),
data: buildHealthPayload(startupState, isShutdownStarted()),
});
});
app.use((req, res, next) => {
if (startupState.core.ready) {
next();
return;
}
res.status(503).json({
code: 1,
msg: startupState.core.lastError || "服务启动中,请稍后重试",
errorCode: "service_not_ready",
time: Math.floor(Date.now() / 1000),
data: {
startup: {
phase: startupState.phase,
attemptCount: startupState.core.attemptCount,
lastAttemptAt: startupState.core.lastAttemptAt,
},
},
});
});
app.use("/api/v1/tencent", tencentRouter);
app.use("/api/v1/open/91", open91Router);
app.use("/api/v1/webhooks", webhooksRouter);
app.use("/api/v1/claim", claimsRouter);
app.use("/api/v1/admin", adminRouter);
app.use((err: unknown, _req: Request, res: Response, _next: NextFunction) => {
sendRouteError(res, err, "服务内部错误", "[global]");
});
return app;
}