@motiadev/plugin-bullmq 0.14.0-beta.165-454838 → 0.14.0-beta.165-210612
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/dist/index.css +1159 -0
- package/dist/index.css.map +1 -0
- package/dist/index.d.ts +62 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2992 -4509
- package/dist/index.js.map +1 -0
- package/dist/plugin.d.ts +6 -2
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +630 -384
- package/dist/plugin.js.map +1 -0
- package/package.json +17 -24
- package/dist/api.d.ts +0 -4
- package/dist/api.d.ts.map +0 -1
- package/dist/components/dlq-panel.d.ts +0 -2
- package/dist/components/dlq-panel.d.ts.map +0 -1
- package/dist/components/job-detail.d.ts +0 -2
- package/dist/components/job-detail.d.ts.map +0 -1
- package/dist/components/jobs-table.d.ts +0 -2
- package/dist/components/jobs-table.d.ts.map +0 -1
- package/dist/components/queue-detail.d.ts +0 -2
- package/dist/components/queue-detail.d.ts.map +0 -1
- package/dist/components/queue-list.d.ts +0 -2
- package/dist/components/queue-list.d.ts.map +0 -1
- package/dist/components/queues-page.d.ts +0 -2
- package/dist/components/queues-page.d.ts.map +0 -1
- package/dist/hooks/use-jobs-mutations.d.ts +0 -23
- package/dist/hooks/use-jobs-mutations.d.ts.map +0 -1
- package/dist/hooks/use-jobs-query.d.ts +0 -5
- package/dist/hooks/use-jobs-query.d.ts.map +0 -1
- package/dist/hooks/use-queues.d.ts +0 -11
- package/dist/hooks/use-queues.d.ts.map +0 -1
- package/dist/index.cjs +0 -98
- package/dist/plugin-bullmq.css +0 -1
- package/dist/plugin.cjs +0 -1
- package/dist/providers/query-provider.d.ts +0 -7
- package/dist/providers/query-provider.d.ts.map +0 -1
- package/dist/stores/use-bullmq-store.d.ts +0 -22
- package/dist/stores/use-bullmq-store.d.ts.map +0 -1
- package/dist/streams/queues-stream.d.ts +0 -33
- package/dist/streams/queues-stream.d.ts.map +0 -1
- package/dist/types/queue.d.ts +0 -54
- package/dist/types/queue.d.ts.map +0 -1
package/dist/plugin.js
CHANGED
|
@@ -1,385 +1,631 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { StreamAdapter
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
return await d(e, o, s).pause(), { status: 200, body: { message: "Queue paused" } };
|
|
154
|
-
} catch (e) {
|
|
155
|
-
return { status: 500, body: { error: e instanceof Error ? e.message : "Unknown error" } };
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
), u(
|
|
159
|
-
{ method: "POST", path: "/__motia/bullmq/queues/:name/resume" },
|
|
160
|
-
async (t) => {
|
|
161
|
-
try {
|
|
162
|
-
const e = t.pathParams.name;
|
|
163
|
-
return await d(e, o, s).resume(), { status: 200, body: { message: "Queue resumed" } };
|
|
164
|
-
} catch (e) {
|
|
165
|
-
return { status: 500, body: { error: e instanceof Error ? e.message : "Unknown error" } };
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
), u(
|
|
169
|
-
{ method: "POST", path: "/__motia/bullmq/queues/:name/clean" },
|
|
170
|
-
async (t) => {
|
|
171
|
-
try {
|
|
172
|
-
const e = t.pathParams.name, n = t.body, i = {
|
|
173
|
-
grace: n.grace ?? 0,
|
|
174
|
-
limit: n.limit ?? 1e3,
|
|
175
|
-
status: n.status ?? "completed"
|
|
176
|
-
}, m = await d(e, o, s).clean(i.grace, i.limit, i.status);
|
|
177
|
-
return { status: 200, body: { deleted: m.length, ids: m } };
|
|
178
|
-
} catch (e) {
|
|
179
|
-
return { status: 500, body: { error: e instanceof Error ? e.message : "Unknown error" } };
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
), u(
|
|
183
|
-
{ method: "POST", path: "/__motia/bullmq/queues/:name/drain" },
|
|
184
|
-
async (t) => {
|
|
185
|
-
try {
|
|
186
|
-
const e = t.pathParams.name;
|
|
187
|
-
return await d(e, o, s).drain(), { status: 200, body: { message: "Queue drained" } };
|
|
188
|
-
} catch (e) {
|
|
189
|
-
return { status: 500, body: { error: e instanceof Error ? e.message : "Unknown error" } };
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
), u(
|
|
193
|
-
{ method: "GET", path: "/__motia/bullmq/queues/:name/jobs" },
|
|
194
|
-
async (t) => {
|
|
195
|
-
try {
|
|
196
|
-
const e = t.pathParams.name, n = t.queryParams.status || "waiting", i = parseInt(t.queryParams.start, 10) || 0, r = parseInt(t.queryParams.end, 10) || 100;
|
|
197
|
-
return { status: 200, body: { jobs: (await d(e, o, s).getJobs([n], i, r)).map((c) => ({
|
|
198
|
-
id: c.id || "",
|
|
199
|
-
name: c.name,
|
|
200
|
-
data: c.data,
|
|
201
|
-
opts: c.opts,
|
|
202
|
-
progress: typeof c.progress == "object" ? JSON.stringify(c.progress) : c.progress,
|
|
203
|
-
timestamp: c.timestamp,
|
|
204
|
-
attemptsMade: c.attemptsMade,
|
|
205
|
-
processedOn: c.processedOn,
|
|
206
|
-
finishedOn: c.finishedOn,
|
|
207
|
-
returnvalue: c.returnvalue,
|
|
208
|
-
failedReason: c.failedReason,
|
|
209
|
-
stacktrace: c.stacktrace
|
|
210
|
-
})) } };
|
|
211
|
-
} catch (e) {
|
|
212
|
-
return { status: 500, body: { error: e instanceof Error ? e.message : "Unknown error" } };
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
), u(
|
|
216
|
-
{ method: "GET", path: "/__motia/bullmq/queues/:queueName/jobs/:jobId" },
|
|
217
|
-
async (t) => {
|
|
218
|
-
try {
|
|
219
|
-
const e = t.pathParams.queueName, n = t.pathParams.jobId, r = await d(e, o, s).getJob(n);
|
|
220
|
-
return r ? { status: 200, body: {
|
|
221
|
-
id: r.id || "",
|
|
222
|
-
name: r.name,
|
|
223
|
-
data: r.data,
|
|
224
|
-
opts: r.opts,
|
|
225
|
-
progress: typeof r.progress == "object" ? JSON.stringify(r.progress) : r.progress,
|
|
226
|
-
timestamp: r.timestamp,
|
|
227
|
-
attemptsMade: r.attemptsMade,
|
|
228
|
-
processedOn: r.processedOn,
|
|
229
|
-
finishedOn: r.finishedOn,
|
|
230
|
-
returnvalue: r.returnvalue,
|
|
231
|
-
failedReason: r.failedReason,
|
|
232
|
-
stacktrace: r.stacktrace
|
|
233
|
-
} } : { status: 404, body: { error: "Job not found" } };
|
|
234
|
-
} catch (e) {
|
|
235
|
-
return { status: 500, body: { error: e instanceof Error ? e.message : "Unknown error" } };
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
), u(
|
|
239
|
-
{ method: "POST", path: "/__motia/bullmq/queues/:queueName/jobs/:jobId/retry" },
|
|
240
|
-
async (t) => {
|
|
241
|
-
try {
|
|
242
|
-
const e = t.pathParams.queueName, n = t.pathParams.jobId, r = await d(e, o, s).getJob(n);
|
|
243
|
-
return r ? (await r.retry(), { status: 200, body: { message: "Job retried" } }) : { status: 404, body: { error: "Job not found" } };
|
|
244
|
-
} catch (e) {
|
|
245
|
-
return { status: 500, body: { error: e instanceof Error ? e.message : "Unknown error" } };
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
), u(
|
|
249
|
-
{ method: "POST", path: "/__motia/bullmq/queues/:queueName/jobs/:jobId/remove" },
|
|
250
|
-
async (t) => {
|
|
251
|
-
try {
|
|
252
|
-
const e = t.pathParams.queueName, n = t.pathParams.jobId, r = await d(e, o, s).getJob(n);
|
|
253
|
-
return r ? (await r.remove(), { status: 200, body: { message: "Job removed" } }) : { status: 404, body: { error: "Job not found" } };
|
|
254
|
-
} catch (e) {
|
|
255
|
-
return { status: 500, body: { error: e instanceof Error ? e.message : "Unknown error" } };
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
), u(
|
|
259
|
-
{ method: "POST", path: "/__motia/bullmq/queues/:queueName/jobs/:jobId/promote" },
|
|
260
|
-
async (t) => {
|
|
261
|
-
try {
|
|
262
|
-
const e = t.pathParams.queueName, n = t.pathParams.jobId, r = await d(e, o, s).getJob(n);
|
|
263
|
-
return r ? (await r.promote(), { status: 200, body: { message: "Job promoted" } }) : { status: 404, body: { error: "Job not found" } };
|
|
264
|
-
} catch (e) {
|
|
265
|
-
return { status: 500, body: { error: e instanceof Error ? e.message : "Unknown error" } };
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
), u(
|
|
269
|
-
{ method: "GET", path: "/__motia/bullmq/dlq/:name/jobs" },
|
|
270
|
-
async (t) => {
|
|
271
|
-
try {
|
|
272
|
-
const e = t.pathParams.name, n = parseInt(t.queryParams.start, 10) || 0, i = parseInt(t.queryParams.end, 10) || 100, r = e.endsWith(a) ? e : `${e}${a}`;
|
|
273
|
-
return { status: 200, body: { jobs: (await d(r, o, s).getJobs(["waiting", "completed"], n, i)).map((c) => ({
|
|
274
|
-
id: c.id || "",
|
|
275
|
-
name: c.name,
|
|
276
|
-
data: c.data,
|
|
277
|
-
timestamp: c.timestamp,
|
|
278
|
-
originalEvent: c.data?.originalEvent,
|
|
279
|
-
failureReason: c.data?.failureReason || c.failedReason,
|
|
280
|
-
failureTimestamp: c.data?.failureTimestamp || c.finishedOn,
|
|
281
|
-
attemptsMade: c.data?.attemptsMade || c.attemptsMade
|
|
282
|
-
})) } };
|
|
283
|
-
} catch (e) {
|
|
284
|
-
return { status: 500, body: { error: e instanceof Error ? e.message : "Unknown error" } };
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
), u(
|
|
288
|
-
{ method: "POST", path: "/__motia/bullmq/dlq/:name/retry/:jobId" },
|
|
289
|
-
async (t) => {
|
|
290
|
-
try {
|
|
291
|
-
const e = t.pathParams.name, n = t.pathParams.jobId, i = e.endsWith(a) ? e : `${e}${a}`, m = await d(i, o, s).getJob(n);
|
|
292
|
-
if (!m)
|
|
293
|
-
return { status: 404, body: { error: "Job not found in DLQ" } };
|
|
294
|
-
const l = m.data?.originalEvent;
|
|
295
|
-
if (l) {
|
|
296
|
-
const p = i.replace(a, ""), c = d(p, o, s), h = {
|
|
297
|
-
topic: l.topic,
|
|
298
|
-
data: l.data,
|
|
299
|
-
traceId: l.traceId
|
|
300
|
-
};
|
|
301
|
-
l.flows && (h.flows = l.flows), l.messageGroupId && (h.messageGroupId = l.messageGroupId), await c.add(l.topic || m.name, h);
|
|
302
|
-
}
|
|
303
|
-
return await m.remove(), { status: 200, body: { message: "Job retried from DLQ" } };
|
|
304
|
-
} catch (e) {
|
|
305
|
-
return { status: 500, body: { error: e instanceof Error ? e.message : "Unknown error" } };
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
), u(
|
|
309
|
-
{ method: "POST", path: "/__motia/bullmq/dlq/:name/retry-all" },
|
|
310
|
-
async (t) => {
|
|
311
|
-
try {
|
|
312
|
-
const e = t.pathParams.name, n = e.endsWith(a) ? e : `${e}${a}`, r = await d(n, o, s).getJobs(["waiting", "completed"]), m = n.replace(a, ""), l = d(m, o, s);
|
|
313
|
-
let p = 0;
|
|
314
|
-
for (const c of r) {
|
|
315
|
-
const h = c.data?.originalEvent;
|
|
316
|
-
if (h) {
|
|
317
|
-
const y = {
|
|
318
|
-
topic: h.topic,
|
|
319
|
-
data: h.data,
|
|
320
|
-
traceId: h.traceId
|
|
321
|
-
};
|
|
322
|
-
h.flows && (y.flows = h.flows), h.messageGroupId && (y.messageGroupId = h.messageGroupId), await l.add(h.topic || c.name, y);
|
|
323
|
-
}
|
|
324
|
-
await c.remove(), p++;
|
|
325
|
-
}
|
|
326
|
-
return { status: 200, body: { message: `Retried ${p} jobs from DLQ`, count: p } };
|
|
327
|
-
} catch (e) {
|
|
328
|
-
return { status: 500, body: { error: e instanceof Error ? e.message : "Unknown error" } };
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
), u(
|
|
332
|
-
{ method: "POST", path: "/__motia/bullmq/dlq/:name/clear" },
|
|
333
|
-
async (t) => {
|
|
334
|
-
try {
|
|
335
|
-
const e = t.pathParams.name, n = e.endsWith(a) ? e : `${e}${a}`;
|
|
336
|
-
return await d(n, o, s).obliterate({ force: !0 }), { status: 200, body: { message: "DLQ cleared" } };
|
|
337
|
-
} catch (e) {
|
|
338
|
-
return { status: 500, body: { error: e instanceof Error ? e.message : "Unknown error" } };
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
);
|
|
342
|
-
}, q = "__motia.bullmq-queues", k = (u) => u !== null && typeof u == "object" && "connection" in u && "prefix" in u && "dlqSuffix" in u;
|
|
343
|
-
function U(u) {
|
|
344
|
-
let s, a, o, t = !1;
|
|
345
|
-
if (k(u.eventAdapter))
|
|
346
|
-
s = u.eventAdapter.connection, a = u.eventAdapter.prefix, o = u.eventAdapter.dlqSuffix;
|
|
347
|
-
else {
|
|
348
|
-
const i = process.env.BULLMQ_REDIS_HOST || process.env.REDIS_HOST || "localhost", r = parseInt(process.env.BULLMQ_REDIS_PORT || process.env.REDIS_PORT || "6379", 10), m = process.env.BULLMQ_REDIS_PASSWORD || process.env.REDIS_PASSWORD || void 0;
|
|
349
|
-
a = process.env.BULLMQ_PREFIX || "motia:events", o = process.env.BULLMQ_DLQ_SUFFIX || ".dlq", s = new P({ host: i, port: r, password: m, maxRetriesPerRequest: null }), t = !0;
|
|
350
|
-
}
|
|
351
|
-
const e = new E(s, a, o), n = u.lockedData.createStream({
|
|
352
|
-
filePath: `${q}.ts`,
|
|
353
|
-
hidden: !0,
|
|
354
|
-
config: {
|
|
355
|
-
name: q,
|
|
356
|
-
baseConfig: { storageType: "custom", factory: () => e },
|
|
357
|
-
schema: null
|
|
358
|
-
}
|
|
359
|
-
})();
|
|
360
|
-
return e.setUpdateCallback((i) => {
|
|
361
|
-
n.set("default", i.id, i);
|
|
362
|
-
}), e.setupAllQueueEvents().then(() => {
|
|
363
|
-
e.getGroup("default").then((i) => {
|
|
364
|
-
for (const r of i)
|
|
365
|
-
n.set("default", r.id, r);
|
|
366
|
-
});
|
|
367
|
-
}), O(u, a, o, s), {
|
|
368
|
-
workbench: [
|
|
369
|
-
{
|
|
370
|
-
packageName: "@motiadev/plugin-bullmq",
|
|
371
|
-
cssImports: ["@motiadev/plugin-bullmq/dist/plugin-bullmq.css"],
|
|
372
|
-
label: "Queues",
|
|
373
|
-
position: "top",
|
|
374
|
-
componentName: "QueuesPage",
|
|
375
|
-
labelIcon: "layers"
|
|
376
|
-
}
|
|
377
|
-
],
|
|
378
|
-
onShutdown: async () => {
|
|
379
|
-
await e.closeAllQueueEvents(), t && await s.quit();
|
|
380
|
-
}
|
|
381
|
-
};
|
|
382
|
-
}
|
|
383
|
-
export {
|
|
384
|
-
U as default
|
|
1
|
+
import IORedis from "ioredis";
|
|
2
|
+
import { StreamAdapter } from "@motiadev/core";
|
|
3
|
+
import { Queue, QueueEvents } from "bullmq";
|
|
4
|
+
|
|
5
|
+
//#region src/streams/queues-stream.ts
|
|
6
|
+
const queues = /* @__PURE__ */ new Map();
|
|
7
|
+
const queueEvents = /* @__PURE__ */ new Map();
|
|
8
|
+
const getOrCreateQueue = (name, connection, prefix) => {
|
|
9
|
+
const existing = queues.get(name);
|
|
10
|
+
if (existing) return existing;
|
|
11
|
+
const queue = new Queue(name, {
|
|
12
|
+
connection,
|
|
13
|
+
prefix
|
|
14
|
+
});
|
|
15
|
+
queues.set(name, queue);
|
|
16
|
+
return queue;
|
|
17
|
+
};
|
|
18
|
+
const discoverQueueNames = async (connection, prefix) => {
|
|
19
|
+
const pattern = `${prefix}:*:id`;
|
|
20
|
+
const keys = await connection.keys(pattern);
|
|
21
|
+
const queueNames = /* @__PURE__ */ new Set();
|
|
22
|
+
for (const key of keys) {
|
|
23
|
+
const withoutId = key.slice(prefix.length + 1).slice(0, -3);
|
|
24
|
+
queueNames.add(withoutId);
|
|
25
|
+
}
|
|
26
|
+
return queueNames;
|
|
27
|
+
};
|
|
28
|
+
const getQueueInfo = async (name, connection, prefix, dlqSuffix) => {
|
|
29
|
+
const queue = getOrCreateQueue(name, connection, prefix);
|
|
30
|
+
const [isPaused, counts] = await Promise.all([queue.isPaused(), queue.getJobCounts()]);
|
|
31
|
+
return {
|
|
32
|
+
name,
|
|
33
|
+
displayName: name,
|
|
34
|
+
isPaused,
|
|
35
|
+
isDLQ: name.endsWith(dlqSuffix),
|
|
36
|
+
stats: {
|
|
37
|
+
waiting: counts.waiting || 0,
|
|
38
|
+
active: counts.active || 0,
|
|
39
|
+
completed: counts.completed || 0,
|
|
40
|
+
failed: counts.failed || 0,
|
|
41
|
+
delayed: counts.delayed || 0,
|
|
42
|
+
paused: counts.paused || 0,
|
|
43
|
+
prioritized: counts.prioritized || 0
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
const DEBOUNCE_MS = 500;
|
|
48
|
+
var QueuesStream = class extends StreamAdapter {
|
|
49
|
+
constructor(connection, prefix, dlqSuffix) {
|
|
50
|
+
super("__motia.bullmq-queues");
|
|
51
|
+
this.knownQueues = /* @__PURE__ */ new Set();
|
|
52
|
+
this.lastStatsCache = /* @__PURE__ */ new Map();
|
|
53
|
+
this.debounceTimers = /* @__PURE__ */ new Map();
|
|
54
|
+
this.connection = connection;
|
|
55
|
+
this.prefix = prefix;
|
|
56
|
+
this.dlqSuffix = dlqSuffix;
|
|
57
|
+
}
|
|
58
|
+
setUpdateCallback(callback) {
|
|
59
|
+
this.onQueueUpdate = callback;
|
|
60
|
+
}
|
|
61
|
+
async get(_groupId, id) {
|
|
62
|
+
try {
|
|
63
|
+
const info = await getQueueInfo(id, this.connection, this.prefix, this.dlqSuffix);
|
|
64
|
+
return {
|
|
65
|
+
...info,
|
|
66
|
+
id: info.name
|
|
67
|
+
};
|
|
68
|
+
} catch {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
async set(_groupId, _id, data) {
|
|
73
|
+
return data;
|
|
74
|
+
}
|
|
75
|
+
async delete(_groupId, id) {
|
|
76
|
+
this.knownQueues.delete(id);
|
|
77
|
+
this.lastStatsCache.delete(id);
|
|
78
|
+
return { id };
|
|
79
|
+
}
|
|
80
|
+
async getGroup(_groupId) {
|
|
81
|
+
const queueNames = await discoverQueueNames(this.connection, this.prefix);
|
|
82
|
+
const queueInfos = [];
|
|
83
|
+
for (const name of queueNames) {
|
|
84
|
+
this.knownQueues.add(name);
|
|
85
|
+
const info = await getQueueInfo(name, this.connection, this.prefix, this.dlqSuffix);
|
|
86
|
+
const streamInfo = {
|
|
87
|
+
...info,
|
|
88
|
+
id: info.name
|
|
89
|
+
};
|
|
90
|
+
queueInfos.push(streamInfo);
|
|
91
|
+
this.lastStatsCache.set(name, JSON.stringify({
|
|
92
|
+
stats: info.stats,
|
|
93
|
+
isPaused: info.isPaused
|
|
94
|
+
}));
|
|
95
|
+
}
|
|
96
|
+
return queueInfos;
|
|
97
|
+
}
|
|
98
|
+
async refreshQueue(name) {
|
|
99
|
+
const info = await getQueueInfo(name, this.connection, this.prefix, this.dlqSuffix);
|
|
100
|
+
const streamInfo = {
|
|
101
|
+
...info,
|
|
102
|
+
id: info.name
|
|
103
|
+
};
|
|
104
|
+
this.knownQueues.add(name);
|
|
105
|
+
const newStatsKey = JSON.stringify({
|
|
106
|
+
stats: info.stats,
|
|
107
|
+
isPaused: info.isPaused
|
|
108
|
+
});
|
|
109
|
+
if (this.lastStatsCache.get(name) !== newStatsKey) {
|
|
110
|
+
this.lastStatsCache.set(name, newStatsKey);
|
|
111
|
+
this.onQueueUpdate?.(streamInfo);
|
|
112
|
+
}
|
|
113
|
+
return streamInfo;
|
|
114
|
+
}
|
|
115
|
+
debouncedRefresh(queueName) {
|
|
116
|
+
const existing = this.debounceTimers.get(queueName);
|
|
117
|
+
if (existing) clearTimeout(existing);
|
|
118
|
+
const timer = setTimeout(() => {
|
|
119
|
+
this.debounceTimers.delete(queueName);
|
|
120
|
+
this.refreshQueue(queueName);
|
|
121
|
+
}, DEBOUNCE_MS);
|
|
122
|
+
this.debounceTimers.set(queueName, timer);
|
|
123
|
+
}
|
|
124
|
+
setupQueueEvents(queueName) {
|
|
125
|
+
if (queueEvents.has(queueName)) return;
|
|
126
|
+
const events = new QueueEvents(queueName, {
|
|
127
|
+
connection: this.connection,
|
|
128
|
+
prefix: this.prefix
|
|
129
|
+
});
|
|
130
|
+
const refreshOnEvent = () => {
|
|
131
|
+
this.debouncedRefresh(queueName);
|
|
132
|
+
};
|
|
133
|
+
events.on("waiting", refreshOnEvent);
|
|
134
|
+
events.on("active", refreshOnEvent);
|
|
135
|
+
events.on("completed", refreshOnEvent);
|
|
136
|
+
events.on("failed", refreshOnEvent);
|
|
137
|
+
events.on("delayed", refreshOnEvent);
|
|
138
|
+
events.on("removed", refreshOnEvent);
|
|
139
|
+
}
|
|
140
|
+
async setupAllQueueEvents() {
|
|
141
|
+
const queueNames = await discoverQueueNames(this.connection, this.prefix);
|
|
142
|
+
for (const name of queueNames) this.setupQueueEvents(name);
|
|
143
|
+
}
|
|
144
|
+
async closeAllQueueEvents() {
|
|
145
|
+
for (const timer of this.debounceTimers.values()) clearTimeout(timer);
|
|
146
|
+
this.debounceTimers.clear();
|
|
147
|
+
for (const [, events] of queueEvents) await events.close();
|
|
148
|
+
queueEvents.clear();
|
|
149
|
+
}
|
|
150
|
+
getKnownQueues() {
|
|
151
|
+
return this.knownQueues;
|
|
152
|
+
}
|
|
385
153
|
};
|
|
154
|
+
|
|
155
|
+
//#endregion
|
|
156
|
+
//#region src/api.ts
|
|
157
|
+
const discoverQueues = async (connection, prefix, dlqSuffix) => {
|
|
158
|
+
const queueNames = await discoverQueueNames(connection, prefix);
|
|
159
|
+
const queueInfos = [];
|
|
160
|
+
for (const name of queueNames) {
|
|
161
|
+
const info = await getQueueInfo(name, connection, prefix, dlqSuffix);
|
|
162
|
+
queueInfos.push(info);
|
|
163
|
+
}
|
|
164
|
+
return queueInfos;
|
|
165
|
+
};
|
|
166
|
+
const api = ({ registerApi }, prefix, dlqSuffix, connection) => {
|
|
167
|
+
registerApi({
|
|
168
|
+
method: "GET",
|
|
169
|
+
path: "/__motia/bullmq/queues"
|
|
170
|
+
}, async () => {
|
|
171
|
+
try {
|
|
172
|
+
return {
|
|
173
|
+
status: 200,
|
|
174
|
+
body: { queues: await discoverQueues(connection, prefix, dlqSuffix) }
|
|
175
|
+
};
|
|
176
|
+
} catch (error) {
|
|
177
|
+
return {
|
|
178
|
+
status: 500,
|
|
179
|
+
body: { error: error instanceof Error ? error.message : "Unknown error" }
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
registerApi({
|
|
184
|
+
method: "GET",
|
|
185
|
+
path: "/__motia/bullmq/queues/:name"
|
|
186
|
+
}, async (req) => {
|
|
187
|
+
try {
|
|
188
|
+
const name = req.pathParams.name;
|
|
189
|
+
const queue = getOrCreateQueue(name, connection, prefix);
|
|
190
|
+
const [isPaused, counts] = await Promise.all([queue.isPaused(), queue.getJobCounts()]);
|
|
191
|
+
return {
|
|
192
|
+
status: 200,
|
|
193
|
+
body: {
|
|
194
|
+
name,
|
|
195
|
+
displayName: name,
|
|
196
|
+
isPaused,
|
|
197
|
+
isDLQ: name.endsWith(dlqSuffix),
|
|
198
|
+
stats: {
|
|
199
|
+
waiting: counts.waiting || 0,
|
|
200
|
+
active: counts.active || 0,
|
|
201
|
+
completed: counts.completed || 0,
|
|
202
|
+
failed: counts.failed || 0,
|
|
203
|
+
delayed: counts.delayed || 0,
|
|
204
|
+
paused: counts.paused || 0,
|
|
205
|
+
prioritized: counts.prioritized || 0
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
} catch (error) {
|
|
210
|
+
return {
|
|
211
|
+
status: 500,
|
|
212
|
+
body: { error: error instanceof Error ? error.message : "Unknown error" }
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
registerApi({
|
|
217
|
+
method: "POST",
|
|
218
|
+
path: "/__motia/bullmq/queues/:name/pause"
|
|
219
|
+
}, async (req) => {
|
|
220
|
+
try {
|
|
221
|
+
const name = req.pathParams.name;
|
|
222
|
+
await getOrCreateQueue(name, connection, prefix).pause();
|
|
223
|
+
return {
|
|
224
|
+
status: 200,
|
|
225
|
+
body: { message: "Queue paused" }
|
|
226
|
+
};
|
|
227
|
+
} catch (error) {
|
|
228
|
+
return {
|
|
229
|
+
status: 500,
|
|
230
|
+
body: { error: error instanceof Error ? error.message : "Unknown error" }
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
registerApi({
|
|
235
|
+
method: "POST",
|
|
236
|
+
path: "/__motia/bullmq/queues/:name/resume"
|
|
237
|
+
}, async (req) => {
|
|
238
|
+
try {
|
|
239
|
+
const name = req.pathParams.name;
|
|
240
|
+
await getOrCreateQueue(name, connection, prefix).resume();
|
|
241
|
+
return {
|
|
242
|
+
status: 200,
|
|
243
|
+
body: { message: "Queue resumed" }
|
|
244
|
+
};
|
|
245
|
+
} catch (error) {
|
|
246
|
+
return {
|
|
247
|
+
status: 500,
|
|
248
|
+
body: { error: error instanceof Error ? error.message : "Unknown error" }
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
registerApi({
|
|
253
|
+
method: "POST",
|
|
254
|
+
path: "/__motia/bullmq/queues/:name/clean"
|
|
255
|
+
}, async (req) => {
|
|
256
|
+
try {
|
|
257
|
+
const name = req.pathParams.name;
|
|
258
|
+
const body = req.body;
|
|
259
|
+
const options = {
|
|
260
|
+
grace: body.grace ?? 0,
|
|
261
|
+
limit: body.limit ?? 1e3,
|
|
262
|
+
status: body.status ?? "completed"
|
|
263
|
+
};
|
|
264
|
+
const deletedIds = await getOrCreateQueue(name, connection, prefix).clean(options.grace, options.limit, options.status);
|
|
265
|
+
return {
|
|
266
|
+
status: 200,
|
|
267
|
+
body: {
|
|
268
|
+
deleted: deletedIds.length,
|
|
269
|
+
ids: deletedIds
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
} catch (error) {
|
|
273
|
+
return {
|
|
274
|
+
status: 500,
|
|
275
|
+
body: { error: error instanceof Error ? error.message : "Unknown error" }
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
registerApi({
|
|
280
|
+
method: "POST",
|
|
281
|
+
path: "/__motia/bullmq/queues/:name/drain"
|
|
282
|
+
}, async (req) => {
|
|
283
|
+
try {
|
|
284
|
+
const name = req.pathParams.name;
|
|
285
|
+
await getOrCreateQueue(name, connection, prefix).drain();
|
|
286
|
+
return {
|
|
287
|
+
status: 200,
|
|
288
|
+
body: { message: "Queue drained" }
|
|
289
|
+
};
|
|
290
|
+
} catch (error) {
|
|
291
|
+
return {
|
|
292
|
+
status: 500,
|
|
293
|
+
body: { error: error instanceof Error ? error.message : "Unknown error" }
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
registerApi({
|
|
298
|
+
method: "GET",
|
|
299
|
+
path: "/__motia/bullmq/queues/:name/jobs"
|
|
300
|
+
}, async (req) => {
|
|
301
|
+
try {
|
|
302
|
+
const name = req.pathParams.name;
|
|
303
|
+
const status = req.queryParams.status || "waiting";
|
|
304
|
+
const start = parseInt(req.queryParams.start, 10) || 0;
|
|
305
|
+
const end = parseInt(req.queryParams.end, 10) || 100;
|
|
306
|
+
return {
|
|
307
|
+
status: 200,
|
|
308
|
+
body: { jobs: (await getOrCreateQueue(name, connection, prefix).getJobs([status], start, end)).map((job) => ({
|
|
309
|
+
id: job.id || "",
|
|
310
|
+
name: job.name,
|
|
311
|
+
data: job.data,
|
|
312
|
+
opts: job.opts,
|
|
313
|
+
progress: typeof job.progress === "object" ? JSON.stringify(job.progress) : job.progress,
|
|
314
|
+
timestamp: job.timestamp,
|
|
315
|
+
attemptsMade: job.attemptsMade,
|
|
316
|
+
processedOn: job.processedOn,
|
|
317
|
+
finishedOn: job.finishedOn,
|
|
318
|
+
returnvalue: job.returnvalue,
|
|
319
|
+
failedReason: job.failedReason,
|
|
320
|
+
stacktrace: job.stacktrace
|
|
321
|
+
})) }
|
|
322
|
+
};
|
|
323
|
+
} catch (error) {
|
|
324
|
+
return {
|
|
325
|
+
status: 500,
|
|
326
|
+
body: { error: error instanceof Error ? error.message : "Unknown error" }
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
});
|
|
330
|
+
registerApi({
|
|
331
|
+
method: "GET",
|
|
332
|
+
path: "/__motia/bullmq/queues/:queueName/jobs/:jobId"
|
|
333
|
+
}, async (req) => {
|
|
334
|
+
try {
|
|
335
|
+
const queueName = req.pathParams.queueName;
|
|
336
|
+
const jobId = req.pathParams.jobId;
|
|
337
|
+
const job = await getOrCreateQueue(queueName, connection, prefix).getJob(jobId);
|
|
338
|
+
if (!job) return {
|
|
339
|
+
status: 404,
|
|
340
|
+
body: { error: "Job not found" }
|
|
341
|
+
};
|
|
342
|
+
return {
|
|
343
|
+
status: 200,
|
|
344
|
+
body: {
|
|
345
|
+
id: job.id || "",
|
|
346
|
+
name: job.name,
|
|
347
|
+
data: job.data,
|
|
348
|
+
opts: job.opts,
|
|
349
|
+
progress: typeof job.progress === "object" ? JSON.stringify(job.progress) : job.progress,
|
|
350
|
+
timestamp: job.timestamp,
|
|
351
|
+
attemptsMade: job.attemptsMade,
|
|
352
|
+
processedOn: job.processedOn,
|
|
353
|
+
finishedOn: job.finishedOn,
|
|
354
|
+
returnvalue: job.returnvalue,
|
|
355
|
+
failedReason: job.failedReason,
|
|
356
|
+
stacktrace: job.stacktrace
|
|
357
|
+
}
|
|
358
|
+
};
|
|
359
|
+
} catch (error) {
|
|
360
|
+
return {
|
|
361
|
+
status: 500,
|
|
362
|
+
body: { error: error instanceof Error ? error.message : "Unknown error" }
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
});
|
|
366
|
+
registerApi({
|
|
367
|
+
method: "POST",
|
|
368
|
+
path: "/__motia/bullmq/queues/:queueName/jobs/:jobId/retry"
|
|
369
|
+
}, async (req) => {
|
|
370
|
+
try {
|
|
371
|
+
const queueName = req.pathParams.queueName;
|
|
372
|
+
const jobId = req.pathParams.jobId;
|
|
373
|
+
const job = await getOrCreateQueue(queueName, connection, prefix).getJob(jobId);
|
|
374
|
+
if (!job) return {
|
|
375
|
+
status: 404,
|
|
376
|
+
body: { error: "Job not found" }
|
|
377
|
+
};
|
|
378
|
+
await job.retry();
|
|
379
|
+
return {
|
|
380
|
+
status: 200,
|
|
381
|
+
body: { message: "Job retried" }
|
|
382
|
+
};
|
|
383
|
+
} catch (error) {
|
|
384
|
+
return {
|
|
385
|
+
status: 500,
|
|
386
|
+
body: { error: error instanceof Error ? error.message : "Unknown error" }
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
registerApi({
|
|
391
|
+
method: "POST",
|
|
392
|
+
path: "/__motia/bullmq/queues/:queueName/jobs/:jobId/remove"
|
|
393
|
+
}, async (req) => {
|
|
394
|
+
try {
|
|
395
|
+
const queueName = req.pathParams.queueName;
|
|
396
|
+
const jobId = req.pathParams.jobId;
|
|
397
|
+
const job = await getOrCreateQueue(queueName, connection, prefix).getJob(jobId);
|
|
398
|
+
if (!job) return {
|
|
399
|
+
status: 404,
|
|
400
|
+
body: { error: "Job not found" }
|
|
401
|
+
};
|
|
402
|
+
await job.remove();
|
|
403
|
+
return {
|
|
404
|
+
status: 200,
|
|
405
|
+
body: { message: "Job removed" }
|
|
406
|
+
};
|
|
407
|
+
} catch (error) {
|
|
408
|
+
return {
|
|
409
|
+
status: 500,
|
|
410
|
+
body: { error: error instanceof Error ? error.message : "Unknown error" }
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
});
|
|
414
|
+
registerApi({
|
|
415
|
+
method: "POST",
|
|
416
|
+
path: "/__motia/bullmq/queues/:queueName/jobs/:jobId/promote"
|
|
417
|
+
}, async (req) => {
|
|
418
|
+
try {
|
|
419
|
+
const queueName = req.pathParams.queueName;
|
|
420
|
+
const jobId = req.pathParams.jobId;
|
|
421
|
+
const job = await getOrCreateQueue(queueName, connection, prefix).getJob(jobId);
|
|
422
|
+
if (!job) return {
|
|
423
|
+
status: 404,
|
|
424
|
+
body: { error: "Job not found" }
|
|
425
|
+
};
|
|
426
|
+
await job.promote();
|
|
427
|
+
return {
|
|
428
|
+
status: 200,
|
|
429
|
+
body: { message: "Job promoted" }
|
|
430
|
+
};
|
|
431
|
+
} catch (error) {
|
|
432
|
+
return {
|
|
433
|
+
status: 500,
|
|
434
|
+
body: { error: error instanceof Error ? error.message : "Unknown error" }
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
});
|
|
438
|
+
registerApi({
|
|
439
|
+
method: "GET",
|
|
440
|
+
path: "/__motia/bullmq/dlq/:name/jobs"
|
|
441
|
+
}, async (req) => {
|
|
442
|
+
try {
|
|
443
|
+
const name = req.pathParams.name;
|
|
444
|
+
const start = parseInt(req.queryParams.start, 10) || 0;
|
|
445
|
+
const end = parseInt(req.queryParams.end, 10) || 100;
|
|
446
|
+
return {
|
|
447
|
+
status: 200,
|
|
448
|
+
body: { jobs: (await getOrCreateQueue(name.endsWith(dlqSuffix) ? name : `${name}${dlqSuffix}`, connection, prefix).getJobs(["waiting", "completed"], start, end)).map((job) => ({
|
|
449
|
+
id: job.id || "",
|
|
450
|
+
name: job.name,
|
|
451
|
+
data: job.data,
|
|
452
|
+
timestamp: job.timestamp,
|
|
453
|
+
originalEvent: job.data?.originalEvent,
|
|
454
|
+
failureReason: job.data?.failureReason || job.failedReason,
|
|
455
|
+
failureTimestamp: job.data?.failureTimestamp || job.finishedOn,
|
|
456
|
+
attemptsMade: job.data?.attemptsMade || job.attemptsMade
|
|
457
|
+
})) }
|
|
458
|
+
};
|
|
459
|
+
} catch (error) {
|
|
460
|
+
return {
|
|
461
|
+
status: 500,
|
|
462
|
+
body: { error: error instanceof Error ? error.message : "Unknown error" }
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
});
|
|
466
|
+
registerApi({
|
|
467
|
+
method: "POST",
|
|
468
|
+
path: "/__motia/bullmq/dlq/:name/retry/:jobId"
|
|
469
|
+
}, async (req) => {
|
|
470
|
+
try {
|
|
471
|
+
const name = req.pathParams.name;
|
|
472
|
+
const jobId = req.pathParams.jobId;
|
|
473
|
+
const dlqName = name.endsWith(dlqSuffix) ? name : `${name}${dlqSuffix}`;
|
|
474
|
+
const job = await getOrCreateQueue(dlqName, connection, prefix).getJob(jobId);
|
|
475
|
+
if (!job) return {
|
|
476
|
+
status: 404,
|
|
477
|
+
body: { error: "Job not found in DLQ" }
|
|
478
|
+
};
|
|
479
|
+
const originalEvent = job.data?.originalEvent;
|
|
480
|
+
if (originalEvent) {
|
|
481
|
+
const originalQueue = getOrCreateQueue(dlqName.replace(dlqSuffix, ""), connection, prefix);
|
|
482
|
+
const jobData = {
|
|
483
|
+
topic: originalEvent.topic,
|
|
484
|
+
data: originalEvent.data,
|
|
485
|
+
traceId: originalEvent.traceId
|
|
486
|
+
};
|
|
487
|
+
if (originalEvent.flows) jobData.flows = originalEvent.flows;
|
|
488
|
+
if (originalEvent.messageGroupId) jobData.messageGroupId = originalEvent.messageGroupId;
|
|
489
|
+
await originalQueue.add(originalEvent.topic || job.name, jobData);
|
|
490
|
+
}
|
|
491
|
+
await job.remove();
|
|
492
|
+
return {
|
|
493
|
+
status: 200,
|
|
494
|
+
body: { message: "Job retried from DLQ" }
|
|
495
|
+
};
|
|
496
|
+
} catch (error) {
|
|
497
|
+
return {
|
|
498
|
+
status: 500,
|
|
499
|
+
body: { error: error instanceof Error ? error.message : "Unknown error" }
|
|
500
|
+
};
|
|
501
|
+
}
|
|
502
|
+
});
|
|
503
|
+
registerApi({
|
|
504
|
+
method: "POST",
|
|
505
|
+
path: "/__motia/bullmq/dlq/:name/retry-all"
|
|
506
|
+
}, async (req) => {
|
|
507
|
+
try {
|
|
508
|
+
const name = req.pathParams.name;
|
|
509
|
+
const dlqName = name.endsWith(dlqSuffix) ? name : `${name}${dlqSuffix}`;
|
|
510
|
+
const jobs = await getOrCreateQueue(dlqName, connection, prefix).getJobs(["waiting", "completed"]);
|
|
511
|
+
const originalQueue = getOrCreateQueue(dlqName.replace(dlqSuffix, ""), connection, prefix);
|
|
512
|
+
let count = 0;
|
|
513
|
+
for (const job of jobs) {
|
|
514
|
+
const originalEvent = job.data?.originalEvent;
|
|
515
|
+
if (originalEvent) {
|
|
516
|
+
const jobData = {
|
|
517
|
+
topic: originalEvent.topic,
|
|
518
|
+
data: originalEvent.data,
|
|
519
|
+
traceId: originalEvent.traceId
|
|
520
|
+
};
|
|
521
|
+
if (originalEvent.flows) jobData.flows = originalEvent.flows;
|
|
522
|
+
if (originalEvent.messageGroupId) jobData.messageGroupId = originalEvent.messageGroupId;
|
|
523
|
+
await originalQueue.add(originalEvent.topic || job.name, jobData);
|
|
524
|
+
}
|
|
525
|
+
await job.remove();
|
|
526
|
+
count++;
|
|
527
|
+
}
|
|
528
|
+
return {
|
|
529
|
+
status: 200,
|
|
530
|
+
body: {
|
|
531
|
+
message: `Retried ${count} jobs from DLQ`,
|
|
532
|
+
count
|
|
533
|
+
}
|
|
534
|
+
};
|
|
535
|
+
} catch (error) {
|
|
536
|
+
return {
|
|
537
|
+
status: 500,
|
|
538
|
+
body: { error: error instanceof Error ? error.message : "Unknown error" }
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
});
|
|
542
|
+
registerApi({
|
|
543
|
+
method: "POST",
|
|
544
|
+
path: "/__motia/bullmq/dlq/:name/clear"
|
|
545
|
+
}, async (req) => {
|
|
546
|
+
try {
|
|
547
|
+
const name = req.pathParams.name;
|
|
548
|
+
await getOrCreateQueue(name.endsWith(dlqSuffix) ? name : `${name}${dlqSuffix}`, connection, prefix).obliterate({ force: true });
|
|
549
|
+
return {
|
|
550
|
+
status: 200,
|
|
551
|
+
body: { message: "DLQ cleared" }
|
|
552
|
+
};
|
|
553
|
+
} catch (error) {
|
|
554
|
+
return {
|
|
555
|
+
status: 500,
|
|
556
|
+
body: { error: error instanceof Error ? error.message : "Unknown error" }
|
|
557
|
+
};
|
|
558
|
+
}
|
|
559
|
+
});
|
|
560
|
+
};
|
|
561
|
+
|
|
562
|
+
//#endregion
|
|
563
|
+
//#region src/plugin.ts
|
|
564
|
+
const STREAM_NAME = "__motia.bullmq-queues";
|
|
565
|
+
const isBullMQAdapter = (adapter) => {
|
|
566
|
+
return adapter !== null && typeof adapter === "object" && "connection" in adapter && "prefix" in adapter && "dlqSuffix" in adapter;
|
|
567
|
+
};
|
|
568
|
+
function plugin(motia) {
|
|
569
|
+
let connection;
|
|
570
|
+
let prefix;
|
|
571
|
+
let dlqSuffix;
|
|
572
|
+
let ownsConnection = false;
|
|
573
|
+
if (isBullMQAdapter(motia.eventAdapter)) {
|
|
574
|
+
connection = motia.eventAdapter.connection;
|
|
575
|
+
prefix = motia.eventAdapter.prefix;
|
|
576
|
+
dlqSuffix = motia.eventAdapter.dlqSuffix;
|
|
577
|
+
} else {
|
|
578
|
+
const host = process.env.BULLMQ_REDIS_HOST || process.env.REDIS_HOST || "localhost";
|
|
579
|
+
const port = parseInt(process.env.BULLMQ_REDIS_PORT || process.env.REDIS_PORT || "6379", 10);
|
|
580
|
+
const password = process.env.BULLMQ_REDIS_PASSWORD || process.env.REDIS_PASSWORD || void 0;
|
|
581
|
+
prefix = process.env.BULLMQ_PREFIX || "motia:events";
|
|
582
|
+
dlqSuffix = process.env.BULLMQ_DLQ_SUFFIX || ".dlq";
|
|
583
|
+
connection = new IORedis({
|
|
584
|
+
host,
|
|
585
|
+
port,
|
|
586
|
+
password,
|
|
587
|
+
maxRetriesPerRequest: null
|
|
588
|
+
});
|
|
589
|
+
ownsConnection = true;
|
|
590
|
+
}
|
|
591
|
+
const queuesStream = new QueuesStream(connection, prefix, dlqSuffix);
|
|
592
|
+
const stream = motia.lockedData.createStream({
|
|
593
|
+
filePath: `${STREAM_NAME}.ts`,
|
|
594
|
+
hidden: true,
|
|
595
|
+
config: {
|
|
596
|
+
name: STREAM_NAME,
|
|
597
|
+
baseConfig: {
|
|
598
|
+
storageType: "custom",
|
|
599
|
+
factory: () => queuesStream
|
|
600
|
+
},
|
|
601
|
+
schema: null
|
|
602
|
+
}
|
|
603
|
+
})();
|
|
604
|
+
queuesStream.setUpdateCallback((queueInfo) => {
|
|
605
|
+
stream.set("default", queueInfo.id, queueInfo);
|
|
606
|
+
});
|
|
607
|
+
queuesStream.setupAllQueueEvents().then(() => {
|
|
608
|
+
queuesStream.getGroup("default").then((queues$1) => {
|
|
609
|
+
for (const queue of queues$1) stream.set("default", queue.id, queue);
|
|
610
|
+
});
|
|
611
|
+
});
|
|
612
|
+
api(motia, prefix, dlqSuffix, connection);
|
|
613
|
+
return {
|
|
614
|
+
workbench: [{
|
|
615
|
+
packageName: "@motiadev/plugin-bullmq",
|
|
616
|
+
cssImports: ["@motiadev/plugin-bullmq/dist/index.css"],
|
|
617
|
+
label: "Queues",
|
|
618
|
+
position: "top",
|
|
619
|
+
componentName: "QueuesPage",
|
|
620
|
+
labelIcon: "layers"
|
|
621
|
+
}],
|
|
622
|
+
onShutdown: async () => {
|
|
623
|
+
await queuesStream.closeAllQueueEvents();
|
|
624
|
+
if (ownsConnection) await connection.quit();
|
|
625
|
+
}
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
//#endregion
|
|
630
|
+
export { plugin as default };
|
|
631
|
+
//# sourceMappingURL=plugin.js.map
|