adonisjs-server-stats 1.9.0 → 1.10.3

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.
Files changed (227) hide show
  1. package/README.md +23 -14
  2. package/dist/core/config-utils.d.ts +8 -0
  3. package/dist/core/constants.d.ts +4 -0
  4. package/dist/core/dashboard-data-controller.d.ts +16 -0
  5. package/dist/core/dashboard-data-helpers.d.ts +12 -0
  6. package/dist/core/debug-data-controller.d.ts +4 -0
  7. package/dist/core/define-config-helpers.d.ts +25 -0
  8. package/dist/core/feature-detect-helpers.d.ts +36 -0
  9. package/dist/core/formatters-helpers.d.ts +23 -0
  10. package/dist/core/index.js +596 -509
  11. package/dist/core/log-utils-helpers.d.ts +13 -0
  12. package/dist/core/metrics.d.ts +3 -28
  13. package/dist/core/pagination.d.ts +0 -9
  14. package/dist/core/server-stats-controller.d.ts +6 -0
  15. package/dist/core/split-pane.d.ts +18 -0
  16. package/dist/core/trace-utils.d.ts +5 -0
  17. package/dist/core/transmit-helpers.d.ts +7 -0
  18. package/dist/core/types-dashboard.d.ts +178 -0
  19. package/dist/core/types-diagnostics.d.ts +85 -0
  20. package/dist/core/types.d.ts +11 -443
  21. package/dist/react/{CacheSection-xH75hwXu.js → CacheSection-baMZotSn.js} +2 -2
  22. package/dist/react/CacheTab-2cw_rMzj.js +117 -0
  23. package/dist/react/{ConfigSection-D8BO1Ry9.js → ConfigSection-DGgqjAal.js} +1 -1
  24. package/dist/react/{ConfigTab-CcN-tfjv.js → ConfigTab-H3OnYqmK.js} +1 -1
  25. package/dist/react/CustomPaneTab-B6r7ha0u.js +98 -0
  26. package/dist/react/{EmailsSection-BzlsTdPs.js → EmailsSection-C-UZISG-.js} +2 -2
  27. package/dist/react/EmailsTab-DbK4Eobn.js +139 -0
  28. package/dist/react/{EventsSection-CGQWiIdV.js → EventsSection-C7RQW_LY.js} +2 -2
  29. package/dist/react/EventsTab-CfVr7AiM.js +57 -0
  30. package/dist/react/{FilterBar-DQRXpWrb.js → FilterBar-CQ7bD669.js} +15 -15
  31. package/dist/react/{JobsSection-D7AHQmZi.js → JobsSection-CQHNK_Ls.js} +2 -2
  32. package/dist/react/{JobsTab-B3Lfdqed.js → JobsTab-znzf6jzk.js} +54 -42
  33. package/dist/react/{LogsSection-Cly1dpvS.js → LogsSection-Dmm3rE2B.js} +9 -3
  34. package/dist/react/LogsTab-D8unMV5P.js +108 -0
  35. package/dist/react/{OverviewSection-CkBGFEWq.js → OverviewSection-ABP9ueBo.js} +1 -1
  36. package/dist/react/{QueriesSection-CfCpnNUD.js → QueriesSection-CnmSkznA.js} +2 -2
  37. package/dist/react/{QueriesTab-DbBmAqzO.js → QueriesTab-BQzcxEiW.js} +37 -40
  38. package/dist/react/RelatedLogs-3A8RuGKH.js +52 -0
  39. package/dist/react/RequestsSection-kW79_M7k.js +341 -0
  40. package/dist/react/{RoutesSection-CRqF-cNM.js → RoutesSection-BRhxrtjZ.js} +2 -2
  41. package/dist/react/RoutesTab-CpYH5lUw.js +68 -0
  42. package/dist/react/TimelineTab-DjLR35Ce.js +214 -0
  43. package/dist/react/index-CsImORX6.js +1121 -0
  44. package/dist/react/index.js +1 -1
  45. package/dist/react/react/components/{Dashboard/shared → shared}/FilterBar.d.ts +4 -3
  46. package/dist/react/react/components/shared/RelatedLogs.d.ts +7 -0
  47. package/dist/react/react/hooks/useDashboardData.d.ts +4 -8
  48. package/dist/react/style.css +1 -1
  49. package/dist/src/collectors/app_collector.d.ts +0 -8
  50. package/dist/src/collectors/app_collector.js +45 -52
  51. package/dist/src/collectors/auto_detect.d.ts +0 -23
  52. package/dist/src/collectors/auto_detect.js +33 -55
  53. package/dist/src/collectors/db_pool_collector.d.ts +14 -16
  54. package/dist/src/collectors/db_pool_collector.js +72 -57
  55. package/dist/src/collectors/log_collector.d.ts +0 -47
  56. package/dist/src/collectors/log_collector.js +36 -65
  57. package/dist/src/collectors/queue_collector.d.ts +0 -20
  58. package/dist/src/collectors/queue_collector.js +60 -76
  59. package/dist/src/collectors/redis_collector.d.ts +10 -10
  60. package/dist/src/collectors/redis_collector.js +69 -66
  61. package/dist/src/config/deprecation_migration.d.ts +7 -0
  62. package/dist/src/config/deprecation_migration.js +201 -0
  63. package/dist/src/controller/debug_controller.d.ts +1 -1
  64. package/dist/src/controller/debug_controller.js +87 -81
  65. package/dist/src/dashboard/cache_handlers.d.ts +14 -0
  66. package/dist/src/dashboard/cache_handlers.js +52 -0
  67. package/dist/src/dashboard/chart_aggregator.d.ts +0 -7
  68. package/dist/src/dashboard/chart_aggregator.js +68 -50
  69. package/dist/src/dashboard/coalesce_cache.d.ts +25 -0
  70. package/dist/src/dashboard/coalesce_cache.js +47 -0
  71. package/dist/src/dashboard/dashboard_controller.d.ts +11 -37
  72. package/dist/src/dashboard/dashboard_controller.js +52 -532
  73. package/dist/src/dashboard/dashboard_page_assets.d.ts +17 -0
  74. package/dist/src/dashboard/dashboard_page_assets.js +51 -0
  75. package/dist/src/dashboard/dashboard_store.d.ts +19 -217
  76. package/dist/src/dashboard/dashboard_store.js +115 -1069
  77. package/dist/src/dashboard/dashboard_types.d.ts +83 -0
  78. package/dist/src/dashboard/dashboard_types.js +4 -0
  79. package/dist/src/dashboard/detail_queries.d.ts +19 -0
  80. package/dist/src/dashboard/detail_queries.js +98 -0
  81. package/dist/src/dashboard/email_event_builder.d.ts +8 -0
  82. package/dist/src/dashboard/email_event_builder.js +65 -0
  83. package/dist/src/dashboard/explain_query.d.ts +8 -0
  84. package/dist/src/dashboard/explain_query.js +22 -0
  85. package/dist/src/dashboard/filter_handlers.d.ts +23 -0
  86. package/dist/src/dashboard/filter_handlers.js +56 -0
  87. package/dist/src/dashboard/filtered_queries.d.ts +15 -0
  88. package/dist/src/dashboard/filtered_queries.js +155 -0
  89. package/dist/src/dashboard/flush_manager.d.ts +25 -0
  90. package/dist/src/dashboard/flush_manager.js +107 -0
  91. package/dist/src/dashboard/format_helpers.d.ts +126 -0
  92. package/dist/src/dashboard/format_helpers.js +140 -0
  93. package/dist/src/dashboard/inspector_manager.d.ts +36 -0
  94. package/dist/src/dashboard/inspector_manager.js +102 -0
  95. package/dist/src/dashboard/integrations/config_inspector.js +11 -13
  96. package/dist/src/dashboard/integrations/queue_inspector.d.ts +3 -3
  97. package/dist/src/dashboard/integrations/queue_inspector.js +13 -10
  98. package/dist/src/dashboard/jobs_handlers.d.ts +14 -0
  99. package/dist/src/dashboard/jobs_handlers.js +61 -0
  100. package/dist/src/dashboard/knex_factory.d.ts +18 -0
  101. package/dist/src/dashboard/knex_factory.js +91 -0
  102. package/dist/src/dashboard/migrator.js +30 -153
  103. package/dist/src/dashboard/migrator_tables.d.ts +19 -0
  104. package/dist/src/dashboard/migrator_tables.js +153 -0
  105. package/dist/src/dashboard/overview_queries.d.ts +66 -0
  106. package/dist/src/dashboard/overview_queries.js +155 -0
  107. package/dist/src/dashboard/overview_query_runners.d.ts +25 -0
  108. package/dist/src/dashboard/overview_query_runners.js +84 -0
  109. package/dist/src/dashboard/overview_store_queries.d.ts +40 -0
  110. package/dist/src/dashboard/overview_store_queries.js +69 -0
  111. package/dist/src/dashboard/paginate_helper.d.ts +12 -0
  112. package/dist/src/dashboard/paginate_helper.js +33 -0
  113. package/dist/src/dashboard/query_explain_handler.d.ts +10 -0
  114. package/dist/src/dashboard/query_explain_handler.js +80 -0
  115. package/dist/src/dashboard/read_queries.d.ts +32 -0
  116. package/dist/src/dashboard/read_queries.js +107 -0
  117. package/dist/src/dashboard/saved_filter_queries.d.ts +10 -0
  118. package/dist/src/dashboard/saved_filter_queries.js +24 -0
  119. package/dist/src/dashboard/storage_stats.d.ts +41 -0
  120. package/dist/src/dashboard/storage_stats.js +81 -0
  121. package/dist/src/dashboard/write_queue.d.ts +106 -0
  122. package/dist/src/dashboard/write_queue.js +225 -0
  123. package/dist/src/data/data_access.d.ts +6 -36
  124. package/dist/src/data/data_access.js +43 -188
  125. package/dist/src/data/data_access_helpers.d.ts +130 -0
  126. package/dist/src/data/data_access_helpers.js +212 -0
  127. package/dist/src/debug/debug_store.js +37 -32
  128. package/dist/src/debug/email_collector.d.ts +1 -10
  129. package/dist/src/debug/email_collector.js +78 -81
  130. package/dist/src/debug/event_collector.d.ts +0 -9
  131. package/dist/src/debug/event_collector.js +79 -62
  132. package/dist/src/debug/query_collector.js +23 -19
  133. package/dist/src/debug/route_inspector.d.ts +1 -5
  134. package/dist/src/debug/route_inspector.js +50 -51
  135. package/dist/src/debug/trace_collector.d.ts +10 -2
  136. package/dist/src/debug/trace_collector.js +23 -16
  137. package/dist/src/debug/types.d.ts +5 -1
  138. package/dist/src/define_config.d.ts +0 -65
  139. package/dist/src/define_config.js +93 -333
  140. package/dist/src/edge/client/dashboard.js +2 -2
  141. package/dist/src/edge/client/debug-panel-deferred.js +1 -1
  142. package/dist/src/edge/client/stats-bar.js +1 -1
  143. package/dist/src/edge/client-vue/dashboard.js +5 -5
  144. package/dist/src/edge/client-vue/debug-panel-deferred.js +3 -3
  145. package/dist/src/edge/client-vue/stats-bar.js +3 -3
  146. package/dist/src/edge/plugin.d.ts +0 -16
  147. package/dist/src/edge/plugin.js +57 -64
  148. package/dist/src/engine/request_metrics.d.ts +1 -0
  149. package/dist/src/engine/request_metrics.js +32 -42
  150. package/dist/src/middleware/request_tracking_middleware.d.ts +3 -8
  151. package/dist/src/middleware/request_tracking_middleware.js +65 -91
  152. package/dist/src/provider/auth_middleware_detector.d.ts +16 -0
  153. package/dist/src/provider/auth_middleware_detector.js +97 -0
  154. package/dist/src/provider/boot_helpers.d.ts +20 -0
  155. package/dist/src/provider/boot_helpers.js +91 -0
  156. package/dist/src/provider/boot_initializer.d.ts +28 -0
  157. package/dist/src/provider/boot_initializer.js +35 -0
  158. package/dist/src/provider/dashboard_init.d.ts +30 -0
  159. package/dist/src/provider/dashboard_init.js +138 -0
  160. package/dist/src/provider/dashboard_setup.d.ts +25 -0
  161. package/dist/src/provider/dashboard_setup.js +78 -0
  162. package/dist/src/provider/diagnostics.d.ts +134 -0
  163. package/dist/src/provider/diagnostics.js +127 -0
  164. package/dist/src/provider/email_bridge.d.ts +43 -0
  165. package/dist/src/provider/email_bridge.js +80 -0
  166. package/dist/src/provider/email_helpers.d.ts +13 -0
  167. package/dist/src/provider/email_helpers.js +68 -0
  168. package/dist/src/provider/pino_hook.d.ts +17 -0
  169. package/dist/src/provider/pino_hook.js +35 -0
  170. package/dist/src/provider/provider_helpers_extra.d.ts +47 -0
  171. package/dist/src/provider/provider_helpers_extra.js +177 -0
  172. package/dist/src/provider/server_stats_provider.d.ts +39 -85
  173. package/dist/src/provider/server_stats_provider.js +131 -936
  174. package/dist/src/provider/shutdown_helpers.d.ts +43 -0
  175. package/dist/src/provider/shutdown_helpers.js +70 -0
  176. package/dist/src/provider/toolbar_setup.d.ts +57 -0
  177. package/dist/src/provider/toolbar_setup.js +141 -0
  178. package/dist/src/routes/dashboard_routes.d.ts +14 -0
  179. package/dist/src/routes/dashboard_routes.js +197 -0
  180. package/dist/src/routes/debug_routes.d.ts +14 -0
  181. package/dist/src/routes/debug_routes.js +101 -0
  182. package/dist/src/routes/register_routes.d.ts +0 -78
  183. package/dist/src/routes/register_routes.js +22 -347
  184. package/dist/src/routes/stats_routes.d.ts +5 -0
  185. package/dist/src/routes/stats_routes.js +14 -0
  186. package/dist/src/styles/components.css +177 -0
  187. package/dist/src/styles/dashboard.css +8 -90
  188. package/dist/src/styles/debug-panel.css +10 -31
  189. package/dist/src/types.d.ts +306 -15
  190. package/dist/vue/{CacheSection-Cx-hj09X.js → CacheSection-ITqvpfH5.js} +1 -1
  191. package/dist/vue/{ConfigSection-CMXyryf6.js → ConfigSection-DTn3GslE.js} +1 -1
  192. package/dist/vue/{EmailsSection-DgKl9xGT.js → EmailsSection-DtLJ4XoS.js} +1 -1
  193. package/dist/vue/{EventsSection-BNMCAim1.js → EventsSection-BOYYz0Ty.js} +1 -1
  194. package/dist/vue/{JobsSection-CCMgMlxd.js → JobsSection-BazTxcJL.js} +1 -1
  195. package/dist/vue/{LogsSection-CvOnTxUu.js → LogsSection-D55PjTKX.js} +9 -3
  196. package/dist/vue/{LogsTab-Bg3o0Mm6.js → LogsTab-47zEK7jL.js} +4 -1
  197. package/dist/vue/{OverviewSection-CHgaKtUR.js → OverviewSection-1uBKo-Tu.js} +1 -1
  198. package/dist/vue/{QueriesSection-BnHRD98z.js → QueriesSection-rpoZ4ogd.js} +1 -1
  199. package/dist/vue/RelatedLogs.vue_vue_type_script_setup_true_lang-CB2_TzYW.js +84 -0
  200. package/dist/vue/RequestsSection-x7LvT0MC.js +401 -0
  201. package/dist/vue/{RoutesSection-BrceOcKQ.js → RoutesSection-CCD0zZqQ.js} +1 -1
  202. package/dist/vue/TimelineTab-zj5Z5OdT.js +338 -0
  203. package/dist/vue/components/Dashboard/sections/RequestsSection.vue.d.ts +4 -0
  204. package/dist/vue/components/DebugPanel/tabs/TimelineTab.vue.d.ts +4 -0
  205. package/dist/vue/components/{Dashboard/sections/TimelineSection.vue.d.ts → shared/RelatedLogs.vue.d.ts} +5 -6
  206. package/dist/vue/composables/useDashboardData.d.ts +12 -23
  207. package/dist/vue/index-C8MxnS7Q.js +1232 -0
  208. package/dist/vue/index.js +1 -1
  209. package/dist/vue/style.css +1 -1
  210. package/package.json +1 -1
  211. package/dist/react/CacheTab-DYmsZJJ1.js +0 -123
  212. package/dist/react/CustomPaneTab-D7_o3Ec6.js +0 -104
  213. package/dist/react/EmailsTab-Uh2CQY3o.js +0 -153
  214. package/dist/react/EventsTab-CC6DQzEm.js +0 -63
  215. package/dist/react/LogsTab-BbYK-iyh.js +0 -103
  216. package/dist/react/RequestsSection-Cb5a6MlT.js +0 -209
  217. package/dist/react/RoutesTab-Bwreij3e.js +0 -74
  218. package/dist/react/TimelineSection-B2y06kRE.js +0 -158
  219. package/dist/react/TimelineTab-6hthfdBB.js +0 -193
  220. package/dist/react/WaterfallChart-Cj73WdfM.js +0 -100
  221. package/dist/react/index-CecA4IdQ.js +0 -1075
  222. package/dist/react/react/components/Dashboard/sections/TimelineSection.d.ts +0 -8
  223. package/dist/vue/RequestsSection-B-uSlM0f.js +0 -243
  224. package/dist/vue/TimelineSection-CfvnA2Oo.js +0 -186
  225. package/dist/vue/TimelineTab-Db6lKKsD.js +0 -250
  226. package/dist/vue/WaterfallChart.vue_vue_type_script_setup_true_lang-tZ13cNj1.js +0 -118
  227. package/dist/vue/index-oLxS08vN.js +0 -1235
