import dotenv from 'dotenv';
import { envConfigSchema, type AppConfig, type EnvConfig } from './config.schema.js';
// Load environment variables
dotenv.config();
/**
* Parse publish time string (HH:MM) to hour and minute
*/
const parsePublishTime = (timeStr: string): { hour: number; minute: number } => {
const [hour, minute] = timeStr.split(':').map(Number);
return { hour, minute };
};
/**
* Parse Instagram collaborators from comma-separated string
*/
const parseCollaborators = (collaboratorsStr?: string): string[] => {
if (!collaboratorsStr) {
return [];
}
return collaboratorsStr
.split(',')
.map(c => c.trim())
.filter(c => c.length > 0)
.map(c => c.startsWith('@') ? c.slice(1) : c)
.slice(0, 3); // Instagram allows maximum 3 collaborators
};
/**
* Application Configuration
* Centralized configuration with validation
*/
export class Config {
private config: AppConfig;
constructor() {
// Validate environment variables
const env = this.validateEnv();
// Build configuration object
this.config = this.buildConfig(env);
}
/**
* Validate environment variables using Zod schema
*/
private validateEnv(): EnvConfig {
try {
return envConfigSchema.parse(process.env);
} catch (error) {
if (error instanceof Error && 'errors' in error) {
const zodError = error as { errors: Array<{ path: string[]; message: string }> };
const errors = zodError.errors.map(err =>
`${err.path.join('.')}: ${err.message}`
).join('\n');
throw new Error(`Configuration validation failed:\n${errors}`);
}
throw new Error(`Configuration validation failed: ${error instanceof Error ? error.message : String(error)}`);
}
}
/**
* Build configuration object from validated env
*/
private buildConfig(env: EnvConfig): AppConfig {
const isDevMode = env.DEV_MODE === 'true' || env.NODE_ENV === 'development';
return {
lateApi: {
key: env.LATE_API_KEY,
profileId: env.LATE_PROFILE_ID,
instagramAccountId: env.LATE_INSTAGRAM_ACCOUNT_ID,
pinterestAccountId: env.LATE_PINTEREST_ACCOUNT_ID,
},
openai: env.OPENAI_API_KEY ? {
apiKey: env.OPENAI_API_KEY,
} : undefined,
content: {
instagramUsername: env.INSTAGRAM_USERNAME,
instagramCollaborators: parseCollaborators(env.INSTAGRAM_COLLABORATORS),
},
cdn: env.CDN_BASE_URL && env.CDN_BASE_URL.length > 0 ? {
baseUrl: env.CDN_BASE_URL,
} : undefined,
scheduling: {
timezone: env.TIMEZONE,
defaultPublishTime: parsePublishTime(env.DEFAULT_PUBLISH_TIME),
},
dev: {
isDevMode,
nodeEnv: env.NODE_ENV || 'production',
},
};
}
/**
* Get full configuration
*/
getConfig(): AppConfig {
return this.config;
}
/**
* Check if Late API is configured
*/
isLateAPIConfigured(): boolean {
return !!(
this.config.lateApi.key &&
this.config.lateApi.profileId
);
}
/**
* Check if Instagram Late API is configured
*/
isInstagramLateConfigured(): boolean {
return this.isLateAPIConfigured() && !!this.config.lateApi.instagramAccountId;
}
/**
* Check if Pinterest Late API is configured
*/
isPinterestLateConfigured(): boolean {
return this.isLateAPIConfigured() && !!this.config.lateApi.pinterestAccountId;
}
/**
* Get CDN base URL
*/
getCDNBaseURL(): string {
return this.config.cdn?.baseUrl || '';
}
/**
* Check if CDN is configured
*/
isCDNConfigured(): boolean {
return !!this.config.cdn?.baseUrl;
}
/**
* Get Instagram username
*/
getInstagramUsername(): string {
return this.config.content.instagramUsername;
}
/**
* Get Instagram collaborators
*/
getInstagramCollaborators(): string[] {
return this.config.content.instagramCollaborators;
}
/**
* Get OpenAI API key
*/
getOpenAIApiKey(): string | undefined {
return this.config.openai?.apiKey;
}
/**
* Check if dev mode is enabled
*/
isDevMode(): boolean {
return this.config.dev.isDevMode;
}
/**
* Get default publish time
*/
getDefaultPublishTime(): { hour: number; minute: number } {
return this.config.scheduling.defaultPublishTime;
}
/**
* Get timezone
*/
getTimezone(): string {
return this.config.scheduling.timezone;
}
}
// Export singleton instance
// Initialize immediately to validate configuration on startup
export const appConfig = new Config();