adonisjs-server-stats 1.3.1 → 1.4.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 +1 -1
- package/dist/src/dashboard/dashboard_controller.d.ts.map +1 -1
- package/dist/src/dashboard/dashboard_controller.js +48 -1
- package/dist/src/dashboard/dashboard_store.d.ts +33 -0
- package/dist/src/dashboard/dashboard_store.d.ts.map +1 -1
- package/dist/src/dashboard/dashboard_store.js +91 -0
- package/dist/src/edge/client/dashboard.css +45 -0
- package/dist/src/edge/client/dashboard.js +160 -13
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/adonisjs-server-stats)
|
|
4
4
|
[](https://www.npmjs.com/package/adonisjs-server-stats)
|
|
5
|
-
[](https://bundlephobia.com/package/adonisjs-server-stats)
|
|
6
5
|
[](https://github.com/simulieren/adonisjs-server-stats/blob/main/LICENSE)
|
|
7
6
|
[](https://www.typescriptlang.org/)
|
|
8
7
|
[](https://adonisjs.com/)
|
|
8
|
+
[](https://adonisjs.com/)
|
|
9
9
|
|
|
10
10
|
A Laravel Telescope-inspired dev toolbar and real-time server monitor for **AdonisJS v6**.
|
|
11
11
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dashboard_controller.d.ts","sourceRoot":"","sources":["../../../src/dashboard/dashboard_controller.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AACtD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AAC9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAC1D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAA;AAOzD;;;;;;GAMG;AACH,MAAM,CAAC,OAAO,OAAO,mBAAmB;IAWpC,OAAO,CAAC,cAAc;IACtB,OAAO,CAAC,UAAU;IAClB,OAAO,CAAC,GAAG;IAZb,OAAO,CAAC,cAAc,CAA8B;IACpD,OAAO,CAAC,cAAc,CAA8B;IACpD,OAAO,CAAC,eAAe,CAAiB;IACxC,OAAO,CAAC,cAAc,CAAuB;IAC7C,OAAO,CAAC,cAAc,CAAuB;IAC7C,OAAO,CAAC,SAAS,CAAsB;IACvC,OAAO,CAAC,QAAQ,CAAsB;IACtC,OAAO,CAAC,oBAAoB,CAAsB;gBAGxC,cAAc,EAAE,cAAc,EAC9B,UAAU,EAAE,UAAU,EACtB,GAAG,EAAE,kBAAkB;IASjC;;;;;OAKG;IACG,IAAI,CAAC,GAAG,EAAE,WAAW;IA0C3B;;OAEG;IACG,QAAQ,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,WAAW;
|
|
1
|
+
{"version":3,"file":"dashboard_controller.d.ts","sourceRoot":"","sources":["../../../src/dashboard/dashboard_controller.ts"],"names":[],"mappings":"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"}
|
|
@@ -87,7 +87,10 @@ export default class DashboardController {
|
|
|
87
87
|
try {
|
|
88
88
|
const range = request.qs().range || '1h';
|
|
89
89
|
// Use getOverviewMetrics for accurate stats (computed from raw requests)
|
|
90
|
-
const overview = await
|
|
90
|
+
const [overview, widgets] = await Promise.all([
|
|
91
|
+
this.dashboardStore.getOverviewMetrics(range),
|
|
92
|
+
this.dashboardStore.getOverviewWidgets(range),
|
|
93
|
+
]);
|
|
91
94
|
if (!overview)
|
|
92
95
|
return response.json(emptyOverview());
|
|
93
96
|
// Add sparklines from the pre-aggregated metrics table
|
|
@@ -96,6 +99,40 @@ export default class DashboardController {
|
|
|
96
99
|
.where('created_at', '>=', cutoff)
|
|
97
100
|
.orderBy('bucket', 'asc');
|
|
98
101
|
const sparklineData = metrics.slice(-15);
|
|
102
|
+
// Fetch cache and queue stats (non-blocking — null if unavailable)
|
|
103
|
+
let cacheStats = null;
|
|
104
|
+
let jobQueueStatus = null;
|
|
105
|
+
try {
|
|
106
|
+
const cacheInspector = await this.getCacheInspector();
|
|
107
|
+
if (cacheInspector) {
|
|
108
|
+
const stats = await cacheInspector.getStats();
|
|
109
|
+
cacheStats = {
|
|
110
|
+
available: true,
|
|
111
|
+
totalKeys: stats.totalKeys,
|
|
112
|
+
hitRate: stats.hitRate,
|
|
113
|
+
memoryUsedHuman: stats.memoryUsedHuman,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
// Cache not available
|
|
119
|
+
}
|
|
120
|
+
try {
|
|
121
|
+
const queueInspector = await this.getQueueInspector();
|
|
122
|
+
if (queueInspector) {
|
|
123
|
+
const qOverview = await queueInspector.getOverview();
|
|
124
|
+
jobQueueStatus = {
|
|
125
|
+
available: true,
|
|
126
|
+
active: qOverview.active,
|
|
127
|
+
waiting: qOverview.waiting,
|
|
128
|
+
failed: qOverview.failed,
|
|
129
|
+
completed: qOverview.completed,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
// Queue not available
|
|
135
|
+
}
|
|
99
136
|
return response.json({
|
|
100
137
|
...overview,
|
|
101
138
|
sparklines: {
|
|
@@ -104,6 +141,9 @@ export default class DashboardController {
|
|
|
104
141
|
requestsPerMinute: sparklineData.map((m) => m.request_count),
|
|
105
142
|
errorRate: sparklineData.map((m) => m.request_count > 0 ? round((m.error_count / m.request_count) * 100) : 0),
|
|
106
143
|
},
|
|
144
|
+
...widgets,
|
|
145
|
+
cacheStats,
|
|
146
|
+
jobQueueStatus,
|
|
107
147
|
});
|
|
108
148
|
}
|
|
109
149
|
catch {
|
|
@@ -962,6 +1002,13 @@ function emptyOverview() {
|
|
|
962
1002
|
slowestEndpoints: [],
|
|
963
1003
|
queryStats: { total: 0, avgDuration: 0, perRequest: 0 },
|
|
964
1004
|
recentErrors: [],
|
|
1005
|
+
topEvents: [],
|
|
1006
|
+
emailActivity: { sent: 0, queued: 0, failed: 0 },
|
|
1007
|
+
logLevelBreakdown: { error: 0, warn: 0, info: 0, debug: 0 },
|
|
1008
|
+
statusDistribution: { '2xx': 0, '3xx': 0, '4xx': 0, '5xx': 0 },
|
|
1009
|
+
slowestQueries: [],
|
|
1010
|
+
cacheStats: null,
|
|
1011
|
+
jobQueueStatus: null,
|
|
965
1012
|
};
|
|
966
1013
|
}
|
|
967
1014
|
// ---------------------------------------------------------------------------
|
|
@@ -137,6 +137,39 @@ export declare class DashboardStore {
|
|
|
137
137
|
* @param range — '1h' | '6h' | '24h' | '7d'
|
|
138
138
|
*/
|
|
139
139
|
getChartData(range?: string): Promise<any[]>;
|
|
140
|
+
/**
|
|
141
|
+
* Widget data for the dashboard overview.
|
|
142
|
+
*
|
|
143
|
+
* @param range — '1h' | '6h' | '24h' | '7d'
|
|
144
|
+
*/
|
|
145
|
+
getOverviewWidgets(range?: string): Promise<{
|
|
146
|
+
topEvents: {
|
|
147
|
+
eventName: string;
|
|
148
|
+
count: number;
|
|
149
|
+
}[];
|
|
150
|
+
emailActivity: {
|
|
151
|
+
sent: number;
|
|
152
|
+
queued: number;
|
|
153
|
+
failed: number;
|
|
154
|
+
};
|
|
155
|
+
logLevelBreakdown: {
|
|
156
|
+
error: number;
|
|
157
|
+
warn: number;
|
|
158
|
+
info: number;
|
|
159
|
+
debug: number;
|
|
160
|
+
};
|
|
161
|
+
statusDistribution: {
|
|
162
|
+
'2xx': number;
|
|
163
|
+
'3xx': number;
|
|
164
|
+
'4xx': number;
|
|
165
|
+
'5xx': number;
|
|
166
|
+
};
|
|
167
|
+
slowestQueries: {
|
|
168
|
+
sqlNormalized: string;
|
|
169
|
+
avgDuration: number;
|
|
170
|
+
count: number;
|
|
171
|
+
}[];
|
|
172
|
+
}>;
|
|
140
173
|
getSavedFilters(section?: string): Promise<any[]>;
|
|
141
174
|
createSavedFilter(name: string, section: string, filterConfig: Record<string, any>): Promise<any>;
|
|
142
175
|
deleteSavedFilter(id: number): Promise<boolean>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dashboard_store.d.ts","sourceRoot":"","sources":["../../../src/dashboard/dashboard_store.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"dashboard_store.d.ts","sourceRoot":"","sources":["../../../src/dashboard/dashboard_store.ts"],"names":[],"mappings":"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"}
|
|
@@ -546,6 +546,97 @@ export class DashboardStore {
|
|
|
546
546
|
avg_query_duration: g._count > 0 ? Math.round((g.avg_query_duration / g._count) * 100) / 100 : 0,
|
|
547
547
|
}));
|
|
548
548
|
}
|
|
549
|
+
/**
|
|
550
|
+
* Widget data for the dashboard overview.
|
|
551
|
+
*
|
|
552
|
+
* @param range — '1h' | '6h' | '24h' | '7d'
|
|
553
|
+
*/
|
|
554
|
+
async getOverviewWidgets(range = '1h') {
|
|
555
|
+
const empty = {
|
|
556
|
+
topEvents: [],
|
|
557
|
+
emailActivity: { sent: 0, queued: 0, failed: 0 },
|
|
558
|
+
logLevelBreakdown: { error: 0, warn: 0, info: 0, debug: 0 },
|
|
559
|
+
statusDistribution: { '2xx': 0, '3xx': 0, '4xx': 0, '5xx': 0 },
|
|
560
|
+
slowestQueries: [],
|
|
561
|
+
};
|
|
562
|
+
if (!this.db)
|
|
563
|
+
return empty;
|
|
564
|
+
const cutoff = rangeToCutoff(range);
|
|
565
|
+
try {
|
|
566
|
+
const [topEventsRaw, emailStatusRaw, logLevelsRaw, statusRaw, slowQueriesRaw] = await Promise.all([
|
|
567
|
+
// Top 5 events by count
|
|
568
|
+
this.db('server_stats_events')
|
|
569
|
+
.select('event_name', this.db.raw('COUNT(*) as count'))
|
|
570
|
+
.where('created_at', '>=', cutoff)
|
|
571
|
+
.groupBy('event_name')
|
|
572
|
+
.orderBy('count', 'desc')
|
|
573
|
+
.limit(5),
|
|
574
|
+
// Email activity by status
|
|
575
|
+
this.db('server_stats_emails')
|
|
576
|
+
.select('status', this.db.raw('COUNT(*) as count'))
|
|
577
|
+
.where('created_at', '>=', cutoff)
|
|
578
|
+
.groupBy('status'),
|
|
579
|
+
// Log level breakdown
|
|
580
|
+
this.db('server_stats_logs')
|
|
581
|
+
.select('level', this.db.raw('COUNT(*) as count'))
|
|
582
|
+
.where('created_at', '>=', cutoff)
|
|
583
|
+
.groupBy('level'),
|
|
584
|
+
// Status code distribution bucketed into 2xx/3xx/4xx/5xx
|
|
585
|
+
this.db('server_stats_requests')
|
|
586
|
+
.select(this.db.raw(`SUM(CASE WHEN status_code >= 200 AND status_code < 300 THEN 1 ELSE 0 END) as "s2xx"`), this.db.raw(`SUM(CASE WHEN status_code >= 300 AND status_code < 400 THEN 1 ELSE 0 END) as "s3xx"`), this.db.raw(`SUM(CASE WHEN status_code >= 400 AND status_code < 500 THEN 1 ELSE 0 END) as "s4xx"`), this.db.raw(`SUM(CASE WHEN status_code >= 500 AND status_code < 600 THEN 1 ELSE 0 END) as "s5xx"`))
|
|
587
|
+
.where('created_at', '>=', cutoff)
|
|
588
|
+
.first(),
|
|
589
|
+
// Slowest queries by avg duration (top 5)
|
|
590
|
+
this.db('server_stats_queries')
|
|
591
|
+
.select('sql_normalized', this.db.raw('ROUND(AVG(duration), 2) as avg_duration'), this.db.raw('COUNT(*) as count'))
|
|
592
|
+
.where('created_at', '>=', cutoff)
|
|
593
|
+
.groupBy('sql_normalized')
|
|
594
|
+
.orderBy('avg_duration', 'desc')
|
|
595
|
+
.limit(5),
|
|
596
|
+
]);
|
|
597
|
+
// Map top events
|
|
598
|
+
const topEvents = (topEventsRaw || []).map((r) => ({
|
|
599
|
+
eventName: r.event_name,
|
|
600
|
+
count: r.count,
|
|
601
|
+
}));
|
|
602
|
+
// Map email activity
|
|
603
|
+
const emailActivity = { sent: 0, queued: 0, failed: 0 };
|
|
604
|
+
for (const row of emailStatusRaw || []) {
|
|
605
|
+
const status = row.status;
|
|
606
|
+
if (status === 'sent')
|
|
607
|
+
emailActivity.sent = row.count;
|
|
608
|
+
else if (status === 'queued')
|
|
609
|
+
emailActivity.queued = row.count;
|
|
610
|
+
else if (status === 'failed')
|
|
611
|
+
emailActivity.failed = row.count;
|
|
612
|
+
}
|
|
613
|
+
// Map log level breakdown
|
|
614
|
+
const logLevelBreakdown = { error: 0, warn: 0, info: 0, debug: 0 };
|
|
615
|
+
for (const row of logLevelsRaw || []) {
|
|
616
|
+
const level = row.level;
|
|
617
|
+
if (level in logLevelBreakdown) {
|
|
618
|
+
logLevelBreakdown[level] = row.count;
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
// Map status distribution
|
|
622
|
+
const statusDistribution = {
|
|
623
|
+
'2xx': statusRaw?.s2xx ?? 0,
|
|
624
|
+
'3xx': statusRaw?.s3xx ?? 0,
|
|
625
|
+
'4xx': statusRaw?.s4xx ?? 0,
|
|
626
|
+
'5xx': statusRaw?.s5xx ?? 0,
|
|
627
|
+
};
|
|
628
|
+
// Map slowest queries
|
|
629
|
+
const slowestQueries = (slowQueriesRaw || []).map((r) => ({
|
|
630
|
+
sqlNormalized: r.sql_normalized,
|
|
631
|
+
avgDuration: r.avg_duration,
|
|
632
|
+
count: r.count,
|
|
633
|
+
}));
|
|
634
|
+
return { topEvents, emailActivity, logLevelBreakdown, statusDistribution, slowestQueries };
|
|
635
|
+
}
|
|
636
|
+
catch {
|
|
637
|
+
return empty;
|
|
638
|
+
}
|
|
639
|
+
}
|
|
549
640
|
// =========================================================================
|
|
550
641
|
// Saved filters CRUD
|
|
551
642
|
// =========================================================================
|
|
@@ -1200,6 +1200,11 @@ html, body {
|
|
|
1200
1200
|
border-bottom: 1px solid var(--ss-border-faint);
|
|
1201
1201
|
min-width: 0;
|
|
1202
1202
|
}
|
|
1203
|
+
.ss-dash-secondary-list li:has(> a) {
|
|
1204
|
+
display: block;
|
|
1205
|
+
padding: 0;
|
|
1206
|
+
border-bottom: none;
|
|
1207
|
+
}
|
|
1203
1208
|
.ss-dash-secondary-list li > span:first-child {
|
|
1204
1209
|
overflow: hidden;
|
|
1205
1210
|
text-overflow: ellipsis;
|
|
@@ -1502,3 +1507,43 @@ tr:hover .ss-dash-copy-row-btn { opacity: 1; }
|
|
|
1502
1507
|
.ss-dash-list-view { display: flex; flex-direction: column; height: 100%; }
|
|
1503
1508
|
.ss-dash-detail-view { display: none; flex-direction: column; height: 100%; }
|
|
1504
1509
|
.ss-dash-detail-view.ss-dash-active { display: flex; }
|
|
1510
|
+
|
|
1511
|
+
/* Widget deep links */
|
|
1512
|
+
.ss-dash-widget-link {
|
|
1513
|
+
color: var(--ss-muted);
|
|
1514
|
+
text-decoration: none;
|
|
1515
|
+
transition: color 0.15s;
|
|
1516
|
+
}
|
|
1517
|
+
.ss-dash-widget-link:hover { color: var(--ss-accent); }
|
|
1518
|
+
|
|
1519
|
+
.ss-dash-widget-row-link {
|
|
1520
|
+
display: flex;
|
|
1521
|
+
justify-content: space-between;
|
|
1522
|
+
gap: 12px;
|
|
1523
|
+
padding: 3px 0;
|
|
1524
|
+
font-size: 11px;
|
|
1525
|
+
color: var(--ss-text);
|
|
1526
|
+
border-bottom: 1px solid var(--ss-border-faint);
|
|
1527
|
+
text-decoration: none;
|
|
1528
|
+
min-width: 0;
|
|
1529
|
+
transition: color 0.15s;
|
|
1530
|
+
}
|
|
1531
|
+
.ss-dash-widget-row-link:last-child { border-bottom: none; }
|
|
1532
|
+
.ss-dash-widget-row-link:hover { color: var(--ss-accent); }
|
|
1533
|
+
.ss-dash-widget-row-link > span:first-child {
|
|
1534
|
+
overflow: hidden;
|
|
1535
|
+
text-overflow: ellipsis;
|
|
1536
|
+
white-space: nowrap;
|
|
1537
|
+
min-width: 0;
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
.ss-dash-widget-stat {
|
|
1541
|
+
display: flex;
|
|
1542
|
+
justify-content: space-between;
|
|
1543
|
+
padding: 3px 0;
|
|
1544
|
+
font-size: 11px;
|
|
1545
|
+
border-bottom: 1px solid var(--ss-border-faint);
|
|
1546
|
+
}
|
|
1547
|
+
.ss-dash-widget-stat:last-child { border-bottom: none; }
|
|
1548
|
+
.ss-dash-widget-stat-label { color: var(--ss-muted); }
|
|
1549
|
+
.ss-dash-widget-stat-value { font-weight: 600; font-variant-numeric: tabular-nums; }
|
|
@@ -283,6 +283,13 @@
|
|
|
283
283
|
var slowest = data.slowestEndpoints || [];
|
|
284
284
|
var queryStats = data.queryStats || {};
|
|
285
285
|
var recentErrors = data.recentErrors || [];
|
|
286
|
+
var topEvents = data.topEvents || [];
|
|
287
|
+
var emailActivity = data.emailActivity || {};
|
|
288
|
+
var logLevelBreakdown = data.logLevelBreakdown || {};
|
|
289
|
+
var cacheStats = data.cacheStats || null;
|
|
290
|
+
var jobQueueStatus = data.jobQueueStatus || null;
|
|
291
|
+
var statusDistribution = data.statusDistribution || {};
|
|
292
|
+
var slowQueries = data.slowestQueries || [];
|
|
286
293
|
|
|
287
294
|
var avgVal = data.avgResponseTime || 0;
|
|
288
295
|
var p95Val = data.p95ResponseTime || 0;
|
|
@@ -325,11 +332,12 @@
|
|
|
325
332
|
|
|
326
333
|
// Slowest endpoints
|
|
327
334
|
html += '<div class="ss-dash-secondary-card">';
|
|
328
|
-
html += '<div class="ss-dash-secondary-card-title">Slowest Endpoints</div>';
|
|
335
|
+
html += '<div class="ss-dash-secondary-card-title"><a href="#requests" class="ss-dash-widget-link">Slowest Endpoints</a></div>';
|
|
329
336
|
if (slowest.length > 0) {
|
|
330
337
|
html += '<ul class="ss-dash-secondary-list">';
|
|
331
338
|
slowest.forEach(function (ep) {
|
|
332
|
-
|
|
339
|
+
var epUrl = ep.url || ep.pattern || '-';
|
|
340
|
+
html += '<li><a href="#requests?url=' + encodeURIComponent(epUrl) + '" class="ss-dash-widget-row-link"><span title="' + esc(epUrl) + '">' + esc(epUrl) + '</span><span class="ss-dash-secondary-list-value ss-dash-duration ' + durationClass(ep.avgDuration || 0) + '">' + (ep.avgDuration || 0).toFixed(1) + 'ms</span></a></li>';
|
|
333
341
|
});
|
|
334
342
|
html += '</ul>';
|
|
335
343
|
} else {
|
|
@@ -339,7 +347,7 @@
|
|
|
339
347
|
|
|
340
348
|
// Query stats
|
|
341
349
|
html += '<div class="ss-dash-secondary-card">';
|
|
342
|
-
html += '<div class="ss-dash-secondary-card-title">Query Stats</div>';
|
|
350
|
+
html += '<div class="ss-dash-secondary-card-title"><a href="#queries" class="ss-dash-widget-link">Query Stats</a></div>';
|
|
343
351
|
html += '<ul class="ss-dash-secondary-list">';
|
|
344
352
|
html += '<li><span>Total Queries</span><span class="ss-dash-secondary-list-value">' + (queryStats.total || 0) + '</span></li>';
|
|
345
353
|
html += '<li><span>Avg Duration</span><span class="ss-dash-secondary-list-value">' + (queryStats.avgDuration || 0).toFixed(1) + 'ms</span></li>';
|
|
@@ -349,11 +357,11 @@
|
|
|
349
357
|
|
|
350
358
|
// Recent errors
|
|
351
359
|
html += '<div class="ss-dash-secondary-card">';
|
|
352
|
-
html += '<div class="ss-dash-secondary-card-title">Recent Errors</div>';
|
|
360
|
+
html += '<div class="ss-dash-secondary-card-title"><a href="#logs?level=error" class="ss-dash-widget-link">Recent Errors</a></div>';
|
|
353
361
|
if (recentErrors.length > 0) {
|
|
354
362
|
html += '<ul class="ss-dash-secondary-list">';
|
|
355
363
|
recentErrors.forEach(function (err) {
|
|
356
|
-
html += '<li><span style="color:var(--ss-red-fg)" title="' + esc(err.message || '') + '">' + esc(err.message || '') + '</span><span class="ss-dash-secondary-list-value">' + timeAgo(err.createdAt || err.created_at || err.timestamp) + '</span></li>';
|
|
364
|
+
html += '<li><a href="#logs?id=' + encodeURIComponent(err.id || '') + '" class="ss-dash-widget-row-link"><span style="color:var(--ss-red-fg)" title="' + esc(err.message || '') + '">' + esc(err.message || '') + '</span><span class="ss-dash-secondary-list-value">' + timeAgo(err.createdAt || err.created_at || err.timestamp) + '</span></a></li>';
|
|
357
365
|
});
|
|
358
366
|
html += '</ul>';
|
|
359
367
|
} else {
|
|
@@ -361,6 +369,95 @@
|
|
|
361
369
|
}
|
|
362
370
|
html += '</div>';
|
|
363
371
|
|
|
372
|
+
// Top Events
|
|
373
|
+
html += '<div class="ss-dash-secondary-card">';
|
|
374
|
+
html += '<div class="ss-dash-secondary-card-title"><a href="#events" class="ss-dash-widget-link">Top Events</a></div>';
|
|
375
|
+
if (topEvents.length > 0) {
|
|
376
|
+
html += '<ul class="ss-dash-secondary-list">';
|
|
377
|
+
topEvents.slice(0, 5).forEach(function (ev) {
|
|
378
|
+
html += '<li><a href="#events?event_name=' + encodeURIComponent(ev.name || ev.event || '') + '" class="ss-dash-widget-row-link"><span title="' + esc(ev.name || ev.event || '') + '">' + esc(ev.name || ev.event || '') + '</span><span class="ss-dash-secondary-list-value">' + (ev.count || 0) + '</span></a></li>';
|
|
379
|
+
});
|
|
380
|
+
html += '</ul>';
|
|
381
|
+
} else {
|
|
382
|
+
html += '<div class="ss-dash-empty" style="min-height:60px">No events yet</div>';
|
|
383
|
+
}
|
|
384
|
+
html += '</div>';
|
|
385
|
+
|
|
386
|
+
// Email Activity
|
|
387
|
+
html += '<div class="ss-dash-secondary-card">';
|
|
388
|
+
html += '<div class="ss-dash-secondary-card-title"><a href="#emails" class="ss-dash-widget-link">Email Activity</a></div>';
|
|
389
|
+
html += '<ul class="ss-dash-secondary-list">';
|
|
390
|
+
html += '<li><a href="#emails?status=sent" class="ss-dash-widget-row-link"><span>Sent</span><span class="ss-dash-secondary-list-value">' + (emailActivity.sent || 0) + '</span></a></li>';
|
|
391
|
+
html += '<li><a href="#emails?status=queued" class="ss-dash-widget-row-link"><span>Queued</span><span class="ss-dash-secondary-list-value">' + (emailActivity.queued || 0) + '</span></a></li>';
|
|
392
|
+
html += '<li><a href="#emails?status=failed" class="ss-dash-widget-row-link"><span>Failed</span><span class="ss-dash-secondary-list-value">' + (emailActivity.failed || 0) + '</span></a></li>';
|
|
393
|
+
html += '</ul>';
|
|
394
|
+
html += '</div>';
|
|
395
|
+
|
|
396
|
+
// Log Level Breakdown
|
|
397
|
+
html += '<div class="ss-dash-secondary-card">';
|
|
398
|
+
html += '<div class="ss-dash-secondary-card-title"><a href="#logs" class="ss-dash-widget-link">Log Levels</a></div>';
|
|
399
|
+
html += '<ul class="ss-dash-secondary-list">';
|
|
400
|
+
html += '<li><a href="#logs?level=error" class="ss-dash-widget-row-link"><span style="color:var(--ss-red-fg)">Error</span><span class="ss-dash-secondary-list-value">' + (logLevelBreakdown.error || 0) + '</span></a></li>';
|
|
401
|
+
html += '<li><a href="#logs?level=warn" class="ss-dash-widget-row-link"><span style="color:var(--ss-amber-fg)">Warn</span><span class="ss-dash-secondary-list-value">' + (logLevelBreakdown.warn || 0) + '</span></a></li>';
|
|
402
|
+
html += '<li><a href="#logs?level=info" class="ss-dash-widget-row-link"><span style="color:var(--ss-green-fg)">Info</span><span class="ss-dash-secondary-list-value">' + (logLevelBreakdown.info || 0) + '</span></a></li>';
|
|
403
|
+
html += '<li><a href="#logs?level=debug" class="ss-dash-widget-row-link"><span style="color:var(--ss-muted)">Debug</span><span class="ss-dash-secondary-list-value">' + (logLevelBreakdown.debug || 0) + '</span></a></li>';
|
|
404
|
+
html += '</ul>';
|
|
405
|
+
html += '</div>';
|
|
406
|
+
|
|
407
|
+
// Cache Stats
|
|
408
|
+
html += '<div class="ss-dash-secondary-card">';
|
|
409
|
+
html += '<div class="ss-dash-secondary-card-title"><a href="#cache" class="ss-dash-widget-link">Cache</a></div>';
|
|
410
|
+
if (cacheStats) {
|
|
411
|
+
html += '<div class="ss-dash-widget-stat"><span class="ss-dash-widget-stat-label">Connected</span><span class="ss-dash-widget-stat-value" style="color:var(--ss-green-fg)">\u2713</span></div>';
|
|
412
|
+
html += '<div class="ss-dash-widget-stat"><span class="ss-dash-widget-stat-label">Total Keys</span><span class="ss-dash-widget-stat-value">' + (cacheStats.totalKeys || 0) + '</span></div>';
|
|
413
|
+
html += '<div class="ss-dash-widget-stat"><span class="ss-dash-widget-stat-label">Hit Rate</span><span class="ss-dash-widget-stat-value">' + (cacheStats.hitRate || 0).toFixed(1) + '%</span></div>';
|
|
414
|
+
html += '<div class="ss-dash-widget-stat"><span class="ss-dash-widget-stat-label">Memory</span><span class="ss-dash-widget-stat-value">' + esc(cacheStats.memory || '-') + '</span></div>';
|
|
415
|
+
} else {
|
|
416
|
+
html += '<div class="ss-dash-empty" style="min-height:60px">Not available</div>';
|
|
417
|
+
}
|
|
418
|
+
html += '</div>';
|
|
419
|
+
|
|
420
|
+
// Job Queue
|
|
421
|
+
html += '<div class="ss-dash-secondary-card">';
|
|
422
|
+
html += '<div class="ss-dash-secondary-card-title"><a href="#jobs" class="ss-dash-widget-link">Job Queue</a></div>';
|
|
423
|
+
if (jobQueueStatus) {
|
|
424
|
+
html += '<ul class="ss-dash-secondary-list">';
|
|
425
|
+
html += '<li><a href="#jobs?status=active" class="ss-dash-widget-row-link"><span>Active</span><span class="ss-dash-secondary-list-value">' + (jobQueueStatus.active || 0) + '</span></a></li>';
|
|
426
|
+
html += '<li><a href="#jobs?status=waiting" class="ss-dash-widget-row-link"><span>Waiting</span><span class="ss-dash-secondary-list-value">' + (jobQueueStatus.waiting || 0) + '</span></a></li>';
|
|
427
|
+
html += '<li><a href="#jobs?status=failed" class="ss-dash-widget-row-link"><span>Failed</span><span class="ss-dash-secondary-list-value">' + (jobQueueStatus.failed || 0) + '</span></a></li>';
|
|
428
|
+
html += '<li><a href="#jobs?status=completed" class="ss-dash-widget-row-link"><span>Completed</span><span class="ss-dash-secondary-list-value">' + (jobQueueStatus.completed || 0) + '</span></a></li>';
|
|
429
|
+
html += '</ul>';
|
|
430
|
+
} else {
|
|
431
|
+
html += '<div class="ss-dash-empty" style="min-height:60px">Not available</div>';
|
|
432
|
+
}
|
|
433
|
+
html += '</div>';
|
|
434
|
+
|
|
435
|
+
// Response Status
|
|
436
|
+
html += '<div class="ss-dash-secondary-card">';
|
|
437
|
+
html += '<div class="ss-dash-secondary-card-title"><a href="#requests" class="ss-dash-widget-link">Response Status</a></div>';
|
|
438
|
+
html += '<ul class="ss-dash-secondary-list">';
|
|
439
|
+
html += '<li><a href="#requests?status=2xx" class="ss-dash-widget-row-link"><span style="color:var(--ss-green-fg)">2xx</span><span class="ss-dash-secondary-list-value">' + (statusDistribution['2xx'] || 0) + '</span></a></li>';
|
|
440
|
+
html += '<li><a href="#requests?status=3xx" class="ss-dash-widget-row-link"><span style="color:var(--ss-blue-fg)">3xx</span><span class="ss-dash-secondary-list-value">' + (statusDistribution['3xx'] || 0) + '</span></a></li>';
|
|
441
|
+
html += '<li><a href="#requests?status=4xx" class="ss-dash-widget-row-link"><span style="color:var(--ss-amber-fg)">4xx</span><span class="ss-dash-secondary-list-value">' + (statusDistribution['4xx'] || 0) + '</span></a></li>';
|
|
442
|
+
html += '<li><a href="#requests?status=5xx" class="ss-dash-widget-row-link"><span style="color:var(--ss-red-fg)">5xx</span><span class="ss-dash-secondary-list-value">' + (statusDistribution['5xx'] || 0) + '</span></a></li>';
|
|
443
|
+
html += '</ul>';
|
|
444
|
+
html += '</div>';
|
|
445
|
+
|
|
446
|
+
// Slowest Queries
|
|
447
|
+
html += '<div class="ss-dash-secondary-card">';
|
|
448
|
+
html += '<div class="ss-dash-secondary-card-title"><a href="#queries" class="ss-dash-widget-link">Slowest Queries</a></div>';
|
|
449
|
+
if (slowQueries.length > 0) {
|
|
450
|
+
html += '<ul class="ss-dash-secondary-list">';
|
|
451
|
+
slowQueries.slice(0, 5).forEach(function (q) {
|
|
452
|
+
var sql = q.normalizedSql || q.sql || '-';
|
|
453
|
+
html += '<li><a href="#queries" class="ss-dash-widget-row-link"><span title="' + esc(sql) + '">' + esc(sql) + '</span><span class="ss-dash-secondary-list-value ss-dash-duration ' + durationClass(q.avgDuration || 0) + '">' + (q.avgDuration || 0).toFixed(1) + 'ms</span></a></li>';
|
|
454
|
+
});
|
|
455
|
+
html += '</ul>';
|
|
456
|
+
} else {
|
|
457
|
+
html += '<div class="ss-dash-empty" style="min-height:60px">No queries yet</div>';
|
|
458
|
+
}
|
|
459
|
+
html += '</div>';
|
|
460
|
+
|
|
364
461
|
html += '</div></div>';
|
|
365
462
|
el.innerHTML = html;
|
|
366
463
|
|
|
@@ -637,9 +734,17 @@
|
|
|
637
734
|
};
|
|
638
735
|
|
|
639
736
|
// ── Requests ──────────────────────────────────────────────────
|
|
737
|
+
var requestUrlFilter = '';
|
|
738
|
+
var requestStatusFilter = '';
|
|
739
|
+
|
|
640
740
|
var fetchRequests = function () {
|
|
641
741
|
var ps = getPage('requests');
|
|
642
|
-
|
|
742
|
+
var url = API + '/requests?page=' + ps.page + '&limit=' + PER_PAGE;
|
|
743
|
+
if (requestUrlFilter) url += '&url=' + encodeURIComponent(requestUrlFilter);
|
|
744
|
+
if (requestStatusFilter) url += '&status=' + encodeURIComponent(requestStatusFilter);
|
|
745
|
+
requestUrlFilter = '';
|
|
746
|
+
requestStatusFilter = '';
|
|
747
|
+
fetchJSON(url)
|
|
643
748
|
.then(function (data) { renderRequests(data); })
|
|
644
749
|
.catch(function () { setInner('ss-dash-requests-body', '<div class="ss-dash-empty">Failed to load requests</div>'); });
|
|
645
750
|
};
|
|
@@ -978,9 +1083,14 @@
|
|
|
978
1083
|
}
|
|
979
1084
|
|
|
980
1085
|
// ── Events ────────────────────────────────────────────────────
|
|
1086
|
+
var eventNameFilter = '';
|
|
1087
|
+
|
|
981
1088
|
var fetchEvents = function () {
|
|
982
1089
|
var ps = getPage('events');
|
|
983
|
-
|
|
1090
|
+
var url = API + '/events?page=' + ps.page + '&limit=' + PER_PAGE;
|
|
1091
|
+
if (eventNameFilter) url += '&event_name=' + encodeURIComponent(eventNameFilter);
|
|
1092
|
+
eventNameFilter = '';
|
|
1093
|
+
fetchJSON(url)
|
|
984
1094
|
.then(function (data) { renderEvents(data); })
|
|
985
1095
|
.catch(function () { setInner('ss-dash-events-body', '<div class="ss-dash-empty">Failed to load events</div>'); });
|
|
986
1096
|
};
|
|
@@ -1073,12 +1183,17 @@
|
|
|
1073
1183
|
// ── Logs ──────────────────────────────────────────────────────
|
|
1074
1184
|
var logLevelFilter = 'all';
|
|
1075
1185
|
var logReqIdFilter = '';
|
|
1186
|
+
var logDeepLevelFilter = '';
|
|
1076
1187
|
var logStructuredFilters = [];
|
|
1077
1188
|
var logSavedFilters = [];
|
|
1078
1189
|
|
|
1079
1190
|
var fetchLogs = function () {
|
|
1080
1191
|
var ps = getPage('logs');
|
|
1081
1192
|
var params = 'page=' + ps.page + '&limit=' + PER_PAGE;
|
|
1193
|
+
if (logDeepLevelFilter) {
|
|
1194
|
+
logLevelFilter = logDeepLevelFilter;
|
|
1195
|
+
logDeepLevelFilter = '';
|
|
1196
|
+
}
|
|
1082
1197
|
if (logLevelFilter !== 'all') params += '&level=' + logLevelFilter;
|
|
1083
1198
|
if (logReqIdFilter) params += '&request_id=' + encodeURIComponent(logReqIdFilter);
|
|
1084
1199
|
logStructuredFilters.forEach(function (f) {
|
|
@@ -1290,9 +1405,14 @@
|
|
|
1290
1405
|
fetchSavedFilters();
|
|
1291
1406
|
|
|
1292
1407
|
// ── Emails ────────────────────────────────────────────────────
|
|
1408
|
+
var emailStatusFilter = '';
|
|
1409
|
+
|
|
1293
1410
|
var fetchEmails = function () {
|
|
1294
1411
|
var ps = getPage('emails');
|
|
1295
|
-
|
|
1412
|
+
var url = API + '/emails?page=' + ps.page + '&limit=' + PER_PAGE;
|
|
1413
|
+
if (emailStatusFilter) url += '&status=' + encodeURIComponent(emailStatusFilter);
|
|
1414
|
+
emailStatusFilter = '';
|
|
1415
|
+
fetchJSON(url)
|
|
1296
1416
|
.then(function (data) { renderEmails(data); })
|
|
1297
1417
|
.catch(function () { setInner('ss-dash-emails-body', '<div class="ss-dash-empty">Failed to load emails</div>'); });
|
|
1298
1418
|
};
|
|
@@ -2337,6 +2457,33 @@
|
|
|
2337
2457
|
return { section: section, params: params };
|
|
2338
2458
|
};
|
|
2339
2459
|
|
|
2460
|
+
var applyRouteParams = function (route) {
|
|
2461
|
+
var section = route.section;
|
|
2462
|
+
if (route.params.requestId && section === 'logs') {
|
|
2463
|
+
logReqIdFilter = route.params.requestId;
|
|
2464
|
+
var input = document.getElementById('ss-dash-log-reqid-input');
|
|
2465
|
+
if (input) input.value = logReqIdFilter;
|
|
2466
|
+
}
|
|
2467
|
+
if (route.params.level && section === 'logs') {
|
|
2468
|
+
logDeepLevelFilter = route.params.level;
|
|
2469
|
+
}
|
|
2470
|
+
if (route.params.url && section === 'requests') {
|
|
2471
|
+
requestUrlFilter = route.params.url;
|
|
2472
|
+
}
|
|
2473
|
+
if (route.params.status && section === 'requests') {
|
|
2474
|
+
requestStatusFilter = route.params.status;
|
|
2475
|
+
}
|
|
2476
|
+
if (route.params.status && section === 'emails') {
|
|
2477
|
+
emailStatusFilter = route.params.status;
|
|
2478
|
+
}
|
|
2479
|
+
if (route.params.event_name && section === 'events') {
|
|
2480
|
+
eventNameFilter = route.params.event_name;
|
|
2481
|
+
}
|
|
2482
|
+
if (route.params.status && section === 'jobs') {
|
|
2483
|
+
jobStatusFilter = route.params.status;
|
|
2484
|
+
}
|
|
2485
|
+
};
|
|
2486
|
+
|
|
2340
2487
|
var initRoute = function () {
|
|
2341
2488
|
var route = parseHash();
|
|
2342
2489
|
var section = route.section;
|
|
@@ -2347,11 +2494,7 @@
|
|
|
2347
2494
|
if (valid.indexOf(section) === -1) section = 'overview';
|
|
2348
2495
|
|
|
2349
2496
|
// Apply deep link params
|
|
2350
|
-
|
|
2351
|
-
logReqIdFilter = route.params.requestId;
|
|
2352
|
-
var input = document.getElementById('ss-dash-log-reqid-input');
|
|
2353
|
-
if (input) input.value = logReqIdFilter;
|
|
2354
|
-
}
|
|
2497
|
+
applyRouteParams({ section: section, params: route.params });
|
|
2355
2498
|
|
|
2356
2499
|
// Switch to section
|
|
2357
2500
|
activeSection = section;
|
|
@@ -2368,7 +2511,11 @@
|
|
|
2368
2511
|
window.addEventListener('hashchange', function () {
|
|
2369
2512
|
var route = parseHash();
|
|
2370
2513
|
if (route.section !== activeSection) {
|
|
2514
|
+
applyRouteParams(route);
|
|
2371
2515
|
switchSection(route.section);
|
|
2516
|
+
} else if (Object.keys(route.params).length > 0) {
|
|
2517
|
+
applyRouteParams(route);
|
|
2518
|
+
loadSection(activeSection);
|
|
2372
2519
|
}
|
|
2373
2520
|
});
|
|
2374
2521
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "adonisjs-server-stats",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Real-time server monitoring for AdonisJS v6 applications",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/src/index.js",
|
|
@@ -127,7 +127,7 @@
|
|
|
127
127
|
"@julr/adonisjs-prometheus": "^1.4.0",
|
|
128
128
|
"@types/better-sqlite3": "^7.0.0",
|
|
129
129
|
"@types/node": "^22.0.0",
|
|
130
|
-
"better-sqlite3": "^11.
|
|
130
|
+
"better-sqlite3": "^11.10.0",
|
|
131
131
|
"bullmq": "^5.0.0",
|
|
132
132
|
"edge.js": "^6.0.0",
|
|
133
133
|
"typescript": "^5.9.0"
|