adonisjs-server-stats 1.4.0 → 1.5.1
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 +114 -116
- package/dist/configure.d.ts.map +1 -1
- package/dist/src/controller/debug_controller.d.ts +2 -2
- package/dist/src/controller/debug_controller.d.ts.map +1 -1
- package/dist/src/controller/server_stats_controller.d.ts +1 -1
- package/dist/src/controller/server_stats_controller.d.ts.map +1 -1
- package/dist/src/dashboard/chart_aggregator.d.ts.map +1 -1
- package/dist/src/dashboard/chart_aggregator.js +8 -8
- package/dist/src/dashboard/dashboard_controller.d.ts +12 -97
- package/dist/src/dashboard/dashboard_controller.d.ts.map +1 -1
- package/dist/src/dashboard/dashboard_controller.js +244 -522
- package/dist/src/dashboard/dashboard_routes.d.ts.map +1 -1
- package/dist/src/dashboard/dashboard_routes.js +7 -2
- package/dist/src/dashboard/dashboard_store.d.ts +6 -3
- package/dist/src/dashboard/dashboard_store.d.ts.map +1 -1
- package/dist/src/dashboard/dashboard_store.js +54 -78
- package/dist/src/dashboard/integrations/cache_inspector.d.ts.map +1 -1
- package/dist/src/dashboard/integrations/queue_inspector.d.ts.map +1 -1
- package/dist/src/dashboard/migrator.d.ts.map +1 -1
- package/dist/src/dashboard/migrator.js +3 -1
- package/dist/src/dashboard/models/stats_event.d.ts +1 -1
- package/dist/src/dashboard/models/stats_event.d.ts.map +1 -1
- package/dist/src/dashboard/models/stats_query.d.ts +1 -1
- package/dist/src/dashboard/models/stats_query.d.ts.map +1 -1
- package/dist/src/dashboard/models/stats_request.d.ts +2 -2
- package/dist/src/dashboard/models/stats_request.d.ts.map +1 -1
- package/dist/src/dashboard/models/stats_request.js +1 -1
- package/dist/src/dashboard/models/stats_trace.d.ts +1 -1
- package/dist/src/dashboard/models/stats_trace.d.ts.map +1 -1
- package/dist/src/debug/debug_store.d.ts +6 -6
- package/dist/src/debug/debug_store.d.ts.map +1 -1
- package/dist/src/debug/debug_store.js +10 -10
- package/dist/src/debug/email_collector.d.ts +0 -9
- package/dist/src/debug/email_collector.d.ts.map +1 -1
- package/dist/src/debug/email_collector.js +6 -28
- package/dist/src/debug/event_collector.d.ts +1 -1
- package/dist/src/debug/event_collector.d.ts.map +1 -1
- package/dist/src/debug/event_collector.js +17 -17
- package/dist/src/debug/query_collector.d.ts +1 -1
- package/dist/src/debug/query_collector.d.ts.map +1 -1
- package/dist/src/debug/query_collector.js +13 -14
- package/dist/src/debug/ring_buffer.d.ts.map +1 -1
- package/dist/src/debug/route_inspector.d.ts +1 -1
- package/dist/src/debug/route_inspector.d.ts.map +1 -1
- package/dist/src/debug/route_inspector.js +12 -12
- package/dist/src/debug/trace_collector.d.ts.map +1 -1
- package/dist/src/debug/trace_collector.js +6 -5
- package/dist/src/edge/client/dashboard.css +516 -171
- package/dist/src/edge/client/dashboard.js +2756 -1662
- package/dist/src/edge/client/debug-panel.css +476 -133
- package/dist/src/edge/client/debug-panel.js +1496 -1043
- package/dist/src/edge/client/stats-bar.css +64 -30
- package/dist/src/edge/client/stats-bar.js +598 -319
- package/dist/src/edge/plugin.d.ts +1 -1
- package/dist/src/edge/plugin.d.ts.map +1 -1
- package/dist/src/edge/plugin.js +41 -59
- package/dist/src/edge/views/stats-bar.edge +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/middleware/request_tracking_middleware.d.ts +4 -4
- package/dist/src/middleware/request_tracking_middleware.d.ts.map +1 -1
- package/dist/src/middleware/request_tracking_middleware.js +7 -6
- package/dist/src/prometheus/prometheus_collector.d.ts +1 -1
- package/dist/src/prometheus/prometheus_collector.d.ts.map +1 -1
- package/dist/src/provider/server_stats_provider.d.ts +1 -1
- package/dist/src/provider/server_stats_provider.d.ts.map +1 -1
- package/dist/src/provider/server_stats_provider.js +31 -31
- package/dist/src/utils/json_helpers.d.ts +8 -0
- package/dist/src/utils/json_helpers.d.ts.map +1 -0
- package/dist/src/utils/json_helpers.js +21 -0
- package/dist/src/utils/mail_helpers.d.ts +13 -0
- package/dist/src/utils/mail_helpers.d.ts.map +1 -0
- package/dist/src/utils/mail_helpers.js +26 -0
- package/dist/src/utils/math_helpers.d.ts +8 -0
- package/dist/src/utils/math_helpers.d.ts.map +1 -0
- package/dist/src/utils/math_helpers.js +11 -0
- package/dist/src/utils/time_helpers.d.ts +12 -0
- package/dist/src/utils/time_helpers.d.ts.map +1 -0
- package/dist/src/utils/time_helpers.js +32 -0
- package/dist/src/utils/transmit_client.d.ts +9 -0
- package/dist/src/utils/transmit_client.d.ts.map +1 -0
- package/dist/src/utils/transmit_client.js +20 -0
- package/package.json +35 -29
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../../src/edge/plugin.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../../src/edge/plugin.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAKpD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,iBAAiB,IACrD,MAAM,GAAG,UAmHlB"}
|
package/dist/src/edge/plugin.js
CHANGED
|
@@ -1,26 +1,10 @@
|
|
|
1
|
-
import { readFileSync } from
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { dirname, join } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { Template } from 'edge.js';
|
|
5
|
+
import { loadTransmitClient } from '../utils/transmit_client.js';
|
|
6
6
|
const DIR = dirname(fileURLToPath(import.meta.url));
|
|
7
|
-
const read = (rel) => readFileSync(join(DIR, rel),
|
|
8
|
-
/**
|
|
9
|
-
* Try to locate and read the @adonisjs/transmit-client build file.
|
|
10
|
-
* Returns the file contents wrapped to expose `window.Transmit`, or
|
|
11
|
-
* an empty string if the package is not installed.
|
|
12
|
-
*/
|
|
13
|
-
function loadTransmitClient() {
|
|
14
|
-
try {
|
|
15
|
-
const req = createRequire(join(process.cwd(), "package.json"));
|
|
16
|
-
const clientPath = req.resolve("@adonisjs/transmit-client/build/index.js");
|
|
17
|
-
const src = readFileSync(clientPath, "utf-8");
|
|
18
|
-
return `(function(){var __exports={};(function(){${src.replace(/^export\s*\{[^}]*\}\s*;?\s*$/m, "")}\n__exports.Transmit=Transmit;})();window.Transmit=__exports.Transmit;})()`;
|
|
19
|
-
}
|
|
20
|
-
catch {
|
|
21
|
-
return "";
|
|
22
|
-
}
|
|
23
|
-
}
|
|
7
|
+
const read = (rel) => readFileSync(join(DIR, rel), 'utf-8');
|
|
24
8
|
/**
|
|
25
9
|
* Edge plugin that registers the `@serverStats()` tag.
|
|
26
10
|
*
|
|
@@ -43,64 +27,62 @@ function loadTransmitClient() {
|
|
|
43
27
|
export function edgePluginServerStats(config) {
|
|
44
28
|
return (edge) => {
|
|
45
29
|
// Mount Edge views under the `ss` disk (needed for @include resolution)
|
|
46
|
-
edge.mount(
|
|
30
|
+
edge.mount('ss', join(DIR, 'views'));
|
|
47
31
|
// Read client assets once at boot
|
|
48
|
-
const css = read(
|
|
49
|
-
const js = read(
|
|
50
|
-
const endpoint = typeof config.endpoint ===
|
|
51
|
-
? config.endpoint
|
|
52
|
-
: "/admin/api/server-stats";
|
|
32
|
+
const css = read('client/stats-bar.css');
|
|
33
|
+
const js = read('client/stats-bar.js');
|
|
34
|
+
const endpoint = typeof config.endpoint === 'string' ? config.endpoint : '/admin/api/server-stats';
|
|
53
35
|
const intervalMs = config.intervalMs || 3000;
|
|
54
36
|
const showDebug = !!config.devToolbar?.enabled;
|
|
55
37
|
// Badge groups for the Edge template
|
|
56
38
|
const groups = [
|
|
57
39
|
// Process
|
|
58
40
|
[
|
|
59
|
-
{ id:
|
|
60
|
-
{ id:
|
|
61
|
-
{ id:
|
|
62
|
-
{ id:
|
|
41
|
+
{ id: 'node', label: 'NODE' },
|
|
42
|
+
{ id: 'up', label: 'UP' },
|
|
43
|
+
{ id: 'cpu', label: 'CPU' },
|
|
44
|
+
{ id: 'evt', label: 'EVT' },
|
|
63
45
|
],
|
|
64
46
|
// Memory
|
|
65
47
|
[
|
|
66
|
-
{ id:
|
|
67
|
-
{ id:
|
|
68
|
-
{ id:
|
|
48
|
+
{ id: 'mem', label: 'HEAP' },
|
|
49
|
+
{ id: 'rss', label: 'RSS' },
|
|
50
|
+
{ id: 'sys', label: 'SYS' },
|
|
69
51
|
],
|
|
70
52
|
// HTTP
|
|
71
53
|
[
|
|
72
|
-
{ id:
|
|
73
|
-
{ id:
|
|
74
|
-
{ id:
|
|
75
|
-
{ id:
|
|
54
|
+
{ id: 'rps', label: 'REQ/s' },
|
|
55
|
+
{ id: 'avg', label: 'AVG' },
|
|
56
|
+
{ id: 'err', label: 'ERR' },
|
|
57
|
+
{ id: 'conn', label: 'CONN' },
|
|
76
58
|
],
|
|
77
59
|
// DB
|
|
78
|
-
[{ id:
|
|
60
|
+
[{ id: 'db', label: 'DB' }],
|
|
79
61
|
// Redis
|
|
80
62
|
[
|
|
81
|
-
{ id:
|
|
82
|
-
{ id:
|
|
83
|
-
{ id:
|
|
84
|
-
{ id:
|
|
63
|
+
{ id: 'redis', label: 'REDIS' },
|
|
64
|
+
{ id: 'rmem', label: 'MEM' },
|
|
65
|
+
{ id: 'rkeys', label: 'KEYS' },
|
|
66
|
+
{ id: 'rhit', label: 'HIT' },
|
|
85
67
|
],
|
|
86
68
|
// Queue
|
|
87
69
|
[
|
|
88
|
-
{ id:
|
|
89
|
-
{ id:
|
|
70
|
+
{ id: 'q', label: 'Q' },
|
|
71
|
+
{ id: 'workers', label: 'WORKERS' },
|
|
90
72
|
],
|
|
91
73
|
// App
|
|
92
74
|
[
|
|
93
|
-
{ id:
|
|
94
|
-
{ id:
|
|
95
|
-
{ id:
|
|
75
|
+
{ id: 'users', label: 'USERS' },
|
|
76
|
+
{ id: 'hooks', label: 'HOOKS' },
|
|
77
|
+
{ id: 'mail', label: 'MAIL' },
|
|
96
78
|
],
|
|
97
79
|
// Logs
|
|
98
80
|
[
|
|
99
|
-
{ id:
|
|
100
|
-
{ id:
|
|
81
|
+
{ id: 'logerr', label: 'LOG ERR' },
|
|
82
|
+
{ id: 'lograte', label: 'LOG/m' },
|
|
101
83
|
],
|
|
102
84
|
// Debug (conditional)
|
|
103
|
-
...(showDebug ? [[{ id:
|
|
85
|
+
...(showDebug ? [[{ id: 'dbg-queries', label: 'QRY' }]] : []),
|
|
104
86
|
];
|
|
105
87
|
const state = {
|
|
106
88
|
css,
|
|
@@ -111,25 +93,25 @@ export function edgePluginServerStats(config) {
|
|
|
111
93
|
groups,
|
|
112
94
|
};
|
|
113
95
|
if (showDebug) {
|
|
114
|
-
state.debugCss = read(
|
|
115
|
-
state.debugJs = read(
|
|
116
|
-
state.logsEndpoint =
|
|
96
|
+
state.debugCss = read('client/debug-panel.css');
|
|
97
|
+
state.debugJs = read('client/debug-panel.js');
|
|
98
|
+
state.logsEndpoint = '/admin/api/debug/logs';
|
|
117
99
|
state.customPanes = config.devToolbar?.panes || [];
|
|
118
100
|
state.showTracing = !!config.devToolbar?.tracing;
|
|
119
101
|
state.dashboardPath = config.devToolbar?.dashboard
|
|
120
|
-
?
|
|
102
|
+
? config.devToolbar.dashboardPath || '/__stats'
|
|
121
103
|
: null;
|
|
122
|
-
state.transmitClient = loadTransmitClient();
|
|
104
|
+
state.transmitClient = loadTransmitClient(join(process.cwd(), 'package.json'));
|
|
123
105
|
}
|
|
124
106
|
// Pre-render via Template directly — bypasses edge.createRenderer() which
|
|
125
107
|
// would re-run #executePlugins and cause infinite recursion.
|
|
126
108
|
const template = new Template(edge.compiler, edge.globals, {}, edge.processor);
|
|
127
|
-
const html = template.render(
|
|
109
|
+
const html = template.render('ss::stats-bar', state);
|
|
128
110
|
const escaped = JSON.stringify(html);
|
|
129
111
|
// Track whether shouldShow is configured (controls render-time guard)
|
|
130
112
|
const hasShouldShow = !!config.shouldShow;
|
|
131
113
|
edge.registerTag({
|
|
132
|
-
tagName:
|
|
114
|
+
tagName: 'serverStats',
|
|
133
115
|
block: false,
|
|
134
116
|
seekable: true,
|
|
135
117
|
compile(_parser, buffer, token) {
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<div id="ss-bar" class="ss-bar" data-endpoint="{{ endpoint }}" data-interval="{{ intervalMs }}">
|
|
6
6
|
<div class="ss-bar-left">
|
|
7
7
|
@if(showDebug)
|
|
8
|
-
<button type="button" id="ss-dbg-wrench" class="ss-dbg-btn" title="Open debug panel"
|
|
8
|
+
<button type="button" id="ss-dbg-wrench" class="ss-dbg-btn" title="Open debug panel">🔍 Open debug panel</button>
|
|
9
9
|
@end
|
|
10
10
|
<div id="ss-dot" class="ss-dot"></div>
|
|
11
11
|
</div>
|
package/dist/src/index.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ export { RequestMetrics } from './engine/request_metrics.js';
|
|
|
4
4
|
export { trace } from './debug/trace_collector.js';
|
|
5
5
|
export { DashboardStore } from './dashboard/dashboard_store.js';
|
|
6
6
|
export type { MetricCollector } from './collectors/collector.js';
|
|
7
|
-
export type { MetricValue, ServerStats, ServerStatsConfig, LogStats, DevToolbarOptions } from './types.js';
|
|
7
|
+
export type { MetricValue, ServerStats, ServerStatsConfig, LogStats, DevToolbarOptions, } from './types.js';
|
|
8
8
|
export type { DebugPane, DebugPaneColumn, DebugPaneFormatType, DebugPaneSearch, BadgeColor, QueryRecord, EventRecord, EmailRecord, RouteRecord, TraceSpan, TraceRecord, DevToolbarConfig, } from './debug/types.js';
|
|
9
9
|
export type { RequestFilters, QueryFilters, EventFilters, EmailFilters, LogFilters, TraceFilters, PaginatedResult, } from './dashboard/dashboard_store.js';
|
|
10
10
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/src/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AAC5D,OAAO,EAAE,KAAK,EAAE,MAAM,4BAA4B,CAAA;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAA;AAC/D,YAAY,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAA;AAChE,YAAY,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AAC5D,OAAO,EAAE,KAAK,EAAE,MAAM,4BAA4B,CAAA;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAA;AAC/D,YAAY,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAA;AAChE,YAAY,EACV,WAAW,EACX,WAAW,EACX,iBAAiB,EACjB,QAAQ,EACR,iBAAiB,GAClB,MAAM,YAAY,CAAA;AACnB,YAAY,EACV,SAAS,EACT,eAAe,EACf,mBAAmB,EACnB,eAAe,EACf,UAAU,EACV,WAAW,EACX,WAAW,EACX,WAAW,EACX,WAAW,EACX,SAAS,EACT,WAAW,EACX,gBAAgB,GACjB,MAAM,kBAAkB,CAAA;AACzB,YAAY,EACV,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,UAAU,EACV,YAAY,EACZ,eAAe,GAChB,MAAM,gCAAgC,CAAA"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
3
|
-
import type {
|
|
4
|
-
import type {
|
|
1
|
+
import type { TraceCollector } from '../debug/trace_collector.js';
|
|
2
|
+
import type { TraceRecord } from '../debug/types.js';
|
|
3
|
+
import type { HttpContext } from '@adonisjs/core/http';
|
|
4
|
+
import type { NextFn } from '@adonisjs/core/types/http';
|
|
5
5
|
/**
|
|
6
6
|
* Returns true if the current async context is inside an excluded request
|
|
7
7
|
* (e.g. a debug panel polling request). Used by collectors to skip
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"request_tracking_middleware.d.ts","sourceRoot":"","sources":["../../../src/middleware/request_tracking_middleware.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"request_tracking_middleware.d.ts","sourceRoot":"","sources":["../../../src/middleware/request_tracking_middleware.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AACjE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AACpD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AACtD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,2BAA2B,CAAA;AASvD;;;;GAIG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAE3C;AAOD,wBAAgB,aAAa,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,IAAI,QAE/D;AAOD,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,cAAc,GAAG,IAAI,QAEjE;AAQD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,QAEnD;AASD,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAErD;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAA;IACd,GAAG,EAAE,MAAM,CAAA;IACX,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,CAAC,EAAE,WAAW,CAAA;CACpB;AAQD,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,mBAAmB,KAAK,IAAI,CAAC,GAAG,IAAI,QAEpF;AAED,MAAM,CAAC,OAAO,OAAO,yBAAyB;IACtC,MAAM,CAAC,GAAG,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM;CAuE5C"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { AsyncLocalStorage } from
|
|
2
|
-
import { performance } from
|
|
3
|
-
import { getRequestMetrics } from
|
|
1
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
2
|
+
import { performance } from 'node:perf_hooks';
|
|
3
|
+
import { getRequestMetrics } from '../collectors/http_collector.js';
|
|
4
4
|
/**
|
|
5
5
|
* AsyncLocalStorage that marks the current request as "excluded" from
|
|
6
6
|
* debug collection. Checked by QueryCollector and EventCollector to
|
|
@@ -70,7 +70,8 @@ export default class RequestTrackingMiddleware {
|
|
|
70
70
|
// runs BEFORE router middleware like initialize_auth_middleware and
|
|
71
71
|
// silentAuth — so ctx.auth isn't populated yet. The function is called
|
|
72
72
|
// at Edge render time (inside the controller), when auth is available.
|
|
73
|
-
if (shouldShowFn && typeof ctx.view?.share ===
|
|
73
|
+
if (shouldShowFn && typeof ctx.view?.share === 'function') {
|
|
74
|
+
;
|
|
74
75
|
ctx.view.share({
|
|
75
76
|
__ssShowFn: () => {
|
|
76
77
|
try {
|
|
@@ -85,8 +86,8 @@ export default class RequestTrackingMiddleware {
|
|
|
85
86
|
// Skip tracing and dashboard persistence for the debug panel's own requests
|
|
86
87
|
// (e.g. /admin/api/debug/*, /admin/api/server-stats) so they don't flood
|
|
87
88
|
// the timeline. HTTP metrics (req/s, avg latency) are still recorded.
|
|
88
|
-
const skipTracing = excludedPrefixes.length > 0
|
|
89
|
-
|
|
89
|
+
const skipTracing = excludedPrefixes.length > 0 &&
|
|
90
|
+
excludedPrefixes.some((prefix) => requestUrl.startsWith(prefix));
|
|
90
91
|
const runRequest = async () => {
|
|
91
92
|
try {
|
|
92
93
|
await next();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { ConfigProvider } from '@adonisjs/core/types';
|
|
2
1
|
import type { ServerStats } from '../types.js';
|
|
2
|
+
import type { ConfigProvider } from '@adonisjs/core/types';
|
|
3
3
|
export declare function serverStatsCollector(): ConfigProvider<any>;
|
|
4
4
|
export declare const ServerStatsCollector: {
|
|
5
5
|
instance: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prometheus_collector.d.ts","sourceRoot":"","sources":["../../../src/prometheus/prometheus_collector.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"prometheus_collector.d.ts","sourceRoot":"","sources":["../../../src/prometheus/prometheus_collector.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAC9C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAE1D,wBAAgB,oBAAoB,IAAI,cAAc,CAAC,GAAG,CAAC,CA6M1D;AAED,eAAO,MAAM,oBAAoB,EAAE;IACjC,QAAQ,EAAE;QAAE,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI,CAAA;CAG/D,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server_stats_provider.d.ts","sourceRoot":"","sources":["../../../src/provider/server_stats_provider.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"server_stats_provider.d.ts","sourceRoot":"","sources":["../../../src/provider/server_stats_provider.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AAE9D,MAAM,CAAC,OAAO,OAAO,mBAAmB;IAY1B,SAAS,CAAC,GAAG,EAAE,kBAAkB;IAX7C,OAAO,CAAC,UAAU,CAA8C;IAChE,OAAO,CAAC,MAAM,CAA2B;IACzC,OAAO,CAAC,UAAU,CAA0B;IAC5C,OAAO,CAAC,cAAc,CAA8B;IACpD,OAAO,CAAC,mBAAmB,CAAmC;IAC9D,OAAO,CAAC,kBAAkB,CAAgC;IAC1D,OAAO,CAAC,uBAAuB,CAA8C;IAC7E,OAAO,CAAC,mBAAmB,CAA6C;IACxE,OAAO,CAAC,WAAW,CAAsB;IACzC,OAAO,CAAC,UAAU,CAA8C;gBAE1C,GAAG,EAAE,kBAAkB;IAEvC,IAAI;IAkCJ,KAAK;YA8EG,eAAe;IAmF7B;;;;;;OAMG;YACW,cAAc;IAoGtB,QAAQ;CAwCf"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
1
|
+
import { registerDashboardRoutes } from '../dashboard/dashboard_routes.js';
|
|
2
|
+
import { DashboardStore } from '../dashboard/dashboard_store.js';
|
|
3
|
+
import { DebugStore } from '../debug/debug_store.js';
|
|
4
|
+
import { StatsEngine } from '../engine/stats_engine.js';
|
|
5
|
+
import { LogStreamService } from '../log_stream/log_stream_service.js';
|
|
6
|
+
import { setShouldShow, setTraceCollector, setDashboardPath, setExcludedPrefixes, setOnRequestComplete, } from '../middleware/request_tracking_middleware.js';
|
|
7
7
|
export default class ServerStatsProvider {
|
|
8
8
|
app;
|
|
9
9
|
intervalId = null;
|
|
@@ -20,7 +20,7 @@ export default class ServerStatsProvider {
|
|
|
20
20
|
this.app = app;
|
|
21
21
|
}
|
|
22
22
|
async boot() {
|
|
23
|
-
const config = this.app.config.get(
|
|
23
|
+
const config = this.app.config.get('server_stats');
|
|
24
24
|
if (!config)
|
|
25
25
|
return;
|
|
26
26
|
// Wire up the per-request shouldShow callback
|
|
@@ -32,7 +32,7 @@ export default class ServerStatsProvider {
|
|
|
32
32
|
const toolbarConfig = config.devToolbar;
|
|
33
33
|
if (toolbarConfig?.enabled && toolbarConfig.dashboard && !this.app.inProduction) {
|
|
34
34
|
try {
|
|
35
|
-
const router = await this.app.container.make(
|
|
35
|
+
const router = await this.app.container.make('router');
|
|
36
36
|
const dashPath = toolbarConfig.dashboardPath ?? '/__stats';
|
|
37
37
|
registerDashboardRoutes(router, dashPath, () => this.dashboardController, config.shouldShow);
|
|
38
38
|
}
|
|
@@ -43,8 +43,8 @@ export default class ServerStatsProvider {
|
|
|
43
43
|
if (!this.app.usingEdgeJS)
|
|
44
44
|
return;
|
|
45
45
|
try {
|
|
46
|
-
const edge = await import(
|
|
47
|
-
const { edgePluginServerStats } = await import(
|
|
46
|
+
const edge = await import('edge.js');
|
|
47
|
+
const { edgePluginServerStats } = await import('../edge/plugin.js');
|
|
48
48
|
edge.default.use(edgePluginServerStats(config));
|
|
49
49
|
}
|
|
50
50
|
catch {
|
|
@@ -52,14 +52,13 @@ export default class ServerStatsProvider {
|
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
async ready() {
|
|
55
|
-
const config = this.app.config.get(
|
|
55
|
+
const config = this.app.config.get('server_stats');
|
|
56
56
|
if (!config)
|
|
57
57
|
return;
|
|
58
58
|
if (this.app.inTest && config.skipInTest !== false)
|
|
59
59
|
return;
|
|
60
60
|
this.engine = new StatsEngine(config.collectors);
|
|
61
|
-
|
|
62
|
-
this.app.container.singleton("server_stats.engine", () => this.engine);
|
|
61
|
+
this.app.container.singleton('server_stats.engine', () => this.engine);
|
|
63
62
|
await this.engine.start();
|
|
64
63
|
// Dev toolbar setup
|
|
65
64
|
const toolbarConfig = config.devToolbar;
|
|
@@ -89,9 +88,9 @@ export default class ServerStatsProvider {
|
|
|
89
88
|
}
|
|
90
89
|
}
|
|
91
90
|
let transmit = null;
|
|
92
|
-
if (config.transport ===
|
|
91
|
+
if (config.transport === 'transmit') {
|
|
93
92
|
try {
|
|
94
|
-
transmit = await this.app.container.make(
|
|
93
|
+
transmit = await this.app.container.make('transmit');
|
|
95
94
|
}
|
|
96
95
|
catch {
|
|
97
96
|
// Transmit not installed — skip broadcasting
|
|
@@ -99,7 +98,7 @@ export default class ServerStatsProvider {
|
|
|
99
98
|
}
|
|
100
99
|
let prometheusCollector = null;
|
|
101
100
|
try {
|
|
102
|
-
const mod = await import(
|
|
101
|
+
const mod = await import('../prometheus/prometheus_collector.js');
|
|
103
102
|
prometheusCollector = mod.ServerStatsCollector.instance;
|
|
104
103
|
}
|
|
105
104
|
catch {
|
|
@@ -123,19 +122,19 @@ export default class ServerStatsProvider {
|
|
|
123
122
|
}
|
|
124
123
|
async setupDevToolbar(toolbarConfig) {
|
|
125
124
|
this.debugStore = new DebugStore(toolbarConfig);
|
|
126
|
-
|
|
127
|
-
this.app.container.singleton("debug.store", () => this.debugStore);
|
|
125
|
+
this.app.container.singleton('debug.store', () => this.debugStore);
|
|
128
126
|
// Load persisted data before starting collectors
|
|
129
127
|
if (toolbarConfig.persistDebugData) {
|
|
130
|
-
this.persistPath =
|
|
131
|
-
|
|
132
|
-
|
|
128
|
+
this.persistPath =
|
|
129
|
+
typeof toolbarConfig.persistDebugData === 'string'
|
|
130
|
+
? this.app.makePath(toolbarConfig.persistDebugData)
|
|
131
|
+
: this.app.makePath('.adonisjs', 'server-stats', 'debug-data.json');
|
|
133
132
|
await this.debugStore.loadFromDisk(this.persistPath);
|
|
134
133
|
}
|
|
135
134
|
// Get the emitter
|
|
136
135
|
let emitter = null;
|
|
137
136
|
try {
|
|
138
|
-
emitter = await this.app.container.make(
|
|
137
|
+
emitter = await this.app.container.make('emitter');
|
|
139
138
|
}
|
|
140
139
|
catch {
|
|
141
140
|
// Emitter not available
|
|
@@ -143,7 +142,7 @@ export default class ServerStatsProvider {
|
|
|
143
142
|
// Get the router
|
|
144
143
|
let router = null;
|
|
145
144
|
try {
|
|
146
|
-
router = await this.app.container.make(
|
|
145
|
+
router = await this.app.container.make('router');
|
|
147
146
|
}
|
|
148
147
|
catch {
|
|
149
148
|
// Router not available
|
|
@@ -167,13 +166,13 @@ export default class ServerStatsProvider {
|
|
|
167
166
|
// ── Transmit broadcasting for debug panel live updates ────────
|
|
168
167
|
let debugTransmit = null;
|
|
169
168
|
try {
|
|
170
|
-
debugTransmit = await this.app.container.make(
|
|
169
|
+
debugTransmit = await this.app.container.make('transmit');
|
|
171
170
|
}
|
|
172
171
|
catch {
|
|
173
172
|
// Transmit not installed — debug panel will use polling
|
|
174
173
|
}
|
|
175
174
|
if (debugTransmit) {
|
|
176
|
-
const debugChannel =
|
|
175
|
+
const debugChannel = 'server-stats/debug';
|
|
177
176
|
const pendingTypes = new Set();
|
|
178
177
|
this.debugStore.onNewItem((type) => {
|
|
179
178
|
// Debounce: coalesce rapid events into a single broadcast
|
|
@@ -224,14 +223,15 @@ export default class ServerStatsProvider {
|
|
|
224
223
|
throw err;
|
|
225
224
|
}
|
|
226
225
|
// Bind to container
|
|
227
|
-
|
|
226
|
+
;
|
|
227
|
+
this.app.container.singleton('dashboard.store', () => this.dashboardStore);
|
|
228
228
|
// Set dashboard path in middleware for self-exclusion
|
|
229
229
|
setDashboardPath(toolbarConfig.dashboardPath);
|
|
230
230
|
// Create the controller — this makes the routes registered in boot() functional
|
|
231
|
-
const DashboardControllerClass = (await import(
|
|
231
|
+
const DashboardControllerClass = (await import('../dashboard/dashboard_controller.js')).default;
|
|
232
232
|
this.dashboardController = new DashboardControllerClass(this.dashboardStore, this.debugStore, this.app);
|
|
233
233
|
// ── Log piping ────────────────────────────────────────────────
|
|
234
|
-
const logPath = this.app.makePath(
|
|
234
|
+
const logPath = this.app.makePath('logs', 'adonisjs.log');
|
|
235
235
|
this.dashboardLogStream = new LogStreamService(logPath, (entry) => {
|
|
236
236
|
this.dashboardStore?.recordLog(entry);
|
|
237
237
|
});
|
|
@@ -271,18 +271,18 @@ export default class ServerStatsProvider {
|
|
|
271
271
|
// ── Transmit streaming for real-time dashboard updates ────────
|
|
272
272
|
let transmit = null;
|
|
273
273
|
try {
|
|
274
|
-
transmit = await this.app.container.make(
|
|
274
|
+
transmit = await this.app.container.make('transmit');
|
|
275
275
|
}
|
|
276
276
|
catch {
|
|
277
277
|
// Transmit not installed — skip real-time updates
|
|
278
278
|
}
|
|
279
279
|
if (transmit) {
|
|
280
|
-
const dashChannel =
|
|
280
|
+
const dashChannel = 'server-stats/dashboard';
|
|
281
281
|
this.dashboardBroadcastTimer = setInterval(async () => {
|
|
282
282
|
try {
|
|
283
283
|
if (!dashStore.isReady())
|
|
284
284
|
return;
|
|
285
|
-
const overview = await dashStore.getOverviewMetrics(
|
|
285
|
+
const overview = await dashStore.getOverviewMetrics('1h');
|
|
286
286
|
transmit.broadcast(dashChannel, overview);
|
|
287
287
|
}
|
|
288
288
|
catch {
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared JSON parsing utilities with safe fallbacks.
|
|
3
|
+
*/
|
|
4
|
+
/** Safely parse a JSON string, returning the original value on failure. */
|
|
5
|
+
export declare function safeParseJson(value: any): any;
|
|
6
|
+
/** Safely parse a JSON string expected to be an array, returning [] on failure. */
|
|
7
|
+
export declare function safeParseJsonArray(value: any): any[];
|
|
8
|
+
//# sourceMappingURL=json_helpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json_helpers.d.ts","sourceRoot":"","sources":["../../../src/utils/json_helpers.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,2EAA2E;AAC3E,wBAAgB,aAAa,CAAC,KAAK,EAAE,GAAG,GAAG,GAAG,CAQ7C;AAED,mFAAmF;AACnF,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,GAAG,GAAG,GAAG,EAAE,CAGpD"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared JSON parsing utilities with safe fallbacks.
|
|
3
|
+
*/
|
|
4
|
+
/** Safely parse a JSON string, returning the original value on failure. */
|
|
5
|
+
export function safeParseJson(value) {
|
|
6
|
+
if (value === null || value === undefined)
|
|
7
|
+
return null;
|
|
8
|
+
if (typeof value !== 'string')
|
|
9
|
+
return value;
|
|
10
|
+
try {
|
|
11
|
+
return JSON.parse(value);
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return value;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/** Safely parse a JSON string expected to be an array, returning [] on failure. */
|
|
18
|
+
export function safeParseJsonArray(value) {
|
|
19
|
+
const parsed = safeParseJson(value);
|
|
20
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
21
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared email address extraction utilities.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Normalize various AdonisJS mail address formats to a comma-separated string.
|
|
6
|
+
*
|
|
7
|
+
* Handles:
|
|
8
|
+
* - A string: `"user@example.com"`
|
|
9
|
+
* - An object: `{ address: "user@example.com", name: "User" }`
|
|
10
|
+
* - An array of strings or objects
|
|
11
|
+
*/
|
|
12
|
+
export declare function extractAddresses(value: any): string;
|
|
13
|
+
//# sourceMappingURL=mail_helpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mail_helpers.d.ts","sourceRoot":"","sources":["../../../src/utils/mail_helpers.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,GAAG,GAAG,MAAM,CAWnD"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared email address extraction utilities.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Normalize various AdonisJS mail address formats to a comma-separated string.
|
|
6
|
+
*
|
|
7
|
+
* Handles:
|
|
8
|
+
* - A string: `"user@example.com"`
|
|
9
|
+
* - An object: `{ address: "user@example.com", name: "User" }`
|
|
10
|
+
* - An array of strings or objects
|
|
11
|
+
*/
|
|
12
|
+
export function extractAddresses(value) {
|
|
13
|
+
if (!value)
|
|
14
|
+
return '';
|
|
15
|
+
if (typeof value === 'string')
|
|
16
|
+
return value;
|
|
17
|
+
if (Array.isArray(value)) {
|
|
18
|
+
return value
|
|
19
|
+
.map((v) => (typeof v === 'string' ? v : v?.address || ''))
|
|
20
|
+
.filter(Boolean)
|
|
21
|
+
.join(', ');
|
|
22
|
+
}
|
|
23
|
+
if (typeof value === 'object' && value.address)
|
|
24
|
+
return value.address;
|
|
25
|
+
return '';
|
|
26
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared math utilities used across dashboard, collectors, and chart aggregator.
|
|
3
|
+
*/
|
|
4
|
+
/** Round a number to 2 decimal places. */
|
|
5
|
+
export declare function round(n: number): number;
|
|
6
|
+
/** Clamp a value between min and max. */
|
|
7
|
+
export declare function clamp(value: number, min: number, max: number): number;
|
|
8
|
+
//# sourceMappingURL=math_helpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"math_helpers.d.ts","sourceRoot":"","sources":["../../../src/utils/math_helpers.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,0CAA0C;AAC1C,wBAAgB,KAAK,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAEvC;AAED,yCAAyC;AACzC,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAErE"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared math utilities used across dashboard, collectors, and chart aggregator.
|
|
3
|
+
*/
|
|
4
|
+
/** Round a number to 2 decimal places. */
|
|
5
|
+
export function round(n) {
|
|
6
|
+
return Math.round(n * 100) / 100;
|
|
7
|
+
}
|
|
8
|
+
/** Clamp a value between min and max. */
|
|
9
|
+
export function clamp(value, min, max) {
|
|
10
|
+
return Math.max(min, Math.min(max, value));
|
|
11
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared time/range utilities for dashboard store, controller, and chart aggregator.
|
|
3
|
+
*/
|
|
4
|
+
/** Convert a range string (e.g. '1h', '7d') to total minutes. */
|
|
5
|
+
export declare function rangeToMinutes(range: string): number;
|
|
6
|
+
/** Convert a range string to a SQLite-compatible datetime cutoff. */
|
|
7
|
+
export declare function rangeToCutoff(range: string): string;
|
|
8
|
+
/** Convert a Date to a SQLite-compatible datetime string (YYYY-MM-DD HH:MM:SS). */
|
|
9
|
+
export declare function toSqliteTimestamp(date: Date): string;
|
|
10
|
+
/** Round a bucket timestamp string down to the nearest N minutes. */
|
|
11
|
+
export declare function roundBucket(bucket: string, minutes: number): string;
|
|
12
|
+
//# sourceMappingURL=time_helpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"time_helpers.d.ts","sourceRoot":"","sources":["../../../src/utils/time_helpers.ts"],"names":[],"mappings":"AAAA;;GAEG;AAYH,iEAAiE;AACjE,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED,qEAAqE;AACrE,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAGnD;AAED,mFAAmF;AACnF,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAEpD;AAED,qEAAqE;AACrE,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAKnE"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared time/range utilities for dashboard store, controller, and chart aggregator.
|
|
3
|
+
*/
|
|
4
|
+
const RANGE_MAP = {
|
|
5
|
+
'5m': 5,
|
|
6
|
+
'15m': 15,
|
|
7
|
+
'30m': 30,
|
|
8
|
+
'1h': 60,
|
|
9
|
+
'6h': 360,
|
|
10
|
+
'24h': 1440,
|
|
11
|
+
'7d': 10080,
|
|
12
|
+
};
|
|
13
|
+
/** Convert a range string (e.g. '1h', '7d') to total minutes. */
|
|
14
|
+
export function rangeToMinutes(range) {
|
|
15
|
+
return RANGE_MAP[range] ?? 60;
|
|
16
|
+
}
|
|
17
|
+
/** Convert a range string to a SQLite-compatible datetime cutoff. */
|
|
18
|
+
export function rangeToCutoff(range) {
|
|
19
|
+
const minutes = rangeToMinutes(range);
|
|
20
|
+
return toSqliteTimestamp(new Date(Date.now() - minutes * 60_000));
|
|
21
|
+
}
|
|
22
|
+
/** Convert a Date to a SQLite-compatible datetime string (YYYY-MM-DD HH:MM:SS). */
|
|
23
|
+
export function toSqliteTimestamp(date) {
|
|
24
|
+
return date.toISOString().replace('T', ' ').slice(0, 19);
|
|
25
|
+
}
|
|
26
|
+
/** Round a bucket timestamp string down to the nearest N minutes. */
|
|
27
|
+
export function roundBucket(bucket, minutes) {
|
|
28
|
+
const date = new Date(bucket.replace(' ', 'T') + 'Z');
|
|
29
|
+
const ms = minutes * 60_000;
|
|
30
|
+
const rounded = new Date(Math.floor(date.getTime() / ms) * ms);
|
|
31
|
+
return toSqliteTimestamp(rounded);
|
|
32
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Try to locate and read the @adonisjs/transmit-client build file.
|
|
3
|
+
* Returns the file contents wrapped to expose `window.Transmit`, or
|
|
4
|
+
* an empty string if the package is not installed.
|
|
5
|
+
*
|
|
6
|
+
* @param packageJsonPath - Absolute path to a package.json for require resolution
|
|
7
|
+
*/
|
|
8
|
+
export declare function loadTransmitClient(packageJsonPath: string): string;
|
|
9
|
+
//# sourceMappingURL=transmit_client.d.ts.map
|