import { NestFactory } from '@nestjs/core'; import { NestExpressApplication } from '@nestjs/platform-express'; import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; import { AppModule } from './app.module'; import { configureApp } from './app.config'; const REQUIRED_ENV_VARS = ['DATABASE_URL', 'JWT_ACCESS_SECRET'] as const; // JWT_ACCESS_SECRET is the root of trust for both authentication (every // access/MFA-challenge/SSO-completion token is signed with it) and MFA // secret confidentiality (mfa-crypto.util.ts derives the AES key that // encrypts stored TOTP secrets from it) - a short or guessable value here // would let an attacker forge tokens for any user AND decrypt everyone's // stored TOTP secret, not just guess a password. 32 chars matches the // output of "openssl rand -hex 32" / "-base64 32", the standard way to // generate one. const MIN_JWT_SECRET_LENGTH = 32; function assertRequiredEnvVars(): void { const missing = REQUIRED_ENV_VARS.filter((name) => !process.env[name]); if (missing.length > 0) { throw new Error( `Missing required environment variable(s): ${missing.join(', ')}`, ); } const jwtSecret = process.env.JWT_ACCESS_SECRET as string; if (jwtSecret.length < MIN_JWT_SECRET_LENGTH) { throw new Error( `JWT_ACCESS_SECRET must be at least ${MIN_JWT_SECRET_LENGTH} characters (got ${jwtSecret.length}) - generate one with e.g. "openssl rand -hex 32"`, ); } } async function bootstrap() { assertRequiredEnvVars(); const app = await NestFactory.create(AppModule); app.enableShutdownHooks(); configureApp(app); // Swagger is mounted as raw Express middleware outside Nest's routing, so // it is never covered by the global JwtAuthGuard - anyone reaching the // host could otherwise read the full API schema (every endpoint and DTO // shape) with no authentication at all. Off by default; an operator who // wants it in a given environment opts in explicitly. if (process.env.ENABLE_SWAGGER === 'true') { const swaggerConfig = new DocumentBuilder() .setTitle('ApiCore API') .setDescription('Digitale Imkereiverwaltung – API-Dokumentation') .setVersion('1.0') .addBearerAuth() .build(); const swaggerDocument = SwaggerModule.createDocument(app, swaggerConfig); SwaggerModule.setup('api/v1/docs', app, swaggerDocument); } await app.listen(process.env.PORT ?? 3001); } void bootstrap();