initial commit
This commit is contained in:
95
packages/config/src/index.ts
Normal file
95
packages/config/src/index.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
const logLevelSchema = z.enum(['fatal', 'error', 'warn', 'info', 'debug', 'trace', 'silent']);
|
||||
|
||||
const appEnvSchema = z.enum(['development', 'test', 'staging', 'production']);
|
||||
|
||||
export const configSchema = z.object({
|
||||
APP_NAME: z.string().default('HexaHost GameCloud'),
|
||||
APP_ENV: appEnvSchema.default('development'),
|
||||
APP_URL: z.string().url(),
|
||||
API_URL: z.string().url(),
|
||||
DATABASE_URL: z.string().min(1),
|
||||
REDIS_URL: z.string().min(1),
|
||||
SESSION_SECRET: z.string().min(32),
|
||||
LOG_LEVEL: logLevelSchema.default('info'),
|
||||
API_PORT: z.coerce.number().int().min(1).max(65535).default(3001),
|
||||
WORKER_PORT: z.coerce.number().int().min(1).max(65535).default(3002),
|
||||
NODE_ENV: z.enum(['development', 'test', 'production']).default('development'),
|
||||
});
|
||||
|
||||
export type AppConfig = z.infer<typeof configSchema>;
|
||||
|
||||
function readEnv(): Record<string, string | undefined> {
|
||||
return { ...process.env };
|
||||
}
|
||||
|
||||
export function loadConfig(env: Record<string, string | undefined> = readEnv()): AppConfig {
|
||||
return configSchema.parse({
|
||||
APP_NAME: env['APP_NAME'],
|
||||
APP_ENV: env['APP_ENV'],
|
||||
APP_URL: env['APP_URL'],
|
||||
API_URL: env['API_URL'],
|
||||
DATABASE_URL: env['DATABASE_URL'],
|
||||
REDIS_URL: env['REDIS_URL'],
|
||||
SESSION_SECRET: env['SESSION_SECRET'],
|
||||
LOG_LEVEL: env['LOG_LEVEL'],
|
||||
API_PORT: env['API_PORT'],
|
||||
WORKER_PORT: env['WORKER_PORT'],
|
||||
NODE_ENV: env['NODE_ENV'],
|
||||
});
|
||||
}
|
||||
|
||||
export function validateConfig(env: Record<string, string | undefined> = readEnv()): AppConfig {
|
||||
const requiredKeys = [
|
||||
'APP_URL',
|
||||
'API_URL',
|
||||
'DATABASE_URL',
|
||||
'REDIS_URL',
|
||||
'SESSION_SECRET',
|
||||
] as const;
|
||||
|
||||
const missing = requiredKeys.filter((key) => !env[key] || env[key]!.trim() === '');
|
||||
|
||||
if (missing.length > 0) {
|
||||
throw new Error(
|
||||
`Missing required environment variables: ${missing.join(', ')}`,
|
||||
);
|
||||
}
|
||||
|
||||
const result = configSchema.safeParse({
|
||||
APP_NAME: env['APP_NAME'],
|
||||
APP_ENV: env['APP_ENV'],
|
||||
APP_URL: env['APP_URL'],
|
||||
API_URL: env['API_URL'],
|
||||
DATABASE_URL: env['DATABASE_URL'],
|
||||
REDIS_URL: env['REDIS_URL'],
|
||||
SESSION_SECRET: env['SESSION_SECRET'],
|
||||
LOG_LEVEL: env['LOG_LEVEL'],
|
||||
API_PORT: env['API_PORT'],
|
||||
WORKER_PORT: env['WORKER_PORT'],
|
||||
NODE_ENV: env['NODE_ENV'],
|
||||
});
|
||||
|
||||
if (!result.success) {
|
||||
const issues = result.error.issues
|
||||
.map((issue) => `${issue.path.join('.')}: ${issue.message}`)
|
||||
.join('; ');
|
||||
throw new Error(`Invalid configuration: ${issues}`);
|
||||
}
|
||||
|
||||
return result.data;
|
||||
}
|
||||
|
||||
let cachedConfig: AppConfig | undefined;
|
||||
|
||||
export function getConfig(): AppConfig {
|
||||
if (!cachedConfig) {
|
||||
cachedConfig = validateConfig();
|
||||
}
|
||||
return cachedConfig;
|
||||
}
|
||||
|
||||
export function resetConfigCache(): void {
|
||||
cachedConfig = undefined;
|
||||
}
|
||||
Reference in New Issue
Block a user