Files
dyolink/backend/src/configs/configurations.ts

112 lines
3.2 KiB
TypeScript

// backend/src/config/configuration.ts
/** Matches values accepted by jsonwebtoken `expiresIn` (via ms), e.g. 7d, 15m, or plain seconds. */
const JWT_TIMESPAN_PATTERN =
/^\d+(\.\d+)?(ms|s|m|h|d|w|y)?$/i;
function assertJwtSecret(value: string, envKey: string): string {
const trimmed = value.trim();
if (!trimmed) {
throw new Error(`❌ Environment variable ${envKey} is required but not set`);
}
if (trimmed.length < 16) {
throw new Error(`${envKey} must be at least 16 characters`);
}
if (/CHANGE_ME/i.test(trimmed)) {
throw new Error(`${envKey} must be changed from the placeholder value`);
}
return trimmed;
}
function assertJwtTimespan(value: string, envKey: string): string {
const trimmed = value.trim();
if (!trimmed) {
throw new Error(`❌ Environment variable ${envKey} is required but not set`);
}
if (!/^\d+$/.test(trimmed) && !JWT_TIMESPAN_PATTERN.test(trimmed)) {
throw new Error(
`${envKey}="${value}" is invalid. Use a duration like 7d, 15m, 30d, or a number of seconds.`,
);
}
return trimmed;
}
export interface Config {
port: number;
database: {
url: string;
};
jwt: {
secret: string;
expiresIn: string;
refreshSecret: string;
refreshExpiresIn: string;
};
throttle: {
ttl: number;
limit: number;
};
sms: {
apiKey: string | null;
templateId: number;
};
}
export default (): Config => {
// Helper function to get required env var with type safety
const getEnvVar = (key: string): string => {
const value = process.env[key];
if (!value) {
throw new Error(`❌ Environment variable ${key} is required but not set`);
}
return value;
};
// Helper for optional env vars with defaults (whitespace-only counts as unset)
const getEnvVarWithDefault = (key: string, defaultValue: string): string => {
const value = process.env[key]?.trim();
return value ? value : defaultValue;
};
const getEnvVarAsNumber = (key: string, defaultValue: number): number => {
const value = process.env[key];
if (!value) return defaultValue;
const parsed = parseInt(value, 10);
return isNaN(parsed) ? defaultValue : parsed;
};
const jwtSecret = assertJwtSecret(getEnvVar('JWT_SECRET'), 'JWT_SECRET');
const jwtExpiresIn = assertJwtTimespan(
getEnvVarWithDefault('JWT_EXPIRES_IN', '7d'),
'JWT_EXPIRES_IN',
);
const jwtRefreshSecret = assertJwtSecret(
getEnvVarWithDefault('JWT_REFRESH_SECRET', jwtSecret),
'JWT_REFRESH_SECRET',
);
const jwtRefreshExpiresIn = assertJwtTimespan(
getEnvVarWithDefault('JWT_REFRESH_EXPIRES_IN', '30d'),
'JWT_REFRESH_EXPIRES_IN',
);
return {
port: getEnvVarAsNumber('PORT', 3000),
database: {
url: getEnvVar('DATABASE_URL'),
},
jwt: {
secret: jwtSecret,
expiresIn: jwtExpiresIn,
refreshSecret: jwtRefreshSecret,
refreshExpiresIn: jwtRefreshExpiresIn,
},
throttle: {
ttl: getEnvVarAsNumber('THROTTLE_TTL', 60),
limit: getEnvVarAsNumber('THROTTLE_LIMIT', 100),
},
sms: {
apiKey: process.env.SMS_IR_API_KEY?.trim() || null,
templateId: getEnvVarAsNumber('SMS_IR_TEMPLATE_ID', 123456),
},
};
};