dorfteich/apps/api/src/main.ts
Claude Fable 5 224ae3e6db
All checks were successful
CD / Build and push images (push) Successful in 2m35s
CD / Deploy to Test (push) Successful in 8s
CI / Lint, typecheck, test (push) Successful in 3m8s
CI / Build container images (push) Has been skipped
CD / Smoke tests against Test (push) Successful in 1m9s
CD / Promote to Int (push) Successful in 9s
CI / Auth e2e pack (push) Successful in 4m52s
CI / Import/export fidelity gate (push) Successful in 46s
Fix boot crash on compose-passed empty setup variables
main.ts parsed process.env raw, so the compose passthroughs
(SETUP_DEFAULT_LOCALE: "") failed the enum parse and crash-looped the
Test api. Route main.ts through the same loadApiEnv as AppConfig, which
overlays the secret store and drops empty strings.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EwZ4jR4KFAPvpjWevfUGX1
2026-07-11 15:20:24 +02:00

48 lines
1.8 KiB
TypeScript

import { execFileSync } from 'node:child_process';
import cookieParser from 'cookie-parser';
import { NestFactory } from '@nestjs/core';
import type { NestExpressApplication } from '@nestjs/platform-express';
import { Logger } from 'nestjs-pino';
import { AppModule } from './app.module';
import { AppConfig, loadApiEnv } from './config/app-config.service';
/**
* Apply pending migrations before the application accepts traffic, so
* self-hosters update by simply pulling a new image (ADR 0002). Prisma
* serializes concurrent deploys with a database advisory lock.
*/
function runMigrations(): void {
const prismaCli = require.resolve('prisma/build/index.js');
execFileSync(process.execPath, [prismaCli, 'migrate', 'deploy'], { stdio: 'inherit' });
}
async function bootstrap(): Promise<void> {
// Validate the environment before doing anything with it; this throws a
// readable list of problems and prevents a half-started process.
const env = loadApiEnv();
if (env.MIGRATE_ON_START) {
runMigrations();
}
// bufferLogs holds early log lines until the pino logger is attached,
// so even bootstrap errors come out as structured JSON.
const app = await NestFactory.create<NestExpressApplication>(AppModule, { bufferLogs: true });
app.useLogger(app.get(Logger));
// One reverse-proxy hop (Caddy) in front of us: req.ip must reflect the
// real client for rate limiting and audit logs.
app.set('trust proxy', 1);
app.use(cookieParser());
// Base64-encoded Yjs page state (max 5 MiB, operations.md) inflates by
// ~4/3; 8 MiB leaves headroom for the JSON envelope around it.
app.useBodyParser('json', { limit: '8mb' });
app.setGlobalPrefix('api/v1');
app.enableShutdownHooks();
const config = app.get(AppConfig);
await app.listen(config.env.PORT);
}
void bootstrap();