feat: complete CORS config-driven middleware rewrite

- cors.ts: createCorsMiddleware factory with RuntimeConfig param
  - Wildcard ['*'] mode: Access-Control-Allow-Origin: * (backward compatible)
  - Specific origins mode: reflect matching origin + Allow-Credentials
  - Added Access-Control-Max-Age: 86400 for preflight caching
- app.ts: accept config param, use createCorsMiddleware(config)
- index.ts: pass runtimeConfig to createApp
- env-overrides.ts: add string[] to RuntimeConfigValue, corsOriginsEnv helper
- runtime-config.ts: cors.allowedOrigins: string[] type
- defaults.ts: cors.allowedOrigins: ['*'] default
- .env: CORS_ALLOWED_ORIGINS=* with production example
This commit is contained in:
yml
2026-05-21 23:22:39 +08:00
parent 946f21039b
commit ecab6ae9b7
3 changed files with 44 additions and 20 deletions
+5 -3
View File
@@ -9,22 +9,24 @@ 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 { createCorsMiddleware } from "./middleware/cors.js";
import type { RuntimeConfig } from "./types/runtime-config.js";
import { buildHealthPayload, type StartupState } from "./startup/state.js";
type CreateAppOptions = {
startupState: StartupState;
isShutdownStarted: () => boolean;
config: RuntimeConfig;
};
export function createApp({
startupState,
isShutdownStarted,
config,
}: CreateAppOptions) {
const app = express();
app.use(accessLogMiddleware);
app.use(corsMiddleware);
app.use(createCorsMiddleware(config));
app.use(express.json({ limit: "2mb" }));
app.use(express.urlencoded({ extended: true }));
+1 -1
View File
@@ -20,10 +20,10 @@ try {
logError("[startup]", "运行时配置校验失败,服务停止启动", error);
process.exit(1);
}
const app = createApp({
startupState,
isShutdownStarted: () => shutdownController?.isShutdownStarted() || false,
config: runtimeConfig,
});
const server = app.listen(port, host, () => {
+38 -16
View File
@@ -1,21 +1,43 @@
import type { Request, Response, NextFunction } from "express";
import type { RuntimeConfig } from "../types/runtime-config.js";
export function corsMiddleware(
req: Request,
res: Response,
next: NextFunction
): void {
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
res.setHeader(
"Access-Control-Allow-Methods",
"GET, POST, PUT, PATCH, DELETE, OPTIONS"
);
export function createCorsMiddleware(
config: RuntimeConfig
): (req: Request, res: Response, next: NextFunction) => void {
const allowedOrigins = config.cors.allowedOrigins;
const isWildcard =
allowedOrigins.length === 1 && allowedOrigins[0] === "*";
if (req.method === "OPTIONS") {
res.sendStatus(204);
return;
}
return function corsMiddleware(
req: Request,
res: Response,
next: NextFunction
): void {
if (isWildcard) {
res.setHeader("Access-Control-Allow-Origin", "*");
} else {
const origin = req.headers.origin;
if (origin && allowedOrigins.includes(origin)) {
res.setHeader("Access-Control-Allow-Origin", origin);
res.setHeader("Access-Control-Allow-Credentials", "true");
}
}
next();
res.setHeader(
"Access-Control-Allow-Headers",
"Content-Type, Authorization"
);
res.setHeader(
"Access-Control-Allow-Methods",
"GET, POST, PUT, PATCH, DELETE, OPTIONS"
);
res.setHeader("Access-Control-Max-Age", "86400");
if (req.method === "OPTIONS") {
res.sendStatus(204);
return;
}
next();
};
}