2026-08-02 08:08:11 +02:00
|
|
|
import { plainToInstance } from 'class-transformer'
|
|
|
|
|
import { IsIn, IsInt, IsOptional, IsString, Min, validateSync } from 'class-validator'
|
|
|
|
|
|
|
|
|
|
export class EnvironmentVariables {
|
|
|
|
|
@IsIn(['development', 'production', 'test'])
|
|
|
|
|
NODE_ENV!: 'development' | 'production' | 'test'
|
|
|
|
|
|
|
|
|
|
@IsInt()
|
|
|
|
|
@Min(1)
|
|
|
|
|
PORT!: number
|
|
|
|
|
|
|
|
|
|
@IsString()
|
|
|
|
|
HOST!: string
|
|
|
|
|
|
|
|
|
|
@IsString()
|
|
|
|
|
CORS_ORIGIN!: string
|
|
|
|
|
|
|
|
|
|
@IsOptional()
|
|
|
|
|
@IsString()
|
|
|
|
|
DB_PATH?: string
|
|
|
|
|
|
2026-08-05 08:21:51 +02:00
|
|
|
/** Alias for DB_PATH (e.g. Docker Compose). DB_PATH takes precedence when both are set. */
|
|
|
|
|
@IsOptional()
|
|
|
|
|
@IsString()
|
|
|
|
|
DATABASE_PATH?: string
|
|
|
|
|
|
2026-08-02 08:08:11 +02:00
|
|
|
@IsOptional()
|
|
|
|
|
@IsString()
|
|
|
|
|
JWT_SECRET?: string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function validateEnv(config: Record<string, unknown>): EnvironmentVariables {
|
|
|
|
|
const nodeEnv = (config.NODE_ENV as string | undefined) ?? 'development'
|
|
|
|
|
const jwtSecret = config.JWT_SECRET as string | undefined
|
|
|
|
|
|
|
|
|
|
if (nodeEnv === 'production' && (!jwtSecret || jwtSecret.trim().length === 0)) {
|
|
|
|
|
throw new Error('JWT_SECRET is required when NODE_ENV=production')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (nodeEnv !== 'production' && !jwtSecret) {
|
|
|
|
|
console.warn(
|
|
|
|
|
'[config] JWT_SECRET is not set — using development fallback. Do not use in production.',
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const normalized = {
|
|
|
|
|
NODE_ENV: nodeEnv,
|
|
|
|
|
PORT: Number(config.PORT ?? 3001),
|
|
|
|
|
HOST: (config.HOST as string | undefined) ?? '0.0.0.0',
|
|
|
|
|
CORS_ORIGIN: (config.CORS_ORIGIN as string | undefined) ?? 'http://localhost:5173',
|
2026-08-05 08:21:51 +02:00
|
|
|
DB_PATH: (config.DB_PATH ?? config.DATABASE_PATH) as string | undefined,
|
2026-08-02 08:08:11 +02:00
|
|
|
JWT_SECRET: jwtSecret ?? 'gold-land-dev-secret-change-in-production',
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const validated = plainToInstance(EnvironmentVariables, normalized, {
|
|
|
|
|
enableImplicitConversion: true,
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const errors = validateSync(validated, { skipMissingProperties: false })
|
|
|
|
|
if (errors.length > 0) {
|
|
|
|
|
throw new Error(errors.toString())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return validated
|
|
|
|
|
}
|