removed configs from all libs and will inject them within the services themselves

This commit is contained in:
Boki 2025-06-18 14:50:47 -04:00
parent fd28162811
commit 6cc5b339bc
32 changed files with 366 additions and 349 deletions

View file

@ -45,7 +45,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
- **strategy-service** - Trading strategies and backtesting (multi-mode: live, event-driven, vectorized, hybrid) - **strategy-service** - Trading strategies and backtesting (multi-mode: live, event-driven, vectorized, hybrid)
- **execution-service** - Order management and risk controls - **execution-service** - Order management and risk controls
- **portfolio-service** - Position tracking and performance analytics - **portfolio-service** - Position tracking and performance analytics
- **web** - React dashboard with real-time updates - **web-app** - React dashboard with real-time updates
### Shared Libraries (`libs/`) ### Shared Libraries (`libs/`)
- **config** - Environment configuration with Zod validation - **config** - Environment configuration with Zod validation

View file

@ -0,0 +1,27 @@
import { PostgreSQLClient } from '@stock-bot/postgres-client';
import { MongoDBClient } from '@stock-bot/mongodb-client';
let postgresClient: PostgreSQLClient | null = null;
let mongodbClient: MongoDBClient | null = null;
export function setPostgreSQLClient(client: PostgreSQLClient): void {
postgresClient = client;
}
export function getPostgreSQLClient(): PostgreSQLClient {
if (!postgresClient) {
throw new Error('PostgreSQL client not initialized. Call setPostgreSQLClient first.');
}
return postgresClient;
}
export function setMongoDBClient(client: MongoDBClient): void {
mongodbClient = client;
}
export function getMongoDBClient(): MongoDBClient {
if (!mongodbClient) {
throw new Error('MongoDB client not initialized. Call setMongoDBClient first.');
}
return mongodbClient;
}

View file

