161 lines
3.9 KiB
TypeScript
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;
|
|
}
|