moved jobs to provider config
This commit is contained in:
parent
f9c2860ff4
commit
52c2f08db2
7 changed files with 183 additions and 76 deletions
|
|
@ -162,7 +162,7 @@ export class StrategyDetailsComponent implements OnChanges {
|
|||
.subscribe((update: any) => {
|
||||
if (update.strategyId === this.strategy?.id) {
|
||||
// Update strategy status if changed
|
||||
if (update.status && this.strategy.status !== update.status) {
|
||||
if (update.status && this.strategy && this.strategy.status !== update.status) {
|
||||
this.strategy.status = update.status;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -188,6 +188,21 @@ app.get('/api/providers', async (c) => {
|
|||
}
|
||||
});
|
||||
|
||||
// Add new endpoint to see scheduled jobs
|
||||
app.get('/api/scheduled-jobs', async (c) => {
|
||||
try {
|
||||
const jobs = queueManager.getScheduledJobsInfo();
|
||||
return c.json({
|
||||
status: 'success',
|
||||
count: jobs.length,
|
||||
jobs
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Failed to get scheduled jobs info', { error });
|
||||
return c.json({ status: 'error', message: 'Failed to get scheduled jobs' }, 500);
|
||||
}
|
||||
});
|
||||
|
||||
// Initialize services
|
||||
async function initializeServices() {
|
||||
logger.info('Initializing data service...');
|
||||
|
|
|
|||
|
|
@ -9,34 +9,25 @@ export const proxyProvider: ProviderConfig = {
|
|||
return await proxyService.fetchProxiesFromSources();
|
||||
},
|
||||
|
||||
// 'check-specific': async (payload: { proxies: any[] }) => {
|
||||
// const { proxyService } = await import('../services/proxy.service');
|
||||
// return await proxyService.checkProxies(payload.proxies);
|
||||
// },
|
||||
'check-specific': async (payload: { proxies: any[] }) => {
|
||||
const { proxyService } = await import('../services/proxy.service');
|
||||
return await proxyService.checkProxies(payload.proxies);
|
||||
},
|
||||
|
||||
// 'get-stats': async (payload: { includeDetails?: boolean }) => {
|
||||
// const { proxyService } = await import('../services/proxy.service');
|
||||
// return await proxyService.getProxyStats(payload.includeDetails);
|
||||
// },
|
||||
|
||||
// 'cleanup-old-data': async (payload: { daysToKeep?: number }) => {
|
||||
// const { proxyService } = await import('../services/proxy.service');
|
||||
// return await proxyService.cleanupOldData(payload.daysToKeep || 7);
|
||||
// },
|
||||
|
||||
// 'get-working-proxy': async (payload: { protocol?: string; country?: string; timeout?: number }) => {
|
||||
// const { proxyService } = await import('../services/proxy.service');
|
||||
// return await proxyService.getWorkingProxy(payload);
|
||||
// },
|
||||
|
||||
// 'validate-proxy': async (payload: { proxy: any; testUrl?: string }) => {
|
||||
// const { proxyService } = await import('../services/proxy.service');
|
||||
// return await proxyService.validateProxy(payload.proxy, payload.testUrl);
|
||||
// },
|
||||
|
||||
// 'rotate-proxies': async (payload: { count?: number }) => {
|
||||
// const { proxyService } = await import('../services/proxy.service');
|
||||
// return await proxyService.rotateProxies(payload.count || 5);
|
||||
// }
|
||||
}
|
||||
'get-working-proxy': async (payload: { protocol?: string; country?: string; timeout?: number }) => {
|
||||
const { proxyService } = await import('../services/proxy.service');
|
||||
return await proxyService.getWorkingProxy();
|
||||
}
|
||||
},
|
||||
|
||||
scheduledJobs: [
|
||||
{
|
||||
type: 'proxy-maintenance',
|
||||
operation: 'fetch-and-check',
|
||||
payload: {},
|
||||
cronPattern: '*/15 * * * *', // Every 15 minutes
|
||||
priority: 5,
|
||||
description: 'Fetch and validate proxy list from sources'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
|
|
|||
|
|
@ -155,8 +155,7 @@ export const quotemediaProvider: ProviderConfig = {
|
|||
}));
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 400 + Math.random() * 300));
|
||||
|
||||
return {
|
||||
return {
|
||||
symbol: payload.symbol,
|
||||
expiration: payload.expiration || new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString().split('T')[0],
|
||||
calls,
|
||||
|
|
@ -164,5 +163,32 @@ export const quotemediaProvider: ProviderConfig = {
|
|||
source: 'quotemedia'
|
||||
};
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
scheduledJobs: [
|
||||
{
|
||||
type: 'quotemedia-premium-refresh',
|
||||
operation: 'batch-quotes',
|
||||
payload: { symbols: ['AAPL', 'GOOGL', 'MSFT'] },
|
||||
cronPattern: '*/2 * * * *', // Every 2 minutes
|
||||
priority: 7,
|
||||
description: 'Refresh premium quotes with detailed market data'
|
||||
},
|
||||
{
|
||||
type: 'quotemedia-options-update',
|
||||
operation: 'options-chain',
|
||||
payload: { symbol: 'SPY' },
|
||||
cronPattern: '*/10 * * * *', // Every 10 minutes
|
||||
priority: 5,
|
||||
description: 'Update options chain data for SPY ETF'
|
||||
},
|
||||
{
|
||||
type: 'quotemedia-profiles',
|
||||
operation: 'company-profile',
|
||||
payload: { symbol: 'AAPL' },
|
||||
cronPattern: '0 9 * * 1-5', // Weekdays at 9 AM
|
||||
priority: 3,
|
||||
description: 'Update company profile data'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
|
|
|||
|
|
@ -222,8 +222,34 @@ export const yahooProvider: ProviderConfig = {
|
|||
};
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 180 + Math.random() * 120));
|
||||
|
||||
return recommendations;
|
||||
return recommendations;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
scheduledJobs: [
|
||||
{
|
||||
type: 'yahoo-market-refresh',
|
||||
operation: 'live-data',
|
||||
payload: { symbol: 'AAPL' },
|
||||
cronPattern: '*/1 * * * *', // Every minute
|
||||
priority: 8,
|
||||
description: 'Refresh Apple stock price from Yahoo Finance'
|
||||
},
|
||||
{
|
||||
type: 'yahoo-sp500-update',
|
||||
operation: 'live-data',
|
||||
payload: { symbol: 'SPY' },
|
||||
cronPattern: '*/2 * * * *', // Every 2 minutes
|
||||
priority: 9,
|
||||
description: 'Update S&P 500 ETF price'
|
||||
},
|
||||
{
|
||||
type: 'yahoo-earnings-check',
|
||||
operation: 'earnings',
|
||||
payload: { symbol: 'AAPL' },
|
||||
cronPattern: '0 16 * * 1-5', // Weekdays at 4 PM (market close)
|
||||
priority: 6,
|
||||
description: 'Check earnings data for Apple'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
|
|
|||
|
|
@ -4,10 +4,20 @@ export interface JobHandler {
|
|||
(payload: any): Promise<any>;
|
||||
}
|
||||
|
||||
export interface ScheduledJob {
|
||||
type: string;
|
||||
operation: string;
|
||||
payload: any;
|
||||
cronPattern: string;
|
||||
priority?: number;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export interface ProviderConfig {
|
||||
name: string;
|
||||
service: string;
|
||||
operations: Record<string, JobHandler>;
|
||||
scheduledJobs?: ScheduledJob[];
|
||||
}
|
||||
|
||||
export class ProviderRegistry {
|
||||
|
|
@ -16,12 +26,12 @@ export class ProviderRegistry {
|
|||
|
||||
/**
|
||||
* Register a provider with its operations
|
||||
*/
|
||||
registerProvider(config: ProviderConfig): void {
|
||||
*/ registerProvider(config: ProviderConfig): void {
|
||||
const key = `${config.service}:${config.name}`;
|
||||
this.providers.set(key, config);
|
||||
this.logger.info(`Registered provider: ${key}`, {
|
||||
operations: Object.keys(config.operations)
|
||||
operations: Object.keys(config.operations),
|
||||
scheduledJobs: config.scheduledJobs?.length || 0
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -49,6 +59,28 @@ export class ProviderRegistry {
|
|||
/**
|
||||
* Get all registered providers
|
||||
*/
|
||||
getAllScheduledJobs(): Array<{
|
||||
service: string;
|
||||
provider: string;
|
||||
job: ScheduledJob;
|
||||
}> {
|
||||
const allJobs: Array<{ service: string; provider: string; job: ScheduledJob }> = [];
|
||||
|
||||
for (const [key, config] of this.providers) {
|
||||
if (config.scheduledJobs) {
|
||||
for (const job of config.scheduledJobs) {
|
||||
allJobs.push({
|
||||
service: config.service,
|
||||
provider: config.name,
|
||||
job
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return allJobs;
|
||||
}
|
||||
|
||||
getProviders(): Array<{ key: string; config: ProviderConfig }> {
|
||||
return Array.from(this.providers.entries()).map(([key, config]) => ({
|
||||
key,
|
||||
|
|
|
|||
|
|
@ -140,46 +140,50 @@ export class QueueService {
|
|||
this.logger.debug('Job progress', { id: job.id, progress });
|
||||
});
|
||||
}
|
||||
|
||||
private async setupScheduledTasks() {
|
||||
try {
|
||||
// Market data refresh every minute using Yahoo Finance
|
||||
await this.addRecurringJob({
|
||||
type: 'market-data-refresh',
|
||||
service: 'market-data',
|
||||
provider: 'yahoo-finance',
|
||||
operation: 'live-data',
|
||||
payload: { symbol: 'AAPL' }
|
||||
}, '*/1 * * * *');
|
||||
this.logger.info('Setting up scheduled tasks from providers...');
|
||||
|
||||
// Get all scheduled jobs from all providers
|
||||
const allScheduledJobs = providerRegistry.getAllScheduledJobs();
|
||||
|
||||
if (allScheduledJobs.length === 0) {
|
||||
this.logger.warn('No scheduled jobs found in providers');
|
||||
return;
|
||||
}
|
||||
|
||||
// Market data refresh using QuoteMedia every 2 minutes
|
||||
await this.addRecurringJob({
|
||||
type: 'market-data-quotemedia',
|
||||
service: 'market-data',
|
||||
provider: 'quotemedia',
|
||||
operation: 'batch-quotes',
|
||||
payload: { symbols: ['GOOGL', 'MSFT', 'TSLA'] }
|
||||
}, '*/2 * * * *');
|
||||
// Register each scheduled job
|
||||
for (const { service, provider, job } of allScheduledJobs) {
|
||||
try {
|
||||
await this.addRecurringJob({
|
||||
type: job.type,
|
||||
service: service,
|
||||
provider: provider,
|
||||
operation: job.operation,
|
||||
payload: job.payload,
|
||||
priority: job.priority
|
||||
}, job.cronPattern);
|
||||
|
||||
// Proxy fetch every 15 minutes
|
||||
await this.addRecurringJob({
|
||||
type: 'proxy-maintenance',
|
||||
service: 'proxy',
|
||||
provider: 'proxy-service',
|
||||
operation: 'fetch-and-check',
|
||||
payload: {}
|
||||
}, '*/15 * * * *');
|
||||
this.logger.info('Scheduled job registered', {
|
||||
type: job.type,
|
||||
service,
|
||||
provider,
|
||||
operation: job.operation,
|
||||
cronPattern: job.cronPattern,
|
||||
description: job.description
|
||||
});
|
||||
} catch (error) {
|
||||
this.logger.error('Failed to register scheduled job', {
|
||||
type: job.type,
|
||||
service,
|
||||
provider,
|
||||
error: error instanceof Error ? error.message : String(error)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Proxy cleanup daily at 2 AM
|
||||
await this.addRecurringJob({
|
||||
type: 'proxy-cleanup',
|
||||
service: 'proxy',
|
||||
provider: 'proxy-service',
|
||||
operation: 'cleanup-old-data',
|
||||
payload: { daysToKeep: 7 }
|
||||
}, '0 2 * * *');
|
||||
|
||||
this.logger.info('Scheduled tasks configured');
|
||||
this.logger.info(`Successfully configured ${allScheduledJobs.length} scheduled tasks`);
|
||||
|
||||
} catch (error) {
|
||||
this.logger.error('Failed to setup scheduled tasks', error);
|
||||
}
|
||||
|
|
@ -256,13 +260,26 @@ export class QueueService {
|
|||
}
|
||||
return this.worker.opts.concurrency || 1;
|
||||
}
|
||||
|
||||
getRegisteredProviders() {
|
||||
return providerRegistry.getProviders().map(({ key, config }) => ({
|
||||
key,
|
||||
name: config.name,
|
||||
service: config.service,
|
||||
operations: Object.keys(config.operations)
|
||||
operations: Object.keys(config.operations),
|
||||
scheduledJobs: config.scheduledJobs?.length || 0
|
||||
}));
|
||||
}
|
||||
|
||||
getScheduledJobsInfo() {
|
||||
return providerRegistry.getAllScheduledJobs().map(({ service, provider, job }) => ({
|
||||
id: `${service}-${provider}-${job.type}`,
|
||||
service,
|
||||
provider,
|
||||
type: job.type,
|
||||
operation: job.operation,
|
||||
cronPattern: job.cronPattern,
|
||||
priority: job.priority,
|
||||
description: job.description
|
||||
}));
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue