@arela/uploader 1.1.3 ā 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/worktrees/agent-multi-profile/.env.template +224 -0
- package/.claude/worktrees/agent-multi-profile/.prettierrc +13 -0
- package/.claude/worktrees/agent-multi-profile/README.md +405 -0
- package/.claude/worktrees/agent-multi-profile/package-lock.json +7096 -0
- package/.claude/worktrees/agent-multi-profile/package.json +78 -0
- package/.claude/worktrees/agent-multi-profile/scripts/cleanup-ds-store.js +109 -0
- package/.claude/worktrees/agent-multi-profile/scripts/cleanup-system-files.js +69 -0
- package/.claude/worktrees/agent-multi-profile/scripts/scoring-compare.js +243 -0
- package/.claude/worktrees/agent-multi-profile/scripts/scoring-phase4-check.js +96 -0
- package/.claude/worktrees/agent-multi-profile/scripts/tests/phase-7-features.test.js +415 -0
- package/.claude/worktrees/agent-multi-profile/scripts/tests/signal-handling.test.js +275 -0
- package/.claude/worktrees/agent-multi-profile/scripts/tests/smart-watch-integration.test.js +554 -0
- package/.claude/worktrees/agent-multi-profile/scripts/tests/watch-service-integration.test.js +584 -0
- package/.claude/worktrees/agent-multi-profile/src/commands/AgentCommand.js +229 -0
- package/.claude/worktrees/agent-multi-profile/src/commands/AgentInitCommand.js +316 -0
- package/.claude/worktrees/agent-multi-profile/src/commands/DatastageCommand.js +164 -0
- package/.claude/worktrees/agent-multi-profile/src/commands/GDriveSyncCommand.js +475 -0
- package/.claude/worktrees/agent-multi-profile/src/commands/IdentifyCommand.js +708 -0
- package/.claude/worktrees/agent-multi-profile/src/commands/PollWorkerCommand.js +169 -0
- package/.claude/worktrees/agent-multi-profile/src/commands/PropagateCommand.js +636 -0
- package/.claude/worktrees/agent-multi-profile/src/commands/PushCommand.js +743 -0
- package/.claude/worktrees/agent-multi-profile/src/commands/ScanCommand.js +722 -0
- package/.claude/worktrees/agent-multi-profile/src/commands/UploadCommand.js +587 -0
- package/.claude/worktrees/agent-multi-profile/src/commands/WatchCommand.js +1342 -0
- package/.claude/worktrees/agent-multi-profile/src/commands/WorkerCommand.js +337 -0
- package/.claude/worktrees/agent-multi-profile/src/config/config.js +862 -0
- package/.claude/worktrees/agent-multi-profile/src/document-type-shared.js +131 -0
- package/.claude/worktrees/agent-multi-profile/src/document-types/_pedimento-shared-extractors.js +348 -0
- package/.claude/worktrees/agent-multi-profile/src/document-types/doda-pdf.js +121 -0
- package/.claude/worktrees/agent-multi-profile/src/document-types/doda-xml.js +118 -0
- package/.claude/worktrees/agent-multi-profile/src/document-types/factura-inter-agencia.js +186 -0
- package/.claude/worktrees/agent-multi-profile/src/document-types/facturas-comerciales.js +233 -0
- package/.claude/worktrees/agent-multi-profile/src/document-types/pedimento-completo-xml.js +372 -0
- package/.claude/worktrees/agent-multi-profile/src/document-types/pedimento-completo.js +108 -0
- package/.claude/worktrees/agent-multi-profile/src/document-types/pedimento-simplificado.js +76 -0
- package/.claude/worktrees/agent-multi-profile/src/document-types/proforma.js +29 -0
- package/.claude/worktrees/agent-multi-profile/src/document-types/support-document.js +200 -0
- package/.claude/worktrees/agent-multi-profile/src/errors/ErrorHandler.js +278 -0
- package/.claude/worktrees/agent-multi-profile/src/errors/ErrorTypes.js +104 -0
- package/.claude/worktrees/agent-multi-profile/src/file-detection.js +338 -0
- package/.claude/worktrees/agent-multi-profile/src/index.js +890 -0
- package/.claude/worktrees/agent-multi-profile/src/scoring/db-matcher-adapter.js +98 -0
- package/.claude/worktrees/agent-multi-profile/src/scoring/matchers-seed.js +386 -0
- package/.claude/worktrees/agent-multi-profile/src/scoring/scoring-engine.js +251 -0
- package/.claude/worktrees/agent-multi-profile/src/services/AdvancedFilterService.js +505 -0
- package/.claude/worktrees/agent-multi-profile/src/services/AutoProcessingService.js +749 -0
- package/.claude/worktrees/agent-multi-profile/src/services/BenchmarkingService.js +381 -0
- package/.claude/worktrees/agent-multi-profile/src/services/DatabaseService.js +2173 -0
- package/.claude/worktrees/agent-multi-profile/src/services/DatastageApiService.js +240 -0
- package/.claude/worktrees/agent-multi-profile/src/services/ErrorMonitor.js +275 -0
- package/.claude/worktrees/agent-multi-profile/src/services/GoogleDriveService.js +217 -0
- package/.claude/worktrees/agent-multi-profile/src/services/LoggingService.js +649 -0
- package/.claude/worktrees/agent-multi-profile/src/services/MonitoringService.js +401 -0
- package/.claude/worktrees/agent-multi-profile/src/services/PerformanceOptimizer.js +511 -0
- package/.claude/worktrees/agent-multi-profile/src/services/PipelineApiService.js +274 -0
- package/.claude/worktrees/agent-multi-profile/src/services/PipelineJobRunner.js +389 -0
- package/.claude/worktrees/agent-multi-profile/src/services/ProfileManager.js +164 -0
- package/.claude/worktrees/agent-multi-profile/src/services/ReportingService.js +511 -0
- package/.claude/worktrees/agent-multi-profile/src/services/ScanApiService.js +775 -0
- package/.claude/worktrees/agent-multi-profile/src/services/SignalHandler.js +255 -0
- package/.claude/worktrees/agent-multi-profile/src/services/SmartWatchDatabaseService.js +527 -0
- package/.claude/worktrees/agent-multi-profile/src/services/WatchService.js +783 -0
- package/.claude/worktrees/agent-multi-profile/src/services/upload/ApiUploadService.js +676 -0
- package/.claude/worktrees/agent-multi-profile/src/services/upload/BaseUploadService.js +36 -0
- package/.claude/worktrees/agent-multi-profile/src/services/upload/MultiApiUploadService.js +233 -0
- package/.claude/worktrees/agent-multi-profile/src/services/upload/SupabaseUploadService.js +148 -0
- package/.claude/worktrees/agent-multi-profile/src/services/upload/UploadServiceFactory.js +100 -0
- package/.claude/worktrees/agent-multi-profile/src/utils/CleanupManager.js +262 -0
- package/.claude/worktrees/agent-multi-profile/src/utils/FileOperations.js +192 -0
- package/.claude/worktrees/agent-multi-profile/src/utils/FileSanitizer.js +99 -0
- package/.claude/worktrees/agent-multi-profile/src/utils/PathDetector.js +198 -0
- package/.claude/worktrees/agent-multi-profile/src/utils/PathNormalizer.js +274 -0
- package/.claude/worktrees/agent-multi-profile/src/utils/WatchEventHandler.js +522 -0
- package/.claude/worktrees/agent-multi-profile/supabase/migrations/001_create_initial_schema.sql +366 -0
- package/.claude/worktrees/agent-multi-profile/supabase/migrations/002_align_with_arela_api_schema.sql +145 -0
- package/.claude/worktrees/agent-multi-profile/tests/commands/IdentifyCommand.test.js +570 -0
- package/.claude/worktrees/agent-multi-profile/tests/commands/PropagateCommand.test.js +568 -0
- package/.claude/worktrees/agent-multi-profile/tests/commands/PushCommand.test.js +754 -0
- package/.claude/worktrees/agent-multi-profile/tests/commands/ScanCommand.test.js +382 -0
- package/.claude/worktrees/agent-multi-profile/tests/unit/PathAndTableNameGeneration.test.js +1211 -0
- package/.claude/worktrees/agent-multi-profile/tests/unit/factura-inter-agencia.test.js +218 -0
- package/.claude/worktrees/agent-multi-profile/tests/unit/pedimento-completo-xml-matcher.test.js +271 -0
- package/.claude/worktrees/agent-multi-profile/tests/unit/pedimento-simplificado-matcher.test.js +185 -0
- package/.claude/worktrees/agent-multi-profile/tests/unit/scoring-engine.test.js +221 -0
- package/README.md +65 -0
- package/package.json +1 -1
- package/src/commands/AgentCommand.js +210 -0
- package/src/commands/AgentInitCommand.js +316 -0
- package/src/commands/PollWorkerCommand.js +11 -322
- package/src/config/config.js +44 -6
- package/src/document-type-shared.js +1 -1
- package/src/document-types/pedimento-completo.js +8 -1
- package/src/file-detection.js +9 -0
- package/src/index.js +55 -0
- package/src/scoring/scoring-engine.js +1 -1
- package/src/services/LoggingService.js +35 -0
- package/src/services/PipelineApiService.js +7 -1
- package/src/services/PipelineJobRunner.js +389 -0
- package/src/services/ProfileManager.js +164 -0
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
import { Worker } from 'bullmq';
|
|
2
|
+
|
|
3
|
+
import logger from '../services/LoggingService.js';
|
|
4
|
+
|
|
5
|
+
import appConfig from '../config/config.js';
|
|
6
|
+
import ErrorHandler from '../errors/ErrorHandler.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Worker Command Handler
|
|
10
|
+
* Runs arela-uploader as a BullMQ worker, listening for jobs from the API
|
|
11
|
+
*
|
|
12
|
+
* This enables UI-controlled execution of:
|
|
13
|
+
* - scan: Filesystem scanning and file cataloging
|
|
14
|
+
* - identify: Document type detection (pedimento-simplificado)
|
|
15
|
+
* - propagate: Spread arela_path to related files
|
|
16
|
+
* - push: Upload files to storage
|
|
17
|
+
*/
|
|
18
|
+
export class WorkerCommand {
|
|
19
|
+
constructor() {
|
|
20
|
+
this.errorHandler = new ErrorHandler(logger);
|
|
21
|
+
this.workers = new Map();
|
|
22
|
+
this.isShuttingDown = false;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Execute the worker command
|
|
27
|
+
* @param {Object} options - Command options
|
|
28
|
+
* @param {string} options.queues - Comma-separated list of queues to listen to
|
|
29
|
+
* @param {number} options.concurrency - Number of concurrent jobs per queue
|
|
30
|
+
*/
|
|
31
|
+
async execute(options = {}) {
|
|
32
|
+
try {
|
|
33
|
+
// Load configuration
|
|
34
|
+
const redisConfig = appConfig.getRedisConfig();
|
|
35
|
+
const workerConfig = appConfig.getWorkerConfig();
|
|
36
|
+
|
|
37
|
+
// Determine which queues to listen to
|
|
38
|
+
const queueNames = options.queues
|
|
39
|
+
? options.queues.split(',').map((q) => q.trim())
|
|
40
|
+
: workerConfig.queues;
|
|
41
|
+
|
|
42
|
+
const concurrency =
|
|
43
|
+
parseInt(options.concurrency) || workerConfig.concurrency;
|
|
44
|
+
|
|
45
|
+
logger.info('š§ Starting Arela Worker');
|
|
46
|
+
logger.info(`š” Redis: ${redisConfig.host}:${redisConfig.port}`);
|
|
47
|
+
logger.info(`š Queues: ${queueNames.join(', ')}`);
|
|
48
|
+
logger.info(`ā” Concurrency: ${concurrency}`);
|
|
49
|
+
|
|
50
|
+
// Setup graceful shutdown handlers
|
|
51
|
+
this.#setupShutdownHandlers();
|
|
52
|
+
|
|
53
|
+
// Start workers for each queue
|
|
54
|
+
for (const queueName of queueNames) {
|
|
55
|
+
await this.#startWorker(queueName, redisConfig, concurrency);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
logger.success('\nā
Worker is running. Press Ctrl+C to stop.\n');
|
|
59
|
+
|
|
60
|
+
// Keep the process alive
|
|
61
|
+
await this.#keepAlive();
|
|
62
|
+
} catch (error) {
|
|
63
|
+
logger.error('ā Worker failed to start:', error.message);
|
|
64
|
+
throw error;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Start a worker for a specific queue
|
|
70
|
+
* @private
|
|
71
|
+
*/
|
|
72
|
+
async #startWorker(queueName, redisConfig, concurrency) {
|
|
73
|
+
const processor = this.#getProcessor(queueName);
|
|
74
|
+
|
|
75
|
+
if (!processor) {
|
|
76
|
+
logger.warn(`ā ļø No processor found for queue: ${queueName}`);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const worker = new Worker(queueName, processor, {
|
|
81
|
+
connection: redisConfig,
|
|
82
|
+
concurrency,
|
|
83
|
+
lockDuration: appConfig.getWorkerConfig().lockDuration,
|
|
84
|
+
stalledInterval: appConfig.getWorkerConfig().stalledInterval,
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// Event handlers
|
|
88
|
+
worker.on('completed', (job) => {
|
|
89
|
+
logger.success(`ā
Job ${job.id} completed on ${queueName}`);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
worker.on('failed', (job, err) => {
|
|
93
|
+
logger.error(`ā Job ${job?.id} failed on ${queueName}: ${err.message}`);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
worker.on('progress', (job, progress) => {
|
|
97
|
+
logger.info(`š Job ${job.id} progress: ${JSON.stringify(progress)}`);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
worker.on('error', (err) => {
|
|
101
|
+
logger.error(`š“ Worker error on ${queueName}: ${err.message}`);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
this.workers.set(queueName, worker);
|
|
105
|
+
logger.info(` ā Worker started for queue: ${queueName}`);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Get the processor function for a queue
|
|
110
|
+
* @private
|
|
111
|
+
*/
|
|
112
|
+
#getProcessor(queueName) {
|
|
113
|
+
const processors = {
|
|
114
|
+
'pipeline-scan': this.#processScanJob.bind(this),
|
|
115
|
+
'pipeline-identify': this.#processIdentifyJob.bind(this),
|
|
116
|
+
'pipeline-propagate': this.#processPropagateJob.bind(this),
|
|
117
|
+
'pipeline-push': this.#processPushJob.bind(this),
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
return processors[queueName];
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Process a scan job
|
|
125
|
+
* @private
|
|
126
|
+
*/
|
|
127
|
+
async #processScanJob(job) {
|
|
128
|
+
logger.info(`š Processing scan job ${job.id}`);
|
|
129
|
+
|
|
130
|
+
const { ScanCommand } = await import('./ScanCommand.js');
|
|
131
|
+
const scanCommand = new ScanCommand();
|
|
132
|
+
|
|
133
|
+
// Create progress callback for the job
|
|
134
|
+
const onProgress = (percent, message) => {
|
|
135
|
+
job.updateProgress({ percent, message });
|
|
136
|
+
job.log(message);
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
try {
|
|
140
|
+
// Extract options from job data
|
|
141
|
+
const options = {
|
|
142
|
+
api: job.data.apiTarget || 'default',
|
|
143
|
+
countFirst: job.data.countFirst || false,
|
|
144
|
+
stream: job.data.stream !== false,
|
|
145
|
+
// Pass progress callback
|
|
146
|
+
onProgress,
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
// Override scan config if provided in job
|
|
150
|
+
if (job.data.scanConfig) {
|
|
151
|
+
this.#overrideScanConfig(job.data.scanConfig);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const result = await scanCommand.execute(options);
|
|
155
|
+
return { success: true, ...result };
|
|
156
|
+
} catch (error) {
|
|
157
|
+
logger.error(`Scan job ${job.id} failed: ${error.message}`);
|
|
158
|
+
throw error;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Process an identify job
|
|
164
|
+
* @private
|
|
165
|
+
*/
|
|
166
|
+
async #processIdentifyJob(job) {
|
|
167
|
+
logger.info(`š Processing identify job ${job.id}`);
|
|
168
|
+
|
|
169
|
+
const { IdentifyCommand } = await import('./IdentifyCommand.js');
|
|
170
|
+
const identifyCommand = new IdentifyCommand();
|
|
171
|
+
|
|
172
|
+
const onProgress = (percent, message) => {
|
|
173
|
+
job.updateProgress({ percent, message });
|
|
174
|
+
job.log(message);
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
try {
|
|
178
|
+
const options = {
|
|
179
|
+
api: job.data.apiTarget || 'default',
|
|
180
|
+
batchSize: job.data.batchSize || 100,
|
|
181
|
+
showStats: job.data.showStats || false,
|
|
182
|
+
onProgress,
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
if (job.data.scanConfig) {
|
|
186
|
+
this.#overrideScanConfig(job.data.scanConfig);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const result = await identifyCommand.execute(options);
|
|
190
|
+
return { success: true, ...result };
|
|
191
|
+
} catch (error) {
|
|
192
|
+
logger.error(`Identify job ${job.id} failed: ${error.message}`);
|
|
193
|
+
throw error;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Process a propagate job
|
|
199
|
+
* @private
|
|
200
|
+
*/
|
|
201
|
+
async #processPropagateJob(job) {
|
|
202
|
+
logger.info(`š Processing propagate job ${job.id}`);
|
|
203
|
+
|
|
204
|
+
const { PropagateCommand } = await import('./PropagateCommand.js');
|
|
205
|
+
|
|
206
|
+
const onProgress = (percent, message) => {
|
|
207
|
+
job.updateProgress({ percent, message });
|
|
208
|
+
job.log(message);
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
try {
|
|
212
|
+
const options = {
|
|
213
|
+
api: job.data.apiTarget || 'default',
|
|
214
|
+
batchSize: job.data.batchSize || 50,
|
|
215
|
+
showStats: job.data.showStats || false,
|
|
216
|
+
onProgress,
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
if (job.data.scanConfig) {
|
|
220
|
+
this.#overrideScanConfig(job.data.scanConfig);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const propagateCommand = new PropagateCommand(options);
|
|
224
|
+
const result = await propagateCommand.execute();
|
|
225
|
+
return { success: true, ...result };
|
|
226
|
+
} catch (error) {
|
|
227
|
+
logger.error(`Propagate job ${job.id} failed: ${error.message}`);
|
|
228
|
+
throw error;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Process a push job
|
|
234
|
+
* @private
|
|
235
|
+
*/
|
|
236
|
+
async #processPushJob(job) {
|
|
237
|
+
logger.info(`š¤ Processing push job ${job.id}`);
|
|
238
|
+
|
|
239
|
+
const { PushCommand } = await import('./PushCommand.js');
|
|
240
|
+
const pushCommand = new PushCommand();
|
|
241
|
+
|
|
242
|
+
const onProgress = (percent, message) => {
|
|
243
|
+
job.updateProgress({ percent, message });
|
|
244
|
+
job.log(message);
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
try {
|
|
248
|
+
const options = {
|
|
249
|
+
api: job.data.apiTarget || 'default',
|
|
250
|
+
scanApi: job.data.scanApi,
|
|
251
|
+
pushApi: job.data.pushApi,
|
|
252
|
+
batchSize: job.data.batchSize || 100,
|
|
253
|
+
uploadBatchSize: job.data.uploadBatchSize || 10,
|
|
254
|
+
rfcs: job.data.rfcs,
|
|
255
|
+
years: job.data.years,
|
|
256
|
+
folderStructure: job.data.folderStructure,
|
|
257
|
+
autoOrganize: job.data.autoOrganize !== false,
|
|
258
|
+
showStats: job.data.showStats || false,
|
|
259
|
+
onProgress,
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
if (job.data.scanConfig) {
|
|
263
|
+
this.#overrideScanConfig(job.data.scanConfig);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const result = await pushCommand.execute(options);
|
|
267
|
+
return { success: true, ...result };
|
|
268
|
+
} catch (error) {
|
|
269
|
+
logger.error(`Push job ${job.id} failed: ${error.message}`);
|
|
270
|
+
throw error;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Override scan configuration from job data
|
|
276
|
+
* @private
|
|
277
|
+
*/
|
|
278
|
+
#overrideScanConfig(scanConfig) {
|
|
279
|
+
// Temporarily override environment variables for this job
|
|
280
|
+
if (scanConfig.companySlug) {
|
|
281
|
+
process.env.ARELA_COMPANY_SLUG = scanConfig.companySlug;
|
|
282
|
+
}
|
|
283
|
+
if (scanConfig.serverId) {
|
|
284
|
+
process.env.ARELA_SERVER_ID = scanConfig.serverId;
|
|
285
|
+
}
|
|
286
|
+
if (scanConfig.basePath) {
|
|
287
|
+
process.env.UPLOAD_BASE_PATH = scanConfig.basePath;
|
|
288
|
+
process.env.ARELA_BASE_PATH_LABEL = scanConfig.basePath;
|
|
289
|
+
}
|
|
290
|
+
if (scanConfig.directoryLevel !== undefined) {
|
|
291
|
+
process.env.SCAN_DIRECTORY_LEVEL = String(scanConfig.directoryLevel);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// Reload cached config from the updated env vars
|
|
295
|
+
appConfig.reloadScanConfig();
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Setup graceful shutdown handlers
|
|
300
|
+
* @private
|
|
301
|
+
*/
|
|
302
|
+
#setupShutdownHandlers() {
|
|
303
|
+
const shutdown = async (signal) => {
|
|
304
|
+
if (this.isShuttingDown) return;
|
|
305
|
+
this.isShuttingDown = true;
|
|
306
|
+
|
|
307
|
+
logger.info(`\nš Received ${signal}. Shutting down workers...`);
|
|
308
|
+
|
|
309
|
+
// Close all workers gracefully
|
|
310
|
+
const closePromises = [];
|
|
311
|
+
for (const [queueName, worker] of this.workers) {
|
|
312
|
+
logger.info(` ā³ Closing worker: ${queueName}`);
|
|
313
|
+
closePromises.push(worker.close());
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
await Promise.all(closePromises);
|
|
317
|
+
logger.info('ā
All workers closed gracefully');
|
|
318
|
+
process.exit(0);
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
process.on('SIGINT', () => shutdown('SIGINT'));
|
|
322
|
+
process.on('SIGTERM', () => shutdown('SIGTERM'));
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Keep the process alive while workers are running
|
|
327
|
+
* @private
|
|
328
|
+
*/
|
|
329
|
+
async #keepAlive() {
|
|
330
|
+
return new Promise((resolve) => {
|
|
331
|
+
// This promise never resolves, keeping the process alive
|
|
332
|
+
// The process will exit via the shutdown handlers
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
export default new WorkerCommand();
|