@@ -6,97 +6,19 @@ import type { AdonisRouter } from './router_types.js';
6
6
  import type { HttpContext } from '@adonisjs/core/http';
7
7
  /**
8
8
  * Options for the unified route registration function.
9
- *
10
- * Brings together stats, debug, and dashboard route registration
11
- * into a single call with all required dependencies declared upfront.
12
9
  */
13
10
  export interface RegisterRoutesOptions {
14
- /** The AdonisJS router instance. */
15
11
  router: AdonisRouter;
16
- /**
17
- * The unified API controller that serves all data resource endpoints
18
- * (queries, events, emails, traces, routes, logs).
19
- *
20
- * Pass `null` during early boot — routes will return 503 until the
21
- * controller is wired up.
22
- */
23
12
  getApiController: () => ApiController | null;
24
- /**
25
- * Lazy getter for the stats bar controller.
26
- *
27
- * The controller is created during `ready()`, but routes are
28
- * registered during `boot()`, so a lazy getter is required.
29
- */
30
13
  getStatsController: () => ServerStatsController | null;
31
- /**
32
- * Lazy getter for the debug controller.
33
- *
34
- * Handles non-data debug endpoints: `/config` and `/diagnostics`.
35
- */
36
14
  getDebugController: () => DebugController | null;
37
- /**
38
- * Lazy getter for the dashboard controller.
39
- *
40
- * Handles dashboard-specific endpoints that are **not** data
41
- * resources: page rendering, overview, requests, cache, jobs,
42
- * config, saved filters, grouped queries, and query explain.
43
- */
44
15
  getDashboardController: () => DashboardController | null;
45
- /**
46
- * Stats polling endpoint path (e.g. `'/admin/api/server-stats'`).
47
- *
48
- * Set to `false` to skip registering the stats endpoint entirely.
49
- */
50
16
  statsEndpoint?: string | false;
51
- /**
52
- * Base path for debug panel API routes (e.g. `'/admin/api/debug'`).
53
- *
54
- * When omitted, debug routes are **not** registered.
55
- */
56
17
  debugEndpoint?: string;
57
- /**
58
- * Base path for the full-page dashboard (e.g. `'/__stats'`).
59
- *
60
- * When omitted, dashboard routes are **not** registered.
61
- */
62
18
  dashboardPath?: string;
63
- /**
64
- * Optional access-control callback applied to **all** registered
65
- * routes as middleware. When it returns `false`, the request
66
- * receives a 403 Forbidden response.
67
- */
68
19
  shouldShow?: (ctx: HttpContext) => boolean;
69
20
  }
