@cloudnux/cli 0.11.0 → 0.15.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/.deploy/aws/scheduler/main.tf +1 -1
- package/.deploy/aws/scheduler/variables.tf +2 -2
- package/.deploy/aws/websocket/main.tf +49 -0
- package/.deploy/aws/websocket/variables.tf +22 -0
- package/dist/cli.mjs +2841 -370
- package/dist/index.d.ts +5 -0
- package/dist/index.mjs +2950 -0
- package/dist/schema/entrypoint.schema.json +16 -3
- package/dist/templates/cloud/entrypoint-build.ts.ejs +4 -18
- package/dist/templates/cloud/entrypoint-triggers.tf.ejs +98 -51
- package/dist/templates/local/dev-server.ts.ejs +8 -14
- package/dist/templates/local/module.ts.ejs +4 -3
- package/dist/types.d.ts +10 -11
- package/package.json +12 -11
- package/dist/templates/cloud/WIP.ts.ejs +0 -564
- package/dist/templates/local/triggers/event.ts.ejs +0 -7
- package/dist/templates/local/triggers/http.ts.ejs +0 -11
- package/dist/templates/local/triggers/schedule.ts.ejs +0 -7
|
@@ -1,564 +0,0 @@
|
|
|
1
|
-
import fs from "fs/promises";
|
|
2
|
-
import path from "path";
|
|
3
|
-
import chalk from "chalk";
|
|
4
|
-
import consola from "consola";
|
|
5
|
-
import logSymbols from "log-symbols";
|
|
6
|
-
import Fastify, { FastifyInstance } from "fastify";
|
|
7
|
-
|
|
8
|
-
import {
|
|
9
|
-
createEventContext,
|
|
10
|
-
createScheduleContext,
|
|
11
|
-
} from "@anubis/cloud-provider";
|
|
12
|
-
<% for(const component of components){ %>
|
|
13
|
-
import {
|
|
14
|
-
entries as <%=component%>Entries,
|
|
15
|
-
registeredEventHandlers as <%=component%>EventHandlers
|
|
16
|
-
}
|
|
17
|
-
from "./<%=component%>/entrypoint.ts" <% }
|
|
18
|
-
%>
|
|
19
|
-
|
|
20
|
-
function logEntryURL(method: string, routePath: string){
|
|
21
|
-
const bgcolors = {
|
|
22
|
-
PUT : chalk.bold.bgYellow.white,
|
|
23
|
-
POST : chalk.bold.bgGreen.white,
|
|
24
|
-
DELETE : chalk.bold.bgRed.white,
|
|
25
|
-
GET : chalk.bold.bgBlue.white,
|
|
26
|
-
PATCH : chalk.bold.bgGreenBright.white
|
|
27
|
-
};
|
|
28
|
-
const colors = {
|
|
29
|
-
PUT : chalk.bold.yellow,
|
|
30
|
-
POST : chalk.bold.green,
|
|
31
|
-
DELETE : chalk.bold.red,
|
|
32
|
-
GET : chalk.bold.blue,
|
|
33
|
-
PATCH : chalk.bold.greenBright
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
const prefix = new Array(Math.floor((6 - method.length) / 2)).fill(" ").join("");
|
|
37
|
-
const postfix = new Array(6 - method.length - prefix.length).fill(" ").join("");
|
|
38
|
-
|
|
39
|
-
consola.log(
|
|
40
|
-
logSymbols.info + bgcolors[method](`[${prefix}${method}${postfix}]`) + colors[method](` http://localhost:3000${routePath}`)
|
|
41
|
-
);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
async function queue(app: FastifyInstance) {
|
|
45
|
-
|
|
46
|
-
consola.log(chalk.underline(`\n\rUrls you have for`, chalk.bold(`Queues`)));
|
|
47
|
-
|
|
48
|
-
const queues = {};
|
|
49
|
-
|
|
50
|
-
const config = {
|
|
51
|
-
batchSize: 10, // Max number of messages to collect before processing
|
|
52
|
-
batchWindowMs: 500, // Max wait time before processing collected messages
|
|
53
|
-
maxRetries: 3, // Maximum retry attempts
|
|
54
|
-
parallel: true, // Process messages in parallel or sequentially
|
|
55
|
-
maxConcurrent: 5, // Max concurrent processing if parallel is true
|
|
56
|
-
retryBackoff: true, // Use exponential backoff for retries
|
|
57
|
-
persistence: {
|
|
58
|
-
enabled: true, // Enable persistence
|
|
59
|
-
directory: './.develop/queue-data', // Directory to store queue data
|
|
60
|
-
saveInterval: 60000, // Save queues every minute (in ms)
|
|
61
|
-
//TODO: we have issue with esbuild watch if saveonShutdown is true
|
|
62
|
-
saveOnShutdown: false, // Save queues on process shutdown
|
|
63
|
-
loadOnStartup: true // Load queues on startup
|
|
64
|
-
}
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
<%_ for(const component of components) { _%>
|
|
68
|
-
Object.entries(<%=component%>EventHandlers).forEach(([queueName, handler]) => {
|
|
69
|
-
queues[queueName] = {
|
|
70
|
-
handler,
|
|
71
|
-
incoming: [],
|
|
72
|
-
processing: [],
|
|
73
|
-
dlq: [],
|
|
74
|
-
timeoutId: null, // Batch window timeout ID
|
|
75
|
-
processingBatch: false, // Flag to prevent concurrent batch processing
|
|
76
|
-
activeProcessing: 0 // Counter for active concurrent processing
|
|
77
|
-
}
|
|
78
|
-
});
|
|
79
|
-
<%_ } _%>
|
|
80
|
-
|
|
81
|
-
// Initialize persistence
|
|
82
|
-
if (config.persistence.enabled) {
|
|
83
|
-
await initializePersistence();
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// Main batch processing function
|
|
87
|
-
async function processBatch(queueName) {
|
|
88
|
-
const queueService = queues[queueName];
|
|
89
|
-
|
|
90
|
-
// Clear timeout to prevent double processing
|
|
91
|
-
if (queueService.timeoutId) {
|
|
92
|
-
clearTimeout(queueService.timeoutId);
|
|
93
|
-
queueService.timeoutId = null;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// If already processing, reschedule
|
|
97
|
-
if (queueService.processingBatch) {
|
|
98
|
-
scheduleProcessing(queueName);
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
try {
|
|
103
|
-
queueService.processingBatch = true;
|
|
104
|
-
|
|
105
|
-
// Move messages from incoming to processing (up to batchSize)
|
|
106
|
-
const messagesToProcess = queueService.incoming.splice(0, config.batchSize);
|
|
107
|
-
|
|
108
|
-
if (messagesToProcess.length === 0) {
|
|
109
|
-
queueService.processingBatch = false;
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// Add to processing queue
|
|
114
|
-
queueService.processing.push(...messagesToProcess);
|
|
115
|
-
|
|
116
|
-
// Process sequentially
|
|
117
|
-
for (const message of messagesToProcess) {
|
|
118
|
-
await processMessage(queueName, message);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
if (queueService.incoming.length > 0) {
|
|
122
|
-
scheduleProcessing(queueName, 0); // Process immediately
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// Save queue state after batch processing
|
|
126
|
-
if (config.persistence.enabled) {
|
|
127
|
-
saveQueueState(queueName);
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
finally {
|
|
131
|
-
queueService.processingBatch = false;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// Process an individual message
|
|
136
|
-
async function processMessage(queueName, message) {
|
|
137
|
-
const queueService = queues[queueName];
|
|
138
|
-
|
|
139
|
-
try {
|
|
140
|
-
const context = createEventContext(
|
|
141
|
-
message.payload,
|
|
142
|
-
message.attributes,
|
|
143
|
-
message.id,
|
|
144
|
-
message.timestamp,
|
|
145
|
-
message.attempts);
|
|
146
|
-
|
|
147
|
-
await queueService.handler(context);
|
|
148
|
-
|
|
149
|
-
if(context.response.status === "error") {
|
|
150
|
-
throw new Error(context.response.body);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// Remove from processing queue on success
|
|
154
|
-
queueService.processing = queueService.processing.filter(
|
|
155
|
-
msg => msg.id !== message.id
|
|
156
|
-
);
|
|
157
|
-
|
|
158
|
-
console.log(`Successfully processed message ${message.id} in queue ${queueName} with response `,context.response.body);
|
|
159
|
-
} catch (error) {
|
|
160
|
-
console.error(`Error processing message ${message.id} in queue ${queueName}:`, error);
|
|
161
|
-
|
|
162
|
-
if (message.attempts >= config.maxRetries) {
|
|
163
|
-
// Move to DLQ after max retries
|
|
164
|
-
queueService.dlq.push({
|
|
165
|
-
...message,
|
|
166
|
-
error: error.message,
|
|
167
|
-
failedAt: new Date()
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
// Remove from processing
|
|
171
|
-
queueService.processing = queueService.processing.filter(
|
|
172
|
-
msg => msg.id !== message.id
|
|
173
|
-
);
|
|
174
|
-
|
|
175
|
-
// Save DLQ state after message moves to DLQ
|
|
176
|
-
if (config.persistence.enabled) {
|
|
177
|
-
saveQueueState(queueName);
|
|
178
|
-
}
|
|
179
|
-
} else {
|
|
180
|
-
// Increment attempt counter
|
|
181
|
-
const index = queueService.processing.findIndex(m => m.id === message.id);
|
|
182
|
-
if (index >= 0) {
|
|
183
|
-
queueService.processing[index].attempts += 1;
|
|
184
|
-
|
|
185
|
-
// Add backoff delay based on attempt count
|
|
186
|
-
if (config.retryBackoff) {
|
|
187
|
-
const delayMs = Math.pow(2, queueService.processing[index].attempts) * 100;
|
|
188
|
-
queueService.processing[index].nextAttempt = new Date(Date.now() + delayMs);
|
|
189
|
-
|
|
190
|
-
// Schedule retry after backoff
|
|
191
|
-
setTimeout(() => {
|
|
192
|
-
processMessage(queueName, queueService.processing[index]);
|
|
193
|
-
}, delayMs);
|
|
194
|
-
} else {
|
|
195
|
-
// Retry immediately if no backoff
|
|
196
|
-
processMessage(queueName, queueService.processing[index]);
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// Schedule batch processing after a delay
|
|
204
|
-
function scheduleProcessing(queueName, overrideDelay = null) {
|
|
205
|
-
const queueService = queues[queueName];
|
|
206
|
-
|
|
207
|
-
if (!queueService.timeoutId) {
|
|
208
|
-
const delay = overrideDelay !== null ? overrideDelay : config.batchWindowMs;
|
|
209
|
-
queueService.timeoutId = setTimeout(() => {
|
|
210
|
-
processBatch(queueName);
|
|
211
|
-
}, delay);
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
// Initialize persistence system
|
|
216
|
-
async function initializePersistence() {
|
|
217
|
-
try {
|
|
218
|
-
// Create persistence directory if it doesn't exist
|
|
219
|
-
await fs.mkdir(config.persistence.directory, { recursive: true });
|
|
220
|
-
|
|
221
|
-
// Load queue data if enabled
|
|
222
|
-
if (config.persistence.loadOnStartup) {
|
|
223
|
-
await loadAllQueueStates();
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
// Set up interval to regularly save queue states
|
|
227
|
-
if (config.persistence.saveInterval > 0) {
|
|
228
|
-
setInterval(saveAllQueueStates, config.persistence.saveInterval);
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
// Set up process shutdown handler
|
|
232
|
-
if (config.persistence.saveOnShutdown) {
|
|
233
|
-
process.on('SIGINT', async () => {
|
|
234
|
-
console.log('Saving queue state before shutdown...');
|
|
235
|
-
await saveAllQueueStates();
|
|
236
|
-
process.exit(0);
|
|
237
|
-
});
|
|
238
|
-
|
|
239
|
-
process.on('SIGTERM', async () => {
|
|
240
|
-
console.log('Saving queue state before shutdown...');
|
|
241
|
-
await saveAllQueueStates();
|
|
242
|
-
process.exit(0);
|
|
243
|
-
});
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
console.log(`Queue persistence initialized: ${config.persistence.directory}`);
|
|
247
|
-
} catch (error) {
|
|
248
|
-
console.error('Failed to initialize queue persistence:', error);
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
// Save state for all queues
|
|
253
|
-
async function saveAllQueueStates() {
|
|
254
|
-
try {
|
|
255
|
-
for (const queueName of Object.keys(queues)) {
|
|
256
|
-
await saveQueueState(queueName);
|
|
257
|
-
}
|
|
258
|
-
console.log(`All queue states saved to ${config.persistence.directory}`);
|
|
259
|
-
} catch (error) {
|
|
260
|
-
console.error('Failed to save all queue states:', error);
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
// Save state for a specific queue
|
|
265
|
-
async function saveQueueState(queueName) {
|
|
266
|
-
try {
|
|
267
|
-
const queueService = queues[queueName];
|
|
268
|
-
if (!queueService) return;
|
|
269
|
-
|
|
270
|
-
const queueData = {
|
|
271
|
-
incoming: queueService.incoming,
|
|
272
|
-
processing: queueService.processing,
|
|
273
|
-
dlq: queueService.dlq,
|
|
274
|
-
savedAt: new Date()
|
|
275
|
-
};
|
|
276
|
-
|
|
277
|
-
// Use a temporary file to avoid corruption if the process crashes during write
|
|
278
|
-
const queueFilePath = path.join(config.persistence.directory, `${queueName}.json`);
|
|
279
|
-
const tempFilePath = path.join(config.persistence.directory, `${queueName}.temp.json`);
|
|
280
|
-
|
|
281
|
-
// Write to temporary file first
|
|
282
|
-
await fs.writeFile(tempFilePath, JSON.stringify(queueData, null, 2), 'utf8');
|
|
283
|
-
|
|
284
|
-
// Rename temporary file to the actual file (atomic operation)
|
|
285
|
-
await fs.rename(tempFilePath, queueFilePath);
|
|
286
|
-
|
|
287
|
-
console.log(`Queue state saved: ${queueName}`);
|
|
288
|
-
} catch (error) {
|
|
289
|
-
console.error(`Failed to save queue state for ${queueName}:`, error);
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
async function loadAllQueueStates() {
|
|
294
|
-
try {
|
|
295
|
-
const files = await fs.readdir(config.persistence.directory);
|
|
296
|
-
const queueFiles = files.filter(file => file.endsWith('.json') && !file.includes('.temp.'));
|
|
297
|
-
|
|
298
|
-
for (const file of queueFiles) {
|
|
299
|
-
const queueName = path.basename(file, '.json');
|
|
300
|
-
await loadQueueState(queueName);
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
console.log('All queue states loaded');
|
|
304
|
-
} catch (error) {
|
|
305
|
-
console.error('Failed to load queue states:', error);
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
// Load state for a specific queue
|
|
310
|
-
async function loadQueueState(queueName) {
|
|
311
|
-
try {
|
|
312
|
-
// Skip if queue doesn't exist
|
|
313
|
-
if (!queues[queueName]) {
|
|
314
|
-
console.warn(`Skipping load for non-existent queue: ${queueName}`);
|
|
315
|
-
return;
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
const queueFilePath = path.join(config.persistence.directory, `${queueName}.json`);
|
|
319
|
-
|
|
320
|
-
try {
|
|
321
|
-
const data = await fs.readFile(queueFilePath, 'utf8');
|
|
322
|
-
const queueData = JSON.parse(data);
|
|
323
|
-
|
|
324
|
-
// Restore date objects (JSON.parse doesn't restore dates)
|
|
325
|
-
queueData.incoming.forEach(msg => {
|
|
326
|
-
msg.timestamp = new Date(msg.timestamp);
|
|
327
|
-
if (msg.nextAttempt) msg.nextAttempt = new Date(msg.nextAttempt);
|
|
328
|
-
});
|
|
329
|
-
|
|
330
|
-
queueData.processing.forEach(msg => {
|
|
331
|
-
msg.timestamp = new Date(msg.timestamp);
|
|
332
|
-
if (msg.nextAttempt) msg.nextAttempt = new Date(msg.nextAttempt);
|
|
333
|
-
});
|
|
334
|
-
|
|
335
|
-
queueData.dlq.forEach(msg => {
|
|
336
|
-
msg.timestamp = new Date(msg.timestamp);
|
|
337
|
-
if (msg.failedAt) msg.failedAt = new Date(msg.failedAt);
|
|
338
|
-
if (msg.nextAttempt) msg.nextAttempt = new Date(msg.nextAttempt);
|
|
339
|
-
});
|
|
340
|
-
|
|
341
|
-
// Restore queue state
|
|
342
|
-
queues[queueName].incoming = queueData.incoming;
|
|
343
|
-
queues[queueName].processing = queueData.processing;
|
|
344
|
-
queues[queueName].dlq = queueData.dlq;
|
|
345
|
-
|
|
346
|
-
console.log(`Queue state loaded: ${queueName} (saved at ${queueData.savedAt})`);
|
|
347
|
-
|
|
348
|
-
// Schedule processing for any messages that were in flight
|
|
349
|
-
if (queues[queueName].incoming.length > 0) {
|
|
350
|
-
scheduleProcessing(queueName);
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
// Re-process any messages that were in the processing queue
|
|
354
|
-
if (queues[queueName].processing.length > 0) {
|
|
355
|
-
for (const message of [...queues[queueName].processing]) {
|
|
356
|
-
// Skip processing if message has exceeded max retries
|
|
357
|
-
if (message.attempts >= config.maxRetries) continue;
|
|
358
|
-
|
|
359
|
-
// Process with a slight delay to avoid overwhelming the system on startup
|
|
360
|
-
setTimeout(() => {
|
|
361
|
-
processMessage(queueName, message);
|
|
362
|
-
}, 100 * (Math.random() * 10));
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
} catch (error) {
|
|
366
|
-
if (error.code === 'ENOENT') {
|
|
367
|
-
console.log(`No saved state found for queue: ${queueName}`);
|
|
368
|
-
} else {
|
|
369
|
-
throw error;
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
} catch (error) {
|
|
373
|
-
console.error(`Failed to load queue state for ${queueName}:`, error);
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
logEntryURL("GET","/queues/dashboard");
|
|
378
|
-
app.get("/dashboard", function(request, reply) {
|
|
379
|
-
const summary = {};
|
|
380
|
-
|
|
381
|
-
for (const [queueName, queue] of Object.entries(queues)) {
|
|
382
|
-
summary[queueName] = {
|
|
383
|
-
incoming: queue.incoming.length,
|
|
384
|
-
processing: queue.processing.length,
|
|
385
|
-
dlq: queue.dlq.length,
|
|
386
|
-
isProcessing: queue.processingBatch,
|
|
387
|
-
activeProcessing: queue.activeProcessing,
|
|
388
|
-
configuration: {
|
|
389
|
-
batchSize: config.batchSize,
|
|
390
|
-
batchWindowMs: config.batchWindowMs,
|
|
391
|
-
parallel: config.parallel,
|
|
392
|
-
maxConcurrent: config.maxConcurrent
|
|
393
|
-
}
|
|
394
|
-
};
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
return reply
|
|
398
|
-
.status(200)
|
|
399
|
-
.send(summary);
|
|
400
|
-
});
|
|
401
|
-
|
|
402
|
-
for(const q of Object.keys(queues))
|
|
403
|
-
logEntryURL("GET",`/queues/${q}`);
|
|
404
|
-
app.get("/:queue", function(request, reply) {
|
|
405
|
-
|
|
406
|
-
const queueName = request.params.queue;
|
|
407
|
-
|
|
408
|
-
if(!queues[queueName]) {
|
|
409
|
-
return reply
|
|
410
|
-
.status(404)
|
|
411
|
-
.send({ error: "Queue not found" });
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
const queue = queues[queueName];
|
|
415
|
-
console.log("[queue]", queue.dlq);
|
|
416
|
-
return reply
|
|
417
|
-
.status(200)
|
|
418
|
-
.send({
|
|
419
|
-
stats: {
|
|
420
|
-
incoming: queue.incoming.length,
|
|
421
|
-
processing: queue.processing.length,
|
|
422
|
-
dlq: queue.dlq.length,
|
|
423
|
-
isProcessing: queue.processingBatch,
|
|
424
|
-
activeProcessing: queue.activeProcessing
|
|
425
|
-
},
|
|
426
|
-
messages: {
|
|
427
|
-
incoming: queue.incoming,
|
|
428
|
-
processing: queue.processing,
|
|
429
|
-
dlq: queue.dlq
|
|
430
|
-
}
|
|
431
|
-
});
|
|
432
|
-
});
|
|
433
|
-
|
|
434
|
-
for(const q of Object.keys(queues))
|
|
435
|
-
logEntryURL("POST",`/queues/${q}`);
|
|
436
|
-
app.post("/:queue", function(request, reply) {
|
|
437
|
-
|
|
438
|
-
const queueName = request.params.queue;
|
|
439
|
-
|
|
440
|
-
if(!queues[queueName]) {
|
|
441
|
-
return reply
|
|
442
|
-
.status(404)
|
|
443
|
-
.send({ error: "Queue not found" });
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
const id = Date.now().toString() + Math.random().toString(36).substring(2, 7);
|
|
447
|
-
|
|
448
|
-
const message = {
|
|
449
|
-
id,
|
|
450
|
-
timestamp: new Date(),
|
|
451
|
-
attempts: 0,
|
|
452
|
-
payload: request.body,
|
|
453
|
-
attributes: request.headers,
|
|
454
|
-
};
|
|
455
|
-
|
|
456
|
-
queues[queueName].incoming.push(message);
|
|
457
|
-
|
|
458
|
-
// Schedule processing if not already scheduled
|
|
459
|
-
scheduleProcessing(queueName);
|
|
460
|
-
|
|
461
|
-
// Trigger immediate processing if we've reached batch size
|
|
462
|
-
if (queues[queueName].incoming.length >= config.batchSize) {
|
|
463
|
-
clearTimeout(queues[queueName].timeoutId);
|
|
464
|
-
queues[queueName].timeoutId = null;
|
|
465
|
-
setImmediate(() => processBatch(queueName));
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
// Save queue state after adding new message
|
|
469
|
-
if (config.persistence.enabled) {
|
|
470
|
-
saveQueueState(queueName);
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
return reply
|
|
474
|
-
.status(200)
|
|
475
|
-
.send({ id, queueName });
|
|
476
|
-
});
|
|
477
|
-
|
|
478
|
-
for(const q of Object.keys(queues))
|
|
479
|
-
logEntryURL("GET",`/queues/${q}/process-dlq`);
|
|
480
|
-
app.get("/:queue/process-dlq", function(request, reply) {
|
|
481
|
-
|
|
482
|
-
const queueName = request.params.queue;
|
|
483
|
-
|
|
484
|
-
if(!queues[queueName]) {
|
|
485
|
-
return reply
|
|
486
|
-
.status(404)
|
|
487
|
-
.send({ error: "Queue not found" });
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
const queue = queues[queueName];
|
|
491
|
-
// Get count of messages before processing
|
|
492
|
-
const dlqCount = queue.dlq.length;
|
|
493
|
-
|
|
494
|
-
if (dlqCount === 0) {
|
|
495
|
-
return reply
|
|
496
|
-
.status(200)
|
|
497
|
-
.send({
|
|
498
|
-
status: "success",
|
|
499
|
-
message: "No messages in DLQ to process",
|
|
500
|
-
processed: 0
|
|
501
|
-
});
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
// Reset attempt counters and move messages from DLQ to incoming queue
|
|
505
|
-
const movedMessages = queue.dlq.map(message => ({
|
|
506
|
-
...message,
|
|
507
|
-
attempts: 0, // Reset attempts counter
|
|
508
|
-
error: undefined, // Clear error message
|
|
509
|
-
failedAt: undefined, // Clear failure timestamp
|
|
510
|
-
reprocessed: true, // Mark as reprocessed for tracking
|
|
511
|
-
originalId: message.id, // Keep original ID for reference
|
|
512
|
-
id: Date.now().toString() + Math.random().toString(36).substring(2, 7) // Create new ID
|
|
513
|
-
}));
|
|
514
|
-
|
|
515
|
-
queue.incoming.push(...movedMessages);
|
|
516
|
-
queue.dlq = []; // Clear the DLQ
|
|
517
|
-
|
|
518
|
-
// Schedule processing if not already scheduled
|
|
519
|
-
scheduleProcessing(queueName);
|
|
520
|
-
|
|
521
|
-
// Save queue state after adding new message
|
|
522
|
-
if (config.persistence.enabled) {
|
|
523
|
-
saveQueueState(queueName);
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
return reply
|
|
527
|
-
.status(200)
|
|
528
|
-
.send({
|
|
529
|
-
status: "success",
|
|
530
|
-
message: `Moved ${dlqCount} messages from DLQ to processing queue`,
|
|
531
|
-
processed: dlqCount
|
|
532
|
-
});
|
|
533
|
-
});
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
const fastify = Fastify({
|
|
537
|
-
maxParamLength : 1000,
|
|
538
|
-
logger: {
|
|
539
|
-
transport: {
|
|
540
|
-
target: 'pino-pretty',
|
|
541
|
-
options: {
|
|
542
|
-
colorize: true,
|
|
543
|
-
translateTime: 'HH:MM:ss Z',
|
|
544
|
-
ignore: 'pid,hostname',
|
|
545
|
-
},
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
});
|
|
549
|
-
|
|
550
|
-
// Register the fastify-raw-body plugin
|
|
551
|
-
fastify.register(require('fastify-raw-body'), {
|
|
552
|
-
field: 'rawBody',
|
|
553
|
-
encoding: 'utf8',
|
|
554
|
-
});
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
<% for(const component of components){ %>
|
|
558
|
-
fastify.register(<%=component%>Entries, { prefix: "api" });
|
|
559
|
-
<%}%>
|
|
560
|
-
|
|
561
|
-
fastify.register(queue, {prefix : "queues"});
|
|
562
|
-
|
|
563
|
-
fastify.listen({ port: 3000, host : "::" }).then((address) => {
|
|
564
|
-
});
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
<%# Template generated a single endpoint entry
|
|
2
|
-
input:
|
|
3
|
-
- entry : entry to be rendered
|
|
4
|
-
%>
|
|
5
|
-
app.route({
|
|
6
|
-
method: "<%= entry.trigger.options.method %>" as any,
|
|
7
|
-
url: `/http<%= $$convertRouteParamstoFastifyRouteTemplate(entry.trigger.options.route) %>`,
|
|
8
|
-
handler: async (request, reply) => {
|
|
9
|
-
await httpHandler(src["<%= entry.handler %>"], request, reply);
|
|
10
|
-
}
|
|
11
|
-
});
|