This commit is contained in:
169
lib/config.ts
169
lib/config.ts
@@ -2,96 +2,135 @@
|
||||
* Centralized configuration management for the application.
|
||||
* This file provides a type-safe way to access environment variables.
|
||||
*/
|
||||
import { env } from './env';
|
||||
import { envSchema, getRawEnv } from './env';
|
||||
|
||||
let memoizedConfig: ReturnType<typeof createConfig> | undefined;
|
||||
|
||||
/**
|
||||
* Creates and validates the configuration object.
|
||||
* Throws if validation fails.
|
||||
*/
|
||||
function createConfig() {
|
||||
const env = envSchema.parse(getRawEnv());
|
||||
|
||||
return {
|
||||
env: env.NODE_ENV,
|
||||
isProduction: env.NODE_ENV === 'production',
|
||||
isDevelopment: env.NODE_ENV === 'development',
|
||||
isTest: env.NODE_ENV === 'test',
|
||||
|
||||
baseUrl: env.NEXT_PUBLIC_BASE_URL,
|
||||
|
||||
analytics: {
|
||||
umami: {
|
||||
websiteId: env.NEXT_PUBLIC_UMAMI_WEBSITE_ID,
|
||||
scriptUrl: env.NEXT_PUBLIC_UMAMI_SCRIPT_URL,
|
||||
// The proxied path used in the frontend
|
||||
proxyPath: '/stats/script.js',
|
||||
enabled: Boolean(env.NEXT_PUBLIC_UMAMI_WEBSITE_ID),
|
||||
},
|
||||
},
|
||||
|
||||
errors: {
|
||||
glitchtip: {
|
||||
// Use SENTRY_DSN for both server and client (proxied)
|
||||
dsn: env.SENTRY_DSN,
|
||||
// The proxied origin used in the frontend
|
||||
proxyPath: '/errors',
|
||||
enabled: Boolean(env.SENTRY_DSN),
|
||||
},
|
||||
},
|
||||
|
||||
cache: {
|
||||
redis: {
|
||||
url: env.REDIS_URL,
|
||||
keyPrefix: env.REDIS_KEY_PREFIX,
|
||||
enabled: Boolean(env.REDIS_URL),
|
||||
},
|
||||
},
|
||||
|
||||
logging: {
|
||||
level: env.LOG_LEVEL,
|
||||
},
|
||||
|
||||
mail: {
|
||||
host: env.MAIL_HOST,
|
||||
port: env.MAIL_PORT,
|
||||
user: env.MAIL_USERNAME,
|
||||
pass: env.MAIL_PASSWORD,
|
||||
from: env.MAIL_FROM,
|
||||
recipients: env.MAIL_RECIPIENTS,
|
||||
},
|
||||
} as const;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the validated configuration.
|
||||
* Memoizes the result after the first call.
|
||||
*/
|
||||
export function getConfig() {
|
||||
if (!memoizedConfig) {
|
||||
memoizedConfig = createConfig();
|
||||
}
|
||||
return memoizedConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Exported config object for convenience.
|
||||
* Uses getters to ensure it's only initialized when accessed.
|
||||
*/
|
||||
export const config = {
|
||||
env: env.NODE_ENV,
|
||||
isProduction: env.NODE_ENV === 'production',
|
||||
isDevelopment: env.NODE_ENV === 'development',
|
||||
isTest: env.NODE_ENV === 'test',
|
||||
|
||||
baseUrl: env.NEXT_PUBLIC_BASE_URL,
|
||||
|
||||
analytics: {
|
||||
umami: {
|
||||
websiteId: env.NEXT_PUBLIC_UMAMI_WEBSITE_ID,
|
||||
scriptUrl: env.NEXT_PUBLIC_UMAMI_SCRIPT_URL,
|
||||
// The proxied path used in the frontend
|
||||
proxyPath: '/stats/script.js',
|
||||
enabled: Boolean(env.NEXT_PUBLIC_UMAMI_WEBSITE_ID),
|
||||
},
|
||||
},
|
||||
|
||||
errors: {
|
||||
glitchtip: {
|
||||
// Use SENTRY_DSN for both server and client (proxied)
|
||||
dsn: env.SENTRY_DSN,
|
||||
// The proxied origin used in the frontend
|
||||
proxyPath: '/errors',
|
||||
enabled: Boolean(env.SENTRY_DSN),
|
||||
},
|
||||
},
|
||||
|
||||
cache: {
|
||||
redis: {
|
||||
url: env.REDIS_URL,
|
||||
keyPrefix: env.REDIS_KEY_PREFIX,
|
||||
enabled: Boolean(env.REDIS_URL),
|
||||
},
|
||||
},
|
||||
|
||||
logging: {
|
||||
level: env.LOG_LEVEL,
|
||||
},
|
||||
|
||||
mail: {
|
||||
host: env.MAIL_HOST,
|
||||
port: env.MAIL_PORT,
|
||||
user: env.MAIL_USERNAME,
|
||||
pass: env.MAIL_PASSWORD,
|
||||
from: env.MAIL_FROM,
|
||||
recipients: env.MAIL_RECIPIENTS,
|
||||
},
|
||||
} as const;
|
||||
get env() { return getConfig().env; },
|
||||
get isProduction() { return getConfig().isProduction; },
|
||||
get isDevelopment() { return getConfig().isDevelopment; },
|
||||
get isTest() { return getConfig().isTest; },
|
||||
get baseUrl() { return getConfig().baseUrl; },
|
||||
get analytics() { return getConfig().analytics; },
|
||||
get errors() { return getConfig().errors; },
|
||||
get cache() { return getConfig().cache; },
|
||||
get logging() { return getConfig().logging; },
|
||||
get mail() { return getConfig().mail; },
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper to get a masked version of the config for logging.
|
||||
*/
|
||||
export function getMaskedConfig() {
|
||||
const c = getConfig();
|
||||
const mask = (val: string | undefined) => (val ? `***${val.slice(-4)}` : 'not set');
|
||||
|
||||
return {
|
||||
env: config.env,
|
||||
baseUrl: config.baseUrl,
|
||||
env: c.env,
|
||||
baseUrl: c.baseUrl,
|
||||
analytics: {
|
||||
umami: {
|
||||
websiteId: mask(config.analytics.umami.websiteId),
|
||||
scriptUrl: config.analytics.umami.scriptUrl,
|
||||
enabled: config.analytics.umami.enabled,
|
||||
websiteId: mask(c.analytics.umami.websiteId),
|
||||
scriptUrl: c.analytics.umami.scriptUrl,
|
||||
enabled: c.analytics.umami.enabled,
|
||||
},
|
||||
},
|
||||
errors: {
|
||||
glitchtip: {
|
||||
dsn: mask(config.errors.glitchtip.dsn),
|
||||
enabled: config.errors.glitchtip.enabled,
|
||||
dsn: mask(c.errors.glitchtip.dsn),
|
||||
enabled: c.errors.glitchtip.enabled,
|
||||
},
|
||||
},
|
||||
cache: {
|
||||
redis: {
|
||||
url: mask(config.cache.redis.url),
|
||||
keyPrefix: config.cache.redis.keyPrefix,
|
||||
enabled: config.cache.redis.enabled,
|
||||
url: mask(c.cache.redis.url),
|
||||
keyPrefix: c.cache.redis.keyPrefix,
|
||||
enabled: c.cache.redis.enabled,
|
||||
},
|
||||
},
|
||||
logging: {
|
||||
level: config.logging.level,
|
||||
level: c.logging.level,
|
||||
},
|
||||
mail: {
|
||||
host: config.mail.host,
|
||||
port: config.mail.port,
|
||||
user: mask(config.mail.user),
|
||||
from: config.mail.from,
|
||||
recipients: config.mail.recipients,
|
||||
host: c.mail.host,
|
||||
port: c.mail.port,
|
||||
user: mask(c.mail.user),
|
||||
from: c.mail.from,
|
||||
recipients: c.mail.recipients,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user