adonisjs-server-stats 1.14.1 → 1.15.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 +28 -0
- package/dist/core/types.d.ts +31 -0
- package/dist/src/define_config.js +26 -1
- package/dist/src/provider/boot_helpers.d.ts +10 -1
- package/dist/src/provider/boot_helpers.js +20 -2
- package/dist/src/provider/server_stats_provider.js +4 -2
- package/dist/src/routes/dashboard_routes.d.ts +1 -0
- package/dist/src/routes/dashboard_routes.js +5 -3
- package/dist/src/routes/debug_routes.d.ts +1 -0
- package/dist/src/routes/debug_routes.js +5 -3
- package/dist/src/routes/register_routes.d.ts +5 -0
- package/dist/src/routes/register_routes.js +9 -1
- package/dist/src/routes/router_types.d.ts +15 -5
- package/dist/src/routes/stats_routes.d.ts +10 -1
- package/dist/src/routes/stats_routes.js +9 -2
- package/dist/src/types.d.ts +31 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -181,6 +181,7 @@ All fields are optional. `defineConfig({})` works with zero configuration.
|
|
|
181
181
|
| `statsEndpoint` | `string \| false` | `'/admin/api/server-stats'` | HTTP endpoint. `false` to disable. |
|
|
182
182
|
| `authorize` | `(ctx) => boolean` | -- | Per-request visibility guard |
|
|
183
183
|
| `unsafeAllowNoAuth` | `boolean` | `false` | Register the dashboard/debug/stats routes even with **no** `authorize` guard. Off by default (routes fail closed). Local dev only — exposes secrets, email bodies, and SQL. |
|
|
184
|
+
| `domain` | `string` | -- | Restrict every route to one host, e.g. `'admin.example.com'`. See [Custom domain](#custom-domain) |
|
|
184
185
|
| `onStats` | `(stats) => void` | -- | Callback after each collection tick |
|
|
185
186
|
| `toolbar` | `boolean \| ToolbarConfig` | -- | `true` to enable with defaults, or pass a `ToolbarConfig` object |
|
|
186
187
|
| `dashboard` | `boolean \| DashboardConfig` | -- | `true` to enable at `/__stats`, or pass a `DashboardConfig` |
|
|
@@ -440,6 +441,33 @@ Registered when `dashboard` is enabled. Base path configurable via `dashboard.pa
|
|
|
440
441
|
| POST | `/api/filters` | Create saved filter |
|
|
441
442
|
| DELETE | `/api/filters/:id` | Delete saved filter |
|
|
442
443
|
|
|
444
|
+
### Custom domain
|
|
445
|
+
|
|
446
|
+
By default every route is registered on whatever host your app serves. Set `domain` to bind them to one host instead -- useful when your admin surface already lives on its own subdomain:
|
|
447
|
+
|
|
448
|
+
```ts
|
|
449
|
+
export default defineConfig({
|
|
450
|
+
domain: 'admin.example.com',
|
|
451
|
+
authorize: (ctx) => ctx.auth?.user?.role === 'admin',
|
|
452
|
+
toolbar: true,
|
|
453
|
+
dashboard: true,
|
|
454
|
+
})
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
The dashboard is then reachable at `admin.example.com/__stats` and nowhere else -- a request to `example.com/__stats` no longer matches any route. Dynamic subdomains use AdonisJS's `:param` syntax:
|
|
458
|
+
|
|
459
|
+
```ts
|
|
460
|
+
export default defineConfig({
|
|
461
|
+
domain: ':tenant.example.com', // matches acme.example.com, globex.example.com, ...
|
|
462
|
+
})
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
Pass a bare host. A protocol, path, or port (`https://admin.example.com`, `admin.example.com:3333`) produces routes that match nothing; `defineConfig` warns when it spots one.
|
|
466
|
+
|
|
467
|
+
> **The toolbar follows the domain.** The `@serverStats()` Edge tag and the React/Vue components request **relative** URLs, so they only work on pages served from the configured domain. Render the toolbar on `example.com` while `domain` points at `admin.example.com` and the bar will sit empty -- its polls 404 against the wrong host. Either serve the toolbar from the same domain, or leave `domain` unset and rely on `authorize` alone.
|
|
468
|
+
|
|
469
|
+
> **Reminder:** routes are never registered in production regardless of this setting (`app.inProduction` short-circuits registration), so `domain` applies to your dev and staging environments.
|
|
470
|
+
|
|
443
471
|
### Global middleware note
|
|
444
472
|
|
|
445
473
|
Auto-registered routes bypass route-level middleware but are still subject to global/server middleware.
|
package/dist/core/types.d.ts
CHANGED
|
@@ -804,6 +804,35 @@ export interface ServerStatsConfig {
|
|
|
804
804
|
* ```
|
|
805
805
|
*/
|
|
806
806
|
dashboard?: boolean | DashboardConfig;
|
|
807
|
+
/**
|
|
808
|
+
* Restrict all server-stats routes to a specific domain or subdomain.
|
|
809
|
+
*
|
|
810
|
+
* When set, routes are only matched when the request's `Host` header
|
|
811
|
+
* matches the given domain. Useful when admin routes live on a
|
|
812
|
+
* dedicated subdomain (e.g. `admin.example.com`).
|
|
813
|
+
*
|
|
814
|
+
* Supports dynamic subdomains using `:param` syntax
|
|
815
|
+
* (e.g. `':tenant.example.com'`).
|
|
816
|
+
*
|
|
817
|
+
* Pass a bare host — a protocol, path, or port (`'https://admin.example.com'`,
|
|
818
|
+
* `'admin.example.com:3333'`) yields routes that match nothing.
|
|
819
|
+
*
|
|
820
|
+
* Note that the `@serverStats()` toolbar and the React/Vue components request
|
|
821
|
+
* relative URLs, so they only work on pages served from this domain.
|
|
822
|
+
*
|
|
823
|
+
* @example
|
|
824
|
+
* ```ts
|
|
825
|
+
* // Fixed subdomain
|
|
826
|
+
* domain: 'admin.example.com'
|
|
827
|
+
* ```
|
|
828
|
+
*
|
|
829
|
+
* @example
|
|
830
|
+
* ```ts
|
|
831
|
+
* // Dynamic subdomain
|
|
832
|
+
* domain: ':tenant.example.com'
|
|
833
|
+
* ```
|
|
834
|
+
*/
|
|
835
|
+
domain?: string;
|
|
807
836
|
/**
|
|
808
837
|
* Advanced options for fine-tuning internal behavior.
|
|
809
838
|
*
|
|
@@ -871,4 +900,6 @@ export interface ResolvedServerStatsConfig {
|
|
|
871
900
|
advanced?: AdvancedConfig;
|
|
872
901
|
/** Whether verbose informational logging is enabled. Always present after `defineConfig()`. */
|
|
873
902
|
verbose: boolean;
|
|
903
|
+
/** Optional domain restriction for all routes. */
|
|
904
|
+
domain?: string;
|
|
874
905
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { logDeprecationWarnings } from './config/deprecation_migration.js';
|
|
2
|
-
import { setVerbose } from './utils/logger.js';
|
|
2
|
+
import { log, setVerbose } from './utils/logger.js';
|
|
3
3
|
// ---------------------------------------------------------------------------
|
|
4
4
|
// Toolbar alias resolution helpers
|
|
5
5
|
// ---------------------------------------------------------------------------
|
|
@@ -126,10 +126,34 @@ function resolveDevToolbar(config) {
|
|
|
126
126
|
function first(primary, fallback, defaultVal) {
|
|
127
127
|
return primary ?? fallback ?? defaultVal;
|
|
128
128
|
}
|
|
129
|
+
/**
|
|
130
|
+
* Warn about `domain` values that AdonisJS will never match.
|
|
131
|
+
*
|
|
132
|
+
* `.domain()` compares against the request's host only -- a protocol, path, or
|
|
133
|
+
* port in the value silently yields routes that match nothing, which is very
|
|
134
|
+
* hard to debug from the outside. Warn instead of throwing so a bad value
|
|
135
|
+
* degrades to "dashboard not reachable" rather than "app won't boot".
|
|
136
|
+
*/
|
|
137
|
+
function warnAboutDomain(domain) {
|
|
138
|
+
const problems = [];
|
|
139
|
+
if (domain.includes('://'))
|
|
140
|
+
problems.push('remove the protocol (`http://` / `https://`)');
|
|
141
|
+
if (domain.includes('/'))
|
|
142
|
+
problems.push('remove the path — only the host is matched');
|
|
143
|
+
if (/:\d+$/.test(domain))
|
|
144
|
+
problems.push('remove the port — it is not part of the host match');
|
|
145
|
+
if (problems.length === 0)
|
|
146
|
+
return;
|
|
147
|
+
log.warn(`server-stats: \`domain: '${domain}'\` looks wrong — ${problems.join('; ')}. ` +
|
|
148
|
+
"Expected a bare host such as 'admin.example.com' or ':tenant.example.com'. " +
|
|
149
|
+
'As written, no server-stats route will ever match.');
|
|
150
|
+
}
|
|
129
151
|
export function defineConfig(config) {
|
|
130
152
|
const verbose = config.verbose ?? false;
|
|
131
153
|
setVerbose(verbose);
|
|
132
154
|
logDeprecationWarnings(config);
|
|
155
|
+
if (config.domain)
|
|
156
|
+
warnAboutDomain(config.domain);
|
|
133
157
|
return {
|
|
134
158
|
intervalMs: first(config.pollInterval, config.intervalMs, 3000),
|
|
135
159
|
transport: resolveTransport(config),
|
|
@@ -142,5 +166,6 @@ export function defineConfig(config) {
|
|
|
142
166
|
shouldShow: config.authorize ?? config.shouldShow,
|
|
143
167
|
unsafeAllowNoAuth: config.unsafeAllowNoAuth,
|
|
144
168
|
verbose,
|
|
169
|
+
domain: config.domain,
|
|
145
170
|
};
|
|
146
171
|
}
|
|
@@ -13,9 +13,18 @@ export declare function computeDashboardPath(devToolbar?: {
|
|
|
13
13
|
dashboard?: boolean;
|
|
14
14
|
dashboardPath?: string;
|
|
15
15
|
}, depsAvailable?: boolean): string | undefined;
|
|
16
|
-
export declare function collectRegisteredPaths(statsEndpoint: string | false, debugEndpoint?: string, dashboardPath?: string): string[];
|
|
16
|
+
export declare function collectRegisteredPaths(statsEndpoint: string | false, debugEndpoint?: string, dashboardPath?: string, domain?: string): string[];
|
|
17
17
|
export declare function checkDashboardDeps(appImport: (name: string) => Promise<unknown>): Promise<string[]>;
|
|
18
18
|
export declare function logMissingDeps(missing: string[]): void;
|
|
19
19
|
export declare function warnAboutAuthMiddleware(config: ResolvedServerStatsConfig, makePath: (dir: string, file: string) => string): void;
|
|
20
|
+
/**
|
|
21
|
+
* Warn when `domain` is combined with the Edge toolbar.
|
|
22
|
+
*
|
|
23
|
+
* The `@serverStats()` tag emits *relative* URLs (`/admin/api/server-stats`
|
|
24
|
+
* etc.), so a toolbar rendered on any other host polls its own origin and gets
|
|
25
|
+
* a 404 on every tick. Nothing breaks visibly — the bar just stays empty — so
|
|
26
|
+
* say it out loud at boot.
|
|
27
|
+
*/
|
|
28
|
+
export declare function warnAboutDomainWithToolbar(config: ResolvedServerStatsConfig): void;
|
|
20
29
|
export declare function warnAboutSessionMiddleware(makePath: (dir: string, file: string) => string): void;
|
|
21
30
|
export declare function logDashboardError(category: 'missing-dep' | 'timeout' | 'unknown', err: unknown): void;
|
|
@@ -12,7 +12,7 @@ export function computeDashboardPath(devToolbar, depsAvailable) {
|
|
|
12
12
|
return undefined;
|
|
13
13
|
return devToolbar.dashboardPath ?? '/__stats';
|
|
14
14
|
}
|
|
15
|
-
export function collectRegisteredPaths(statsEndpoint, debugEndpoint, dashboardPath) {
|
|
15
|
+
export function collectRegisteredPaths(statsEndpoint, debugEndpoint, dashboardPath, domain) {
|
|
16
16
|
const paths = [];
|
|
17
17
|
if (typeof statsEndpoint === 'string')
|
|
18
18
|
paths.push(statsEndpoint);
|
|
@@ -20,7 +20,9 @@ export function collectRegisteredPaths(statsEndpoint, debugEndpoint, dashboardPa
|
|
|
20
20
|
paths.push(debugEndpoint + '/*');
|
|
21
21
|
if (dashboardPath)
|
|
22
22
|
paths.push(dashboardPath + '/*');
|
|
23
|
-
|
|
23
|
+
// Without the host prefix the logged paths are misleading when the routes are
|
|
24
|
+
// domain-restricted — they only resolve on that host.
|
|
25
|
+
return domain ? paths.map((p) => domain + p) : paths;
|
|
24
26
|
}
|
|
25
27
|
export async function checkDashboardDeps(appImport) {
|
|
26
28
|
const missing = [];
|
|
@@ -57,6 +59,22 @@ export function warnAboutAuthMiddleware(config, makePath) {
|
|
|
57
59
|
return;
|
|
58
60
|
log.block(bold('found global auth middleware that will run on every poll:'), buildAuthMiddlewareWarning(found, dim, bold));
|
|
59
61
|
}
|
|
62
|
+
/**
|
|
63
|
+
* Warn when `domain` is combined with the Edge toolbar.
|
|
64
|
+
*
|
|
65
|
+
* The `@serverStats()` tag emits *relative* URLs (`/admin/api/server-stats`
|
|
66
|
+
* etc.), so a toolbar rendered on any other host polls its own origin and gets
|
|
67
|
+
* a 404 on every tick. Nothing breaks visibly — the bar just stays empty — so
|
|
68
|
+
* say it out loud at boot.
|
|
69
|
+
*/
|
|
70
|
+
export function warnAboutDomainWithToolbar(config) {
|
|
71
|
+
if (!config.domain || !config.devToolbar?.enabled)
|
|
72
|
+
return;
|
|
73
|
+
log.block(bold(`routes are restricted to ${config.domain}:`), [
|
|
74
|
+
dim('The @serverStats() toolbar uses relative URLs, so it only works on pages'),
|
|
75
|
+
dim(`served from ${config.domain}. On any other host the bar will stay empty.`),
|
|
76
|
+
]);
|
|
77
|
+
}
|
|
60
78
|
export function warnAboutSessionMiddleware(makePath) {
|
|
61
79
|
const found = detectGlobalSessionMiddleware(makePath);
|
|
62
80
|
if (found.length === 0)
|
|
@@ -2,7 +2,7 @@ import { StatsEngine } from '../engine/stats_engine.js';
|
|
|
2
2
|
import { setShouldShow, setExcludedPrefixes } from '../middleware/request_tracking_middleware.js';
|
|
3
3
|
import { registerAllRoutes } from '../routes/register_routes.js';
|
|
4
4
|
import { log, dim, setVerbose } from '../utils/logger.js';
|
|
5
|
-
import { deriveEndpointPaths, computeDashboardPath, collectRegisteredPaths, warnAboutAuthMiddleware, warnAboutSessionMiddleware, } from './boot_helpers.js';
|
|
5
|
+
import { deriveEndpointPaths, computeDashboardPath, collectRegisteredPaths, warnAboutAuthMiddleware, warnAboutSessionMiddleware, warnAboutDomainWithToolbar, } from './boot_helpers.js';
|
|
6
6
|
import { resolveToolbarConfig, buildExcludedPrefixes } from './dashboard_setup.js';
|
|
7
7
|
import { buildDiagnostics } from './diagnostics.js';
|
|
8
8
|
import { hookPinoToLogStream, setupLogStreamBroadcast, setupStatsIntervalHelper, checkDashboardDepsHelper, registerEdgePluginHelper, setupNonWebBridgeHelper, setupDevToolbarCore, applyToolbarResult, } from './provider_helpers_extra.js';
|
|
@@ -96,13 +96,15 @@ export default class ServerStatsProvider {
|
|
|
96
96
|
shouldShow: config.shouldShow,
|
|
97
97
|
unsafeAllowNoAuth: config.unsafeAllowNoAuth,
|
|
98
98
|
whenReady: () => this.whenReady(),
|
|
99
|
+
domain: config.domain,
|
|
99
100
|
});
|
|
100
|
-
const paths = collectRegisteredPaths(statsEndpoint, debugEndpoint, dashboardPath);
|
|
101
|
+
const paths = collectRegisteredPaths(statsEndpoint, debugEndpoint, dashboardPath, config.domain);
|
|
101
102
|
if (paths.length === 0)
|
|
102
103
|
return;
|
|
103
104
|
log.list('routes auto-registered (no manual setup needed):', paths);
|
|
104
105
|
warnAboutAuthMiddleware(config, this.app.makePath.bind(this.app));
|
|
105
106
|
warnAboutSessionMiddleware(this.app.makePath.bind(this.app));
|
|
107
|
+
warnAboutDomainWithToolbar(config);
|
|
106
108
|
}
|
|
107
109
|
async ready() {
|
|
108
110
|
const config = this.app.config.get('server_stats');
|
|
@@ -9,6 +9,7 @@ interface DashboardRoutesOpts {
|
|
|
9
9
|
getApiController: () => ApiController | null;
|
|
10
10
|
middleware: Array<(ctx: HttpContext, next: () => Promise<void>) => Promise<void>>;
|
|
11
11
|
whenReady?: () => Promise<void>;
|
|
12
|
+
domain?: string;
|
|
12
13
|
}
|
|
13
14
|
/** Register dashboard routes. */
|
|
14
15
|
export declare function registerDashboardRoutes(opts: DashboardRoutesOpts): void;
|
|
@@ -230,7 +230,7 @@ export function registerDashboardRoutes(opts) {
|
|
|
230
230
|
const { router, dashboardPath, getDashboardController, getApiController, middleware } = opts;
|
|
231
231
|
const whenReady = opts.whenReady;
|
|
232
232
|
const base = dashboardPath.replace(/\/+$/, '');
|
|
233
|
-
router
|
|
233
|
+
const group = router
|
|
234
234
|
.group(() => {
|
|
235
235
|
registerDashboardPageRoutes(router, getDashboardController, whenReady);
|
|
236
236
|
registerQueryRoutes(router, getApiController, whenReady);
|
|
@@ -244,6 +244,8 @@ export function registerDashboardRoutes(opts) {
|
|
|
244
244
|
registerJobRoutes(router, getDashboardController, whenReady);
|
|
245
245
|
registerConfigAndFilterRoutes(router, getDashboardController, whenReady);
|
|
246
246
|
})
|
|
247
|
-
.prefix(base)
|
|
248
|
-
|
|
247
|
+
.prefix(base);
|
|
248
|
+
if (opts.domain)
|
|
249
|
+
group.domain(opts.domain);
|
|
250
|
+
group.use(middleware);
|
|
249
251
|
}
|
|
@@ -13,6 +13,7 @@ interface DebugRoutesOpts {
|
|
|
13
13
|
getApp?: () => ApplicationService | null;
|
|
14
14
|
middleware: Array<(ctx: HttpContext, next: () => Promise<void>) => Promise<void>>;
|
|
15
15
|
whenReady?: () => Promise<void>;
|
|
16
|
+
domain?: string;
|
|
16
17
|
}
|
|
17
18
|
/** Register debug panel API routes. */
|
|
18
19
|
export declare function registerDebugRoutes(opts: DebugRoutesOpts): void;
|
|
@@ -149,7 +149,7 @@ export function registerDebugRoutes(opts) {
|
|
|
149
149
|
const { router, debugEndpoint, getDebugController, getApiController, middleware } = opts;
|
|
150
150
|
const whenReady = opts.whenReady;
|
|
151
151
|
const base = debugEndpoint.replace(/\/+$/, '');
|
|
152
|
-
router
|
|
152
|
+
const group = router
|
|
153
153
|
.group(() => {
|
|
154
154
|
registerDebugConfigRoutes(router, getDebugController, whenReady);
|
|
155
155
|
registerDebugQueryAndEventRoutes(router, getApiController, whenReady);
|
|
@@ -158,6 +158,8 @@ export function registerDebugRoutes(opts) {
|
|
|
158
158
|
registerDebugEmailRoutes(router, getApiController, whenReady);
|
|
159
159
|
registerDebugTraceRoutes(router, getApiController, whenReady);
|
|
160
160
|
})
|
|
161
|
-
.prefix(base)
|
|
162
|
-
|
|
161
|
+
.prefix(base);
|
|
162
|
+
if (opts.domain)
|
|
163
|
+
group.domain(opts.domain);
|
|
164
|
+
group.use(middleware);
|
|
163
165
|
}
|
|
@@ -31,6 +31,11 @@ export interface RegisterRoutesOptions {
|
|
|
31
31
|
unsafeAllowNoAuth?: boolean;
|
|
32
32
|
/** Optional promise that resolves when controllers are initialized. */
|
|
33
33
|
whenReady?: () => Promise<void>;
|
|
34
|
+
/**
|
|
35
|
+
* Restrict every registered route to this host, via `router.group().domain()`.
|
|
36
|
+
* Supports dynamic segments (`':tenant.example.com'`). Unset = no restriction.
|
|
37
|
+
*/
|
|
38
|
+
domain?: string;
|
|
34
39
|
}
|
|
35
40
|
/**
|
|
36
41
|
* Register all server-stats routes in a single call.
|
|
@@ -31,7 +31,13 @@ export function registerAllRoutes(options) {
|
|
|
31
31
|
...(options.shouldShow ? [createAccessMiddleware(options.shouldShow)] : []),
|
|
32
32
|
];
|
|
33
33
|
if (typeof options.statsEndpoint === 'string') {
|
|
34
|
-
registerStatsRoute(
|
|
34
|
+
registerStatsRoute({
|
|
35
|
+
router: options.router,
|
|
36
|
+
endpoint: options.statsEndpoint,
|
|
37
|
+
getController: options.getStatsController,
|
|
38
|
+
middleware,
|
|
39
|
+
domain: options.domain,
|
|
40
|
+
});
|
|
35
41
|
}
|
|
36
42
|
if (options.debugEndpoint) {
|
|
37
43
|
registerDebugRoutes({
|
|
@@ -43,6 +49,7 @@ export function registerAllRoutes(options) {
|
|
|
43
49
|
getApp: options.getApp,
|
|
44
50
|
middleware,
|
|
45
51
|
whenReady: options.whenReady,
|
|
52
|
+
domain: options.domain,
|
|
46
53
|
});
|
|
47
54
|
}
|
|
48
55
|
if (options.dashboardPath) {
|
|
@@ -53,6 +60,7 @@ export function registerAllRoutes(options) {
|
|
|
53
60
|
getApiController: options.getApiController,
|
|
54
61
|
middleware,
|
|
55
62
|
whenReady: options.whenReady,
|
|
63
|
+
domain: options.domain,
|
|
56
64
|
});
|
|
57
65
|
}
|
|
58
66
|
}
|
|
@@ -10,6 +10,20 @@ export interface AdonisRoute {
|
|
|
10
10
|
where(key: string, matcher: RegExp): AdonisRoute;
|
|
11
11
|
use(middleware: unknown[]): void;
|
|
12
12
|
}
|
|
13
|
+
/**
|
|
14
|
+
* Return type of `router.group()` supporting the chaining patterns
|
|
15
|
+
* used by server-stats route registration.
|
|
16
|
+
*
|
|
17
|
+
* AdonisJS allows chaining `.prefix()`, `.domain()`, and `.use()` in
|
|
18
|
+
* any order on a route group. This interface covers the combinations
|
|
19
|
+
* we use: `.prefix().use()`, `.prefix().domain().use()`, and
|
|
20
|
+
* `.domain().prefix().use()`.
|
|
21
|
+
*/
|
|
22
|
+
export interface AdonisRouteGroup {
|
|
23
|
+
prefix(path: string): AdonisRouteGroup;
|
|
24
|
+
domain(host: string): AdonisRouteGroup;
|
|
25
|
+
use(middleware: unknown[]): AdonisRouteGroup;
|
|
26
|
+
}
|
|
13
27
|
/**
|
|
14
28
|
* Minimal interface for the AdonisJS router used in route registration.
|
|
15
29
|
*
|
|
@@ -20,9 +34,5 @@ export interface AdonisRouter {
|
|
|
20
34
|
get(pattern: string, handler: (ctx: HttpContext) => unknown): AdonisRoute;
|
|
21
35
|
post(pattern: string, handler: (ctx: HttpContext) => unknown): AdonisRoute;
|
|
22
36
|
delete(pattern: string, handler: (ctx: HttpContext) => unknown): AdonisRoute;
|
|
23
|
-
group(callback: () => void):
|
|
24
|
-
prefix(path: string): {
|
|
25
|
-
use(middleware: unknown[]): void;
|
|
26
|
-
};
|
|
27
|
-
};
|
|
37
|
+
group(callback: () => void): AdonisRouteGroup;
|
|
28
38
|
}
|
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import type ServerStatsController from '../controller/server_stats_controller.js';
|
|
2
2
|
import type { AdonisRouter } from './router_types.js';
|
|
3
3
|
import type { HttpContext } from '@adonisjs/core/http';
|
|
4
|
+
interface StatsRouteOpts {
|
|
5
|
+
router: AdonisRouter;
|
|
6
|
+
endpoint: string;
|
|
7
|
+
getController: () => ServerStatsController | null;
|
|
8
|
+
middleware: Array<(ctx: HttpContext, next: () => Promise<void>) => Promise<void>>;
|
|
9
|
+
/** Optional domain restriction — see `ServerStatsConfig['domain']`. */
|
|
10
|
+
domain?: string;
|
|
11
|
+
}
|
|
4
12
|
/** Register the stats polling endpoint. */
|
|
5
|
-
export declare function registerStatsRoute(
|
|
13
|
+
export declare function registerStatsRoute(opts: StatsRouteOpts): void;
|
|
14
|
+
export {};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/** Register the stats polling endpoint. */
|
|
2
|
-
export function registerStatsRoute(
|
|
3
|
-
router
|
|
2
|
+
export function registerStatsRoute(opts) {
|
|
3
|
+
const { router, endpoint, getController, middleware, domain } = opts;
|
|
4
|
+
const register = () => router
|
|
4
5
|
.get(endpoint, async (ctx) => {
|
|
5
6
|
const controller = getController();
|
|
6
7
|
if (!controller)
|
|
@@ -11,4 +12,10 @@ export function registerStatsRoute(router, endpoint, getController, middleware)
|
|
|
11
12
|
})
|
|
12
13
|
.as('server-stats.api')
|
|
13
14
|
.use(middleware);
|
|
15
|
+
// A domain restriction needs a group to hang `.domain()` on; without one we
|
|
16
|
+
// register the route directly so the route tree stays exactly as before.
|
|
17
|
+
if (domain)
|
|
18
|
+
router.group(register).domain(domain);
|
|
19
|
+
else
|
|
20
|
+
register();
|
|
14
21
|
}
|
package/dist/src/types.d.ts
CHANGED
|
@@ -804,6 +804,35 @@ export interface ServerStatsConfig {
|
|
|
804
804
|
* ```
|
|
805
805
|
*/
|
|
806
806
|
dashboard?: boolean | DashboardConfig;
|
|
807
|
+
/**
|
|
808
|
+
* Restrict all server-stats routes to a specific domain or subdomain.
|
|
809
|
+
*
|
|
810
|
+
* When set, routes are only matched when the request's `Host` header
|
|
811
|
+
* matches the given domain. Useful when admin routes live on a
|
|
812
|
+
* dedicated subdomain (e.g. `admin.example.com`).
|
|
813
|
+
*
|
|
814
|
+
* Supports dynamic subdomains using `:param` syntax
|
|
815
|
+
* (e.g. `':tenant.example.com'`).
|
|
816
|
+
*
|
|
817
|
+
* Pass a bare host — a protocol, path, or port (`'https://admin.example.com'`,
|
|
818
|
+
* `'admin.example.com:3333'`) yields routes that match nothing.
|
|
819
|
+
*
|
|
820
|
+
* Note that the `@serverStats()` toolbar and the React/Vue components request
|
|
821
|
+
* relative URLs, so they only work on pages served from this domain.
|
|
822
|
+
*
|
|
823
|
+
* @example
|
|
824
|
+
* ```ts
|
|
825
|
+
* // Fixed subdomain
|
|
826
|
+
* domain: 'admin.example.com'
|
|
827
|
+
* ```
|
|
828
|
+
*
|
|
829
|
+
* @example
|
|
830
|
+
* ```ts
|
|
831
|
+
* // Dynamic subdomain
|
|
832
|
+
* domain: ':tenant.example.com'
|
|
833
|
+
* ```
|
|
834
|
+
*/
|
|
835
|
+
domain?: string;
|
|
807
836
|
/**
|
|
808
837
|
* Advanced options for fine-tuning internal behavior.
|
|
809
838
|
*
|
|
@@ -871,4 +900,6 @@ export interface ResolvedServerStatsConfig {
|
|
|
871
900
|
advanced?: AdvancedConfig;
|
|
872
901
|
/** Whether verbose informational logging is enabled. Always present after `defineConfig()`. */
|
|
873
902
|
verbose: boolean;
|
|
903
|
+
/** Optional domain restriction for all routes. */
|
|
904
|
+
domain?: string;
|
|
874
905
|
}
|