UNPKG

recoder-code

Version:

🚀 AI-powered development platform - Chat with 32+ models, build projects, automate workflows. Free models included!

229 lines (198 loc) • 6.64 kB
/** * Queue Service - Handles background job processing */ import Bull, { Job } from 'bull'; import { Package } from '../entities/Package'; import { PackageVersion } from '../entities/PackageVersion'; import { User } from '../entities/User'; export interface JobData { type: string; payload: Record<string, any>; priority?: number; delay?: number; attempts?: number; } export class QueueService { private queues: Map<string, Bull.Queue> = new Map(); private logger = { info: (msg: string, ...args: any[]) => console.log(`[INFO] ${msg}`, ...args), error: (msg: string, ...args: any[]) => console.error(`[ERROR] ${msg}`, ...args), warn: (msg: string, ...args: any[]) => console.warn(`[WARN] ${msg}`, ...args) }; constructor() { this.initializeQueues(); } private initializeQueues(): void { // Initialize different queues for different job types const queueNames = [ 'package-processing', 'notifications', 'security-scanning', 'analytics', 'cleanup' ]; queueNames.forEach(name => { const queue = new Bull(name, { redis: { host: process.env.REDIS_HOST || 'localhost', port: parseInt(process.env.REDIS_PORT || '6379'), password: process.env.REDIS_PASSWORD }, defaultJobOptions: { removeOnComplete: 10, removeOnFail: 5, attempts: 3, backoff: { type: 'exponential', delay: 2000 } } }); this.queues.set(name, queue); this.setupJobProcessors(name, queue); }); } private setupJobProcessors(queueName: string, queue: Bull.Queue): void { queue.process('*', async (job: Job) => { try { this.logger.info(`Processing job ${job.data.type} in queue ${queueName}`); switch (job.data.type) { case 'package-published': await this.processPackagePublished(job.data); break; case 'package-updated': await this.processPackageUpdated(job.data); break; case 'security-scan': await this.processSecurityScan(job.data); break; case 'analytics-update': await this.processAnalyticsUpdate(job.data); break; case 'cleanup-temp-files': await this.processCleanupTempFiles(job.data); break; default: this.logger.warn(`Unknown job type: ${job.data.type}`); } this.logger.info(`Completed job ${job.data.type} in queue ${queueName}`); } catch (error) { this.logger.error(`Failed to process job ${job.data.type}:`, error); throw error; } }); queue.on('completed', (job: Job) => { this.logger.info(`Job ${job.id} completed in queue ${queueName}`); }); queue.on('failed', (job: Job, err: Error) => { this.logger.error(`Job ${job.id} failed in queue ${queueName}:`, err); }); } async addJob(queueName: string, jobType: string, payload: Record<string, any>, options?: { priority?: number; delay?: number; attempts?: number; }): Promise<Job> { const queue = this.queues.get(queueName); if (!queue) { throw new Error(`Queue ${queueName} not found`); } const jobData: JobData = { type: jobType, payload, ...options }; const job = await queue.add(jobType, jobData, { priority: options?.priority, delay: options?.delay, attempts: options?.attempts }); this.logger.info(`Added job ${jobType} to queue ${queueName} with ID ${job.id}`); return job; } // Specific job processors private async processPackagePublished(data: any): Promise<void> { // Process package publication // - Send notifications // - Update search indexes // - Update statistics // - Trigger security scans this.logger.info(`Processing package published: ${data.packageName}@${data.version}`); } private async processPackageUpdated(data: any): Promise<void> { // Process package updates // - Send notifications to followers // - Update search indexes // - Check for breaking changes this.logger.info(`Processing package updated: ${data.packageName}@${data.version}`); } private async processSecurityScan(data: any): Promise<void> { // Process security scanning // - Run security scanners // - Check for vulnerabilities // - Send alerts if issues found this.logger.info(`Processing security scan: ${data.packageName}@${data.version}`); } private async processAnalyticsUpdate(data: any): Promise<void> { // Process analytics updates // - Update download counts // - Calculate popularity metrics // - Update trends this.logger.info(`Processing analytics update: ${data.type}`); } private async processCleanupTempFiles(data: any): Promise<void> { // Clean up temporary files // - Remove old upload files // - Clean cache files // - Remove expired tokens this.logger.info(`Processing cleanup: ${data.type}`); } async getQueueStats(queueName: string): Promise<{ waiting: number; active: number; completed: number; failed: number; delayed: number; }> { const queue = this.queues.get(queueName); if (!queue) { throw new Error(`Queue ${queueName} not found`); } const [waiting, active, completed, failed, delayed] = await Promise.all([ queue.getWaiting(), queue.getActive(), queue.getCompleted(), queue.getFailed(), queue.getDelayed() ]); return { waiting: waiting.length, active: active.length, completed: completed.length, failed: failed.length, delayed: delayed.length }; } async pauseQueue(queueName: string): Promise<void> { const queue = this.queues.get(queueName); if (!queue) { throw new Error(`Queue ${queueName} not found`); } await queue.pause(); this.logger.info(`Paused queue ${queueName}`); } async resumeQueue(queueName: string): Promise<void> { const queue = this.queues.get(queueName); if (!queue) { throw new Error(`Queue ${queueName} not found`); } await queue.resume(); this.logger.info(`Resumed queue ${queueName}`); } async shutdown(): Promise<void> { this.logger.info('Shutting down queue service...'); const closePromises = Array.from(this.queues.values()).map(queue => queue.close()); await Promise.all(closePromises); this.logger.info('Queue service shut down successfully'); } }