added a smart queue manager and moved proxy logic to proxy manager to make handler just schedule a call to it
This commit is contained in:
parent
da1c52a841
commit
e7c0fe2798
19 changed files with 903 additions and 231 deletions
|
|
@ -1,25 +1,28 @@
|
|||
import { z } from 'zod';
|
||||
import { redisConfigSchema } from './redis.schema';
|
||||
import { mongodbConfigSchema } from './mongodb.schema';
|
||||
import { postgresConfigSchema } from './postgres.schema';
|
||||
import { questdbConfigSchema } from './questdb.schema';
|
||||
import { proxyConfigSchema, browserConfigSchema, queueConfigSchema } from './service.schema';
|
||||
|
||||
export const appConfigSchema = z.object({
|
||||
redis: redisConfigSchema,
|
||||
mongodb: mongodbConfigSchema,
|
||||
postgres: postgresConfigSchema,
|
||||
questdb: questdbConfigSchema.optional(),
|
||||
proxy: proxyConfigSchema.optional(),
|
||||
browser: browserConfigSchema.optional(),
|
||||
queue: queueConfigSchema.optional(),
|
||||
});
|
||||
|
||||
export type AppConfig = z.infer<typeof appConfigSchema>;
|
||||
|
||||
// Re-export individual schemas and types
|
||||
export * from './redis.schema';
|
||||
export * from './mongodb.schema';
|
||||
export * from './postgres.schema';
|
||||
export * from './questdb.schema';
|
||||
import { z } from 'zod';
|
||||
import { redisConfigSchema } from './redis.schema';
|
||||
import { mongodbConfigSchema } from './mongodb.schema';
|
||||
import { postgresConfigSchema } from './postgres.schema';
|
||||
import { questdbConfigSchema } from './questdb.schema';
|
||||
import { proxyConfigSchema, browserConfigSchema, queueConfigSchema } from './service.schema';
|
||||
|
||||
export const appConfigSchema = z.object({
|
||||
redis: redisConfigSchema,
|
||||
mongodb: mongodbConfigSchema,
|
||||
postgres: postgresConfigSchema,
|
||||
questdb: questdbConfigSchema.optional(),
|
||||
proxy: proxyConfigSchema.optional(),
|
||||
browser: browserConfigSchema.optional(),
|
||||
queue: queueConfigSchema.optional(),
|
||||
service: z.object({
|
||||
name: z.string(),
|
||||
}).optional(),
|
||||
});
|
||||
|
||||
export type AppConfig = z.infer<typeof appConfigSchema>;
|
||||
|
||||
// Re-export individual schemas and types
|
||||
export * from './redis.schema';
|
||||
export * from './mongodb.schema';
|
||||
export * from './postgres.schema';
|
||||
export * from './questdb.schema';
|
||||
export * from './service.schema';
|
||||
|
|
@ -1,33 +1,38 @@
|
|||
import { z } from 'zod';
|
||||
|
||||
export const proxyConfigSchema = z.object({
|
||||
cachePrefix: z.string().optional().default('proxy:'),
|
||||
ttl: z.number().optional().default(3600),
|
||||
});
|
||||
|
||||
export const browserConfigSchema = z.object({
|
||||
headless: z.boolean().optional().default(true),
|
||||
timeout: z.number().optional().default(30000),
|
||||
});
|
||||
|
||||
export const queueConfigSchema = z.object({
|
||||
enabled: z.boolean().optional().default(true),
|
||||
workers: z.number().optional().default(1),
|
||||
concurrency: z.number().optional().default(1),
|
||||
enableScheduledJobs: z.boolean().optional().default(true),
|
||||
delayWorkerStart: z.boolean().optional().default(false),
|
||||
defaultJobOptions: z.object({
|
||||
attempts: z.number().default(3),
|
||||
backoff: z.object({
|
||||
type: z.enum(['exponential', 'fixed']).default('exponential'),
|
||||
delay: z.number().default(1000),
|
||||
}).default({}),
|
||||
removeOnComplete: z.number().default(100),
|
||||
removeOnFail: z.number().default(50),
|
||||
timeout: z.number().optional(),
|
||||
}).optional().default({}),
|
||||
});
|
||||
|
||||
export type ProxyConfig = z.infer<typeof proxyConfigSchema>;
|
||||
export type BrowserConfig = z.infer<typeof browserConfigSchema>;
|
||||
import { z } from 'zod';
|
||||
|
||||
export const proxyConfigSchema = z.object({
|
||||
enabled: z.boolean().default(false),
|
||||
cachePrefix: z.string().optional().default('proxy:'),
|
||||
ttl: z.number().optional().default(3600),
|
||||
webshare: z.object({
|
||||
apiKey: z.string(),
|
||||
apiUrl: z.string().default('https://proxy.webshare.io/api/v2/'),
|
||||
}).optional(),
|
||||
});
|
||||
|
||||
export const browserConfigSchema = z.object({
|
||||
headless: z.boolean().optional().default(true),
|
||||
timeout: z.number().optional().default(30000),
|
||||
});
|
||||
|
||||
export const queueConfigSchema = z.object({
|
||||
enabled: z.boolean().optional().default(true),
|
||||
workers: z.number().optional().default(1),
|
||||
concurrency: z.number().optional().default(1),
|
||||
enableScheduledJobs: z.boolean().optional().default(true),
|
||||
delayWorkerStart: z.boolean().optional().default(false),
|
||||
defaultJobOptions: z.object({
|
||||
attempts: z.number().default(3),
|
||||
backoff: z.object({
|
||||
type: z.enum(['exponential', 'fixed']).default('exponential'),
|
||||
delay: z.number().default(1000),
|
||||
}).default({}),
|
||||
removeOnComplete: z.number().default(100),
|
||||
removeOnFail: z.number().default(50),
|
||||
timeout: z.number().optional(),
|
||||
}).optional().default({}),
|
||||
});
|
||||
|
||||
export type ProxyConfig = z.infer<typeof proxyConfigSchema>;
|
||||
export type BrowserConfig = z.infer<typeof browserConfigSchema>;
|
||||
export type QueueConfig = z.infer<typeof queueConfigSchema>;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import { createContainer, InjectionMode, type AwilixContainer } from 'awilix';
|
||||
import { createContainer, InjectionMode, asFunction, type AwilixContainer } from 'awilix';
|
||||
import type { AppConfig as StockBotAppConfig } from '@stock-bot/config';
|
||||
import { appConfigSchema, type AppConfig } from '../config/schemas';
|
||||
import {
|
||||
|
|
@ -100,7 +100,7 @@ export class ServiceContainerBuilder {
|
|||
influxPort: 9009,
|
||||
database: 'questdb',
|
||||
}) : undefined,
|
||||
proxy: this.options.enableProxy ? (config.proxy || { cachePrefix: 'proxy:', ttl: 3600 }) : undefined,
|
||||
proxy: this.options.enableProxy ? (config.proxy || { enabled: false, cachePrefix: 'proxy:', ttl: 3600 }) : undefined,
|
||||
browser: this.options.enableBrowser ? (config.browser || { headless: true, timeout: 30000 }) : undefined,
|
||||
queue: this.options.enableQueue ? (config.queue || {
|
||||
enabled: true,
|
||||
|
|
@ -127,11 +127,12 @@ export class ServiceContainerBuilder {
|
|||
// Register service container aggregate
|
||||
container.register({
|
||||
serviceContainer: asFunction(({
|
||||
config, logger, cache, proxyManager, browser,
|
||||
config, logger, cache, globalCache, proxyManager, browser,
|
||||
queueManager, mongoClient, postgresClient, questdbClient
|
||||
}) => ({
|
||||
logger,
|
||||
cache,
|
||||
globalCache,
|
||||
proxy: proxyManager, // Map proxyManager to proxy
|
||||
browser,
|
||||
queue: queueManager, // Map queueManager to queue
|
||||
|
|
@ -181,10 +182,14 @@ export class ServiceContainerBuilder {
|
|||
} : undefined,
|
||||
queue: stockBotConfig.queue,
|
||||
browser: stockBotConfig.browser,
|
||||
proxy: stockBotConfig.proxy,
|
||||
proxy: stockBotConfig.proxy ? {
|
||||
...{
|
||||
enabled: false,
|
||||
cachePrefix: 'proxy:',
|
||||
ttl: 3600,
|
||||
},
|
||||
...stockBotConfig.proxy
|
||||
} : undefined,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Add missing import
|
||||
import { asFunction } from 'awilix';
|
||||
}
|
||||
|
|
@ -1,13 +1,13 @@
|
|||
import type { Browser } from '@stock-bot/browser';
|
||||
import type { CacheProvider } from '@stock-bot/cache';
|
||||
import type { IServiceContainer } from '@stock-bot/handlers';
|
||||
import type { Logger } from '@stock-bot/logger';
|
||||
import type { AppConfig } from '../config/schemas';
|
||||
import type { CacheProvider } from '@stock-bot/cache';
|
||||
import type { ProxyManager } from '@stock-bot/proxy';
|
||||
import type { Browser } from '@stock-bot/browser';
|
||||
import type { QueueManager } from '@stock-bot/queue';
|
||||
import type { MongoDBClient } from '@stock-bot/mongodb';
|
||||
import type { PostgreSQLClient } from '@stock-bot/postgres';
|
||||
import type { ProxyManager } from '@stock-bot/proxy';
|
||||
import type { QuestDBClient } from '@stock-bot/questdb';
|
||||
import type { SmartQueueManager } from '@stock-bot/queue';
|
||||
import type { AppConfig } from '../config/schemas';
|
||||
|
||||
export interface ServiceDefinitions {
|
||||
// Configuration
|
||||
|
|
@ -16,9 +16,10 @@ export interface ServiceDefinitions {
|
|||
|
||||
// Core services
|
||||
cache: CacheProvider | null;
|
||||
globalCache: CacheProvider | null;
|
||||
proxyManager: ProxyManager | null;
|
||||
browser: Browser;
|
||||
queueManager: QueueManager | null;
|
||||
queueManager: SmartQueueManager | null;
|
||||
|
||||
// Database clients
|
||||
mongoClient: MongoDBClient | null;
|
||||
|
|
|
|||
|
|
@ -10,18 +10,34 @@ export function registerCacheServices(
|
|||
if (config.redis.enabled) {
|
||||
container.register({
|
||||
cache: asFunction(() => {
|
||||
return createCache({
|
||||
redisConfig: {
|
||||
host: config.redis.host,
|
||||
port: config.redis.port,
|
||||
password: config.redis.password,
|
||||
},
|
||||
const { createServiceCache } = require('@stock-bot/queue');
|
||||
const serviceName = config.service?.name || 'unknown';
|
||||
|
||||
// Create service-specific cache that uses the service's Redis DB
|
||||
return createServiceCache(serviceName, {
|
||||
host: config.redis.host,
|
||||
port: config.redis.port,
|
||||
password: config.redis.password,
|
||||
db: config.redis.db, // This will be overridden by ServiceCache
|
||||
});
|
||||
}).singleton(),
|
||||
|
||||
// Also provide global cache for shared data
|
||||
globalCache: asFunction(() => {
|
||||
const { createServiceCache } = require('@stock-bot/queue');
|
||||
const serviceName = config.service?.name || 'unknown';
|
||||
|
||||
return createServiceCache(serviceName, {
|
||||
host: config.redis.host,
|
||||
port: config.redis.port,
|
||||
password: config.redis.password,
|
||||
}, { global: true });
|
||||
}).singleton(),
|
||||
});
|
||||
} else {
|
||||
container.register({
|
||||
cache: asValue(null),
|
||||
globalCache: asValue(null),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -32,9 +32,13 @@ export function registerApplicationServices(
|
|||
if (config.proxy && config.redis.enabled) {
|
||||
container.register({
|
||||
proxyManager: asFunction(({ cache, logger }) => {
|
||||
if (!cache) {return null;}
|
||||
if (!cache) return null;
|
||||
|
||||
const proxyCache = new NamespacedCache(cache, 'proxy');
|
||||
return new ProxyManager(proxyCache, logger);
|
||||
const proxyManager = new ProxyManager(proxyCache, config.proxy, logger);
|
||||
|
||||
// Note: Initialization will be handled by the lifecycle manager
|
||||
return proxyManager;
|
||||
}).singleton(),
|
||||
});
|
||||
} else {
|
||||
|
|
@ -47,8 +51,9 @@ export function registerApplicationServices(
|
|||
if (config.queue?.enabled && config.redis.enabled) {
|
||||
container.register({
|
||||
queueManager: asFunction(({ logger }) => {
|
||||
const { QueueManager } = require('@stock-bot/queue');
|
||||
const { SmartQueueManager } = require('@stock-bot/queue');
|
||||
const queueConfig = {
|
||||
serviceName: config.service?.name || 'unknown',
|
||||
redis: {
|
||||
host: config.redis.host,
|
||||
port: config.redis.port,
|
||||
|
|
@ -62,8 +67,9 @@ export function registerApplicationServices(
|
|||
},
|
||||
enableScheduledJobs: config.queue!.enableScheduledJobs ?? true,
|
||||
delayWorkerStart: config.queue!.delayWorkerStart ?? false,
|
||||
autoDiscoverHandlers: true,
|
||||
};
|
||||
return new QueueManager(queueConfig, logger);
|
||||
return new SmartQueueManager(queueConfig, logger);
|
||||
}).singleton(),
|
||||
});
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ export class ServiceLifecycleManager {
|
|||
{ name: 'mongoClient', key: 'mongoClient' as const },
|
||||
{ name: 'postgresClient', key: 'postgresClient' as const },
|
||||
{ name: 'questdbClient', key: 'questdbClient' as const },
|
||||
{ name: 'proxyManager', key: 'proxyManager' as const },
|
||||
{ name: 'queueManager', key: 'queueManager' as const },
|
||||
];
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue