214 lines
6.6 KiB
TypeScript
214 lines
6.6 KiB
TypeScript
import { asClass, asFunction, createContainer, InjectionMode, type AwilixContainer } from 'awilix';
|
|
import type { BaseAppConfig as StockBotAppConfig, UnifiedAppConfig } from '@stock-bot/config';
|
|
import { toUnifiedConfig } from '@stock-bot/config';
|
|
import { HandlerRegistry } from '@stock-bot/handler-registry';
|
|
import { appConfigSchema, type AppConfig } from '../config/schemas';
|
|
import {
|
|
registerApplicationServices,
|
|
registerCacheServices,
|
|
registerCoreServices,
|
|
registerDatabaseServices,
|
|
} from '../registrations';
|
|
import { HandlerScanner } from '../scanner';
|
|
import { ServiceLifecycleManager } from '../utils/lifecycle';
|
|
import type { ContainerBuildOptions, ServiceDefinitions } from './types';
|
|
|
|
export class ServiceContainerBuilder {
|
|
private config: Partial<AppConfig> = {};
|
|
private unifiedConfig: UnifiedAppConfig | null = null;
|
|
private options: ContainerBuildOptions = {
|
|
enableCache: true,
|
|
enableQueue: true,
|
|
enableMongoDB: true,
|
|
enablePostgres: true,
|
|
enableQuestDB: true,
|
|
enableBrowser: true,
|
|
enableProxy: true,
|
|
skipInitialization: false,
|
|
initializationTimeout: 30000,
|
|
};
|
|
|
|
withConfig(config: AppConfig | StockBotAppConfig | UnifiedAppConfig): this {
|
|
// Convert to unified config format
|
|
this.unifiedConfig = toUnifiedConfig(config);
|
|
this.config = this.transformStockBotConfig(this.unifiedConfig);
|
|
return this;
|
|
}
|
|
|
|
withOptions(options: Partial<ContainerBuildOptions>): this {
|
|
Object.assign(this.options, options);
|
|
return this;
|
|
}
|
|
|
|
enableService(
|
|
service: keyof Omit<ContainerBuildOptions, 'skipInitialization' | 'initializationTimeout'>,
|
|
enabled = true
|
|
): this {
|
|
this.options[service] = enabled;
|
|
return this;
|
|
}
|
|
|
|
skipInitialization(skip = true): this {
|
|
this.options.skipInitialization = skip;
|
|
return this;
|
|
}
|
|
|
|
async build(): Promise<AwilixContainer<ServiceDefinitions>> {
|
|
// Validate and prepare config
|
|
const validatedConfig = this.prepareConfig();
|
|
|
|
// Create container
|
|
const container = createContainer<ServiceDefinitions>({
|
|
injectionMode: InjectionMode.PROXY,
|
|
strict: true,
|
|
});
|
|
|
|
// Register services
|
|
this.registerServices(container, validatedConfig);
|
|
|
|
// Initialize services if not skipped
|
|
if (!this.options.skipInitialization) {
|
|
const lifecycleManager = new ServiceLifecycleManager();
|
|
await lifecycleManager.initializeServices(container, this.options.initializationTimeout);
|
|
}
|
|
|
|
return container;
|
|
}
|
|
|
|
private prepareConfig(): AppConfig {
|
|
const finalConfig = this.applyServiceOptions(this.config);
|
|
return appConfigSchema.parse(finalConfig);
|
|
}
|
|
|
|
private applyServiceOptions(config: Partial<AppConfig>): AppConfig {
|
|
// Ensure questdb config has the right field names for DI
|
|
const questdbConfig = config.questdb
|
|
? {
|
|
...config.questdb,
|
|
influxPort: (config.questdb as any).influxPort || (config.questdb as any).ilpPort || 9009,
|
|
}
|
|
: {
|
|
enabled: true,
|
|
host: 'localhost',
|
|
httpPort: 9000,
|
|
pgPort: 8812,
|
|
influxPort: 9009,
|
|
database: 'questdb',
|
|
};
|
|
|
|
return {
|
|
redis: config.redis || {
|
|
enabled: this.options.enableCache ?? true,
|
|
host: 'localhost',
|
|
port: 6379,
|
|
db: 0,
|
|
},
|
|
mongodb: config.mongodb || {
|
|
enabled: this.options.enableMongoDB ?? true,
|
|
uri: '',
|
|
database: '',
|
|
},
|
|
postgres: config.postgres || {
|
|
enabled: this.options.enablePostgres ?? true,
|
|
host: 'localhost',
|
|
port: 5432,
|
|
database: 'postgres',
|
|
user: 'postgres',
|
|
password: 'postgres',
|
|
},
|
|
questdb: this.options.enableQuestDB ? questdbConfig : undefined,
|
|
proxy: this.options.enableProxy
|
|
? config.proxy || { enabled: false, cachePrefix: 'proxy:', ttl: 3600 }
|
|
: undefined,
|
|
browser: this.options.enableBrowser
|
|
? config.browser || { headless: true, timeout: 30000 }
|
|
: undefined,
|
|
queue: this.options.enableQueue
|
|
? config.queue || {
|
|
enabled: true,
|
|
workers: 1,
|
|
concurrency: 1,
|
|
enableScheduledJobs: true,
|
|
delayWorkerStart: false,
|
|
defaultJobOptions: {
|
|
attempts: 3,
|
|
backoff: { type: 'exponential' as const, delay: 1000 },
|
|
removeOnComplete: 100,
|
|
removeOnFail: 50,
|
|
},
|
|
}
|
|
: undefined,
|
|
service: config.service,
|
|
};
|
|
}
|
|
|
|
private registerServices(
|
|
container: AwilixContainer<ServiceDefinitions>,
|
|
config: AppConfig
|
|
): void {
|
|
// Register handler infrastructure first
|
|
container.register({
|
|
handlerRegistry: asClass(HandlerRegistry).singleton(),
|
|
handlerScanner: asClass(HandlerScanner).singleton(),
|
|
});
|
|
|
|
registerCoreServices(container, config);
|
|
registerCacheServices(container, config);
|
|
registerDatabaseServices(container, config);
|
|
registerApplicationServices(container, config);
|
|
|
|
// Register service container aggregate
|
|
container.register({
|
|
serviceContainer: asFunction(
|
|
({
|
|
config: _config,
|
|
logger,
|
|
cache,
|
|
globalCache,
|
|
proxyManager,
|
|
browser,
|
|
queueManager,
|
|
mongoClient,
|
|
postgresClient,
|
|
questdbClient,
|
|
}) => ({
|
|
logger,
|
|
cache,
|
|
globalCache,
|
|
proxy: proxyManager, // Map proxyManager to proxy
|
|
browser,
|
|
queue: queueManager, // Map queueManager to queue
|
|
mongodb: mongoClient, // Map mongoClient to mongodb
|
|
postgres: postgresClient, // Map postgresClient to postgres
|
|
questdb: questdbClient, // Map questdbClient to questdb
|
|
})
|
|
).singleton(),
|
|
});
|
|
}
|
|
|
|
private transformStockBotConfig(config: UnifiedAppConfig): Partial<AppConfig> {
|
|
// Unified config already has flat structure, just extract what we need
|
|
// Handle questdb field name mapping
|
|
const questdb = config.questdb
|
|
? {
|
|
enabled: config.questdb.enabled || true,
|
|
host: config.questdb.host || 'localhost',
|
|
httpPort: config.questdb.httpPort || 9000,
|
|
pgPort: config.questdb.pgPort || 8812,
|
|
influxPort: (config.questdb as any).influxPort || (config.questdb as any).ilpPort || 9009,
|
|
database: config.questdb.database || 'questdb',
|
|
}
|
|
: undefined;
|
|
|
|
return {
|
|
redis: config.redis,
|
|
mongodb: config.mongodb,
|
|
postgres: config.postgres,
|
|
questdb,
|
|
queue: config.queue,
|
|
browser: config.browser,
|
|
proxy: config.proxy,
|
|
service: config.service,
|
|
};
|
|
}
|
|
}
|