adonisjs-server-stats 1.3.2 → 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.
@@ -1 +1 @@
1
- {"version":3,"file":"dashboard_controller.d.ts","sourceRoot":"","sources":["../../../src/dashboard/dashboard_controller.ts"],"names":[],"mappings":"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"}
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 this.dashboardStore.getOverviewMetrics(range);
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":"AAIA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AACzD,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAM3F,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,8DAA8D;IAC9D,UAAU,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,QAAQ,GAAG,UAAU,GAAG,YAAY,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;CAChG;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,eAAe,CAAC,CAAC;IAChC,IAAI,EAAE,CAAC,EAAE,CAAA;IACT,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,MAAM,CAAA;CACjB;AAMD;;;;;;GAMG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,EAAE,CAAY;IACtB,OAAO,CAAC,OAAO,CAAY;IAC3B,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,aAAa,CAAQ;IAC7B,OAAO,CAAC,cAAc,CAA8C;IACpE,OAAO,CAAC,eAAe,CAA+B;IACtD,OAAO,CAAC,QAAQ,CAAwD;gBAE5D,MAAM,EAAE,gBAAgB;IASpC;;;OAGG;IACG,KAAK,CAAC,QAAQ,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA0CxE,kEAAkE;IAC5D,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IA2B3B,sEAAsE;IACtE,KAAK,IAAI,GAAG;IAIZ,kDAAkD;IAClD,OAAO,IAAI,OAAO;IAQlB;;;OAGG;IACG,aAAa,CACjB,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,EACX,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,SAAS,GAAE,MAAU,EACrB,YAAY,GAAE,MAAU,GACvB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAmBzB,uFAAuF;IACjF,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IA4B7E,yCAAyC;IACnC,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAkB3E,6BAA6B;IACvB,WAAW,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAsBrD,yDAAyD;IACnD,SAAS,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAoB9D,oCAAoC;IAC9B,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAmBvE;;;OAGG;IACG,cAAc,CAClB,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,EACX,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,WAAW,EAAE,EACtB,KAAK,EAAE,WAAW,GAAG,IAAI,GACxB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAwBzB,uDAAuD;IACjD,WAAW,CACf,IAAI,GAAE,MAAU,EAChB,OAAO,GAAE,MAAW,EACpB,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC;IAWhC,qDAAqD;IAC/C,UAAU,CACd,IAAI,GAAE,MAAU,EAChB,OAAO,GAAE,MAAW,EACpB,OAAO,CAAC,EAAE,YAAY,GACrB,OAAO,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC;IAWhC;;;OAGG;IACG,iBAAiB,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IAiBzC,qDAAqD;IAC/C,SAAS,CACb,IAAI,GAAE,MAAU,EAChB,OAAO,GAAE,MAAW,EACpB,OAAO,CAAC,EAAE,YAAY,GACrB,OAAO,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC;IAMhC,qDAAqD;IAC/C,SAAS,CACb,IAAI,GAAE,MAAU,EAChB,OAAO,GAAE,MAAW,EACpB,OAAO,CAAC,EAAE,YAAY,GACrB,OAAO,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC;IAUhC,uCAAuC;IACjC,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAMtD;;;;;OAKG;IACG,OAAO,CACX,IAAI,GAAE,MAAU,EAChB,OAAO,GAAE,MAAW,EACpB,OAAO,CAAC,EAAE,UAAU,GACnB,OAAO,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC;IA0BhC,qDAAqD;IAC/C,SAAS,CACb,IAAI,GAAE,MAAU,EAChB,OAAO,GAAE,MAAW,EACpB,OAAO,CAAC,EAAE,YAAY,GACrB,OAAO,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC;IAShC,wCAAwC;IAClC,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC;IAiBrD,iEAAiE;IAC3D,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC;IAkCvD;;;;OAIG;IACG,kBAAkB,CAAC,KAAK,GAAE,MAAa,GAAG,OAAO,CAAC,GAAG,CAAC;IAoF5D;;;;OAIG;IACG,YAAY,CAAC,KAAK,GAAE,MAAa,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IA2DlD,eAAe,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAQjD,iBAAiB,CACrB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAChC,OAAO,CAAC,GAAG,CAAC;IAYT,iBAAiB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAWrD;;;;;;OAMG;IACG,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC;IAuB3D,oDAAoD;YACtC,QAAQ;IA8BtB;;OAEG;IACH,OAAO,CAAC,kBAAkB;CAiC3B"}
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
- html += '<li><span title="' + esc(ep.url || ep.pattern || '-') + '">' + esc(ep.url || ep.pattern || '-') + '</span><span class="ss-dash-secondary-list-value ss-dash-duration ' + durationClass(ep.avgDuration || 0) + '">' + (ep.avgDuration || 0).toFixed(1) + 'ms</span></li>';
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
- fetchJSON(API + '/requests?page=' + ps.page + '&limit=' + PER_PAGE)
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
- fetchJSON(API + '/events?page=' + ps.page + '&limit=' + PER_PAGE)
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
- fetchJSON(API + '/emails?page=' + ps.page + '&limit=' + PER_PAGE)
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
- if (route.params.requestId && section === 'logs') {
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.2",
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.0.0",
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"