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) => {
|
.subscribe((update: any) => {
|
||||||
if (update.strategyId === this.strategy?.id) {
|
if (update.strategyId === this.strategy?.id) {
|
||||||
// Update strategy status if changed
|
// 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;
|
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
|
// Initialize services
|
||||||
async function initializeServices() {
|
async function initializeServices() {
|
||||||
logger.info('Initializing data service...');
|
logger.info('Initializing data service...');
|
||||||
|
|
|
||||||
|
|
@ -9,34 +9,25 @@ export const proxyProvider: ProviderConfig = {
|
||||||
return await proxyService.fetchProxiesFromSources();
|
return await proxyService.fetchProxiesFromSources();
|
||||||
},
|
},
|
||||||
|
|
||||||
// 'check-specific': async (payload: { proxies: any[] }) => {
|
'check-specific': async (payload: { proxies: any[] }) => {
|
||||||
// const { proxyService } = await import('../services/proxy.service');
|
const { proxyService } = await import('../services/proxy.service');
|
||||||
// return await proxyService.checkProxies(payload.proxies);
|
return await proxyService.checkProxies(payload.proxies);
|
||||||
// },
|
},
|
||||||
|
|
||||||
// 'get-stats': async (payload: { includeDetails?: boolean }) => {
|
'get-working-proxy': async (payload: { protocol?: string; country?: string; timeout?: number }) => {
|
||||||
// const { proxyService } = await import('../services/proxy.service');
|
const { proxyService } = await import('../services/proxy.service');
|
||||||
// return await proxyService.getProxyStats(payload.includeDetails);
|
return await proxyService.getWorkingProxy();
|
||||||
// },
|
}
|
||||||
|
},
|
||||||
// 'cleanup-old-data': async (payload: { daysToKeep?: number }) => {
|
|
||||||
// const { proxyService } = await import('../services/proxy.service');
|
scheduledJobs: [
|
||||||
// return await proxyService.cleanupOldData(payload.daysToKeep || 7);
|
{
|
||||||
// },
|
type: 'proxy-maintenance',
|
||||||
|
operation: 'fetch-and-check',
|
||||||
// 'get-working-proxy': async (payload: { protocol?: string; country?: string; timeout?: number }) => {
|
payload: {},
|
||||||
// const { proxyService } = await import('../services/proxy.service');
|
cronPattern: '*/15 * * * *', // Every 15 minutes
|
||||||
// return await proxyService.getWorkingProxy(payload);
|
priority: 5,
|
||||||
// },
|
description: 'Fetch and validate proxy list from sources'
|
||||||
|
}
|
||||||
// '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);
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -155,8 +155,7 @@ export const quotemediaProvider: ProviderConfig = {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 400 + Math.random() * 300));
|
await new Promise(resolve => setTimeout(resolve, 400 + Math.random() * 300));
|
||||||
|
return {
|
||||||
return {
|
|
||||||
symbol: payload.symbol,
|
symbol: payload.symbol,
|
||||||
expiration: payload.expiration || new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString().split('T')[0],
|
expiration: payload.expiration || new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString().split('T')[0],
|
||||||
calls,
|
calls,
|
||||||
|
|
@ -164,5 +163,32 @@ export const quotemediaProvider: ProviderConfig = {
|
||||||
source: 'quotemedia'
|
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));
|
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>;
|
(payload: any): Promise<any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ScheduledJob {
|
||||||
|
type: string;
|
||||||
|
operation: string;
|
||||||
|
payload: any;
|
||||||
|
cronPattern: string;
|
||||||
|
priority?: number;
|
||||||
|
description?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ProviderConfig {
|
export interface ProviderConfig {
|
||||||
name: string;
|
name: string;
|
||||||
service: string;
|
service: string;
|
||||||
operations: Record<string, JobHandler>;
|
operations: Record<string, JobHandler>;
|
||||||
|
scheduledJobs?: ScheduledJob[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ProviderRegistry {
|
export class ProviderRegistry {
|
||||||
|
|
@ -16,12 +26,12 @@ export class ProviderRegistry {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register a provider with its operations
|
* Register a provider with its operations
|
||||||
*/
|
*/ registerProvider(config: ProviderConfig): void {
|
||||||
registerProvider(config: ProviderConfig): void {
|
|
||||||
const key = `${config.service}:${config.name}`;
|
const key = `${config.service}:${config.name}`;
|
||||||
this.providers.set(key, config);
|
this.providers.set(key, config);
|
||||||
this.logger.info(`Registered provider: ${key}`, {
|
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
|
* 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 }> {
|
getProviders(): Array<{ key: string; config: ProviderConfig }> {
|
||||||
return Array.from(this.providers.entries()).map(([key, config]) => ({
|
return Array.from(this.providers.entries()).map(([key, config]) => ({
|
||||||
key,
|
key,
|
||||||
|
|
|
||||||
|
|
@ -140,46 +140,50 @@ export class QueueService {
|
||||||
this.logger.debug('Job progress', { id: job.id, progress });
|
this.logger.debug('Job progress', { id: job.id, progress });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async setupScheduledTasks() {
|
private async setupScheduledTasks() {
|
||||||
try {
|
try {
|
||||||
// Market data refresh every minute using Yahoo Finance
|
this.logger.info('Setting up scheduled tasks from providers...');
|
||||||
await this.addRecurringJob({
|
|
||||||
type: 'market-data-refresh',
|
// Get all scheduled jobs from all providers
|
||||||
service: 'market-data',
|
const allScheduledJobs = providerRegistry.getAllScheduledJobs();
|
||||||
provider: 'yahoo-finance',
|
|
||||||
operation: 'live-data',
|
if (allScheduledJobs.length === 0) {
|
||||||
payload: { symbol: 'AAPL' }
|
this.logger.warn('No scheduled jobs found in providers');
|
||||||
}, '*/1 * * * *');
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Market data refresh using QuoteMedia every 2 minutes
|
// Register each scheduled job
|
||||||
await this.addRecurringJob({
|
for (const { service, provider, job } of allScheduledJobs) {
|
||||||
type: 'market-data-quotemedia',
|
try {
|
||||||
service: 'market-data',
|
await this.addRecurringJob({
|
||||||
provider: 'quotemedia',
|
type: job.type,
|
||||||
operation: 'batch-quotes',
|
service: service,
|
||||||
payload: { symbols: ['GOOGL', 'MSFT', 'TSLA'] }
|
provider: provider,
|
||||||
}, '*/2 * * * *');
|
operation: job.operation,
|
||||||
|
payload: job.payload,
|
||||||
|
priority: job.priority
|
||||||
|
}, job.cronPattern);
|
||||||
|
|
||||||
// Proxy fetch every 15 minutes
|
this.logger.info('Scheduled job registered', {
|
||||||
await this.addRecurringJob({
|
type: job.type,
|
||||||
type: 'proxy-maintenance',
|
service,
|
||||||
service: 'proxy',
|
provider,
|
||||||
provider: 'proxy-service',
|
operation: job.operation,
|
||||||
operation: 'fetch-and-check',
|
cronPattern: job.cronPattern,
|
||||||
payload: {}
|
description: job.description
|
||||||
}, '*/15 * * * *');
|
});
|
||||||
|
} 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
|
this.logger.info(`Successfully configured ${allScheduledJobs.length} scheduled tasks`);
|
||||||
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');
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error('Failed to setup scheduled tasks', error);
|
this.logger.error('Failed to setup scheduled tasks', error);
|
||||||
}
|
}
|
||||||
|
|
@ -256,13 +260,26 @@ export class QueueService {
|
||||||
}
|
}
|
||||||
return this.worker.opts.concurrency || 1;
|
return this.worker.opts.concurrency || 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
getRegisteredProviders() {
|
getRegisteredProviders() {
|
||||||
return providerRegistry.getProviders().map(({ key, config }) => ({
|
return providerRegistry.getProviders().map(({ key, config }) => ({
|
||||||
key,
|
key,
|
||||||
name: config.name,
|
name: config.name,
|
||||||
service: config.service,
|
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