feat(runtime): add production control-plane foundation

This commit is contained in:
chick
2026-07-18 12:43:46 +08:00
parent 9451e1ee42
commit 293c015f89
13 changed files with 513 additions and 31 deletions
+39 -2
View File
@@ -1,4 +1,5 @@
import { randomUUID } from 'node:crypto';
import { createHash, randomUUID, timingSafeEqual } from 'node:crypto';
import { GATEWAY_AUTH_HEADER } from './runtime-config.js';
import type { Writable } from 'node:stream';
import Fastify, {
type FastifyInstance,
@@ -12,6 +13,7 @@ export const API_DEFAULT_PORT = 8790 as const;
const LEGACY_PORT = 8788;
export const REDACT_PATHS = Object.freeze([
`req.headers.${GATEWAY_AUTH_HEADER}`,
'req.headers.authorization',
'req.headers.cookie',
'req.headers.x-confirmation-token',
@@ -26,11 +28,24 @@ export interface ReadinessResult {
}
export interface BuildAppOptions {
readonly gatewayToken?: string;
readonly readiness?: () => ReadinessResult | Promise<ReadinessResult>;
readonly registerRoutes?: (app: FastifyInstance) => void | Promise<void>;
readonly logger?: false | { readonly stream?: Writable };
}
export function secureTokenEqual(
supplied: string,
expected: string,
compare: (
actual: NodeJS.ArrayBufferView,
expected: NodeJS.ArrayBufferView,
) => boolean = timingSafeEqual,
): boolean {
const digest = (value: string): Buffer => createHash('sha256').update(value, 'utf8').digest();
return compare(digest(supplied), digest(expected));
}
export interface ListenEnvironment {
readonly API_HOST?: string;
readonly API_PORT?: string;
@@ -171,10 +186,32 @@ export function buildApp(options: BuildAppOptions = {}): FastifyInstance {
},
};
const app = Fastify(fastifyOptions);
const readiness = options.readiness ?? (() => ({ ready: true, checks: { bootstrap: 'ready' } }));
const readiness =
options.readiness ?? (() => ({ ready: false, checks: { bootstrap: 'unconfigured' } }));
app.addHook('onRequest', async (request, reply) => {
reply.header('X-Request-Id', request.id);
if (options.gatewayToken !== undefined) {
const supplied = request.headers[GATEWAY_AUTH_HEADER];
const authenticated = secureTokenEqual(
typeof supplied === 'string' ? supplied : '',
options.gatewayToken,
);
if (!authenticated) {
return reply
.code(401)
.type('application/problem+json')
.send(
sendProblem(
request,
401,
'Unauthorized',
'GATEWAY_AUTH_REQUIRED',
'Trusted gateway authentication is required.',
),
);
}
}
const host = request.headers.host;
if (!host || !isLoopbackAuthority(host)) {
return reply