70
21
  /**
71
22
  * Register all server-stats routes in a single call.
72
- *
73
- * This is the unified replacement for the three separate registrars:
74
- *
75
- * - `registerStatsRoutes` — stats bar polling endpoint
76
- * - `registerDebugRoutes` — debug panel API endpoints
77
- * - `registerDashboardRoutes` — full-page dashboard endpoints
78
- *
79
- * **Data resource** endpoints (queries, events, emails, traces, routes,
80
- * logs) are routed through the unified {@link ApiController} so that
81
- * both the debug panel and the dashboard read from the same handler.
82
- *
83
- * **Non-data** endpoints (stats polling, config, diagnostics, overview,
84
- * requests, cache, jobs, saved filters, grouped queries, query explain,
85
- * and the dashboard HTML page) continue to use their original controllers.
86
- *
87
- * @example
88
- * ```ts
89
- * registerAllRoutes({
90
- * router,
91
- * getApiController: () => apiController,
92
- * getStatsController: () => statsController,
93
- * getDebugController: () => debugController,
94
- * getDashboardController: () => dashboardController,
95
- * statsEndpoint: '/admin/api/server-stats',
96
- * debugEndpoint: '/admin/api/debug',
97
- * dashboardPath: '/__stats',
98
- * shouldShow: (ctx) => ctx.auth?.user?.role === 'admin',
99
- * })
100
- * ```
101
23
  */
