removed configs from all libs and will inject them within the services themselves
This commit is contained in:
parent
fd28162811
commit
6cc5b339bc
32 changed files with 366 additions and 349 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
27
apps/data-sync-service/src/clients.ts
Normal file
27
apps/data-sync-service/src/clients.ts
Normal 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;
|
||||||
|
}
|
||||||
|
|
@ -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 });
|
||||||
|
|
|
||||||
|
|
@ -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');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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');
|
||||||
|
|
||||||
|
|
|
||||||
27
apps/web-api/src/clients.ts
Normal file
27
apps/web-api/src/clients.ts
Normal 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;
|
||||||
|
}
|
||||||
|
|
@ -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 });
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
1
libs/cache/package.json
vendored
1
libs/cache/package.json
vendored
|
|
@ -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"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
47
libs/cache/src/connection-manager.ts
vendored
47
libs/cache/src/connection-manager.ts
vendored
|
|
@ -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', () => {
|
||||||
|
|
|
||||||
14
libs/cache/src/index.ts
vendored
14
libs/cache/src/index.ts
vendored
|
|
@ -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';
|
||||||
|
|
|
||||||
3
libs/cache/src/redis-cache.ts
vendored
3
libs/cache/src/redis-cache.ts
vendored
|
|
@ -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
|
||||||
|
|
|
||||||
21
libs/cache/src/types.ts
vendored
21
libs/cache/src/types.ts
vendored
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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';
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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}@` : '';
|
||||||
|
|
|
||||||
|
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
@ -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';
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,5 @@ export type {
|
||||||
// Utils
|
// Utils
|
||||||
export {
|
export {
|
||||||
createPostgreSQLClient,
|
createPostgreSQLClient,
|
||||||
getPostgreSQLClient,
|
createAndConnectPostgreSQLClient,
|
||||||
connectPostgreSQL,
|
|
||||||
disconnectPostgreSQL,
|
|
||||||
} from './factory';
|
} from './factory';
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -29,4 +29,4 @@ export type {
|
||||||
} from './types';
|
} from './types';
|
||||||
|
|
||||||
// Utils
|
// Utils
|
||||||
export { createQuestDBClient, getQuestDBClient } from './factory';
|
export { createQuestDBClient, createAndConnectQuestDBClient } from './factory';
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue