完善 readiness、发布追溯与敏感治理(07-15-production-trust-closeout-cleanup)
这个提交包含在:
+13
-13
@@ -104,8 +104,10 @@ function setStaticCacheHeaders(res, filePath) {
|
||||
}
|
||||
}
|
||||
|
||||
async function buildApp() {
|
||||
async function buildApp(dependencies = {}) {
|
||||
const config = getConfig();
|
||||
const prismaPluginImpl = dependencies.prismaPlugin ?? prismaPlugin;
|
||||
const startJobsImpl = dependencies.startJobs ?? startJobs;
|
||||
|
||||
const app = fastify({
|
||||
logger: {
|
||||
@@ -246,7 +248,7 @@ async function buildApp() {
|
||||
});
|
||||
});
|
||||
|
||||
await app.register(prismaPlugin);
|
||||
await app.register(prismaPluginImpl);
|
||||
await app.register(authPlugin);
|
||||
|
||||
if (config.nodeEnv !== "production" && config.openapiDocsEnabled) {
|
||||
@@ -254,10 +256,6 @@ async function buildApp() {
|
||||
app.log.info("OpenAPI docs enabled at /api/docs");
|
||||
}
|
||||
|
||||
if (config.otelTracesEnabled) {
|
||||
app.log.info("OTel tracing enabled");
|
||||
}
|
||||
|
||||
app.get(
|
||||
"/health",
|
||||
{
|
||||
@@ -267,16 +265,17 @@ async function buildApp() {
|
||||
response: {
|
||||
200: buildDataEnvelopeSchema({
|
||||
type: "object",
|
||||
required: ["ok"],
|
||||
required: ["ok", "buildSha"],
|
||||
properties: {
|
||||
ok: { type: "boolean" },
|
||||
buildSha: { type: "string" },
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
async (request, reply) => {
|
||||
return reply.send({ requestId: request.id, data: { ok: true } });
|
||||
return reply.send({ requestId: request.id, data: { ok: true, buildSha: app.config.buildSha } });
|
||||
},
|
||||
);
|
||||
|
||||
@@ -289,16 +288,17 @@ async function buildApp() {
|
||||
response: {
|
||||
200: buildDataEnvelopeSchema({
|
||||
type: "object",
|
||||
required: ["ok"],
|
||||
required: ["ok", "buildSha"],
|
||||
properties: {
|
||||
ok: { type: "boolean" },
|
||||
buildSha: { type: "string" },
|
||||
},
|
||||
}),
|
||||
503: buildErrorEnvelopeSchema({
|
||||
description: "数据库不可用",
|
||||
codeExample: "SYS_503",
|
||||
messageExample: "Service Unavailable",
|
||||
detailsExample: { db: "unreachable" },
|
||||
detailsExample: { db: "unreachable", buildSha: "abcdef123456" },
|
||||
}),
|
||||
},
|
||||
},
|
||||
@@ -306,14 +306,14 @@ async function buildApp() {
|
||||
async (request, reply) => {
|
||||
try {
|
||||
await app.prisma.$queryRaw`SELECT 1`;
|
||||
return reply.send({ requestId: request.id, data: { ok: true } });
|
||||
return reply.send({ requestId: request.id, data: { ok: true, buildSha: app.config.buildSha } });
|
||||
} catch (error) {
|
||||
app.log.error({ err: error, requestId: request.id }, "Readiness check failed");
|
||||
throw new AppError({
|
||||
code: "SYS_503",
|
||||
message: "Service Unavailable",
|
||||
statusCode: 503,
|
||||
details: { db: "unreachable" },
|
||||
details: { db: "unreachable", buildSha: app.config.buildSha },
|
||||
});
|
||||
}
|
||||
},
|
||||
@@ -399,7 +399,7 @@ async function buildApp() {
|
||||
await app.register(aiRoutes, { prefix: "/api" });
|
||||
await app.register(exportsRoutes, { prefix: "/api" });
|
||||
|
||||
await startJobs(app);
|
||||
await startJobsImpl(app);
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,11 @@ function requiredEnv(name) {
|
||||
return value;
|
||||
}
|
||||
|
||||
function normalizeBuildSha(value) {
|
||||
const sha = String(value || "").trim().toLowerCase();
|
||||
return /^[0-9a-f]{7,64}$/.test(sha) ? sha.slice(0, 12) : "unknown";
|
||||
}
|
||||
|
||||
function getConfig() {
|
||||
const nodeEnv = process.env.NODE_ENV ?? "development";
|
||||
const serverRoot = path.resolve(__dirname, "..");
|
||||
@@ -21,6 +26,7 @@ function getConfig() {
|
||||
nodeEnv,
|
||||
port: Number(process.env.PORT ?? 3000),
|
||||
host: process.env.HOST ?? "0.0.0.0",
|
||||
buildSha: normalizeBuildSha(process.env.BUILD_SHA),
|
||||
|
||||
metricsEnabled: process.env.METRICS_ENABLED !== "false",
|
||||
|
||||
|
||||
在新工单中引用
屏蔽一个用户