102
24
  export declare function registerAllRoutes(options: RegisterRoutesOptions): void;
@@ -1,356 +1,31 @@
1
1
  import { createAccessMiddleware } from './access_middleware.js';
2
- // ---------------------------------------------------------------------------
3
- // Registration
4
- // ---------------------------------------------------------------------------
2
+ import { registerDashboardRoutes } from './dashboard_routes.js';
3
+ import { registerDebugRoutes } from './debug_routes.js';
4
+ import { registerStatsRoute } from './stats_routes.js';
5
5
  /**
6
6
  * Register all server-stats routes in a single call.
7
- *
8
- * This is the unified replacement for the three separate registrars:
9
- *
10
- * - `registerStatsRoutes` — stats bar polling endpoint
11
- * - `registerDebugRoutes` — debug panel API endpoints
12
- * - `registerDashboardRoutes` — full-page dashboard endpoints
13
- *
14
- * **Data resource** endpoints (queries, events, emails, traces, routes,
15
- * logs) are routed through the unified {@link ApiController} so that
16
- * both the debug panel and the dashboard read from the same handler.
17
- *
18
- * **Non-data** endpoints (stats polling, config, diagnostics, overview,
19
- * requests, cache, jobs, saved filters, grouped queries, query explain,
20
- * and the dashboard HTML page) continue to use their original controllers.
21
- *
22
- * @example
23
- * ```ts
24
- * registerAllRoutes({
25
- * router,
26
- * getApiController: () => apiController,
27
- * getStatsController: () => statsController,
28
- * getDebugController: () => debugController,
29
- * getDashboardController: () => dashboardController,
30
- * statsEndpoint: '/admin/api/server-stats',
31
- * debugEndpoint: '/admin/api/debug',
32
- * dashboardPath: '/__stats',
33
- * shouldShow: (ctx) => ctx.auth?.user?.role === 'admin',
34
- * })
35
- * ```
36
7
  */
