refactored di into more composable parts

This commit is contained in:
Boki 2025-06-22 21:47:39 -04:00
parent 177fe30586
commit 26ebc77fe6
22 changed files with 908 additions and 281 deletions

View file

@ -0,0 +1,27 @@
import { asClass, asFunction, asValue, type AwilixContainer } from 'awilix';
import { createCache, type CacheProvider } from '@stock-bot/cache';
import type { AppConfig } from '../config/schemas';
import type { ServiceDefinitions } from '../container/types';
export function registerCacheServices(
container: AwilixContainer<ServiceDefinitions>,
config: AppConfig
): void {
if (config.redis.enabled) {
container.register({
cache: asFunction(() => {
return createCache({
redisConfig: {
host: config.redis.host,
port: config.redis.port,
password: config.redis.password,
},
});
}).singleton(),
});
} else {
container.register({
cache: asValue(null),
});
}
}

View file

@ -0,0 +1,14 @@
import { asValue, type AwilixContainer } from 'awilix';
import { getLogger, type Logger } from '@stock-bot/logger';
import type { AppConfig } from '../config/schemas';
import type { ServiceDefinitions } from '../container/types';
export function registerCoreServices(
container: AwilixContainer<ServiceDefinitions>,
config: AppConfig
): void {
container.register({
config: asValue(config),
logger: asValue(getLogger('di-container')),
});
}

View file

@ -0,0 +1,79 @@
import { MongoDBClient } from '@stock-bot/mongodb';
import { PostgreSQLClient } from '@stock-bot/postgres';
import { QuestDBClient } from '@stock-bot/questdb';
import { asFunction, asValue, type AwilixContainer } from 'awilix';
import type { AppConfig } from '../config/schemas';
import type { ServiceDefinitions } from '../container/types';
export function registerDatabaseServices(
container: AwilixContainer<ServiceDefinitions>,
config: AppConfig
): void {
// MongoDB
if (config.mongodb.enabled) {
container.register({
mongoClient: asFunction(({ logger }) => {
// Parse MongoDB URI to extract components
const uriMatch = config.mongodb.uri.match(/mongodb:\/\/(?:([^:]+):([^@]+)@)?([^:\/]+):(\d+)\/([^?]+)(?:\?authSource=(.+))?/);
const mongoConfig = {
host: uriMatch?.[3] || 'localhost',
port: parseInt(uriMatch?.[4] || '27017'),
database: config.mongodb.database,
username: uriMatch?.[1],
password: uriMatch?.[2],
authSource: uriMatch?.[6] || 'admin',
uri: config.mongodb.uri,
};
return new MongoDBClient(mongoConfig, logger);
}).singleton(),
});
} else {
container.register({
mongoClient: asValue(null),
});
}
// PostgreSQL
if (config.postgres.enabled) {
container.register({
postgresClient: asFunction(({ logger }) => {
return new PostgreSQLClient(
{
host: config.postgres.host,
port: config.postgres.port,
database: config.postgres.database,
username: config.postgres.user,
password: config.postgres.password,
},
logger
);
}).singleton(),
});
} else {
container.register({
postgresClient: asValue(null),
});
}
// QuestDB
if (config.questdb?.enabled) {
container.register({
questdbClient: asFunction(({ logger }) => {
return new QuestDBClient(
{
host: config.questdb!.host,
httpPort: config.questdb!.httpPort,
pgPort: config.questdb!.pgPort,
influxPort: config.questdb!.influxPort,
database: config.questdb!.database,
},
logger
);
}).singleton(),
});
} else {
container.register({
questdbClient: asValue(null),
});
}
}

View file

@ -0,0 +1,4 @@
export { registerCoreServices } from './core.registration';
export { registerCacheServices } from './cache.registration';
export { registerDatabaseServices } from './database.registration';
export { registerApplicationServices } from './service.registration';

View file

@ -0,0 +1,81 @@
import { asClass, asFunction, asValue, type AwilixContainer } from 'awilix';
import { Browser } from '@stock-bot/browser';
import { ProxyManager } from '@stock-bot/proxy';
import { NamespacedCache } from '@stock-bot/cache';
import type { QueueManager } from '@stock-bot/queue';
import type { AppConfig } from '../config/schemas';
import type { ServiceDefinitions } from '../container/types';
export function registerApplicationServices(
container: AwilixContainer<ServiceDefinitions>,
config: AppConfig
): void {
// Browser
if (config.browser) {
container.register({
browser: asClass(Browser)
.singleton()
.inject(() => ({
options: {
headless: config.browser!.headless,
timeout: config.browser!.timeout,
},
})),
});
} else {
container.register({
browser: asValue(null as any), // Required field
});
}
// Proxy Manager
if (config.proxy && config.redis.enabled) {
container.register({
proxyManager: asFunction(({ cache, logger }) => {
if (!cache) return null;
const proxyCache = new NamespacedCache(cache, 'proxy');
return new ProxyManager(proxyCache, logger);
}).singleton(),
});
} else {
container.register({
proxyManager: asValue(null),
});
}
// Queue Manager
if (config.queue?.enabled && config.redis.enabled) {
container.register({
queueManager: asFunction(({ logger }) => {
const { QueueManager } = require('@stock-bot/queue');
const queueConfig = {
redis: {
host: config.redis.host,
port: config.redis.port,
password: config.redis.password,
db: config.redis.db,
},
defaultQueueOptions: {
workers: 1,
concurrency: 1,
defaultJobOptions: {
removeOnComplete: 100,
removeOnFail: 50,
attempts: 3,
backoff: {
type: 'exponential',
delay: 1000,
},
},
},
enableScheduledJobs: true,
};
return new QueueManager(queueConfig, logger);
}).singleton(),
});
} else {
container.register({
queueManager: asValue(null),
});
}
}