Files
nroxy/packages/config/src/index.ts
2026-03-10 14:03:52 +03:00

161 lines
3.9 KiB
TypeScript

export interface RuntimeUrls {
appBaseUrl: URL;
adminBaseUrl: URL;
nanoBananaApiBaseUrl: URL;
s3Endpoint: URL;
}
export interface DatabaseConfig {
url: string;
}
export interface AuthConfig {
sessionSecret: string;
passwordPepper: string;
}
export interface ProviderConfig {
nanoBananaDefaultModel: string;
}
export interface PaymentConfig {
provider: string;
apiKey: string;
webhookSecret: string;
}
export interface StorageConfig {
region: string;
bucket: string;
accessKey: string;
secretKey: string;
forcePathStyle: boolean;
}
export interface TelegramConfig {
botToken: string;
botMode: "polling";
}
export interface EmailConfig {
provider: string;
from: string;
apiKey: string;
}
export interface KeyPoolConfig {
cooldownMinutes: number;
failuresBeforeManualReview: number;
balancePollSeconds: number;
}
export interface AppRuntimeConfig {
nodeEnv: string;
urls: RuntimeUrls;
database: DatabaseConfig;
auth: AuthConfig;
provider: ProviderConfig;
payment: PaymentConfig;
storage: StorageConfig;
telegram: TelegramConfig;
email: EmailConfig;
keyPool: KeyPoolConfig;
}
export function loadConfig(env: NodeJS.ProcessEnv = process.env): AppRuntimeConfig {
return {
nodeEnv: readString(env, "NODE_ENV"),
urls: {
appBaseUrl: readUrl(env, "APP_BASE_URL"),
adminBaseUrl: readUrl(env, "ADMIN_BASE_URL"),
nanoBananaApiBaseUrl: readUrl(env, "NANO_BANANA_API_BASE_URL"),
s3Endpoint: readUrl(env, "S3_ENDPOINT"),
},
database: {
url: readString(env, "DATABASE_URL"),
},
auth: {
sessionSecret: readString(env, "SESSION_SECRET"),
passwordPepper: readString(env, "PASSWORD_PEPPER"),
},
provider: {
nanoBananaDefaultModel: readString(env, "NANO_BANANA_DEFAULT_MODEL"),
},
payment: {
provider: readString(env, "PAYMENT_PROVIDER"),
apiKey: readString(env, "PAYMENT_PROVIDER_API_KEY"),
webhookSecret: readString(env, "PAYMENT_PROVIDER_WEBHOOK_SECRET"),
},
storage: {
region: readString(env, "S3_REGION"),
bucket: readString(env, "S3_BUCKET"),
accessKey: readString(env, "S3_ACCESS_KEY"),
secretKey: readString(env, "S3_SECRET_KEY"),
forcePathStyle: readBoolean(env, "S3_FORCE_PATH_STYLE"),
},
telegram: {
botToken: readString(env, "TELEGRAM_BOT_TOKEN"),
botMode: readTelegramMode(env, "TELEGRAM_BOT_MODE"),
},
email: {
provider: readString(env, "EMAIL_PROVIDER"),
from: readString(env, "EMAIL_FROM"),
apiKey: readString(env, "EMAIL_API_KEY"),
},
keyPool: {
cooldownMinutes: readInteger(env, "KEY_COOLDOWN_MINUTES"),
failuresBeforeManualReview: readInteger(env, "KEY_FAILURES_BEFORE_MANUAL_REVIEW"),
balancePollSeconds: readInteger(env, "KEY_BALANCE_POLL_SECONDS"),
},
};
}
function readString(env: NodeJS.ProcessEnv, key: string): string {
const value = env[key];
if (!value) {
throw new Error(`Missing required environment variable: ${key}`);
}
return value;
}
function readInteger(env: NodeJS.ProcessEnv, key: string): number {
const value = readString(env, key);
const parsed = Number.parseInt(value, 10);
if (!Number.isInteger(parsed)) {
throw new Error(`Environment variable ${key} must be an integer`);
}
return parsed;
}
function readBoolean(env: NodeJS.ProcessEnv, key: string): boolean {
const value = readString(env, key).toLowerCase();
if (value === "true") {
return true;
}
if (value === "false") {
return false;
}
throw new Error(`Environment variable ${key} must be "true" or "false"`);
}
function readUrl(env: NodeJS.ProcessEnv, key: string): URL {
return new URL(readString(env, key));
}
function readTelegramMode(env: NodeJS.ProcessEnv, key: string): "polling" {
const value = readString(env, key);
if (value !== "polling") {
throw new Error(`Environment variable ${key} must be "polling" for MVP`);
}
return value;
}