37
8
  export function registerAllRoutes(options) {
38
- const { router, getApiController, getStatsController, getDebugController, getDashboardController, statsEndpoint, debugEndpoint, dashboardPath, shouldShow, } = options;
39
- const middleware = shouldShow ? [createAccessMiddleware(shouldShow)] : [];
40
- // =========================================================================
41
- // Stats polling endpoint
42
- // =========================================================================
43
- if (typeof statsEndpoint === 'string') {
44
- router
45
- .get(statsEndpoint, async (ctx) => {
46
- const controller = getStatsController();
47
- if (!controller) {
48
- return ctx.response.serviceUnavailable({
49
- error: 'Stats engine is starting up, please retry',
50
- });
51
- }
52
- return controller.index(ctx);
53
- })
54
- .as('server-stats.api')
55
- .use(middleware);
9
+ const middleware = options.shouldShow ? [createAccessMiddleware(options.shouldShow)] : [];
10
+ if (typeof options.statsEndpoint === 'string') {
11
+ registerStatsRoute(options.router, options.statsEndpoint, options.getStatsController, middleware);
56
12
  }
57
- // =========================================================================
58
- // Debug panel API routes
59
- // =========================================================================
60
- if (debugEndpoint) {
61
- const base = debugEndpoint.replace(/\/+$/, '');
62
- /**
63
- * Bind a debug controller method with lazy initialization and
64
- * 503 fallback when the controller is not yet ready.
65
- */
66
- const bindDebug = (method) => {
67
- return async (ctx) => {
68
- const controller = getDebugController();
69
- if (!controller) {
70
- return ctx.response.serviceUnavailable({
71
- error: 'Debug toolbar is starting up, please retry',
72
- });
73
- }
74
- return controller[method].call(controller, ctx);
75
- };
76
- };
77
- /**
78
- * Wrap an ApiController method as an HTTP handler with lazy
79
- * initialization and 503 fallback.
80
- */
81
- const bindApi = (fn) => {
82
- return async (ctx) => {
83
- const api = getApiController();
84
- if (!api) {
85
- return ctx.response.serviceUnavailable({
86
- error: 'Debug toolbar is starting up, please retry',
87
- });
88
- }
89
- return fn(api, ctx);
90
- };
91
- };
92
- router
93
- .group(() => {
94
- // Non-data endpoints — stay with DebugController
95
- router.get('/config', bindDebug('config')).as('server-stats.debug.config');
96
- router.get('/diagnostics', bindDebug('diagnostics')).as('server-stats.debug.diagnostics');
97
- // Data endpoints — unified through ApiController
98
- // Debug panel always reads from ring buffers (source: 'memory')
99
- // because SQLite column names (snake_case) differ from the
100
- // camelCase QueryRecord/EventRecord/etc. shapes the frontend expects.
101
- router
102
- .get('/queries', bindApi(async (api, ctx) => {
103
- const queries = await api.getQueries({ source: 'memory' });
104
- const summary = api.getQuerySummary();
105
- return ctx.response.json({ queries: queries.data, summary });
106
- }))
107
- .as('server-stats.debug.queries');
108
- router
109
- .get('/events', bindApi(async (api, ctx) => {
110
- const result = await api.getEvents({ source: 'memory' });
111
- return ctx.response.json({ events: result.data, total: result.meta.total });
112
- }))
113
- .as('server-stats.debug.events');
114
- router
115
- .get('/routes', bindApi((api, ctx) => {
116
- const result = api.getRoutes();
117
- return ctx.response.json({ routes: result.data, total: result.meta.total });
118
- }))
119
- .as('server-stats.debug.routes');
120
- router
121
- .get('/logs', bindApi(async (api, ctx) => {
122
- const result = await api.getLogs({ source: 'memory' });
123
- return ctx.response.json(result.data);
124
- }))
125
- .as('server-stats.debug.logs');
126
- router
127
- .get('/emails', bindApi(async (api, ctx) => {
128
- const result = await api.getEmails({});
129
- return ctx.response.json({ emails: result.data, total: result.meta.total });
130
- }))
131
- .as('server-stats.debug.emails');
132
- router
133
- .get('/emails/:id/preview', bindApi(async (api, ctx) => {
134
- const id = Number(ctx.params.id);
135
- const html = await api.getEmailPreview(id);
136
- if (!html) {
137
- return ctx.response.notFound({ error: 'Email not found' });
138
- }
139
- return ctx.response.header('Content-Type', 'text/html; charset=utf-8').send(html);
140
- }))
141
- .as('server-stats.debug.emailPreview');
142
- router
143
- .get('/traces', bindApi(async (api, ctx) => {
144
- const result = await api.getTraces({ source: 'memory' });
145
- return ctx.response.json({ traces: result.data, total: result.meta.total });
146
- }))
147
- .as('server-stats.debug.traces');
148
- router
149
- .get('/traces/:id', bindApi(async (api, ctx) => {
150
- const id = Number(ctx.params.id);
151
- const trace = await api.getTraceDetail(id, 'memory');
152
- if (!trace) {
153
- return ctx.response.notFound({ error: 'Trace not found' });
154
- }
155
- return ctx.response.json(trace);
156
- }))
157
- .as('server-stats.debug.traceDetail');
158
- })
159
- .prefix(base)
160
- .use(middleware);
13
+ if (options.debugEndpoint) {
14
+ registerDebugRoutes({
15
+ router: options.router,
16
+ debugEndpoint: options.debugEndpoint,
17
+ getDebugController: options.getDebugController,
18
+ getApiController: options.getApiController,
19
+ middleware,
20
+ });
161
21
  }
162
- // =========================================================================
163
- // Dashboard routes
164
- // =========================================================================
165
- if (dashboardPath) {
166
- const base = dashboardPath.replace(/\/+$/, '');
167
- /**
168
- * Bind a dashboard controller method with lazy initialization
169
- * and 503 fallback.
170
- */
171
- const bindDash = (method) => {
172
- return async (ctx) => {
173
- const controller = getDashboardController();
174
- if (!controller) {
175
- return ctx.response.serviceUnavailable({
176
- error: 'Dashboard is starting up, please retry',
177
- });
178
- }
179
- return controller[method].call(controller, ctx);
180
- };
181
- };
182
- /**
183
- * Wrap an ApiController method as a dashboard HTTP handler with
184
- * lazy initialization and 503 fallback.
185
- */
186
- const bindApi = (fn) => {
187
- return async (ctx) => {
188
- const api = getApiController();
189
- if (!api) {
190
- return ctx.response.serviceUnavailable({
191
- error: 'Dashboard is starting up, please retry',
192
- });
193
- }
194
- return fn(api, ctx);
195
- };
196
- };
197
- router
198
- .group(() => {
199
- // ── Page ────────────────────────────────────────────────────
200
- router.get('/', bindDash('page')).as('server-stats.dashboard');
201
- // ── Overview (dashboard-only) ───────────────────────────────
202
- router.get('/api/overview', bindDash('overview')).as('server-stats.overview');
203
- router
204
- .get('/api/overview/chart', bindDash('overviewChart'))
205
- .as('server-stats.overview.chart');
206
- // ── Requests (dashboard-only) ───────────────────────────────
207
- router.get('/api/requests', bindDash('requests')).as('server-stats.requests');
208
- router.get('/api/requests/:id', bindDash('requestDetail')).as('server-stats.requests.show');
209
- // ── Queries — unified data via ApiController ────────────────
210
- router
211
- .get('/api/queries', bindApi(async (api, ctx) => {
212
- const qs = ctx.request.qs();
213
- const result = await api.getQueries({
214
- page: Number(qs.page) || 1,
215
- perPage: Number(qs.perPage) || 25,
216
- search: qs.search || undefined,
217
- filters: {
218
- durationMin: qs.duration_min ? Number(qs.duration_min) : undefined,
219
- model: qs.model || undefined,
220
- method: qs.method || undefined,
221
- connection: qs.connection || undefined,
222
- },
223
- });
224
- return ctx.response.json(result);
225
- }))
226
- .as('server-stats.queries');
227
- // Grouped queries and explain stay on DashboardController
228
- router
229
- .get('/api/queries/grouped', bindDash('queriesGrouped'))
230
- .as('server-stats.queries.grouped');
231
- router
232
- .get('/api/queries/:id/explain', bindDash('queryExplain'))
233
- .as('server-stats.queries.explain');
234
- // ── Events — unified data via ApiController ─────────────────
235
- router
236
- .get('/api/events', bindApi(async (api, ctx) => {
237
- const qs = ctx.request.qs();
238
- const result = await api.getEvents({
239
- page: Number(qs.page) || 1,
240
- perPage: Number(qs.perPage) || 25,
241
- search: qs.search || undefined,
242
- filters: {
243
- eventName: qs.event_name || undefined,
244
- },
245
- });
246
- return ctx.response.json(result);
247
- }))
248
- .as('server-stats.events');
249
- // ── Routes — unified data via ApiController ─────────────────
250
- router
251
- .get('/api/routes', bindApi((api, ctx) => {
252
- const qs = ctx.request.qs();
253
- const result = api.getRoutes(qs.search || undefined);
254
- return ctx.response.json(result);
255
- }))
256
- .as('server-stats.routes');
257
- // ── Logs — unified data via ApiController ───────────────────
258
- router
259
- .get('/api/logs', bindApi(async (api, ctx) => {
260
- const qs = ctx.request.qs();
261
- const result = await api.getLogs({
262
- page: Number(qs.page) || 1,
263
- perPage: Number(qs.perPage) || 50,
264
- search: qs.message || qs.search || undefined,
265
- filters: {
266
- level: qs.level || undefined,
267
- requestId: qs.request_id || qs.requestId || undefined,
268
- },
269
- });
270
- return ctx.response.json(result);
271
- }))
272
- .as('server-stats.logs');
273
- // ── Emails — unified data via ApiController ─────────────────
274
- router
275
- .get('/api/emails', bindApi(async (api, ctx) => {
276
- const qs = ctx.request.qs();
277
- const result = await api.getEmails({
278
- page: Number(qs.page) || 1,
279
- perPage: Number(qs.perPage) || 25,
280
- search: qs.search || undefined,
281
- filters: {
282
- from: qs.from || undefined,
283
- to: qs.to || undefined,
284
- subject: qs.subject || undefined,
285
- status: qs.status || undefined,
286
- mailer: qs.mailer || undefined,
287
- },
288
- });
289
- return ctx.response.json(result);
290
- }))
291
- .as('server-stats.emails');
292
- router
293
- .get('/api/emails/:id/preview', bindApi(async (api, ctx) => {
294
- const id = Number(ctx.params.id);
295
- const html = await api.getEmailPreview(id);
296
- if (!html) {
297
- return ctx.response.notFound({ error: 'Email not found' });
298
- }
299
- return ctx.response.header('Content-Type', 'text/html; charset=utf-8').send(html);
300
- }))
301
- .as('server-stats.emails.preview');
302
- // ── Traces — unified data via ApiController ─────────────────
303
- router
304
- .get('/api/traces', bindApi(async (api, ctx) => {
305
- const qs = ctx.request.qs();
306
- const result = await api.getTraces({
307
- page: Number(qs.page) || 1,
308
- perPage: Number(qs.perPage) || 25,
309
- search: qs.search || undefined,
310
- filters: {
311
- method: qs.method ? qs.method.toUpperCase() : undefined,
312
- url: qs.url || undefined,
313
- statusMin: qs.status_min ? Number(qs.status_min) : undefined,
314
- statusMax: qs.status_max ? Number(qs.status_max) : undefined,
315
- },
316
- });
317
- return ctx.response.json(result);
318
- }))
319
- .as('server-stats.traces');
320
- router
321
- .get('/api/traces/:id', bindApi(async (api, ctx) => {
322
- const id = Number(ctx.params.id);
323
- const trace = await api.getTraceDetail(id);
324
- if (!trace) {
325
- return ctx.response.notFound({ error: 'Trace not found' });
326
- }
327
- return ctx.response.json(trace);
328
- }))
329
- .as('server-stats.traces.show');
330
- // ── Cache (dashboard-only) ──────────────────────────────────
331
- router.get('/api/cache', bindDash('cacheStats')).as('server-stats.cache');
332
- router
333
- .get('/api/cache/:key', bindDash('cacheKey'))
334
- .as('server-stats.cache.show')
335
- .where('key', /.*/);
336
- router
337
- .delete('/api/cache/:key', bindDash('cacheKeyDelete'))
338
- .as('server-stats.cache.delete')
339
- .where('key', /.*/);
340
- // ── Jobs / Queue (dashboard-only) ───────────────────────────
341
- router.get('/api/jobs', bindDash('jobs')).as('server-stats.jobs');
342
- router.get('/api/jobs/:id', bindDash('jobDetail')).as('server-stats.jobs.show');
343
- router.post('/api/jobs/:id/retry', bindDash('jobRetry')).as('server-stats.jobs.retry');
344
- // ── Config (dashboard-only) ─────────────────────────────────
345
- router.get('/api/config', bindDash('config')).as('server-stats.config');
346
- // ── Saved Filters (dashboard-only) ──────────────────────────
347
- router.get('/api/filters', bindDash('savedFilters')).as('server-stats.filters');
348
- router.post('/api/filters', bindDash('createSavedFilter')).as('server-stats.filters.create');
349
- router
350
- .delete('/api/filters/:id', bindDash('deleteSavedFilter'))
351
- .as('server-stats.filters.delete');
352
- })
353
- .prefix(base)
354
- .use(middleware);
22
+ if (options.dashboardPath) {
23
+ registerDashboardRoutes({
24
+ router: options.router,
25
+ dashboardPath: options.dashboardPath,
26
+ getDashboardController: options.getDashboardController,
27
+ getApiController: options.getApiController,
28
+ middleware,
29
+ });
355
30
  }
356
31
  }
@@ -0,0 +1,5 @@
1
+ import type ServerStatsController from '../controller/server_stats_controller.js';
2
+ import type { AdonisRouter } from './router_types.js';
3
+ import type { HttpContext } from '@adonisjs/core/http';
4
+ /** Register the stats polling endpoint. */
5
+ export declare function registerStatsRoute(router: AdonisRouter, endpoint: string, getController: () => ServerStatsController | null, middleware: Array<(ctx: HttpContext, next: () => Promise<void>) => Promise<void>>): void;
@@ -0,0 +1,14 @@
1
+ /** Register the stats polling endpoint. */
2
+ export function registerStatsRoute(router, endpoint, getController, middleware) {
3
+ router
4
+ .get(endpoint, async (ctx) => {
5
+ const controller = getController();
6
+ if (!controller)
7
+ return ctx.response.serviceUnavailable({
8
+ error: 'Stats engine is starting up, please retry',
9
+ });
10
+ return controller.index(ctx);
11
+ })
12
+ .as('server-stats.api')
13
+ .use(middleware);
14
+ }
@@ -309,6 +309,102 @@
309
309
  color: var(--ss-border);
310
310
  }
311
311
 
312
+ /* ── Filter / Search Bar ─────────────────────────────────────── */
313
+ /* Padding can be overridden via --ss-filter-px (debug: 12px, dashboard: 16px). */
314
+ .ss-filter-bar,
315
+ .ss-dash-filter-bar,
316
+ .ss-dash-search-bar,
317
+ .ss-dbg-filter-bar,
318
+ .ss-dbg-search-bar {
319
+ display: flex;
320
+ align-items: center;
321
+ gap: 8px;
322
+ padding: 8px var(--ss-filter-px, 12px);
323
+ border-bottom: 1px solid var(--ss-border-dim);
324
+ background: var(--ss-surface-alt);
325
+ flex-shrink: 0;
326
+ flex-wrap: wrap;
327
+ }
328
+ .ss-search-wrapper,
329
+ .ss-dash-search-wrapper,
330
+ .ss-dbg-search-wrapper {
331
+ position: relative;
332
+ flex: 1;
333
+ min-width: 180px;
334
+ }
335
+ .ss-search-icon,
336
+ .ss-dash-search-icon,
337
+ .ss-dbg-search-icon {
338
+ position: absolute;
339
+ left: 8px;
340
+ top: 50%;
341
+ transform: translateY(-50%);
342
+ width: 14px;
343
+ height: 14px;
344
+ color: var(--ss-dim);
345
+ pointer-events: none;
346
+ }
347
+ .ss-search,
348
+ .ss-dash-search,
349
+ .ss-dbg-search {
350
+ flex: 1;
351
+ width: 100%;
352
+ padding: var(--ss-search-py, 4px) var(--ss-search-px, 8px);
353
+ padding-left: 28px;
354
+ padding-right: 24px;
355
+ font-size: 11px;
356
+ color: var(--ss-text);
357
+ background: var(--ss-input-bg);
358
+ border: 1px solid var(--ss-border);
359
+ border-radius: 4px;
360
+ outline: none;
361
+ }
362
+ .ss-search:focus,
363
+ .ss-dash-search:focus,
364
+ .ss-dbg-search:focus {
365
+ border-color: var(--ss-accent);
366
+ }
367
+ .ss-search::placeholder,
368
+ .ss-dash-search::placeholder,
369
+ .ss-dbg-search::placeholder {
370
+ color: var(--ss-dim);
371
+ }
372
+ .ss-search-clear,
373
+ .ss-dash-search-clear,
374
+ .ss-dbg-search-clear {
375
+ position: absolute;
376
+ right: 6px;
377
+ top: 50%;
378
+ transform: translateY(-50%);
379
+ background: none;
380
+ border: none;
381
+ color: var(--ss-dim);
382
+ cursor: pointer;
383
+ font-size: 14px;
384
+ padding: 2px 4px;
385
+ line-height: 1;
386
+ }
387
+ .ss-search-clear:hover,
388
+ .ss-dash-search-clear:hover,
389
+ .ss-dbg-search-clear:hover {
390
+ color: var(--ss-text);
391
+ }
392
+ .ss-filter-controls,
393
+ .ss-dash-filter-controls,
394
+ .ss-dbg-filter-controls {
395
+ display: flex;
396
+ align-items: center;
397
+ gap: 6px;
398
+ margin-left: auto;
399
+ }
400
+ .ss-summary,
401
+ .ss-dash-summary,
402
+ .ss-dbg-summary {
403
+ font-size: 11px;
404
+ color: var(--ss-muted);
405
+ white-space: nowrap;
406
+ }
407
+
312
408
  /* ═══════════════════════════════════════════════════════════════
313
409
  * Phase 4: Full Component Unification
314
410
  * ═══════════════════════════════════════════════════════════════ */
@@ -1130,3 +1226,84 @@ tr:hover .ss-dbg-copy-row-btn,
1130
1226
  .ss-dbg-btn-group .ss-dbg-btn:last-child {
1131
1227
  border-radius: 0 4px 4px 0;
1132
1228
  }
1229
+
1230
+ /* ── 8. Split pane layout ────────────────────────────────────── */
1231
+ .ss-split-container,
1232
+ .ss-dash-split-container,
1233
+ .ss-dbg-split-container {
1234
+ display: flex;
1235
+ flex-direction: column;
1236
+ flex: 1;
1237
+ min-height: 0;
1238
+ overflow: hidden;
1239
+ }
1240
+ .ss-split-top,
1241
+ .ss-dash-split-top,
1242
+ .ss-dbg-split-top {
1243
+ flex: 0.6;
1244
+ min-height: 60px;
1245
+ overflow: auto;
1246
+ }
1247
+ .ss-split-handle,
1248
+ .ss-dash-split-handle,
1249
+ .ss-dbg-split-handle {
1250
+ height: 6px;
1251
+ background: var(--ss-border-dim);
1252
+ cursor: row-resize;
1253
+ flex-shrink: 0;
1254
+ position: relative;
1255
+ user-select: none;
1256
+ touch-action: none;
1257
+ }
1258
+ .ss-split-handle:hover,
1259
+ .ss-dash-split-handle:hover,
1260
+ .ss-dbg-split-handle:hover {
1261
+ background: var(--ss-accent, #6d28d9);
1262
+ opacity: 0.8;
1263
+ }
1264
+ .ss-split-handle::after,
1265
+ .ss-dash-split-handle::after,
1266
+ .ss-dbg-split-handle::after {
1267
+ content: '';
1268
+ position: absolute;
1269
+ left: 50%;
1270
+ top: 50%;
1271
+ transform: translate(-50%, -50%);
1272
+ width: 32px;
1273
+ height: 2px;
1274
+ background: var(--ss-muted);
1275
+ border-radius: 1px;
1276
+ opacity: 0.5;
1277
+ }
1278
+ .ss-split-bottom,
1279
+ .ss-dash-split-bottom,
1280
+ .ss-dbg-split-bottom {
1281
+ flex: 0.4;
1282
+ min-height: 60px;
1283
+ overflow: auto;
1284
+ border-top: 1px solid var(--ss-border-dim);
1285
+ }
1286
+ .ss-related-logs-title,
1287
+ .ss-dash-related-logs-title,
1288
+ .ss-dbg-related-logs-title {
1289
+ position: sticky;
1290
+ top: 0;
1291
+ z-index: 2;
1292
+ display: flex;
1293
+ align-items: center;
1294
+ gap: 6px;
1295
+ padding: 6px 12px;
1296
+ font-size: 10px;
1297
+ font-weight: 600;
1298
+ text-transform: uppercase;
1299
+ letter-spacing: 0.05em;
1300
+ color: var(--ss-muted);
1301
+ background: var(--ss-surface);
1302
+ border-bottom: 1px solid var(--ss-border-faint);
1303
+ }
1304
+ .ss-related-logs-count,
1305
+ .ss-dash-related-logs-count,
1306
+ .ss-dbg-related-logs-count {
1307
+ font-weight: 400;
1308
+ color: var(--ss-dim);
1309
+ }