mirror of
https://github.com/bookorbit/bookorbit.git
synced 2026-11-03 00:11:37 -06:00
chore: env validation, shutdown hooks, logger noise reduction
- add Zod-based env validation schema; fail fast on missing/invalid vars - suppress NestJS framework boot logs (InstanceLoader, RouterExplorer, RoutesResolver) - enable graceful shutdown hooks
This commit is contained in:
@@ -5,6 +5,7 @@ import { ScheduleModule } from '@nestjs/schedule';
|
||||
import { LoggerModule } from 'nestjs-pino';
|
||||
|
||||
import { appConfig, authConfig, dbConfig, externalApiConfig, mailerConfig, storageConfig } from './config/config';
|
||||
import { validateEnv } from './config/env.validation';
|
||||
import { loggerConfig } from './common/logger.config';
|
||||
import { CommonModule } from './common/common.module';
|
||||
import { JwtAuthGuard } from './common/guards/jwt-auth.guard';
|
||||
@@ -44,6 +45,7 @@ import { UserModule } from './modules/user/user.module';
|
||||
LoggerModule.forRoot(loggerConfig),
|
||||
ConfigModule.forRoot({
|
||||
isGlobal: true,
|
||||
validate: validateEnv,
|
||||
load: [appConfig, dbConfig, authConfig, storageConfig, mailerConfig, externalApiConfig],
|
||||
}),
|
||||
ScheduleModule.forRoot(),
|
||||
|
||||
@@ -3,9 +3,21 @@ import type { IncomingMessage, ServerResponse } from 'http';
|
||||
|
||||
const isDev = process.env.NODE_ENV !== 'production';
|
||||
|
||||
const FRAMEWORK_CONTEXTS = new Set(['InstanceLoader', 'RouterExplorer', 'RoutesResolver']);
|
||||
|
||||
export const loggerConfig: Params = {
|
||||
pinoHttp: {
|
||||
level: isDev ? 'debug' : 'info',
|
||||
hooks: {
|
||||
logMethod(inputArgs, method) {
|
||||
const first = inputArgs[0];
|
||||
if (first !== null && typeof first === 'object' && 'context' in first && FRAMEWORK_CONTEXTS.has((first as Record<string, unknown>).context as string)) {
|
||||
return;
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
||||
return method.apply(this, inputArgs);
|
||||
},
|
||||
},
|
||||
customProps: () => ({ context: 'HTTP' }),
|
||||
customSuccessMessage: (req: IncomingMessage, res: ServerResponse, responseTime: number) => {
|
||||
return `${req.method} ${req.url} ${res.statusCode} +${Math.round(responseTime)}ms`;
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
const envSchema = z.object({
|
||||
NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
|
||||
PORT: z.coerce.number().int().positive().default(3000),
|
||||
DATABASE_URL: z.string().url('DATABASE_URL must be a valid URL'),
|
||||
JWT_SECRET: z
|
||||
.string()
|
||||
.min(16, 'JWT_SECRET must be at least 16 characters')
|
||||
.refine(
|
||||
(val) => process.env.NODE_ENV !== 'production' || val !== 'change-me-in-production',
|
||||
'JWT_SECRET must be changed from the default value in production',
|
||||
),
|
||||
JWT_EXPIRES_IN: z.string().default('15m'),
|
||||
JWT_REFRESH_EXPIRES_IN: z.string().default('7d'),
|
||||
BOOKS_PATH: z.string().default('/data/books'),
|
||||
STAGING_PATH: z.string().optional(),
|
||||
CLIENT_URL: z.string().url().optional(),
|
||||
GOOGLE_BOOKS_API_KEY: z.string().default(''),
|
||||
SMTP_HOST: z.string().optional(),
|
||||
SMTP_PORT: z.coerce.number().int().positive().default(587),
|
||||
SMTP_SECURE: z.string().optional(),
|
||||
SMTP_USER: z.string().optional(),
|
||||
SMTP_PASS: z.string().optional(),
|
||||
SMTP_FROM: z.string().default('noreply@projectx.local'),
|
||||
APP_URL: z.string().url().default('http://localhost:5173'),
|
||||
});
|
||||
|
||||
export function validateEnv(config: Record<string, unknown>) {
|
||||
const result = envSchema.safeParse(config);
|
||||
if (!result.success) {
|
||||
const errors = result.error.issues.map((i) => ` ${i.path.join('.')}: ${i.message}`).join('\n');
|
||||
throw new Error(`Environment validation failed:\n${errors}`);
|
||||
}
|
||||
return result.data;
|
||||
}
|
||||
@@ -119,6 +119,7 @@ async function bootstrap() {
|
||||
});
|
||||
}
|
||||
|
||||
app.enableShutdownHooks();
|
||||
await app.listen(process.env.PORT ?? 3000, '0.0.0.0');
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user