All checks were successful
Build & Deploy KLZ Cables / build-and-deploy (push) Successful in 4m14s
147 lines
5.0 KiB
TypeScript
147 lines
5.0 KiB
TypeScript
import { AppServices } from './app-services';
|
|
import { NoopAnalyticsService } from './analytics/noop-analytics-service';
|
|
import { MemoryCacheService } from './cache/memory-cache-service';
|
|
import { GlitchtipErrorReportingService } from './errors/glitchtip-error-reporting-service';
|
|
import { NoopErrorReportingService } from './errors/noop-error-reporting-service';
|
|
import { NoopLoggerService } from './logging/noop-logger-service';
|
|
import { PinoLoggerService } from './logging/pino-logger-service';
|
|
import { config, getMaskedConfig } from '../config';
|
|
|
|
/**
|
|
* Singleton instance of AppServices.
|
|
*
|
|
* In Next.js, module singletons are per-process (server) and per-tab (client).
|
|
* This is sufficient for a small service layer and provides better performance
|
|
* than creating new instances on every request.
|
|
*
|
|
* @private
|
|
*/
|
|
let singleton: AppServices | undefined;
|
|
|
|
/**
|
|
* Get the application services singleton.
|
|
*
|
|
* This function creates and caches the application services, including:
|
|
* - Analytics service (Umami or no-op)
|
|
* - Error reporting service (GlitchTip/Sentry or no-op)
|
|
* - Cache service (in-memory)
|
|
*
|
|
* The services are configured based on environment variables:
|
|
* - `NEXT_PUBLIC_UMAMI_WEBSITE_ID` - Enables Umami analytics
|
|
* - `NEXT_PUBLIC_SENTRY_DSN` - Enables client-side error reporting
|
|
* - `SENTRY_DSN` - Enables server-side error reporting
|
|
*
|
|
* @returns {AppServices} The application services singleton
|
|
*
|
|
* @example
|
|
* ```typescript
|
|
* // Get services in a client component
|
|
* import { getAppServices } from '@/lib/services/create-services';
|
|
*
|
|
* const services = getAppServices();
|
|
* services.analytics.track('button_click', { button_id: 'cta' });
|
|
* ```
|
|
*
|
|
* @example
|
|
* ```typescript
|
|
* // Get services in a server component or API route
|
|
* import { getAppServices } from '@/lib/services/create-services';
|
|
*
|
|
* const services = getAppServices();
|
|
* await services.cache.set('key', 'value');
|
|
* ```
|
|
*
|
|
* @example
|
|
* ```typescript
|
|
* // Automatic service selection based on environment
|
|
* // If NEXT_PUBLIC_UMAMI_WEBSITE_ID is set:
|
|
* // services.analytics = UmamiAnalyticsService
|
|
* // If not set:
|
|
* // services.analytics = NoopAnalyticsService (safe no-op)
|
|
* ```
|
|
*
|
|
* @see {@link UmamiAnalyticsService} for analytics implementation
|
|
* @see {@link NoopAnalyticsService} for no-op fallback
|
|
* @see {@link GlitchtipErrorReportingService} for error reporting
|
|
* @see {@link MemoryCacheService} for caching
|
|
*/
|
|
export function getAppServices(): AppServices {
|
|
// Return cached instance if available
|
|
if (singleton) return singleton;
|
|
|
|
// Create logger first to log initialization
|
|
const logger =
|
|
typeof window === 'undefined'
|
|
? new PinoLoggerService('server')
|
|
: new NoopLoggerService();
|
|
|
|
// Log initialization
|
|
if (typeof window === 'undefined') {
|
|
// Server-side
|
|
logger.info('Initializing server application services', {
|
|
environment: getMaskedConfig(),
|
|
timestamp: new Date().toISOString(),
|
|
});
|
|
} else {
|
|
// Client-side
|
|
logger.info('Initializing client application services', {
|
|
environment: getMaskedConfig(),
|
|
timestamp: new Date().toISOString(),
|
|
});
|
|
}
|
|
|
|
// Determine which services to enable based on environment variables
|
|
const umamiEnabled = config.analytics.umami.enabled;
|
|
const sentryEnabled = config.errors.glitchtip.enabled;
|
|
|
|
logger.info('Service configuration', {
|
|
umamiEnabled,
|
|
sentryEnabled,
|
|
isServer: typeof window === 'undefined',
|
|
});
|
|
|
|
// Create analytics service (Umami or no-op)
|
|
// Use dynamic import to avoid importing server-only code in client components
|
|
const analytics = umamiEnabled
|
|
? (() => {
|
|
const { UmamiAnalyticsService } = require('./analytics/umami-analytics-service');
|
|
return new UmamiAnalyticsService({ enabled: true });
|
|
})()
|
|
: new NoopAnalyticsService();
|
|
|
|
if (umamiEnabled) {
|
|
logger.info('Umami analytics service initialized');
|
|
} else {
|
|
logger.info('Noop analytics service initialized (analytics disabled)');
|
|
}
|
|
|
|
// Create error reporting service (GlitchTip/Sentry or no-op)
|
|
const errors = sentryEnabled
|
|
? new GlitchtipErrorReportingService({ enabled: true })
|
|
: new NoopErrorReportingService();
|
|
|
|
if (sentryEnabled) {
|
|
logger.info(`GlitchTip error reporting service initialized (${typeof window === 'undefined' ? 'server' : 'client'})`);
|
|
} else {
|
|
logger.info('Noop error reporting service initialized (error reporting disabled)');
|
|
}
|
|
|
|
// IMPORTANT: This module is imported by client components.
|
|
// Do not import Node-only modules (like the `redis` client) here.
|
|
// Use [`getServerAppServices()`](lib/services/create-services.server.ts:1) on the server.
|
|
const cache = new MemoryCacheService();
|
|
logger.info('Memory cache service initialized');
|
|
|
|
logger.info('Pino logger service initialized', {
|
|
name: typeof window === 'undefined' ? 'server' : 'client',
|
|
level: config.logging.level,
|
|
});
|
|
|
|
// Create and cache the singleton
|
|
singleton = new AppServices(analytics, errors, cache, logger);
|
|
|
|
logger.info('All application services initialized successfully');
|
|
|
|
return singleton;
|
|
}
|