adonisjs-server-stats 1.12.3 → 1.13.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/README.md +8 -4
- package/dist/src/collectors/adonisjs_queue_collector.d.ts +21 -0
- package/dist/src/collectors/adonisjs_queue_collector.js +61 -0
- package/dist/src/collectors/auto_detect.js +8 -2
- package/dist/src/collectors/index.d.ts +2 -0
- package/dist/src/collectors/index.js +1 -0
- package/dist/src/dashboard/inspector_manager.d.ts +4 -4
- package/dist/src/dashboard/inspector_manager.js +20 -10
- package/dist/src/dashboard/integrations/adonisjs_queue_inspector.d.ts +42 -0
- package/dist/src/dashboard/integrations/adonisjs_queue_inspector.js +135 -0
- package/dist/src/dashboard/integrations/adonisjs_queue_store.d.ts +166 -0
- package/dist/src/dashboard/integrations/adonisjs_queue_store.js +629 -0
- package/dist/src/dashboard/integrations/index.d.ts +4 -1
- package/dist/src/dashboard/integrations/index.js +2 -0
- package/dist/src/dashboard/integrations/queue_inspector.d.ts +5 -61
- package/dist/src/dashboard/integrations/queue_inspector.js +5 -1
- package/dist/src/dashboard/integrations/queue_inspector_contract.d.ts +73 -0
- package/dist/src/dashboard/integrations/queue_inspector_contract.js +15 -0
- package/dist/src/provider/email_bridge.d.ts +14 -3
- package/dist/src/provider/email_bridge.js +19 -1
- package/dist/src/provider/toolbar_setup.js +15 -3
- package/package.json +11 -1
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import type { ApplicationService } from '@adonisjs/core/types';
|
|
2
|
+
import type { QueueInspectorContract } from './queue_inspector_contract.js';
|
|
3
|
+
export type { QueueOverview, QueueJobSummary, QueueJobDetail, QueueJobListResult, JobStatus, } from './queue_inspector_contract.js';
|
|
4
|
+
export { ALL_STATUSES } from './queue_inspector_contract.js';
|
|
5
|
+
import type { QueueOverview, QueueJobDetail, QueueJobListResult, JobStatus } from './queue_inspector_contract.js';
|
|
2
6
|
/** Minimal interface for a BullMQ Job as accessed by the inspector. */
|
|
3
7
|
interface BullMQJob {
|
|
4
8
|
id: string | number;
|
|
@@ -29,65 +33,6 @@ interface QueueManager {
|
|
|
29
33
|
getOrSet?(name: string): BullMQQueue;
|
|
30
34
|
getJobCounts?(...statuses: string[]): Promise<Record<string, number>>;
|
|
31
35
|
}
|
|
32
|
-
export interface QueueOverview {
|
|
33
|
-
/** Total jobs currently being processed. */
|
|
34
|
-
active: number;
|
|
35
|
-
/** Jobs waiting to be picked up by a worker. */
|
|
36
|
-
waiting: number;
|
|
37
|
-
/** Jobs scheduled for future execution. */
|
|
38
|
-
delayed: number;
|
|
39
|
-
/** Jobs that completed successfully. */
|
|
40
|
-
completed: number;
|
|
41
|
-
/** Jobs that permanently failed. */
|
|
42
|
-
failed: number;
|
|
43
|
-
/** Jobs paused in the queue. */
|
|
44
|
-
paused: number;
|
|
45
|
-
}
|
|
46
|
-
export interface QueueJobSummary {
|
|
47
|
-
/** Bull job ID. */
|
|
48
|
-
id: string;
|
|
49
|
-
/** Human-readable job name (cleaned from file URLs). */
|
|
50
|
-
name: string;
|
|
51
|
-
/** Current job status. */
|
|
52
|
-
status: 'active' | 'waiting' | 'delayed' | 'completed' | 'failed' | 'paused';
|
|
53
|
-
/** Job payload (data). */
|
|
54
|
-
data: Record<string, unknown> | null;
|
|
55
|
-
/** Alias for `data` — used by some frontends. */
|
|
56
|
-
payload: Record<string, unknown> | null;
|
|
57
|
-
/** Number of attempts so far. */
|
|
58
|
-
attempts: number;
|
|
59
|
-
/** Maximum allowed attempts. */
|
|
60
|
-
maxAttempts: number;
|
|
61
|
-
/** Job progress (0-100 or custom). */
|
|
62
|
-
progress: number | object;
|
|
63
|
-
/** Error message if the job failed, or null. */
|
|
64
|
-
failedReason: string | null;
|
|
65
|
-
/** When the job was added (Unix timestamp ms). */
|
|
66
|
-
createdAt: number;
|
|
67
|
-
/** Alias for `createdAt` — BullMQ compat. */
|
|
68
|
-
timestamp: number;
|
|
69
|
-
/** When processing started (Unix timestamp ms), or null. */
|
|
70
|
-
processedAt: number | null;
|
|
71
|
-
/** When the job finished (Unix timestamp ms), or null. */
|
|
72
|
-
finishedAt: number | null;
|
|
73
|
-
/** Processing duration in ms, or null if not finished. */
|
|
74
|
-
duration: number | null;
|
|
75
|
-
}
|
|
76
|
-
export interface QueueJobDetail extends QueueJobSummary {
|
|
77
|
-
/** Full stack trace if the job failed. */
|
|
78
|
-
stackTrace: string[];
|
|
79
|
-
/** Return value from the job handler, if any. */
|
|
80
|
-
returnValue: unknown;
|
|
81
|
-
/** Job options (delay, priority, repeat, etc.). */
|
|
82
|
-
opts: Record<string, unknown>;
|
|
83
|
-
}
|
|
84
|
-
export interface QueueJobListResult {
|
|
85
|
-
/** Jobs for the requested page. */
|
|
86
|
-
jobs: QueueJobSummary[];
|
|
87
|
-
/** Total number of jobs matching the status filter. */
|
|
88
|
-
total: number;
|
|
89
|
-
}
|
|
90
|
-
type JobStatus = 'active' | 'waiting' | 'delayed' | 'completed' | 'failed' | 'paused';
|
|
91
36
|
/**
|
|
92
37
|
* Inspects Bull Queue jobs, counts, and allows retrying failed jobs.
|
|
93
38
|
*
|
|
@@ -95,7 +40,7 @@ type JobStatus = 'active' | 'waiting' | 'delayed' | 'completed' | 'failed' | 'pa
|
|
|
95
40
|
* Only functional when `@rlanz/bull-queue` is installed.
|
|
96
41
|
* All methods catch errors and return safe defaults.
|
|
97
42
|
*/
|
|
98
|
-
export declare class QueueInspector {
|
|
43
|
+
export declare class QueueInspector implements QueueInspectorContract {
|
|
99
44
|
private queueManager;
|
|
100
45
|
constructor(queueManager: QueueManager);
|
|
101
46
|
/**
|
|
@@ -147,4 +92,3 @@ export declare class QueueInspector {
|
|
|
147
92
|
/** Format a Bull job into our summary shape. */
|
|
148
93
|
private formatJobSummary;
|
|
149
94
|
}
|
|
150
|
-
export {};
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
export { ALL_STATUSES } from './queue_inspector_contract.js';
|
|
2
|
+
import { ALL_STATUSES } from './queue_inspector_contract.js';
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
// QueueInspector
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
2
6
|
/**
|
|
3
7
|
* Inspects Bull Queue jobs, counts, and allows retrying failed jobs.
|
|
4
8
|
*
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
export interface QueueOverview {
|
|
2
|
+
/** Total jobs currently being processed. */
|
|
3
|
+
active: number;
|
|
4
|
+
/** Jobs waiting to be picked up by a worker. */
|
|
5
|
+
waiting: number;
|
|
6
|
+
/** Jobs scheduled for future execution. */
|
|
7
|
+
delayed: number;
|
|
8
|
+
/** Jobs that completed successfully. */
|
|
9
|
+
completed: number;
|
|
10
|
+
/** Jobs that permanently failed. */
|
|
11
|
+
failed: number;
|
|
12
|
+
/** Jobs paused in the queue. */
|
|
13
|
+
paused: number;
|
|
14
|
+
}
|
|
15
|
+
export interface QueueJobSummary {
|
|
16
|
+
/** Bull / @boringnode job ID. */
|
|
17
|
+
id: string;
|
|
18
|
+
/** Human-readable job name (cleaned from file URLs). */
|
|
19
|
+
name: string;
|
|
20
|
+
/** Current job status. */
|
|
21
|
+
status: 'active' | 'waiting' | 'delayed' | 'completed' | 'failed' | 'paused';
|
|
22
|
+
/** Job payload (data). */
|
|
23
|
+
data: Record<string, unknown> | null;
|
|
24
|
+
/** Alias for `data` — used by some frontends. */
|
|
25
|
+
payload: Record<string, unknown> | null;
|
|
26
|
+
/** Number of attempts so far. */
|
|
27
|
+
attempts: number;
|
|
28
|
+
/** Maximum allowed attempts. */
|
|
29
|
+
maxAttempts: number;
|
|
30
|
+
/** Job progress (0-100 or custom). */
|
|
31
|
+
progress: number | object;
|
|
32
|
+
/** Error message if the job failed, or null. */
|
|
33
|
+
failedReason: string | null;
|
|
34
|
+
/** When the job was added (Unix timestamp ms). */
|
|
35
|
+
createdAt: number;
|
|
36
|
+
/** Alias for `createdAt` — BullMQ compat. */
|
|
37
|
+
timestamp: number;
|
|
38
|
+
/** When processing started (Unix timestamp ms), or null. */
|
|
39
|
+
processedAt: number | null;
|
|
40
|
+
/** When the job finished (Unix timestamp ms), or null. */
|
|
41
|
+
finishedAt: number | null;
|
|
42
|
+
/** Processing duration in ms, or null if not finished. */
|
|
43
|
+
duration: number | null;
|
|
44
|
+
}
|
|
45
|
+
export interface QueueJobDetail extends QueueJobSummary {
|
|
46
|
+
/** Full stack trace if the job failed. */
|
|
47
|
+
stackTrace: string[];
|
|
48
|
+
/** Return value from the job handler, if any. */
|
|
49
|
+
returnValue: unknown;
|
|
50
|
+
/** Job options (delay, priority, repeat, etc.). */
|
|
51
|
+
opts: Record<string, unknown>;
|
|
52
|
+
}
|
|
53
|
+
export interface QueueJobListResult {
|
|
54
|
+
/** Jobs for the requested page. */
|
|
55
|
+
jobs: QueueJobSummary[];
|
|
56
|
+
/** Total number of jobs matching the status filter. */
|
|
57
|
+
total: number;
|
|
58
|
+
}
|
|
59
|
+
/** The statuses understood by the dashboard UI. */
|
|
60
|
+
export type JobStatus = 'active' | 'waiting' | 'delayed' | 'completed' | 'failed' | 'paused';
|
|
61
|
+
/** All dashboard-level job statuses. */
|
|
62
|
+
export declare const ALL_STATUSES: JobStatus[];
|
|
63
|
+
/**
|
|
64
|
+
* Common interface implemented by both BullMQ and @adonisjs/queue inspectors.
|
|
65
|
+
*
|
|
66
|
+
* All methods catch errors internally and return safe defaults.
|
|
67
|
+
*/
|
|
68
|
+
export interface QueueInspectorContract {
|
|
69
|
+
getOverview(): Promise<QueueOverview>;
|
|
70
|
+
listJobs(status: JobStatus | 'all', page?: number, perPage?: number): Promise<QueueJobListResult>;
|
|
71
|
+
getJob(id: string): Promise<QueueJobDetail | null>;
|
|
72
|
+
retryJob(id: string): Promise<boolean>;
|
|
73
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Shared response types and contract interface for queue inspection.
|
|
3
|
+
//
|
|
4
|
+
// Both QueueInspector (BullMQ / @rlanz/bull-queue) and
|
|
5
|
+
// AdonisQueueInspector (@adonisjs/queue) implement QueueInspectorContract.
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
/** All dashboard-level job statuses. */
|
|
8
|
+
export const ALL_STATUSES = [
|
|
9
|
+
'active',
|
|
10
|
+
'waiting',
|
|
11
|
+
'delayed',
|
|
12
|
+
'completed',
|
|
13
|
+
'failed',
|
|
14
|
+
'paused',
|
|
15
|
+
];
|
|
@@ -13,14 +13,25 @@ interface RedisPublisher {
|
|
|
13
13
|
interface RedisSubscriber extends RedisPublisher {
|
|
14
14
|
subscribe(channel: string, handler: (message: string) => void): unknown;
|
|
15
15
|
}
|
|
16
|
+
/** Sink that persists an ingested email (the SQLite dashboard store). */
|
|
17
|
+
interface DashboardEmailSink {
|
|
18
|
+
recordEmail(record: Record<string, unknown>): void;
|
|
19
|
+
}
|
|
16
20
|
/** Targets for ingesting remote emails. */
|
|
17
21
|
export interface EmailBridgeTargets {
|
|
18
22
|
debugEmails: {
|
|
19
23
|
ingest(record: Record<string, unknown>): void;
|
|
20
24
|
} | null;
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
25
|
+
/**
|
|
26
|
+
* The persistent dashboard store, or a (possibly async) getter for it.
|
|
27
|
+
*
|
|
28
|
+
* A getter is used because the bridge subscribes during early boot —
|
|
29
|
+
* before the SQLite dashboard store exists — yet remote emails arrive
|
|
30
|
+
* later, by which time the store is available. Resolving lazily lets
|
|
31
|
+
* cross-process (queue-worker) emails land in SQLite, where the
|
|
32
|
+
* dashboard/debug APIs read from when persistence is enabled.
|
|
33
|
+
*/
|
|
34
|
+
dashboardStore: DashboardEmailSink | (() => DashboardEmailSink | null | Promise<DashboardEmailSink | null>) | null;
|
|
24
35
|
}
|
|
25
36
|
/**
|
|
26
37
|
* Ingest a remote email message from Redis pub/sub.
|
|
@@ -3,6 +3,20 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { log } from '../utils/logger.js';
|
|
5
5
|
import { buildEmailPayload, MAIL_STATUS_MAP } from './email_helpers.js';
|
|
6
|
+
/** Resolve the dashboard sink whether it's a direct object, a getter, or null. */
|
|
7
|
+
async function resolveDashboardSink(store) {
|
|
8
|
+
if (!store)
|
|
9
|
+
return null;
|
|
10
|
+
if (typeof store === 'function') {
|
|
11
|
+
try {
|
|
12
|
+
return (await store()) ?? null;
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return store;
|
|
19
|
+
}
|
|
6
20
|
/**
|
|
7
21
|
* Ingest a remote email message from Redis pub/sub.
|
|
8
22
|
* Skips messages from the same process.
|
|
@@ -19,7 +33,11 @@ export function ingestRemoteEmail(message, processTag, targets) {
|
|
|
19
33
|
text: fields.text || null,
|
|
20
34
|
};
|
|
21
35
|
targets.debugEmails?.ingest(record);
|
|
22
|
-
|
|
36
|
+
// Persist to SQLite too (where the APIs read from). The store may be
|
|
37
|
+
// resolved lazily, so fire-and-forget without blocking ingestion.
|
|
38
|
+
void resolveDashboardSink(targets.dashboardStore).then((sink) => {
|
|
39
|
+
sink?.recordEmail({ id: 0, ...record });
|
|
40
|
+
});
|
|
23
41
|
}
|
|
24
42
|
catch {
|
|
25
43
|
// Ignore malformed messages
|
|
@@ -18,7 +18,7 @@ export async function setupDevToolbarCore(opts) {
|
|
|
18
18
|
if (!em)
|
|
19
19
|
log.warn('emitter not available — query/event collection disabled');
|
|
20
20
|
await debugStore.start(em, await resolve('router'));
|
|
21
|
-
const emailBridgeRedis = await setupBridgeInternal(em, debugStore);
|
|
21
|
+
const emailBridgeRedis = await setupBridgeInternal(em, debugStore, app);
|
|
22
22
|
const debugController = await createDebugController(debugStore, config, getDiagnostics, app);
|
|
23
23
|
if (debugStore.traces)
|
|
24
24
|
setTraceCollector(debugStore.traces);
|
|
@@ -66,14 +66,26 @@ function createFlushTimer(debugStore, persistPath) {
|
|
|
66
66
|
catch { }
|
|
67
67
|
}, 30_000);
|
|
68
68
|
}
|
|
69
|
-
async function setupBridgeInternal(emitter, debugStore) {
|
|
69
|
+
async function setupBridgeInternal(emitter, debugStore, app) {
|
|
70
70
|
if (!emitter)
|
|
71
71
|
return null;
|
|
72
72
|
try {
|
|
73
73
|
const { appImport } = await import('../utils/app_import.js');
|
|
74
74
|
const mod = await appImport('@adonisjs/redis/services/main');
|
|
75
75
|
const redis = mod.default;
|
|
76
|
-
|
|
76
|
+
// Lazily resolve the SQLite dashboard store: it's registered as a
|
|
77
|
+
// container singleton later in boot, but remote emails only arrive
|
|
78
|
+
// post-boot, so cross-process (queue-worker) emails get persisted
|
|
79
|
+
// where the dashboard/debug APIs read from.
|
|
80
|
+
const getDashboardStore = async () => {
|
|
81
|
+
try {
|
|
82
|
+
return (await app.container.make('dashboard.store'));
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
return await setupFullEmailBridge(emitter, redis, 'adonisjs-server-stats:emails', { debugEmails: debugStore.emails ?? null, dashboardStore: getDashboardStore });
|
|
77
89
|
}
|
|
78
90
|
catch {
|
|
79
91
|
return null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "adonisjs-server-stats",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.13.0",
|
|
4
4
|
"description": "Real-time server monitoring for AdonisJS v6 applications",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"adonisjs",
|
|
@@ -138,6 +138,8 @@
|
|
|
138
138
|
"@vitejs/plugin-react": "^5.1.4",
|
|
139
139
|
"@vitejs/plugin-vue": "^6.0.4",
|
|
140
140
|
"better-sqlite3": "^11.10.0",
|
|
141
|
+
"@adonisjs/queue": "^0.6.1",
|
|
142
|
+
"@boringnode/queue": "^0.5.2",
|
|
141
143
|
"bullmq": "^5.0.0",
|
|
142
144
|
"edge.js": "^6.0.0",
|
|
143
145
|
"oxfmt": "^0.35.0",
|
|
@@ -159,6 +161,8 @@
|
|
|
159
161
|
"@adonisjs/transmit-client": "^1.0.0",
|
|
160
162
|
"@julr/adonisjs-prometheus": "^1.4.0 || ^2.0.0",
|
|
161
163
|
"better-sqlite3": "^7.0.0 || ^11.0.0 || ^12.0.0",
|
|
164
|
+
"@adonisjs/queue": "^0.6.0",
|
|
165
|
+
"@boringnode/queue": "^0.5.0",
|
|
162
166
|
"bullmq": "^5.0.0",
|
|
163
167
|
"edge.js": "^6.0.0",
|
|
164
168
|
"knex": "^3.0.0",
|
|
@@ -179,6 +183,12 @@
|
|
|
179
183
|
"@julr/adonisjs-prometheus": {
|
|
180
184
|
"optional": true
|
|
181
185
|
},
|
|
186
|
+
"@adonisjs/queue": {
|
|
187
|
+
"optional": true
|
|
188
|
+
},
|
|
189
|
+
"@boringnode/queue": {
|
|
190
|
+
"optional": true
|
|
191
|
+
},
|
|
182
192
|
"bullmq": {
|
|
183
193
|
"optional": true
|
|
184
194
|
},
|