@ -3,16 +3,29 @@
*/ */
import { Hono } from 'hono'; import { Hono } from 'hono';
import { cors } from 'hono/cors'; import { cors } from 'hono/cors';
import { initializeConfig, getServiceConfig } from '@stock-bot/config-new'; import { initializeConfig, getServiceConfig, getLoggingConfig } from '@stock-bot/config-new';
import { getLogger, shutdownLoggers } from '@stock-bot/logger'; import { getLogger, shutdownLoggers, setLoggerConfig } from '@stock-bot/logger';
import { connectMongoDB, disconnectMongoDB } from '@stock-bot/mongodb-client'; import { createAndConnectMongoDBClient, MongoDBClient } from '@stock-bot/mongodb-client';
import { connectPostgreSQL, disconnectPostgreSQL } from '@stock-bot/postgres-client'; import { createAndConnectPostgreSQLClient, PostgreSQLClient } from '@stock-bot/postgres-client';
import { Shutdown } from '@stock-bot/shutdown'; import { Shutdown } from '@stock-bot/shutdown';
import { enhancedSyncManager } from './services/enhanced-sync-manager'; import { enhancedSyncManager } from './services/enhanced-sync-manager';
import { syncManager } from './services/sync-manager'; import { syncManager } from './services/sync-manager';
import { setPostgreSQLClient, setMongoDBClient } from './clients';
// Initialize configuration // Initialize configuration
await initializeConfig(); await initializeConfig();
const serviceConfig = getServiceConfig();
// Initialize logger with config
const loggingConfig = getLoggingConfig();
if (loggingConfig) {
setLoggerConfig({
logLevel: loggingConfig.level,
logConsole: true,
logFile: false,
environment: serviceConfig.environment,
});
}
const app = new Hono(); const app = new Hono();
@ -28,9 +41,10 @@ app.use(
); );
const logger = getLogger('data-sync-service'); const logger = getLogger('data-sync-service');
const serviceConfig = getServiceConfig();
const PORT = serviceConfig.port; const PORT = serviceConfig.port;
let server: ReturnType<typeof Bun.serve> | null = null; let server: ReturnType<typeof Bun.serve> | null = null;
let postgresClient: PostgreSQLClient | null = null;
let mongoClient: MongoDBClient | null = null;
// Initialize shutdown manager // Initialize shutdown manager
const shutdown = Shutdown.getInstance({ timeout: 15000 }); const shutdown = Shutdown.getInstance({ timeout: 15000 });
@ -177,12 +191,37 @@ async function initializeServices() {
try { try {
// Initialize MongoDB client // Initialize MongoDB client
logger.info('Connecting to MongoDB...'); logger.info('Connecting to MongoDB...');
await connectMongoDB(); const mongoConfig = serviceConfig.database.mongodb;
mongoClient = await createAndConnectMongoDBClient({
uri: mongoConfig.uri,
database: mongoConfig.database,
host: 'localhost',
port: 27017,
timeouts: {
connectTimeout: mongoConfig.connectionTimeout || 30000,
socketTimeout: 30000,
serverSelectionTimeout: mongoConfig.serverSelectionTimeout || 5000,
},
});
setMongoDBClient(mongoClient);
logger.info('MongoDB connected'); logger.info('MongoDB connected');
// Initialize PostgreSQL client // Initialize PostgreSQL client
logger.info('Connecting to PostgreSQL...'); logger.info('Connecting to PostgreSQL...');
await connectPostgreSQL(); const pgConfig = serviceConfig.database.postgres;
postgresClient = await createAndConnectPostgreSQLClient({
host: pgConfig.host,
port: pgConfig.port,
database: pgConfig.database,
username: pgConfig.username,
password: pgConfig.password,
poolSettings: {
min: 2,
max: pgConfig.maxConnections || 10,
idleTimeoutMillis: 30000,
},
});
setPostgreSQLClient(postgresClient);
logger.info('PostgreSQL connected'); logger.info('PostgreSQL connected');
// Initialize sync managers // Initialize sync managers
@ -238,8 +277,12 @@ shutdown.onShutdown(async () => {
shutdown.onShutdown(async () => { shutdown.onShutdown(async () => {
logger.info('Disconnecting from databases...'); logger.info('Disconnecting from databases...');
try { try {
await disconnectMongoDB(); if (mongoClient) {
await disconnectPostgreSQL(); await mongoClient.disconnect();
}
if (postgresClient) {
await postgresClient.disconnect();
}
logger.info('Database connections closed'); logger.info('Database connections closed');
} catch (error) { } catch (error) {
logger.error('Error closing database connections', { error }); logger.error('Error closing database connections', { error });

View file

@ -2,8 +2,7 @@
* Enhanced Sync Manager - Improved syncing with comprehensive exchange data * Enhanced Sync Manager - Improved syncing with comprehensive exchange data
*/ */
import { getLogger } from '@stock-bot/logger'; import { getLogger } from '@stock-bot/logger';
import { getMongoDBClient } from '@stock-bot/mongodb-client'; import { getMongoDBClient, getPostgreSQLClient } from '../clients';
import { getPostgreSQLClient } from '@stock-bot/postgres-client';
const logger = getLogger('enhanced-sync-manager'); const logger = getLogger('enhanced-sync-manager');

View file

@ -2,8 +2,7 @@
* Sync Manager - Handles syncing raw MongoDB data to PostgreSQL master records * Sync Manager - Handles syncing raw MongoDB data to PostgreSQL master records
*/ */
import { getLogger } from '@stock-bot/logger'; import { getLogger } from '@stock-bot/logger';
import { getMongoDBClient } from '@stock-bot/mongodb-client'; import { getMongoDBClient, getPostgreSQLClient } from '../clients';
import { getPostgreSQLClient } from '@stock-bot/postgres-client';
const logger = getLogger('sync-manager'); const logger = getLogger('sync-manager');

View file

@ -0,0 +1,27 @@
import { PostgreSQLClient } from '@stock-bot/postgres-client';
import { MongoDBClient } from '@stock-bot/mongodb-client';
let postgresClient: PostgreSQLClient | null = null;
let mongodbClient: MongoDBClient | null = null;
export function setPostgreSQLClient(client: PostgreSQLClient): void {
postgresClient = client;
}
export function getPostgreSQLClient(): PostgreSQLClient {
if (!postgresClient) {
throw new Error('PostgreSQL client not initialized. Call setPostgreSQLClient first.');
}
return postgresClient;
}
export function setMongoDBClient(client: MongoDBClient): void {
mongodbClient = client;
}
export function getMongoDBClient(): MongoDBClient {
if (!mongodbClient) {
throw new Error('MongoDB client not initialized. Call setMongoDBClient first.');
}
return mongodbClient;
}

View file

@ -3,17 +3,30 @@
*/ */
import { Hono } from 'hono'; import { Hono } from 'hono';
import { cors } from 'hono/cors'; import { cors } from 'hono/cors';
import { initializeConfig, getServiceConfig } from '@stock-bot/config-new'; import { initializeConfig, getServiceConfig, getLoggingConfig } from '@stock-bot/config-new';
import { getLogger, shutdownLoggers } from '@stock-bot/logger'; import { getLogger, shutdownLoggers, setLoggerConfig } from '@stock-bot/logger';
import { connectMongoDB, disconnectMongoDB } from '@stock-bot/mongodb-client'; import { createAndConnectMongoDBClient, MongoDBClient } from '@stock-bot/mongodb-client';
import { connectPostgreSQL, disconnectPostgreSQL } from '@stock-bot/postgres-client'; import { createAndConnectPostgreSQLClient, PostgreSQLClient } from '@stock-bot/postgres-client';
import { Shutdown } from '@stock-bot/shutdown'; import { Shutdown } from '@stock-bot/shutdown';
import { setPostgreSQLClient, setMongoDBClient } from './clients';
// Import routes // Import routes
import { exchangeRoutes } from './routes/exchange.routes'; import { exchangeRoutes } from './routes/exchange.routes';
import { healthRoutes } from './routes/health.routes'; import { healthRoutes } from './routes/health.routes';
// Initialize configuration // Initialize configuration
await initializeConfig(); await initializeConfig();
const serviceConfig = getServiceConfig();
// Initialize logger with config
const loggingConfig = getLoggingConfig();
if (loggingConfig) {
setLoggerConfig({
logLevel: loggingConfig.level,
logConsole: true,
logFile: false,
environment: serviceConfig.environment,
});
}
const app = new Hono(); const app = new Hono();
@ -29,9 +42,10 @@ app.use(
); );
const logger = getLogger('web-api'); const logger = getLogger('web-api');
const serviceConfig = getServiceConfig();
const PORT = serviceConfig.port; const PORT = serviceConfig.port;
let server: ReturnType<typeof Bun.serve> | null = null; let server: ReturnType<typeof Bun.serve> | null = null;
let postgresClient: PostgreSQLClient | null = null;
let mongoClient: MongoDBClient | null = null;
// Initialize shutdown manager // Initialize shutdown manager
const shutdown = Shutdown.getInstance({ timeout: 15000 }); const shutdown = Shutdown.getInstance({ timeout: 15000 });
@ -61,12 +75,37 @@ async function initializeServices() {
try { try {
// Initialize MongoDB client // Initialize MongoDB client
logger.info('Connecting to MongoDB...'); logger.info('Connecting to MongoDB...');
await connectMongoDB(); const mongoConfig = serviceConfig.database.mongodb;
mongoClient = await createAndConnectMongoDBClient({
uri: mongoConfig.uri,
database: mongoConfig.database,
host: 'localhost',
port: 27017,
timeouts: {
connectTimeout: mongoConfig.connectionTimeout || 30000,
socketTimeout: 30000,
serverSelectionTimeout: mongoConfig.serverSelectionTimeout || 5000,
},
});
setMongoDBClient(mongoClient);
logger.info('MongoDB connected'); logger.info('MongoDB connected');
// Initialize PostgreSQL client // Initialize PostgreSQL client
logger.info('Connecting to PostgreSQL...'); logger.info('Connecting to PostgreSQL...');
await connectPostgreSQL(); const pgConfig = serviceConfig.database.postgres;
postgresClient = await createAndConnectPostgreSQLClient({
host: pgConfig.host,
port: pgConfig.port,
database: pgConfig.database,
username: pgConfig.username,
password: pgConfig.password,
poolSettings: {
min: 2,
max: pgConfig.maxConnections || 10,
idleTimeoutMillis: 30000,
},
});
setPostgreSQLClient(postgresClient);
logger.info('PostgreSQL connected'); logger.info('PostgreSQL connected');
logger.info('All services initialized successfully'); logger.info('All services initialized successfully');
@ -105,8 +144,12 @@ shutdown.onShutdown(async () => {
shutdown.onShutdown(async () => { shutdown.onShutdown(async () => {
logger.info('Disconnecting from databases...'); logger.info('Disconnecting from databases...');
try { try {
await disconnectMongoDB(); if (mongoClient) {
await disconnectPostgreSQL(); await mongoClient.disconnect();
}
if (postgresClient) {
await postgresClient.disconnect();
}
logger.info('Database connections closed'); logger.info('Database connections closed');
} catch (error) { } catch (error) {
logger.error('Error closing database connections', { error }); logger.error('Error closing database connections', { error });

View file

@ -3,8 +3,7 @@
*/ */
import { Hono } from 'hono'; import { Hono } from 'hono';
import { getLogger } from '@stock-bot/logger'; import { getLogger } from '@stock-bot/logger';
import { getMongoDBClient } from '@stock-bot/mongodb-client'; import { getPostgreSQLClient, getMongoDBClient } from '../clients';
import { getPostgreSQLClient } from '@stock-bot/postgres-client';
const logger = getLogger('health-routes'); const logger = getLogger('health-routes');
export const healthRoutes = new Hono(); export const healthRoutes = new Hono();

View file

@ -1,6 +1,5 @@
import { getLogger } from '@stock-bot/logger'; import { getLogger } from '@stock-bot/logger';
import { getPostgreSQLClient } from '@stock-bot/postgres-client'; import { getPostgreSQLClient, getMongoDBClient } from '../clients';
import { getMongoDBClient } from '@stock-bot/mongodb-client';
import { import {
Exchange, Exchange,
ExchangeWithMappings, ExchangeWithMappings,

View file

@ -11,7 +11,6 @@
"test": "bun test" "test": "bun test"
}, },
"dependencies": { "dependencies": {
"@stock-bot/config": "*",
"@stock-bot/logger": "*", "@stock-bot/logger": "*",
"ioredis": "^5.3.2" "ioredis": "^5.3.2"
}, },

View file

@ -1,11 +1,12 @@
import Redis from 'ioredis'; import Redis from 'ioredis';
import { dragonflyConfig } from '@stock-bot/config';
import { getLogger } from '@stock-bot/logger'; import { getLogger } from '@stock-bot/logger';
import type { RedisConfig } from './types';
interface ConnectionConfig { interface ConnectionConfig {
name: string; name: string;
singleton?: boolean; singleton?: boolean;
db?: number; db?: number;
redisConfig: RedisConfig;
} }
/** /**
@ -32,12 +33,12 @@ export class RedisConnectionManager {
* @returns Redis connection instance * @returns Redis connection instance
*/ */
getConnection(config: ConnectionConfig): Redis { getConnection(config: ConnectionConfig): Redis {
const { name, singleton = false, db } = config; const { name, singleton = false, db, redisConfig } = config;
if (singleton) { if (singleton) {
// Use shared connection across all instances // Use shared connection across all instances
if (!RedisConnectionManager.sharedConnections.has(name)) { if (!RedisConnectionManager.sharedConnections.has(name)) {
const connection = this.createConnection(name, db); const connection = this.createConnection(name, redisConfig, db);
RedisConnectionManager.sharedConnections.set(name, connection); RedisConnectionManager.sharedConnections.set(name, connection);
this.logger.info(`Created shared Redis connection: ${name}`); this.logger.info(`Created shared Redis connection: ${name}`);
} }
@ -45,7 +46,7 @@ export class RedisConnectionManager {
} else { } else {
// Create unique connection per instance // Create unique connection per instance
const uniqueName = `${name}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; const uniqueName = `${name}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
const connection = this.createConnection(uniqueName, db); const connection = this.createConnection(uniqueName, redisConfig, db);
this.connections.set(uniqueName, connection); this.connections.set(uniqueName, connection);
this.logger.info(`Created unique Redis connection: ${uniqueName}`); this.logger.info(`Created unique Redis connection: ${uniqueName}`);
return connection; return connection;
@ -55,33 +56,31 @@ export class RedisConnectionManager {
/** /**
* Create a new Redis connection with configuration * Create a new Redis connection with configuration
*/ */
private createConnection(name: string, db?: number): Redis { private createConnection(name: string, config: RedisConfig, db?: number): Redis {
const redisConfig = { const redisOptions = {
host: dragonflyConfig.DRAGONFLY_HOST, host: config.host,
port: dragonflyConfig.DRAGONFLY_PORT, port: config.port,
password: dragonflyConfig.DRAGONFLY_PASSWORD || undefined, password: config.password || undefined,
username: dragonflyConfig.DRAGONFLY_USERNAME || undefined, username: config.username || undefined,
db: db ?? dragonflyConfig.DRAGONFLY_DATABASE, db: db ?? config.db ?? 0,
maxRetriesPerRequest: dragonflyConfig.DRAGONFLY_MAX_RETRIES, maxRetriesPerRequest: config.maxRetriesPerRequest ?? 3,
retryDelayOnFailover: dragonflyConfig.DRAGONFLY_RETRY_DELAY, retryDelayOnFailover: config.retryDelayOnFailover ?? 100,
connectTimeout: dragonflyConfig.DRAGONFLY_CONNECT_TIMEOUT, connectTimeout: config.connectTimeout ?? 10000,
commandTimeout: dragonflyConfig.DRAGONFLY_COMMAND_TIMEOUT, commandTimeout: config.commandTimeout ?? 5000,
keepAlive: dragonflyConfig.DRAGONFLY_ENABLE_KEEPALIVE keepAlive: config.keepAlive ?? 0,
? dragonflyConfig.DRAGONFLY_KEEPALIVE_INTERVAL * 1000
: 0,
connectionName: name, connectionName: name,
lazyConnect: false, // Connect immediately instead of waiting for first command lazyConnect: false, // Connect immediately instead of waiting for first command
...(dragonflyConfig.DRAGONFLY_TLS && { ...(config.tls && {
tls: { tls: {
cert: dragonflyConfig.DRAGONFLY_TLS_CERT_FILE || undefined, cert: config.tls.cert || undefined,
key: dragonflyConfig.DRAGONFLY_TLS_KEY_FILE || undefined, key: config.tls.key || undefined,
ca: dragonflyConfig.DRAGONFLY_TLS_CA_FILE || undefined, ca: config.tls.ca || undefined,
rejectUnauthorized: !dragonflyConfig.DRAGONFLY_TLS_SKIP_VERIFY, rejectUnauthorized: config.tls.rejectUnauthorized ?? true,
}, },
}), }),
}; };
const redis = new Redis(redisConfig); const redis = new Redis(redisOptions);
// Setup event handlers // Setup event handlers
redis.on('connect', () => { redis.on('connect', () => {

View file

@ -1,6 +1,6 @@
import { RedisConnectionManager } from './connection-manager'; import { RedisConnectionManager } from './connection-manager';
import { RedisCache } from './redis-cache'; import { RedisCache } from './redis-cache';
import type { CacheOptions, CacheProvider } from './types'; import type { CacheOptions, CacheProvider, RedisConfig } from './types';
// Cache instances registry to prevent multiple instances with same prefix // Cache instances registry to prevent multiple instances with same prefix
const cacheInstances = new Map<string, CacheProvider>(); const cacheInstances = new Map<string, CacheProvider>();
@ -8,7 +8,7 @@ const cacheInstances = new Map<string, CacheProvider>();
/** /**
* Create a Redis cache instance with trading-optimized defaults * Create a Redis cache instance with trading-optimized defaults
*/ */
export function createCache(options: Partial<CacheOptions> = {}): CacheProvider { export function createCache(options: CacheOptions): CacheProvider {
const defaultOptions: CacheOptions = { const defaultOptions: CacheOptions = {
keyPrefix: 'cache:', keyPrefix: 'cache:',
ttl: 3600, // 1 hour default ttl: 3600, // 1 hour default
@ -37,39 +37,42 @@ export function createCache(options: Partial<CacheOptions> = {}): CacheProvider
/** /**
* Create a cache instance for trading data * Create a cache instance for trading data
*/ */
export function createTradingCache(options: Partial<CacheOptions> = {}): CacheProvider { export function createTradingCache(redisConfig: RedisConfig, options?: Partial<Omit<CacheOptions, 'redisConfig'>>): CacheProvider {
return createCache({ return createCache({
keyPrefix: 'trading:', keyPrefix: 'trading:',
ttl: 3600, // 1 hour default ttl: 3600, // 1 hour default
enableMetrics: true, enableMetrics: true,
shared: true, shared: true,
...options, ...options,
redisConfig,
}); });
} }
/** /**
* Create a cache for market data with shorter TTL * Create a cache for market data with shorter TTL
*/ */
export function createMarketDataCache(options: Partial<CacheOptions> = {}): CacheProvider { export function createMarketDataCache(redisConfig: RedisConfig, options?: Partial<Omit<CacheOptions, 'redisConfig'>>): CacheProvider {
return createCache({ return createCache({
keyPrefix: 'market:', keyPrefix: 'market:',
ttl: 300, // 5 minutes for market data ttl: 300, // 5 minutes for market data
enableMetrics: true, enableMetrics: true,
shared: true, shared: true,
...options, ...options,
redisConfig,
}); });
} }
/** /**
* Create a cache for indicators with longer TTL * Create a cache for indicators with longer TTL
*/ */
export function createIndicatorCache(options: Partial<CacheOptions> = {}): CacheProvider { export function createIndicatorCache(redisConfig: RedisConfig, options?: Partial<Omit<CacheOptions, 'redisConfig'>>): CacheProvider {
return createCache({ return createCache({
keyPrefix: 'indicators:', keyPrefix: 'indicators:',
ttl: 1800, // 30 minutes for indicators ttl: 1800, // 30 minutes for indicators
enableMetrics: true, enableMetrics: true,
shared: true, shared: true,
...options, ...options,
redisConfig,
}); });
} }
@ -81,6 +84,7 @@ export type {
CacheStats, CacheStats,
CacheKey, CacheKey,
SerializationOptions, SerializationOptions,
RedisConfig,
} from './types'; } from './types';
export { RedisCache } from './redis-cache'; export { RedisCache } from './redis-cache';

View file

@ -25,7 +25,7 @@ export class RedisCache implements CacheProvider {
uptime: 0, uptime: 0,
}; };
constructor(options: CacheOptions = {}) { constructor(options: CacheOptions) {
this.defaultTTL = options.ttl ?? 3600; // 1 hour default this.defaultTTL = options.ttl ?? 3600; // 1 hour default
this.keyPrefix = options.keyPrefix ?? 'cache:'; this.keyPrefix = options.keyPrefix ?? 'cache:';
this.enableMetrics = options.enableMetrics ?? true; this.enableMetrics = options.enableMetrics ?? true;
@ -46,6 +46,7 @@ export class RedisCache implements CacheProvider {
this.redis = this.connectionManager.getConnection({ this.redis = this.connectionManager.getConnection({
name: `${baseName}-SERVICE`, name: `${baseName}-SERVICE`,
singleton: options.shared ?? true, // Default to shared connection for cache singleton: options.shared ?? true, // Default to shared connection for cache
redisConfig: options.redisConfig,
}); });
// Only setup event handlers for non-shared connections to avoid memory leaks // Only setup event handlers for non-shared connections to avoid memory leaks

View file

@ -1,3 +1,23 @@
export interface RedisConfig {
host: string;
port: number;
password?: string;
username?: string;
db?: number;
keyPrefix?: string;
maxRetriesPerRequest?: number;
retryDelayOnFailover?: number;
connectTimeout?: number;
commandTimeout?: number;
keepAlive?: number;
tls?: {
cert?: string;
key?: string;
ca?: string;
rejectUnauthorized?: boolean;
};
}
export interface CacheProvider { export interface CacheProvider {
get<T>(key: string): Promise<T | null>; get<T>(key: string): Promise<T | null>;
set<T>( set<T>(
@ -64,6 +84,7 @@ export interface CacheOptions {
enableMetrics?: boolean; enableMetrics?: boolean;
name?: string; // Name for connection identification name?: string; // Name for connection identification
shared?: boolean; // Whether to use shared connection shared?: boolean; // Whether to use shared connection
redisConfig: RedisConfig;
} }
export interface CacheStats { export interface CacheStats {

View file

@ -12,7 +12,6 @@
}, },
"dependencies": { "dependencies": {
"@stock-bot/logger": "*", "@stock-bot/logger": "*",
"@stock-bot/config": "*",
"ioredis": "^5.3.2", "ioredis": "^5.3.2",
"eventemitter3": "^5.0.1" "eventemitter3": "^5.0.1"
}, },

View file

@ -1,6 +1,5 @@
import { EventEmitter } from 'eventemitter3'; import { EventEmitter } from 'eventemitter3';
import Redis from 'ioredis'; import Redis from 'ioredis';
import { dragonflyConfig } from '@stock-bot/config';
import { getLogger } from '@stock-bot/logger'; import { getLogger } from '@stock-bot/logger';
export interface EventBusMessage { export interface EventBusMessage {
@ -16,12 +15,21 @@ export interface EventHandler<T = any> {
(message: EventBusMessage & { data: T }): Promise<void> | void; (message: EventBusMessage & { data: T }): Promise<void> | void;
} }
export interface RedisConfig {
host: string;
port: number;
password?: string;
db?: number;
maxRetriesPerRequest?: number;
}
export interface EventBusOptions { export interface EventBusOptions {
serviceName: string; serviceName: string;
enablePersistence?: boolean; enablePersistence?: boolean;
useStreams?: boolean; useStreams?: boolean;
maxRetries?: number; maxRetries?: number;
retryDelay?: number; retryDelay?: number;
redisConfig: RedisConfig;
} }
export interface StreamConsumerInfo { export interface StreamConsumerInfo {
@ -53,21 +61,22 @@ export class EventBus extends EventEmitter {
this.retryDelay = options.retryDelay ?? 1000; this.retryDelay = options.retryDelay ?? 1000;
this.logger = getLogger(`event-bus:${this.serviceName}`); this.logger = getLogger(`event-bus:${this.serviceName}`);
const { redisConfig } = options;
this.redis = new Redis({ this.redis = new Redis({
host: dragonflyConfig.DRAGONFLY_HOST, host: redisConfig.host,
port: dragonflyConfig.DRAGONFLY_PORT, port: redisConfig.port,
password: dragonflyConfig.DRAGONFLY_PASSWORD, password: redisConfig.password,
db: dragonflyConfig.DRAGONFLY_DATABASE || 0, db: redisConfig.db || 0,
maxRetriesPerRequest: dragonflyConfig.DRAGONFLY_MAX_RETRIES, maxRetriesPerRequest: redisConfig.maxRetriesPerRequest || 3,
lazyConnect: false, lazyConnect: false,
}); });
if (!this.useStreams) { if (!this.useStreams) {
this.subscriber = new Redis({ this.subscriber = new Redis({
host: dragonflyConfig.DRAGONFLY_HOST, host: redisConfig.host,
port: dragonflyConfig.DRAGONFLY_PORT, port: redisConfig.port,
password: dragonflyConfig.DRAGONFLY_PASSWORD, password: redisConfig.password,
db: dragonflyConfig.DRAGONFLY_DATABASE || 0, db: redisConfig.db || 0,
}); });
this.subscriber.on('message', this.handleRedisMessage.bind(this)); this.subscriber.on('message', this.handleRedisMessage.bind(this));
} }

View file

@ -11,7 +11,6 @@
"test": "bun test" "test": "bun test"
}, },
"dependencies": { "dependencies": {
"@stock-bot/config": "*",
"got": "^14.4.7", "got": "^14.4.7",
"pino": "^9.7.0", "pino": "^9.7.0",
"pino-loki": "^2.6.0", "pino-loki": "^2.6.0",

View file

@ -5,10 +5,10 @@
*/ */
// Core logger classes and functions // Core logger classes and functions
export { Logger, getLogger, shutdownLoggers } from './logger'; export { Logger, getLogger, shutdownLoggers, setLoggerConfig } from './logger';
// Type definitions // Type definitions
export type { LogLevel, LogContext, LogMetadata } from './types'; export type { LogLevel, LogContext, LogMetadata, LoggerConfig } from './types';
// Default export // Default export
export { getLogger as default } from './logger'; export { getLogger as default } from './logger';

View file

@ -9,23 +9,41 @@
*/ */
import pino from 'pino'; import pino from 'pino';
import { loggingConfig, lokiConfig } from '@stock-bot/config'; import type { LogContext, LogLevel, LogMetadata, LoggerConfig } from './types';
import type { LogContext, LogLevel, LogMetadata } from './types';
// Simple cache for logger instances // Simple cache for logger instances
const loggerCache = new Map<string, pino.Logger>(); const loggerCache = new Map<string, pino.Logger>();
console.log('Logger cache initialized: ', loggingConfig.LOG_LEVEL);
// Global config that can be set
let globalConfig: LoggerConfig = {
logLevel: 'info',
logConsole: true,
logFile: false,
logFilePath: './logs',
logLoki: false,
environment: 'development',
};
/**
* Set global logger configuration
*/
export function setLoggerConfig(config: LoggerConfig): void {
globalConfig = { ...globalConfig, ...config };
// Clear cache to force recreation with new config
loggerCache.clear();
console.log('Logger config updated:', globalConfig.logLevel);
}
/** /**
* Create transport configuration * Create transport configuration
*/ */
function createTransports(serviceName: string): any { function createTransports(serviceName: string, config: LoggerConfig = globalConfig): any {
const targets: any[] = []; const targets: any[] = [];
// const isDev = loggingConfig.LOG_ENVIRONMENT === 'development';
// Console transport // Console transport
if (loggingConfig.LOG_CONSOLE) { if (config.logConsole) {
targets.push({ targets.push({
target: 'pino-pretty', target: 'pino-pretty',
level: loggingConfig.LOG_LEVEL, // Only show errors on console level: config.logLevel || 'info',
options: { options: {
colorize: true, colorize: true,
translateTime: 'yyyy-mm-dd HH:MM:ss.l', translateTime: 'yyyy-mm-dd HH:MM:ss.l',
@ -40,29 +58,35 @@ function createTransports(serviceName: string): any {
} }
// File transport // File transport
if (loggingConfig.LOG_FILE) { if (config.logFile) {
targets.push({ targets.push({
target: 'pino/file', target: 'pino/file',
level: loggingConfig.LOG_LEVEL, level: config.logLevel || 'info',
options: { options: {
destination: `${loggingConfig.LOG_FILE_PATH}/${serviceName}.log`, destination: `${config.logFilePath}/${serviceName}.log`,
mkdir: true, mkdir: true,
}, },
}); });
} }
// Loki transport // Loki transport
if (lokiConfig.LOKI_HOST) { if (config.logLoki && config.lokiHost) {
targets.push({ targets.push({
target: 'pino-loki', target: 'pino-loki',
level: loggingConfig.LOG_LEVEL, level: config.logLevel || 'info',
options: { options: {
host: lokiConfig.LOKI_URL || `http://${lokiConfig.LOKI_HOST}:${lokiConfig.LOKI_PORT}`, host: config.lokiHost,
labels: { labels: {
service: serviceName, service: serviceName,
environment: lokiConfig.LOKI_ENVIRONMENT_LABEL, environment: config.environment || 'development',
}, },
ignore: 'childName', ignore: 'childName',
...(config.lokiUser && config.lokiPassword ? {
basicAuth: {
username: config.lokiUser,
password: config.lokiPassword,
},
} : {}),
}, },
}); });
} }
@ -73,27 +97,28 @@ function createTransports(serviceName: string): any {
/** /**
* Get or create pino logger * Get or create pino logger
*/ */
function getPinoLogger(serviceName: string): pino.Logger { function getPinoLogger(serviceName: string, config: LoggerConfig = globalConfig): pino.Logger {
if (!loggerCache.has(serviceName)) { const cacheKey = `${serviceName}-${JSON.stringify(config)}`;
const transport = createTransports(serviceName); if (!loggerCache.has(cacheKey)) {
const transport = createTransports(serviceName, config);
const config: pino.LoggerOptions = { const loggerOptions: pino.LoggerOptions = {
level: loggingConfig.LOG_LEVEL, level: config.logLevel || 'info',
base: { base: {
service: serviceName, service: serviceName,
environment: loggingConfig.LOG_ENVIRONMENT, environment: config.environment || 'development',
version: loggingConfig.LOG_SERVICE_VERSION, version: '1.0.0',
}, },
}; };
if (transport) { if (transport) {
config.transport = transport; loggerOptions.transport = transport;
} }
loggerCache.set(serviceName, pino(config)); loggerCache.set(cacheKey, pino(loggerOptions));
} }
return loggerCache.get(serviceName)!; return loggerCache.get(cacheKey)!;
} }
/** /**
@ -105,8 +130,8 @@ export class Logger {
private serviceName: string; private serviceName: string;
private childName?: string; private childName?: string;
constructor(serviceName: string, context: LogContext = {}) { constructor(serviceName: string, context: LogContext = {}, config?: LoggerConfig) {
this.pino = getPinoLogger(serviceName); this.pino = getPinoLogger(serviceName, config);
this.context = context; this.context = context;
this.serviceName = serviceName; this.serviceName = serviceName;
} }
@ -232,8 +257,8 @@ export class Logger {
/** /**
* Main factory function * Main factory function
*/ */
export function getLogger(serviceName: string, context?: LogContext): Logger { export function getLogger(serviceName: string, context?: LogContext, config?: LoggerConfig): Logger {
return new Logger(serviceName, context); return new Logger(serviceName, context, config);
} }
/** /**

View file

@ -14,3 +14,16 @@ export interface LogContext {
export interface LogMetadata { export interface LogMetadata {
[key: string]: any; [key: string]: any;
} }
// Logger configuration
export interface LoggerConfig {
logLevel?: LogLevel;
logConsole?: boolean;
logFile?: boolean;
logFilePath?: string;
logLoki?: boolean;
lokiHost?: string;
lokiUser?: string;
lokiPassword?: string;
environment?: string;
}

View file

@ -13,12 +13,10 @@
"clean": "rimraf dist" "clean": "rimraf dist"
}, },
"dependencies": { "dependencies": {
"@stock-bot/config": "*",
"@stock-bot/logger": "*", "@stock-bot/logger": "*",
"@stock-bot/types": "*", "@stock-bot/types": "*",
"@types/mongodb": "^4.0.7", "@types/mongodb": "^4.0.7",
"mongodb": "^6.17.0", "mongodb": "^6.17.0"
"yup": "^1.6.1"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^20.11.0", "@types/node": "^20.11.0",

View file

@ -1,32 +1,24 @@
import { Collection, Db, MongoClient, OptionalUnlessRequiredId } from 'mongodb'; import { Collection, Db, MongoClient, OptionalUnlessRequiredId } from 'mongodb';
import { mongodbConfig } from '@stock-bot/config';
import { getLogger } from '@stock-bot/logger'; import { getLogger } from '@stock-bot/logger';
import type { DocumentBase } from './types'; import type { DocumentBase, MongoDBClientConfig } from './types';
/** /**
* Simplified MongoDB Client for Stock Bot Data Service * MongoDB Client for Stock Bot Data Service
* *
* A singleton MongoDB client focused solely on batch upsert operations * MongoDB client focused on batch upsert operations
* with minimal configuration and no health monitoring complexity. * with minimal configuration and no health monitoring complexity.
*/ */
export class MongoDBClient { export class MongoDBClient {
private static instance: MongoDBClient | null = null;
private client: MongoClient | null = null; private client: MongoClient | null = null;
private db: Db | null = null; private db: Db | null = null;
private defaultDatabase: string = 'stock'; // Default database name private readonly config: MongoDBClientConfig;
private readonly logger = getLogger('mongodb-client-simple'); private defaultDatabase: string;
private readonly logger = getLogger('mongodb-client');
private isConnected = false; private isConnected = false;
private constructor() {} constructor(config: MongoDBClientConfig) {
this.config = config;
/** this.defaultDatabase = config.database || 'stock';
* Get singleton instance
*/
static getInstance(): MongoDBClient {
if (!MongoDBClient.instance) {
MongoDBClient.instance = new MongoDBClient();
}
return MongoDBClient.instance;
} }
/** /**
@ -42,18 +34,17 @@ export class MongoDBClient {
this.logger.info('Connecting to MongoDB...'); this.logger.info('Connecting to MongoDB...');
this.client = new MongoClient(uri, { this.client = new MongoClient(uri, {
maxPoolSize: 10, maxPoolSize: this.config.poolSettings?.maxPoolSize || 10,
minPoolSize: 1, minPoolSize: this.config.poolSettings?.minPoolSize || 1,
connectTimeoutMS: 10000, connectTimeoutMS: this.config.timeouts?.connectTimeout || 10000,
socketTimeoutMS: 30000, socketTimeoutMS: this.config.timeouts?.socketTimeout || 30000,
serverSelectionTimeoutMS: 5000, serverSelectionTimeoutMS: this.config.timeouts?.serverSelectionTimeout || 5000,
}); });
await this.client.connect(); await this.client.connect();
await this.client.db(mongodbConfig.MONGODB_DATABASE).admin().ping(); await this.client.db(this.defaultDatabase).admin().ping();
// Set default database from config // Set default database from config
this.defaultDatabase = mongodbConfig.MONGODB_DATABASE;
this.db = this.client.db(this.defaultDatabase); this.db = this.client.db(this.defaultDatabase);
this.isConnected = true; this.isConnected = true;
@ -347,18 +338,11 @@ export class MongoDBClient {
} }
private buildConnectionUri(): string { private buildConnectionUri(): string {
if (mongodbConfig.MONGODB_URI) { if (this.config.uri) {
return mongodbConfig.MONGODB_URI; return this.config.uri;
} }
const { const { host, port, username, password, database, authSource } = this.config;
MONGODB_HOST: host,
MONGODB_PORT: port,
MONGODB_USERNAME: username,
MONGODB_PASSWORD: password,
MONGODB_DATABASE: database,
MONGODB_AUTH_SOURCE: authSource,
} = mongodbConfig;
// Build URI components // Build URI components
const auth = username && password ? `${username}:${password}@` : ''; const auth = username && password ? `${username}:${password}@` : '';

View file

@ -1,53 +1,20 @@
import { MongoDBClient } from './client'; import { MongoDBClient } from './client';
import type { MongoDBClientConfig } from './types';
/** /**
* Get the singleton MongoDB client instance * Factory function to create a MongoDB client instance
*/ */
export function getMongoDBClient(): MongoDBClient { export function createMongoDBClient(config: MongoDBClientConfig): MongoDBClient {
return MongoDBClient.getInstance(); return new MongoDBClient(config);
} }
/** /**
* Connect to MongoDB using the singleton client * Create and connect a MongoDB client
*/ */
export async function connectMongoDB(): Promise<MongoDBClient> { export async function createAndConnectMongoDBClient(
const client = getMongoDBClient(); config: MongoDBClientConfig
if (!client.connected) { ): Promise<MongoDBClient> {
await client.connect(); const client = createMongoDBClient(config);
} await client.connect();
return client; return client;
} }
/**
* Disconnect from MongoDB
*/
export async function disconnectMongoDB(): Promise<void> {
const client = getMongoDBClient();
if (client.connected) {
await client.disconnect();
}
}
/**
* Set the default database for all operations
*/
export function setDefaultDatabase(databaseName: string): void {
const client = getMongoDBClient();
client.setDefaultDatabase(databaseName);
}
/**
* Get the current default database name
*/
export function getCurrentDatabase(): string {
const client = getMongoDBClient();
return client.getDefaultDatabase();
}
/**
* Get a database instance by name
*/
export function getDatabase(databaseName?: string) {
const client = getMongoDBClient();
return client.getDatabase(databaseName);
}

View file

@ -1,7 +1,7 @@
/** /**
* Simplified MongoDB Client Library for Stock Bot Data Service * MongoDB Client Library for Stock Bot Data Service
* *
* Provides a singleton MongoDB client focused on batch upsert operations * Provides a MongoDB client focused on batch upsert operations
* for high-performance data ingestion. * for high-performance data ingestion.
*/ */
@ -14,6 +14,8 @@ export type {
EarningsTranscript, EarningsTranscript,
ExchangeSourceMapping, ExchangeSourceMapping,
MasterExchange, MasterExchange,
MongoDBClientConfig,
MongoDBConnectionOptions,
NewsArticle, NewsArticle,
RawDocument, RawDocument,
SecFiling, SecFiling,
@ -22,10 +24,6 @@ export type {
// Factory functions // Factory functions
export { export {
connectMongoDB, createMongoDBClient,
disconnectMongoDB, createAndConnectMongoDBClient,
getCurrentDatabase,
getDatabase,
getMongoDBClient,
setDefaultDatabase,
} from './factory'; } from './factory';

View file

@ -13,11 +13,9 @@
"clean": "rimraf dist" "clean": "rimraf dist"
}, },
"dependencies": { "dependencies": {
"@stock-bot/config": "*",
"@stock-bot/logger": "*", "@stock-bot/logger": "*",
"@stock-bot/types": "*", "@stock-bot/types": "*",
"pg": "^8.11.3", "pg": "^8.11.3"
"yup": "^1.6.1"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^20.11.0", "@types/node": "^20.11.0",

View file

@ -1,5 +1,4 @@
import { Pool, QueryResultRow } from 'pg'; import { Pool, QueryResultRow } from 'pg';
import { postgresConfig } from '@stock-bot/config';
import { getLogger } from '@stock-bot/logger'; import { getLogger } from '@stock-bot/logger';
import { PostgreSQLHealthMonitor } from './health'; import { PostgreSQLHealthMonitor } from './health';
import { PostgreSQLQueryBuilder } from './query-builder'; import { PostgreSQLQueryBuilder } from './query-builder';
@ -26,8 +25,8 @@ export class PostgreSQLClient {
private readonly transactionManager: PostgreSQLTransactionManager; private readonly transactionManager: PostgreSQLTransactionManager;
private isConnected = false; private isConnected = false;
constructor(config?: Partial<PostgreSQLClientConfig>, options?: PostgreSQLConnectionOptions) { constructor(config: PostgreSQLClientConfig, options?: PostgreSQLConnectionOptions) {
this.config = this.buildConfig(config); this.config = config;
this.options = { this.options = {
retryAttempts: 3, retryAttempts: 3,
retryDelay: 1000, retryDelay: 1000,
@ -367,34 +366,6 @@ export class PostgreSQLClient {
return this.pool; return this.pool;
} }
private buildConfig(config?: Partial<PostgreSQLClientConfig>): PostgreSQLClientConfig {
return {
host: config?.host || postgresConfig.POSTGRES_HOST,
port: config?.port || postgresConfig.POSTGRES_PORT,
database: config?.database || postgresConfig.POSTGRES_DATABASE,
username: config?.username || postgresConfig.POSTGRES_USERNAME,
password: config?.password || postgresConfig.POSTGRES_PASSWORD,
poolSettings: {
min: postgresConfig.POSTGRES_POOL_MIN,
max: postgresConfig.POSTGRES_POOL_MAX,
idleTimeoutMillis: postgresConfig.POSTGRES_POOL_IDLE_TIMEOUT,
...config?.poolSettings,
},
ssl: {
enabled: postgresConfig.POSTGRES_SSL,
rejectUnauthorized: postgresConfig.POSTGRES_SSL_REJECT_UNAUTHORIZED,
...config?.ssl,
},
timeouts: {
query: postgresConfig.POSTGRES_QUERY_TIMEOUT,
connection: postgresConfig.POSTGRES_CONNECTION_TIMEOUT,
statement: postgresConfig.POSTGRES_STATEMENT_TIMEOUT,
lock: postgresConfig.POSTGRES_LOCK_TIMEOUT,
idleInTransaction: postgresConfig.POSTGRES_IDLE_IN_TRANSACTION_SESSION_TIMEOUT,
...config?.timeouts,
},
};
}
private buildPoolConfig(): any { private buildPoolConfig(): any {
return { return {

View file

@ -1,4 +1,3 @@
import { postgresConfig } from '@stock-bot/config';
import { PostgreSQLClient } from './client'; import { PostgreSQLClient } from './client';
import type { PostgreSQLClientConfig, PostgreSQLConnectionOptions } from './types'; import type { PostgreSQLClientConfig, PostgreSQLConnectionOptions } from './types';
@ -6,59 +5,21 @@ import type { PostgreSQLClientConfig, PostgreSQLConnectionOptions } from './type
* Factory function to create a PostgreSQL client instance * Factory function to create a PostgreSQL client instance
*/ */
export function createPostgreSQLClient( export function createPostgreSQLClient(
config?: Partial<PostgreSQLClientConfig>, config: PostgreSQLClientConfig,
options?: PostgreSQLConnectionOptions options?: PostgreSQLConnectionOptions
): PostgreSQLClient { ): PostgreSQLClient {
return new PostgreSQLClient(config, options); return new PostgreSQLClient(config, options);
} }
/** /**
* Create a PostgreSQL client with default configuration * Create and connect a PostgreSQL client
*/ */
export function createDefaultPostgreSQLClient(): PostgreSQLClient { export async function createAndConnectPostgreSQLClient(
const config: Partial<PostgreSQLClientConfig> = { config: PostgreSQLClientConfig,
host: postgresConfig.POSTGRES_HOST, options?: PostgreSQLConnectionOptions
port: postgresConfig.POSTGRES_PORT, ): Promise<PostgreSQLClient> {
database: postgresConfig.POSTGRES_DATABASE, const client = createPostgreSQLClient(config, options);
username: postgresConfig.POSTGRES_USERNAME, await client.connect();
password: postgresConfig.POSTGRES_PASSWORD,
};
return new PostgreSQLClient(config);
}
/**
* Singleton PostgreSQL client instance
*/
let defaultClient: PostgreSQLClient | null = null;
/**
* Get or create the default PostgreSQL client instance
*/
export function getPostgreSQLClient(): PostgreSQLClient {
if (!defaultClient) {
defaultClient = createDefaultPostgreSQLClient();
}
return defaultClient;
}
/**
* Connect to PostgreSQL using the default client
*/
export async function connectPostgreSQL(): Promise<PostgreSQLClient> {
const client = getPostgreSQLClient();
if (!client.connected) {
await client.connect();
}
return client; return client;
} }
/**
* Disconnect from PostgreSQL
*/
export async function disconnectPostgreSQL(): Promise<void> {
if (defaultClient) {
await defaultClient.disconnect();
defaultClient = null;
}
}

View file

@ -33,7 +33,5 @@ export type {
// Utils // Utils
export { export {
createPostgreSQLClient, createPostgreSQLClient,
getPostgreSQLClient, createAndConnectPostgreSQLClient,
connectPostgreSQL,
disconnectPostgreSQL,
} from './factory'; } from './factory';

View file

@ -13,9 +13,9 @@
"clean": "rimraf dist" "clean": "rimraf dist"
}, },
"dependencies": { "dependencies": {
"@stock-bot/config": "*",
"@stock-bot/logger": "*", "@stock-bot/logger": "*",
"@stock-bot/types": "*" "@stock-bot/types": "*",
"pg": "^8.11.3"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^20.11.0", "@types/node": "^20.11.0",

View file

@ -1,5 +1,4 @@
import { Pool } from 'pg'; import { Pool } from 'pg';
import { questdbConfig } from '@stock-bot/config';
import { getLogger } from '@stock-bot/logger'; import { getLogger } from '@stock-bot/logger';
import { QuestDBHealthMonitor } from './health'; import { QuestDBHealthMonitor } from './health';
import { QuestDBInfluxWriter } from './influx-writer'; import { QuestDBInfluxWriter } from './influx-writer';
@ -30,8 +29,8 @@ export class QuestDBClient {
private readonly schemaManager: QuestDBSchemaManager; private readonly schemaManager: QuestDBSchemaManager;
private isConnected = false; private isConnected = false;
constructor(config?: Partial<QuestDBClientConfig>, options?: QuestDBConnectionOptions) { constructor(config: QuestDBClientConfig, options?: QuestDBConnectionOptions) {
this.config = this.buildConfig(config); this.config = config;
this.options = { this.options = {
protocol: 'pg', protocol: 'pg',
retryAttempts: 3, retryAttempts: 3,
@ -408,29 +407,6 @@ export class QuestDBClient {
return { ...this.config }; return { ...this.config };
} }
private buildConfig(config?: Partial<QuestDBClientConfig>): QuestDBClientConfig {
return {
host: config?.host || questdbConfig.QUESTDB_HOST,
httpPort: config?.httpPort || questdbConfig.QUESTDB_HTTP_PORT,
pgPort: config?.pgPort || questdbConfig.QUESTDB_PG_PORT,
influxPort: config?.influxPort || questdbConfig.QUESTDB_INFLUX_PORT,
user: config?.user || questdbConfig.QUESTDB_USER,
password: config?.password || questdbConfig.QUESTDB_PASSWORD,
database: config?.database || questdbConfig.QUESTDB_DEFAULT_DATABASE,
tls: {
enabled: questdbConfig.QUESTDB_TLS_ENABLED,
verifyServerCert: questdbConfig.QUESTDB_TLS_VERIFY_SERVER_CERT,
...config?.tls,
},
timeouts: {
connection: questdbConfig.QUESTDB_CONNECTION_TIMEOUT,
request: questdbConfig.QUESTDB_REQUEST_TIMEOUT,
...config?.timeouts,
},
retryAttempts: questdbConfig.QUESTDB_RETRY_ATTEMPTS,
...config,
};
}
private buildPgPoolConfig(): any { private buildPgPoolConfig(): any {
return { return {

View file

@ -1,4 +1,3 @@
import { questdbConfig } from '@stock-bot/config';
import { QuestDBClient } from './client'; import { QuestDBClient } from './client';
import type { QuestDBClientConfig, QuestDBConnectionOptions } from './types'; import type { QuestDBClientConfig, QuestDBConnectionOptions } from './types';
@ -6,58 +5,20 @@ import type { QuestDBClientConfig, QuestDBConnectionOptions } from './types';
* Factory function to create a QuestDB client instance * Factory function to create a QuestDB client instance
*/ */
export function createQuestDBClient( export function createQuestDBClient(
config?: Partial<QuestDBClientConfig>, config: QuestDBClientConfig,
options?: QuestDBConnectionOptions options?: QuestDBConnectionOptions
): QuestDBClient { ): QuestDBClient {
return new QuestDBClient(config, options); return new QuestDBClient(config, options);
} }
/** /**
* Create a QuestDB client with default configuration * Create and connect a QuestDB client
*/ */
export function createDefaultQuestDBClient(): QuestDBClient { export async function createAndConnectQuestDBClient(
const config: Partial<QuestDBClientConfig> = { config: QuestDBClientConfig,
host: questdbConfig.QUESTDB_HOST, options?: QuestDBConnectionOptions
httpPort: questdbConfig.QUESTDB_HTTP_PORT, ): Promise<QuestDBClient> {
pgPort: questdbConfig.QUESTDB_PG_PORT, const client = createQuestDBClient(config, options);
influxPort: questdbConfig.QUESTDB_INFLUX_PORT,
user: questdbConfig.QUESTDB_USER,
password: questdbConfig.QUESTDB_PASSWORD,
};
return new QuestDBClient(config);
}
/**
* Singleton QuestDB client instance
*/
let defaultClient: QuestDBClient | null = null;
/**
* Get or create the default QuestDB client instance
*/
export function getQuestDBClient(): QuestDBClient {
if (!defaultClient) {
defaultClient = createDefaultQuestDBClient();
}
return defaultClient;
}
/**
* Connect to QuestDB using the default client
*/
export async function connectQuestDB(): Promise<QuestDBClient> {
const client = getQuestDBClient();
await client.connect(); await client.connect();
return client; return client;
} }
/**
* Disconnect from QuestDB
*/
export async function disconnectQuestDB(): Promise<void> {
if (defaultClient) {
await defaultClient.disconnect();
defaultClient = null;
}
}

View file

@ -29,4 +29,4 @@ export type {
} from './types'; } from './types';
// Utils // Utils
export { createQuestDBClient, getQuestDBClient } from './factory'; export { createQuestDBClient, createAndConnectQuestDBClient } from './factory';