adonisjs-server-stats 1.16.1 → 1.18.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 +25 -8
- package/dist/core/core/dashboard-api.d.ts +6 -4
- package/dist/core/core/define-config-helpers.d.ts +1 -0
- package/dist/core/core/types-diagnostics.d.ts +2 -0
- package/dist/core/debug/types.d.ts +2 -0
- package/dist/core/index.js +9 -6
- package/dist/core/types.d.ts +55 -4
- package/dist/react/{CacheSection-DkLtukWz.js → CacheSection-B-lSIN8Q.js} +48 -44
- package/dist/react/{CacheTab-DWeXnEBI.js → CacheTab-BN0QgPXz.js} +1 -1
- package/dist/react/{ConfigSection-DaVbFPDg.js → ConfigSection-u_TSfb9f.js} +1 -1
- package/dist/react/{ConfigTab-D4640WJZ.js → ConfigTab-au26s-ON.js} +1 -1
- package/dist/react/{CustomPaneTab-DglmAVMC.js → CustomPaneTab-qvMnh8ss.js} +1 -1
- package/dist/react/{EmailsSection-vYx6ExTb.js → EmailsSection-HfEMeRex.js} +65 -57
- package/dist/react/{EmailsTab-DKDrRmRH.js → EmailsTab-BWwq-CxB.js} +1 -1
- package/dist/react/{EventsSection-CapH5xut.js → EventsSection-CA9b15dJ.js} +1 -1
- package/dist/react/{EventsTab-BNVEySAm.js → EventsTab-CgSdHbKU.js} +1 -1
- package/dist/react/{InternalsContent-C9lIA92C.js → InternalsContent-CI7HvBtH.js} +51 -47
- package/dist/react/{InternalsSection-lOv_UcSV.js → InternalsSection-IEeWXJqP.js} +1 -1
- package/dist/react/{InternalsTab-BygoSorC.js → InternalsTab-CoVLHQbF.js} +1 -1
- package/dist/react/{JobsSection-ZIQsJWid.js → JobsSection-DgsS_VwU.js} +1 -1
- package/dist/react/{JobsTab-DXQzXrDt.js → JobsTab-CzwLSRUC.js} +1 -1
- package/dist/react/{LogEntryRow-BWkHE51-.js → LogEntryRow-VCCzwD1H.js} +1 -1
- package/dist/react/{LogsSection-ConXdBkL.js → LogsSection-BujNn_cO.js} +2 -2
- package/dist/react/{LogsTab-CJM47LPn.js → LogsTab-BtD1h7DM.js} +2 -2
- package/dist/react/{OverviewSection-DOMu2qvl.js → OverviewSection-B28em0gQ.js} +119 -111
- package/dist/react/{QueriesSection-DFsaOSJI.js → QueriesSection-B0tFn_Dc.js} +1 -1
- package/dist/react/{QueriesTab-CY9CG_7L.js → QueriesTab-DJY7aavR.js} +1 -1
- package/dist/react/{RequestsSection-CC-eVHsl.js → RequestsSection-DPmYM7tq.js} +2 -2
- package/dist/react/{RoutesSection-p1DMq41y.js → RoutesSection-Ck-RcFVS.js} +1 -1
- package/dist/react/{RoutesTab-Cnqy8UcK.js → RoutesTab-Ch3PDLzo.js} +1 -1
- package/dist/react/{SplitPaneWrapper-XgkA0QxE.js → SplitPaneWrapper-BZ6VrBr-.js} +1 -1
- package/dist/react/{TimelineTab-1YOERxe5.js → TimelineTab-_byxh2mJ.js} +2 -2
- package/dist/react/components/Dashboard/overview-data-context.d.ts +15 -0
- package/dist/react/components/Dashboard/sections/OverviewSection.d.ts +9 -0
- package/dist/react/{index-DOSlCpZ9.js → index-De7nsWBN.js} +148 -141
- package/dist/react/index.js +1 -1
- package/dist/src/collectors/app_collector.d.ts +11 -0
- package/dist/src/collectors/app_collector.js +11 -0
- package/dist/src/collectors/redis_collector.js +13 -13
- package/dist/src/dashboard/cache_handlers.d.ts +17 -3
- package/dist/src/dashboard/cache_handlers.js +14 -19
- package/dist/src/dashboard/coalesce_cache.d.ts +2 -8
- package/dist/src/dashboard/coalesce_cache.js +25 -0
- package/dist/src/dashboard/dashboard_controller.d.ts +1 -0
- package/dist/src/dashboard/dashboard_controller.js +8 -3
- package/dist/src/dashboard/dashboard_store.d.ts +0 -1
- package/dist/src/dashboard/dashboard_store.js +18 -18
- package/dist/src/dashboard/filter_handlers.js +26 -4
- package/dist/src/dashboard/flush_manager.d.ts +1 -0
- package/dist/src/dashboard/flush_manager.js +6 -1
- package/dist/src/dashboard/format_helpers.d.ts +0 -4
- package/dist/src/dashboard/format_helpers.js +0 -19
- package/dist/src/dashboard/integrations/config_inspector.js +6 -3
- package/dist/src/dashboard/knex_factory.js +8 -0
- package/dist/src/dashboard/migrator.d.ts +7 -2
- package/dist/src/dashboard/migrator.js +140 -6
- package/dist/src/dashboard/migrator_tables.js +8 -0
- package/dist/src/dashboard/paginate_helper.js +21 -11
- package/dist/src/dashboard/sensitive_patterns.d.ts +37 -0
- package/dist/src/dashboard/sensitive_patterns.js +146 -5
- package/dist/src/dashboard/storage_stats.d.ts +5 -0
- package/dist/src/dashboard/storage_stats.js +15 -1
- package/dist/src/dashboard/write_queue.d.ts +20 -21
- package/dist/src/dashboard/write_queue.js +37 -60
- package/dist/src/debug/query_collector.js +6 -1
- package/dist/src/debug/trace_collector.js +26 -13
- package/dist/src/debug/types.d.ts +2 -0
- package/dist/src/define_config.js +2 -0
- package/dist/src/edge/client/dashboard.js +2 -2
- package/dist/src/edge/client/debug-panel-deferred.js +1 -1
- package/dist/src/edge/client-vue/dashboard.js +5 -5
- package/dist/src/edge/client-vue/debug-panel-deferred.js +2 -2
- package/dist/src/edge/views/dashboard.edge +1 -1
- package/dist/src/edge/views/debug-panel.edge +1 -1
- package/dist/src/edge/views/stats-bar.edge +1 -1
- package/dist/src/middleware/request_tracking_middleware.js +7 -2
- package/dist/src/provider/dashboard_init.d.ts +9 -1
- package/dist/src/provider/dashboard_init.js +10 -0
- package/dist/src/provider/dashboard_setup.js +14 -0
- package/dist/src/provider/diagnostics.d.ts +2 -0
- package/dist/src/provider/diagnostics.js +1 -0
- package/dist/src/provider/email_helpers.js +20 -5
- package/dist/src/provider/server_stats_provider.d.ts +10 -0
- package/dist/src/provider/server_stats_provider.js +26 -7
- package/dist/src/provider/shutdown_helpers.js +3 -1
- package/dist/src/provider/toolbar_setup.js +21 -15
- package/dist/src/stubs/config.stub +4 -0
- package/dist/src/types.d.ts +55 -4
- package/dist/vue/CacheSection-z2TQRez9.js +161 -0
- package/dist/vue/{ConfigSection-BiRAiaHj.js → ConfigSection-CFEaig69.js} +1 -1
- package/dist/vue/{EmailsSection-Dl44qyqY.js → EmailsSection-CYohBo62.js} +89 -85
- package/dist/vue/{EventsSection-CWjeitjU.js → EventsSection-B_TuVrGj.js} +1 -1
- package/dist/vue/{InternalsSection-jhTCuK3j.js → InternalsSection-B1P2kNT-.js} +92 -84
- package/dist/vue/{InternalsTab-Z3c82glB.js → InternalsTab-CRI-IFTo.js} +155 -147
- package/dist/vue/{JobsSection-DOBb4LjZ.js → JobsSection-AjkEzyiv.js} +1 -1
- package/dist/vue/{LogsSection-CXx-HOWJ.js → LogsSection-DZD0CCDi.js} +1 -1
- package/dist/vue/{OverviewSection-CyfNQ8uV.js → OverviewSection-BEc4Gktf.js} +1 -1
- package/dist/vue/{QueriesSection-CXBsFp-y.js → QueriesSection-BMImGXTn.js} +1 -1
- package/dist/vue/{RequestsSection-YTIaZGZd.js → RequestsSection-DOI2MUFX.js} +1 -1
- package/dist/vue/{RoutesSection-CDKMey49.js → RoutesSection-C1sxt-AW.js} +1 -1
- package/dist/vue/composables/useDashboardData.d.ts +1 -1
- package/dist/vue/{index-14x39RI_.js → index-BfPAAjL5.js} +95 -95
- package/dist/vue/index.js +1 -1
- package/package.json +12 -3
- package/dist/src/dashboard/explain_query.d.ts +0 -8
- package/dist/src/dashboard/explain_query.js +0 -22
- package/dist/src/provider/boot_initializer.d.ts +0 -28
- package/dist/src/provider/boot_initializer.js +0 -35
- package/dist/vue/CacheSection-CxEBVVkF.js +0 -156
package/README.md
CHANGED
|
@@ -13,6 +13,8 @@ Drop a single Edge tag into your layout and get a live stats bar showing CPU, me
|
|
|
13
13
|
|
|
14
14
|
Zero frontend dependencies. Zero build step. Just `@serverStats()` and go.
|
|
15
15
|
|
|
16
|
+
**Zero runtime dependencies.** Everything this package needs at runtime is either built into Node or an optional peer dependency you already chose to install (`@adonisjs/lucid`, `better-sqlite3`, ...). Nothing extra lands in your production `node_modules` supply chain.
|
|
17
|
+
|
|
16
18
|
**New (alpha):** Native [React & Vue components](#react--vue-inertiajs--alpha) for Inertia.js apps — same features, framework-native.
|
|
17
19
|
|
|
18
20
|

|
|
@@ -201,10 +203,11 @@ All fields are optional. `defineConfig({})` works with zero configuration.
|
|
|
201
203
|
|
|
202
204
|
### `DashboardConfig`
|
|
203
205
|
|
|
204
|
-
| Option | Type | Default | Description
|
|
205
|
-
| --------------- | -------- | ------------ |
|
|
206
|
-
| `path` | `string` | `'/__stats'` | URL path for the dashboard page
|
|
207
|
-
| `retentionDays` | `number` | `7` | Days to keep historical data in SQLite
|
|
206
|
+
| Option | Type | Default | Description |
|
|
207
|
+
| --------------- | -------- | ------------ | --------------------------------------------------------------------------------- |
|
|
208
|
+
| `path` | `string` | `'/__stats'` | URL path for the dashboard page |
|
|
209
|
+
| `retentionDays` | `number` | `7` | Days to keep historical data in SQLite |
|
|
210
|
+
| `maxDbSizeMb` | `number` | `500` | Size cap for the SQLite database -- oldest rows are pruned when exceeded; `0` disables |
|
|
208
211
|
|
|
209
212
|
### `ProductionConfig`
|
|
210
213
|
|
|
@@ -213,6 +216,7 @@ All fields are optional. `defineConfig({})` works with zero configuration.
|
|
|
213
216
|
| `enabled` | `boolean` | `false` | Register routes and build the dashboard when `NODE_ENV=production` |
|
|
214
217
|
| `capture` | `CaptureConfig` | all off | Which capture subsystems to switch on -- opt in one at a time |
|
|
215
218
|
| `retentionDays` | `number` | `3` | SQLite history to keep in production (the usual default is 7) |
|
|
219
|
+
| `maxDbSizeMb` | `number` | `500` | Size cap for the SQLite database in production; `0` disables |
|
|
216
220
|
|
|
217
221
|
`CaptureConfig` fields, each `boolean` and each defaulting to `false` in production: `queries`, `events`, `emails`, `traces`, `logs`.
|
|
218
222
|
|
|
@@ -262,9 +266,12 @@ The following field names still work but will show deprecation warnings at boot.
|
|
|
262
266
|
| `dashboard` | `boolean` | `false` | Enable the full-page dashboard (requires `better-sqlite3`) |
|
|
263
267
|
| `dashboardPath` | `string` | `'/__stats'` | URL path for the dashboard page |
|
|
264
268
|
| `retentionDays` | `number` | `7` | Days to keep historical data in SQLite |
|
|
269
|
+
| `maxDbSizeMb` | `number` | `500` | Size cap for the SQLite database's live data. When exceeded, the cleanup deletes the oldest records regardless of age. `0` disables the cap. |
|
|
265
270
|
| `dbPath` | `string` | `'.adonisjs/server-stats/dashboard.sqlite3'` | Path to the SQLite database file (relative to app root) |
|
|
266
271
|
| `debugEndpoint` | `string` | `'/admin/api/debug'` | Base path for the debug toolbar API endpoints |
|
|
267
272
|
| `excludeFromTracing` | `string[]` | `['/admin/api/debug', '/admin/api/server-stats']` | URL prefixes to exclude from tracing and dashboard persistence. Requests still count toward HTTP metrics but won't appear in the timeline or be stored. The stats endpoint is always excluded automatically. |
|
|
273
|
+
| `cacheKeyPrefix` | `string` | -- | Allow-list prefix for the Cache tab (e.g. `'myapp:cache:'`). Scopes which Redis keys can be listed, read, and deleted. Unset: unrestricted in dev, single-key access refused in production. Env var `SERVER_STATS_CACHE_KEY_PREFIX` works as a fallback. |
|
|
274
|
+
| `emailBridgeChannel` | `string` | `'adonisjs-server-stats:emails'` | Redis pub/sub channel for the cross-process email bridge. Namespace it on a shared Redis so other subscribers cannot read your outbound mail. |
|
|
268
275
|
| `panes` | `DebugPane[]` | -- | Custom debug panel tabs |
|
|
269
276
|
|
|
270
277
|
---
|
|
@@ -288,6 +295,14 @@ Each collector is a factory function that returns a `MetricCollector`. All colle
|
|
|
288
295
|
| `logCollector(opts?)` | Errors/warnings/entries (5m window), entries/minute | optional | -- |
|
|
289
296
|
| `appCollector()` | Online users, pending webhooks, pending emails | none | `@adonisjs/lucid` |
|
|
290
297
|
|
|
298
|
+
> [!WARNING]
|
|
299
|
+
> `appCollector()` polls **your primary database** -- three `COUNT(*)` queries every collection interval (3s by default), forever. Index the columns it filters on or every tick is a full table scan:
|
|
300
|
+
>
|
|
301
|
+
> ```sql
|
|
302
|
+
> CREATE INDEX webhook_events_status_index ON webhook_events (status);
|
|
303
|
+
> CREATE INDEX scheduled_emails_status_index ON scheduled_emails (status);
|
|
304
|
+
> ```
|
|
305
|
+
|
|
291
306
|
### Collector Options
|
|
292
307
|
|
|
293
308
|
```ts
|
|
@@ -449,9 +464,11 @@ A disabled subsystem is never subscribed, so it costs nothing -- its dashboard p
|
|
|
449
464
|
|
|
450
465
|
### Disk
|
|
451
466
|
|
|
452
|
-
Retention prunes rows older than `retentionDays` on boot and hourly
|
|
467
|
+
Retention prunes rows older than `retentionDays` on boot and hourly, and returns freed pages to the OS incrementally, so the `.sqlite3` file genuinely shrinks after cleanup. Watch real numbers in the dashboard's storage panel (`GET {dashboardPath}/api/storage`), which reports file size, WAL size, and per-table row counts.
|
|
468
|
+
|
|
469
|
+
`maxDbSizeMb` (default 500) additionally caps the database's live data size. When write volume outruns what `retentionDays` alone would keep small -- tracing plus persistence on a busy app can write hundreds of MB per day -- the cleanup deletes the globally oldest records, regardless of age, until usage drops below the cap. In other words, the retention window shortens dynamically instead of the file growing without bound. Set `maxDbSizeMb: 0` to disable the cap.
|
|
453
470
|
|
|
454
|
-
There is no sampling
|
|
471
|
+
There is no sampling. On a high-traffic app, enable capture deliberately and keep `retentionDays` short.
|
|
455
472
|
|
|
456
473
|
### Async guards and the toolbar
|
|
457
474
|
|
|
@@ -464,9 +481,9 @@ An `authorize` callback may be sync or async -- the route guard awaits it, so `a
|
|
|
464
481
|
|
|
465
482
|
Worth knowing before you expose it, even behind an admin guard:
|
|
466
483
|
|
|
467
|
-
- **One guard, no tiers.** Anyone who passes `authorize` can also `DELETE /api/cache/:key` and `POST /api/jobs/:id/retry`. Scope cache access with `SERVER_STATS_CACHE_KEY_PREFIX` -- unset means unrestricted.
|
|
484
|
+
- **One guard, no tiers.** Anyone who passes `authorize` can also `DELETE /api/cache/:key` and `POST /api/jobs/:id/retry`. Scope cache access with `advanced.cacheKeyPrefix` (or the `SERVER_STATS_CACHE_KEY_PREFIX` env var) -- unset means unrestricted in dev, and in production single-key cache reads/deletes are refused until a prefix is configured.
|
|
468
485
|
- **No audit log and no rate limiting** on those mutating endpoints.
|
|
469
|
-
- **SQL bindings
|
|
486
|
+
- **Redaction is pattern-based, not exhaustive.** SQL bindings, config/env values, stored URLs, and persisted log records are all redacted against a shared rule set (credential-shaped names incl. camelCase, hashes, JWTs, provider key prefixes, long hex/base64 blobs), and anything that survives is truncated at 256 chars. A secret under a name the patterns don't recognise, with an ordinary-looking value, still gets stored.
|
|
470
487
|
|
|
471
488
|
---
|
|
472
489
|
|
|
@@ -45,9 +45,10 @@ export declare class DashboardApi {
|
|
|
45
45
|
/**
|
|
46
46
|
* Fetch the value of a specific cache key.
|
|
47
47
|
*
|
|
48
|
-
* @param key
|
|
48
|
+
* @param key - Cache key (will be URI-encoded).
|
|
49
|
+
* @param init - Optional `RequestInit` overrides (e.g. `{ signal }`).
|
|
49
50
|
*/
|
|
50
|
-
fetchCacheKey(key: string): Promise<Record<string, unknown>>;
|
|
51
|
+
fetchCacheKey(key: string, init?: RequestInit): Promise<Record<string, unknown>>;
|
|
51
52
|
/**
|
|
52
53
|
* Delete a cache entry by key.
|
|
53
54
|
*
|
|
@@ -59,7 +60,8 @@ export declare class DashboardApi {
|
|
|
59
60
|
/**
|
|
60
61
|
* Fetch the HTML preview for an email record.
|
|
61
62
|
*
|
|
62
|
-
* @param id
|
|
63
|
+
* @param id - Email record ID.
|
|
64
|
+
* @param init - Optional `RequestInit` overrides (e.g. `{ signal }`).
|
|
63
65
|
*/
|
|
64
|
-
fetchEmailPreview(id: number): Promise<string>;
|
|
66
|
+
fetchEmailPreview(id: number, init?: RequestInit): Promise<string>;
|
|
65
67
|
}
|
|
@@ -14,6 +14,7 @@ export declare function applyToolbarSetting(toolbar: boolean | ToolbarConfig, re
|
|
|
14
14
|
export declare function applyDashboardSetting(dashboard: boolean | {
|
|
15
15
|
path?: string;
|
|
16
16
|
retentionDays?: number;
|
|
17
|
+
maxDbSizeMb?: number;
|
|
17
18
|
}, result: DevToolbarOptions): void;
|
|
18
19
|
/**
|
|
19
20
|
* Apply advanced config fields onto the result DevToolbarOptions.
|
|
@@ -51,6 +51,7 @@ export interface DiagnosticsResponse {
|
|
|
51
51
|
dashboardPath: string;
|
|
52
52
|
debugEndpoint: string;
|
|
53
53
|
retentionDays: number;
|
|
54
|
+
maxDbSizeMb: number;
|
|
54
55
|
dbPath: string;
|
|
55
56
|
persistDebugData: boolean | string;
|
|
56
57
|
renderer: string;
|
|
@@ -77,6 +78,7 @@ export interface DiagnosticsResponse {
|
|
|
77
78
|
fileSizeMb: number;
|
|
78
79
|
walSizeMb: number;
|
|
79
80
|
retentionDays: number;
|
|
81
|
+
maxDbSizeMb: number;
|
|
80
82
|
tables: Array<{
|
|
81
83
|
name: string;
|
|
82
84
|
rowCount: number;
|
|
@@ -284,6 +284,8 @@ export interface DevToolbarConfig {
|
|
|
284
284
|
dashboardPath: string;
|
|
285
285
|
/** Data retention period in days for historical persistence. */
|
|
286
286
|
retentionDays: number;
|
|
287
|
+
/** Size cap in MB for the SQLite database's live data; `0` disables it. */
|
|
288
|
+
maxDbSizeMb: number;
|
|
287
289
|
/** Path to the SQLite database file for historical persistence. */
|
|
288
290
|
dbPath: string;
|
|
289
291
|
/** Base path for the debug toolbar API endpoints. */
|
package/dist/core/index.js
CHANGED
|
@@ -1110,11 +1110,13 @@ class At {
|
|
|
1110
1110
|
/**
|
|
1111
1111
|
* Fetch the value of a specific cache key.
|
|
1112
1112
|
*
|
|
1113
|
-
* @param key
|
|
1113
|
+
* @param key - Cache key (will be URI-encoded).
|
|
1114
|
+
* @param init - Optional `RequestInit` overrides (e.g. `{ signal }`).
|
|
1114
1115
|
*/
|
|
1115
|
-
async fetchCacheKey(e) {
|
|
1116
|
+
async fetchCacheKey(e, r) {
|
|
1116
1117
|
return this.client.fetch(
|
|
1117
|
-
`${this.basePath}/cache/${encodeURIComponent(e)}
|
|
1118
|
+
`${this.basePath}/cache/${encodeURIComponent(e)}`,
|
|
1119
|
+
r
|
|
1118
1120
|
);
|
|
1119
1121
|
}
|
|
1120
1122
|
/**
|
|
@@ -1133,10 +1135,11 @@ class At {
|
|
|
1133
1135
|
/**
|
|
1134
1136
|
* Fetch the HTML preview for an email record.
|
|
1135
1137
|
*
|
|
1136
|
-
* @param id
|
|
1138
|
+
* @param id - Email record ID.
|
|
1139
|
+
* @param init - Optional `RequestInit` overrides (e.g. `{ signal }`).
|
|
1137
1140
|
*/
|
|
1138
|
-
async fetchEmailPreview(e) {
|
|
1139
|
-
return this.client.fetch(`${this.basePath}/emails/${e}/preview
|
|
1141
|
+
async fetchEmailPreview(e, r) {
|
|
1142
|
+
return this.client.fetch(`${this.basePath}/emails/${e}/preview`, r);
|
|
1140
1143
|
}
|
|
1141
1144
|
}
|
|
1142
1145
|
function $t(t) {
|
package/dist/core/types.d.ts
CHANGED
|
@@ -263,6 +263,15 @@ export interface DevToolbarOptions {
|
|
|
263
263
|
* @default 7
|
|
264
264
|
*/
|
|
265
265
|
retentionDays?: number;
|
|
266
|
+
/**
|
|
267
|
+
* Size cap in MB for the SQLite database's live data. When exceeded,
|
|
268
|
+
* the hourly cleanup deletes the oldest records — regardless of age —
|
|
269
|
+
* until usage drops below the cap, effectively shortening the retention
|
|
270
|
+
* window when write volume outruns `retentionDays`. Set to `0` to
|
|
271
|
+
* disable the cap.
|
|
272
|
+
* @default 500
|
|
273
|
+
*/
|
|
274
|
+
maxDbSizeMb?: number;
|
|
266
275
|
/**
|
|
267
276
|
* Path to the SQLite database file for historical persistence.
|
|
268
277
|
* Relative to app root.
|
|
@@ -434,6 +443,17 @@ export interface DashboardConfig {
|
|
|
434
443
|
* @default 7
|
|
435
444
|
*/
|
|
436
445
|
retentionDays?: number;
|
|
446
|
+
/**
|
|
447
|
+
* Size cap in MB for the SQLite database's live data.
|
|
448
|
+
*
|
|
449
|
+
* When exceeded, the cleanup deletes the oldest records — regardless
|
|
450
|
+
* of age — until usage drops below the cap. This bounds disk usage
|
|
451
|
+
* when write volume (tracing, verbose query logging) outruns what
|
|
452
|
+
* `retentionDays` alone would keep small. Set to `0` to disable.
|
|
453
|
+
*
|
|
454
|
+
* @default 500
|
|
455
|
+
*/
|
|
456
|
+
maxDbSizeMb?: number;
|
|
437
457
|
}
|
|
438
458
|
/**
|
|
439
459
|
* Access-control callback signature.
|
|
@@ -461,7 +481,7 @@ export type AccessGuard = (ctx: import('@adonisjs/core/http').HttpContext) => bo
|
|
|
461
481
|
export interface CaptureConfig {
|
|
462
482
|
/** SQL text, bindings, and timings for every query. */
|
|
463
483
|
queries?: boolean;
|
|
464
|
-
/** Application events
|
|
484
|
+
/** Application events, linked to the request that follows them and persisted with it. */
|
|
465
485
|
events?: boolean;
|
|
466
486
|
/** Sent mail, including subject and body. */
|
|
467
487
|
emails?: boolean;
|
|
@@ -515,13 +535,21 @@ export interface ProductionConfig {
|
|
|
515
535
|
capture?: CaptureConfig;
|
|
516
536
|
/**
|
|
517
537
|
* How many days of history to keep in SQLite, overriding the usual default
|
|
518
|
-
* of 7. Retention deletes rows hourly
|
|
519
|
-
*
|
|
520
|
-
* the dashboard's storage panel.
|
|
538
|
+
* of 7. Retention deletes rows hourly and returns freed pages to the OS
|
|
539
|
+
* incrementally — watch actual size via the dashboard's storage panel.
|
|
521
540
|
*
|
|
522
541
|
* @default 3
|
|
523
542
|
*/
|
|
524
543
|
retentionDays?: number;
|
|
544
|
+
/**
|
|
545
|
+
* Size cap in MB for the SQLite database's live data in production,
|
|
546
|
+
* overriding the usual default. When exceeded, the hourly cleanup deletes
|
|
547
|
+
* the oldest records — regardless of age — until usage drops below the
|
|
548
|
+
* cap. Set to `0` to disable.
|
|
549
|
+
*
|
|
550
|
+
* @default 500
|
|
551
|
+
*/
|
|
552
|
+
maxDbSizeMb?: number;
|
|
525
553
|
}
|
|
526
554
|
/**
|
|
527
555
|
* Advanced options that most users never need to touch.
|
|
@@ -622,6 +650,29 @@ export interface AdvancedConfig {
|
|
|
622
650
|
* @default 200
|
|
623
651
|
*/
|
|
624
652
|
maxTraces?: number;
|
|
653
|
+
/**
|
|
654
|
+
* Allow-list prefix for the dashboard's cache inspector.
|
|
655
|
+
*
|
|
656
|
+
* Scopes which Redis keys the Cache tab may list, read, and delete
|
|
657
|
+
* (e.g. `'myapp:cache:'`). Unset keeps full access in development for
|
|
658
|
+
* back-compat, but in production single-key reads and deletes are
|
|
659
|
+
* refused until a prefix is configured — a production dashboard must
|
|
660
|
+
* not be able to read session keys or delete rate-limit counters.
|
|
661
|
+
*
|
|
662
|
+
* The `SERVER_STATS_CACHE_KEY_PREFIX` env var is used as a fallback.
|
|
663
|
+
*/
|
|
664
|
+
cacheKeyPrefix?: string;
|
|
665
|
+
/**
|
|
666
|
+
* Redis pub/sub channel for the cross-process email bridge.
|
|
667
|
+
*
|
|
668
|
+
* Queue workers publish captured mail on this channel and the web
|
|
669
|
+
* process persists it. On a Redis instance shared between apps or
|
|
670
|
+
* tenants, namespace this (e.g. `myapp:server-stats:emails`) so
|
|
671
|
+
* other subscribers cannot read your outbound mail.
|
|
672
|
+
*
|
|
673
|
+
* @default 'adonisjs-server-stats:emails'
|
|
674
|
+
*/
|
|
675
|
+
emailBridgeChannel?: string;
|
|
625
676
|
}
|
|
626
677
|
/**
|
|
627
678
|
* Top-level configuration for `adonisjs-server-stats`.
|
|
@@ -1,46 +1,50 @@
|
|
|
1
|
-
import { jsxs as
|
|
2
|
-
import { useState as
|
|
3
|
-
import { formatCacheSize as
|
|
4
|
-
import { a as
|
|
5
|
-
import { C as
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
function
|
|
9
|
-
const [
|
|
10
|
-
...
|
|
11
|
-
search:
|
|
12
|
-
}), a =
|
|
1
|
+
import { jsxs as u, jsx as t } from "react/jsx-runtime";
|
|
2
|
+
import { useState as d, useRef as N, useCallback as v, useEffect as V } from "react";
|
|
3
|
+
import { formatCacheSize as L, formatTtl as R } from "adonisjs-server-stats/core";
|
|
4
|
+
import { a as E, J as F } from "./index-De7nsWBN.js";
|
|
5
|
+
import { C as T } from "./CacheStatsBar-CRodCOeP.js";
|
|
6
|
+
import { F as A } from "./FilterBar-CQ7bD669.js";
|
|
7
|
+
import { D as z } from "./DataTable-YyShr5B-.js";
|
|
8
|
+
function M({ options: g = {} }) {
|
|
9
|
+
const [y, w] = d(""), [r, h] = d(null), [C, n] = d(null), [K, m] = d(!1), [p, c] = d(null), s = N(null), { data: f, isLoading: S, mutate: k, getApi: b } = E("cache", {
|
|
10
|
+
...g,
|
|
11
|
+
search: y
|
|
12
|
+
}), a = f, x = v(
|
|
13
13
|
async (e) => {
|
|
14
14
|
if (confirm(`Delete cache key "${e}"?`))
|
|
15
15
|
try {
|
|
16
|
-
await
|
|
16
|
+
await k(`cache/${encodeURIComponent(e)}`, "delete"), r === e && (h(null), n(null), c(null));
|
|
17
17
|
} catch {
|
|
18
18
|
}
|
|
19
19
|
},
|
|
20
|
-
[
|
|
21
|
-
),
|
|
20
|
+
[k, r]
|
|
21
|
+
), D = v(
|
|
22
22
|
async (e) => {
|
|
23
|
-
if (
|
|
24
|
-
|
|
23
|
+
if (s.current?.abort(), r === e) {
|
|
24
|
+
s.current = null, h(null), n(null), c(null);
|
|
25
25
|
return;
|
|
26
26
|
}
|
|
27
|
-
|
|
27
|
+
h(e), n(null), c(null), m(!0);
|
|
28
|
+
const l = new AbortController();
|
|
29
|
+
s.current = l;
|
|
28
30
|
try {
|
|
29
|
-
const
|
|
31
|
+
const i = await b().fetchCacheKey(e, { signal: l.signal });
|
|
32
|
+
if (s.current !== l) return;
|
|
30
33
|
n(
|
|
31
|
-
|
|
32
|
-
),
|
|
33
|
-
} catch {
|
|
34
|
-
|
|
34
|
+
i.value !== void 0 ? i.value : i.data !== void 0 ? i.data : i
|
|
35
|
+
), c(null);
|
|
36
|
+
} catch (o) {
|
|
37
|
+
if (o instanceof Error && o.name === "AbortError" || s.current !== l) return;
|
|
38
|
+
n(null), c("Failed to fetch key value");
|
|
35
39
|
} finally {
|
|
36
|
-
|
|
40
|
+
s.current === l && m(!1);
|
|
37
41
|
}
|
|
38
42
|
},
|
|
39
|
-
[
|
|
43
|
+
[r, b]
|
|
40
44
|
);
|
|
41
|
-
return /* @__PURE__ */
|
|
45
|
+
return V(() => () => s.current?.abort(), []), /* @__PURE__ */ u("div", { children: [
|
|
42
46
|
a?.available && a?.stats && /* @__PURE__ */ t(
|
|
43
|
-
|
|
47
|
+
T,
|
|
44
48
|
{
|
|
45
49
|
hitRate: a.stats.hitRate ?? 0,
|
|
46
50
|
hits: a.stats.hits ?? 0,
|
|
@@ -49,16 +53,16 @@ function _({ options: v = {} }) {
|
|
|
49
53
|
}
|
|
50
54
|
),
|
|
51
55
|
/* @__PURE__ */ t(
|
|
52
|
-
|
|
56
|
+
A,
|
|
53
57
|
{
|
|
54
|
-
search:
|
|
55
|
-
onSearchChange:
|
|
58
|
+
search: y,
|
|
59
|
+
onSearchChange: w,
|
|
56
60
|
placeholder: "Filter cache keys...",
|
|
57
61
|
summary: `${(a?.keys || a?.data || []).length} keys`
|
|
58
62
|
}
|
|
59
63
|
),
|
|
60
|
-
|
|
61
|
-
|
|
64
|
+
S && !f ? /* @__PURE__ */ t("div", { className: "ss-dash-empty", children: "Loading cache..." }) : !a || !a.available ? /* @__PURE__ */ t("div", { className: "ss-dash-empty", children: "Cache inspector not available" }) : /* @__PURE__ */ t("div", { className: "ss-dash-table-wrap", children: /* @__PURE__ */ t(
|
|
65
|
+
z,
|
|
62
66
|
{
|
|
63
67
|
columns: [
|
|
64
68
|
{
|
|
@@ -89,25 +93,25 @@ function _({ options: v = {} }) {
|
|
|
89
93
|
key: "size",
|
|
90
94
|
label: "Size",
|
|
91
95
|
width: "60px",
|
|
92
|
-
render: (e) => e != null && e > 0 ?
|
|
96
|
+
render: (e) => e != null && e > 0 ? L(e) : "-"
|
|
93
97
|
},
|
|
94
98
|
{
|
|
95
99
|
key: "ttl",
|
|
96
100
|
label: "TTL",
|
|
97
101
|
width: "70px",
|
|
98
|
-
render: (e) => e > 0 ?
|
|
102
|
+
render: (e) => e > 0 ? R(e) : "-"
|
|
99
103
|
},
|
|
100
104
|
{
|
|
101
105
|
key: "_actions",
|
|
102
106
|
label: "",
|
|
103
107
|
width: "60px",
|
|
104
|
-
render: (e,
|
|
108
|
+
render: (e, l) => /* @__PURE__ */ t(
|
|
105
109
|
"button",
|
|
106
110
|
{
|
|
107
111
|
type: "button",
|
|
108
112
|
className: "ss-dash-retry-btn",
|
|
109
|
-
onClick: (
|
|
110
|
-
|
|
113
|
+
onClick: (o) => {
|
|
114
|
+
o.stopPropagation(), x(l.key);
|
|
111
115
|
},
|
|
112
116
|
children: "Delete"
|
|
113
117
|
}
|
|
@@ -116,20 +120,20 @@ function _({ options: v = {} }) {
|
|
|
116
120
|
],
|
|
117
121
|
data: a.keys || a.data || [],
|
|
118
122
|
keyField: "key",
|
|
119
|
-
onRowClick: (e) =>
|
|
123
|
+
onRowClick: (e) => D(e.key),
|
|
120
124
|
emptyMessage: "No cache keys found"
|
|
121
125
|
}
|
|
122
126
|
) }),
|
|
123
|
-
|
|
124
|
-
/* @__PURE__ */
|
|
127
|
+
r && /* @__PURE__ */ u("div", { className: "ss-dash-cache-detail", children: [
|
|
128
|
+
/* @__PURE__ */ u("h4", { children: [
|
|
125
129
|
"Key: ",
|
|
126
|
-
|
|
130
|
+
r
|
|
127
131
|
] }),
|
|
128
|
-
|
|
132
|
+
K ? /* @__PURE__ */ t("div", { className: "ss-dash-empty", children: "Loading value..." }) : p ? /* @__PURE__ */ t("div", { className: "ss-dash-empty", style: { color: "var(--ss-red-fg)" }, children: p }) : /* @__PURE__ */ t(F, { data: C })
|
|
129
133
|
] })
|
|
130
134
|
] });
|
|
131
135
|
}
|
|
132
136
|
export {
|
|
133
|
-
|
|
134
|
-
|
|
137
|
+
M as CacheSection,
|
|
138
|
+
M as default
|
|
135
139
|
};
|
|
@@ -2,7 +2,7 @@ import { jsx as e, jsxs as t } from "react/jsx-runtime";
|
|
|
2
2
|
import { useState as p, useRef as z, useMemo as A, useCallback as K, useEffect as L } from "react";
|
|
3
3
|
import { formatTtl as $, formatCacheSize as j } from "adonisjs-server-stats/core";
|
|
4
4
|
import { u as F } from "./useDashboardApiBase-Bi36pJ2L.js";
|
|
5
|
-
import { u as D, J as E } from "./index-
|
|
5
|
+
import { u as D, J as E } from "./index-De7nsWBN.js";
|
|
6
6
|
import { u as V } from "./useResizableTable-CNJmACdt.js";
|
|
7
7
|
import { C as J } from "./CacheStatsBar-CRodCOeP.js";
|
|
8
8
|
import { F as M } from "./FilterBar-CQ7bD669.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as t } from "react/jsx-runtime";
|
|
2
|
-
import { a as i } from "./index-
|
|
2
|
+
import { a as i } from "./index-De7nsWBN.js";
|
|
3
3
|
import { C as n } from "./ConfigContent-Bwvfl_G7.js";
|
|
4
4
|
function m({ options: o = {} }) {
|
|
5
5
|
const { data: a, isLoading: s } = i("config", o);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsxs as i, jsx as n } from "react/jsx-runtime";
|
|
2
2
|
import { u as f } from "./useDashboardApiBase-Bi36pJ2L.js";
|
|
3
|
-
import { u as m } from "./index-
|
|
3
|
+
import { u as m } from "./index-De7nsWBN.js";
|
|
4
4
|
import { C as d } from "./ConfigContent-Bwvfl_G7.js";
|
|
5
5
|
function b({ options: r, dashboardPath: o }) {
|
|
6
6
|
const { resolvedOptions: a } = f(o, r), { data: e, isLoading: t, error: s } = m("config", a);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsxs as l, jsx as r } from "react/jsx-runtime";
|
|
2
2
|
import { useState as w, useMemo as f } from "react";
|
|
3
3
|
import { compactPreview as k, formatDuration as x, durationClassName as M } from "adonisjs-server-stats/core";
|
|
4
|
-
import { u as L, B as A, M as B } from "./index-
|
|
4
|
+
import { u as L, B as A, M as B } from "./index-De7nsWBN.js";
|
|
5
5
|
import { T as b } from "./TimeAgoCell-o3KigGfM.js";
|
|
6
6
|
import { u as D } from "./useResizableTable-CNJmACdt.js";
|
|
7
7
|
import { F as P } from "./FilterBar-CQ7bD669.js";
|