import { AppServices } from './app-services'; import { NoopAnalyticsService } from './analytics/noop-analytics-service'; import { UmamiAnalyticsService } from './analytics/umami-analytics-service'; import { MemoryCacheService } from './cache/memory-cache-service'; import { RedisCacheService } from './cache/redis-cache-service'; import { GlitchtipErrorReportingService } from './errors/glitchtip-error-reporting-service'; import { NoopErrorReportingService } from './errors/noop-error-reporting-service'; import { PinoLoggerService } from './logging/pino-logger-service'; let singleton: AppServices | undefined; export function getServerAppServices(): AppServices { if (singleton) return singleton; // Create logger first to log initialization const logger = new PinoLoggerService('server'); // Log environment variables (safely masked) const envLog = { // Mask sensitive values - show only last 4 characters or '***' for empty NEXT_PUBLIC_UMAMI_WEBSITE_ID: process.env.NEXT_PUBLIC_UMAMI_WEBSITE_ID ? `***${process.env.NEXT_PUBLIC_UMAMI_WEBSITE_ID.slice(-4)}` : 'not set', SENTRY_DSN: process.env.SENTRY_DSN ? `***${process.env.SENTRY_DSN.slice(-4)}` : 'not set', REDIS_URL: process.env.REDIS_URL ? `***${process.env.REDIS_URL.slice(-4)}` : 'not set', REDIS_KEY_PREFIX: process.env.REDIS_KEY_PREFIX ?? 'klz:', LOG_LEVEL: process.env.LOG_LEVEL ?? 'info', NODE_ENV: process.env.NODE_ENV ?? 'not set', // Safe to show - no sensitive data NEXT_PUBLIC_BASE_URL: process.env.NEXT_PUBLIC_BASE_URL ?? 'not set', }; logger.info('Initializing server application services', { environment: envLog, timestamp: new Date().toISOString(), }); const umamiEnabled = Boolean(process.env.NEXT_PUBLIC_UMAMI_WEBSITE_ID); const sentryEnabled = Boolean(process.env.SENTRY_DSN); logger.info('Service configuration', { umamiEnabled, sentryEnabled, redisEnabled: Boolean(process.env.REDIS_URL), }); const analytics = umamiEnabled ? new UmamiAnalyticsService({ enabled: true }) : new NoopAnalyticsService(); if (umamiEnabled) { logger.info('Umami analytics service initialized'); } else { logger.info('Noop analytics service initialized (analytics disabled)'); } const errors = sentryEnabled ? new GlitchtipErrorReportingService({ enabled: true }) : new NoopErrorReportingService(); if (sentryEnabled) { logger.info('GlitchTip error reporting service initialized'); } else { logger.info('Noop error reporting service initialized (error reporting disabled)'); } const redisUrl = process.env.REDIS_URL; const cache = redisUrl ? new RedisCacheService({ url: redisUrl, keyPrefix: process.env.REDIS_KEY_PREFIX ?? 'klz:', }) : new MemoryCacheService(); if (redisUrl) { logger.info('Redis cache service initialized', { keyPrefix: process.env.REDIS_KEY_PREFIX ?? 'klz:' }); } else { logger.info('Memory cache service initialized (Redis not configured)'); } logger.info('Pino logger service initialized', { name: 'server', level: process.env.LOG_LEVEL ?? 'info', }); singleton = new AppServices(analytics, errors, cache, logger); logger.info('All application services initialized successfully'); return singleton; }