@webda/debug 4.0.0-beta.5 → 4.0.0-beta.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,19 +1,12 @@
1
1
  import { Service, ServiceParameters } from "@webda/core";
2
2
  import { RequestLog } from "./requestlog.js";
3
3
  import { CancelablePromise } from "@webda/utils";
4
- /**
5
- * Determine whether the given Origin header value is allowed to access the debug API.
6
- *
7
- * Allowed origins:
8
- * - http://localhost:3000 (docs dev server)
9
- * - http://127.0.0.1:3000 (docs dev server, alternate address)
10
- * - https://webda.io (production docs)
11
- * - https://*.webda.io (any HTTPS subdomain of webda.io)
12
- *
13
- * @param origin - The value of the HTTP `Origin` request header.
14
- * @returns `true` if the origin is allowed, `false` otherwise.
15
- */
16
- export declare function isAllowedOrigin(origin: string | undefined): boolean;
4
+ export { isAllowedOrigin, allowedOrigins, DEBUG_API_VERSION } from "./security.js";
5
+ /** Options of {@link DebugService.startDebugServer}. */
6
+ export interface DebugServerOptions {
7
+ /** Serve the bundled dashboard and the one-time code exchange (`--local`) */
8
+ local?: boolean;
9
+ }
17
10
  /**
18
11
  * Configuration parameters for the {@link DebugService}.
19
12
  *
@@ -48,7 +41,9 @@ export declare class DebugService extends Service<DebugServiceParameters> {
48
41
  requestLog: RequestLog;
49
42
  /** Ring buffer of application log entries */
50
43
  private logBuffer;
51
- /** HTTP server for the debug API */
44
+ /** HTTP servers for the debug API (one per loopback address) */
45
+ private servers;
46
+ /** The IPv4 server, kept for the tests and the port lookup */
52
47
  private server?;
53
48
  /** WebSocket server for live event push */
54
49
  private wss?;
@@ -62,6 +57,46 @@ export declare class DebugService extends Service<DebugServiceParameters> {
62
57
  private running?;
63
58
  /** Timing map: requestId -> start timestamp */
64
59
  private timings;
60
+ /** Per-session token required on the API and the websocket */
61
+ private token;
62
+ /** Port the debug server actually listens on (resolved after listen) */
63
+ private listeningPort;
64
+ /** URL opened by the last `debug --web` run */
65
+ private dashboardUrl?;
66
+ /** Version of @webda/core, captured while the application context is available */
67
+ private frameworkVersion?;
68
+ /** Whether the bundled dashboard is served (`--local`) */
69
+ private localMode;
70
+ /** One-time code the local page exchanges for the token, with its expiry */
71
+ private bootstrap?;
72
+ /** Origins allowed by CORS (computed at start from the environment) */
73
+ private origins;
74
+ /**
75
+ * Port the debug server listens on (resolved after start).
76
+ * @returns the port
77
+ */
78
+ getPort(): number;
79
+ /**
80
+ * Loopback addresses the debug server is bound to.
81
+ * @returns the addresses, e.g. `["127.0.0.1", "::1"]`
82
+ */
83
+ getBoundAddresses(): string[];
84
+ /**
85
+ * Issue a one-time bootstrap code for the local page (`--local`).
86
+ *
87
+ * The code is random, single-use and expires after {@link BOOTSTRAP_CODE_TTL};
88
+ * `POST /api/session` exchanges it for the session token.
89
+ * @param ttl - lifetime in milliseconds (tests use a negative value for an expired code)
90
+ * @returns the code
91
+ */
92
+ issueBootstrapCode(ttl?: number): string;
93
+ /**
94
+ * Session token required on every `/api/*` request and websocket connection.
95
+ *
96
+ * It is generated when the service is created and never logged.
97
+ * @returns the token
98
+ */
99
+ getToken(): string;
65
100
  /**
66
101
  * Resolve the typed parameters and the capture knobs (with safe defaults
67
102
  * when the surrounding test environment does not load real parameters).
@@ -97,7 +132,10 @@ export declare class DebugService extends Service<DebugServiceParameters> {
97
132
  *
98
133
  * @param port - Port for the debug dashboard API
99
134
  * @param servePort - Port for the application HTTP server
100
- * @param web - Disable TUI and only serve the web dashboard
135
+ * @param web - Disable TUI and open the web dashboard instead
136
+ * @param local - Serve the bundled dashboard from the debug server instead of the hosted one
137
+ * @param open - Open the dashboard in the default browser (`--no-open` to disable)
138
+ * @param telemetry - Allow usage analytics on the hosted dashboard (`--no-telemetry` to disable)
101
139
  * @returns a promise settled once the service stops or the TUI quits
102
140
  */
103
141
  debug(
@@ -105,16 +143,35 @@ export declare class DebugService extends Service<DebugServiceParameters> {
105
143
  port?: number,
106
144
  /** @alias s @description Application server port */
107
145
  servePort?: number,
108
- /** @description Disable TUI and only serve the web dashboard */
109
- web?: boolean): CancelablePromise<void>;
146
+ /** @description Disable TUI and open the web dashboard (hosted on webda.io) */
147
+ web?: boolean,
148
+ /** @description With --web: serve the bundled dashboard from the debug port instead of webda.io */
149
+ local?: boolean,
150
+ /** @description With --web: open the dashboard in the browser (--no-open to only print the URL) */
151
+ open?: boolean,
152
+ /** @description With --web: allow usage analytics on the hosted dashboard (--no-telemetry or WEBDA_TELEMETRY=0 to disable) */
153
+ telemetry?: boolean): CancelablePromise<void>;
110
154
  /**
111
155
  * Start the application server, the debug server and the TUI or browser
112
156
  *
113
157
  * @param port - Port for the debug dashboard API
114
158
  * @param servePort - Port for the application HTTP server
115
- * @param web - Disable TUI and only serve the web dashboard
159
+ * @param web - Disable TUI and open the web dashboard
160
+ * @param local - Serve the bundled dashboard instead of the hosted one
161
+ * @param open - Open the browser
162
+ * @param telemetry - Allow usage analytics on the hosted dashboard
116
163
  */
117
- protected startDebug(port: number, servePort: number, web?: boolean): Promise<void>;
164
+ protected startDebug(port: number, servePort: number, web?: boolean, local?: boolean, open?: boolean, telemetry?: boolean): Promise<void>;
165
+ /**
166
+ * Print the dashboard URL for the user.
167
+ *
168
+ * The hosted URL carries the session token in its fragment, so it is written
169
+ * straight to stdout rather than through the logger: log output is captured
170
+ * by the log buffer, broadcast to the dashboard and may be persisted by other
171
+ * loggers.
172
+ * @param url - the dashboard URL
173
+ */
174
+ protected printDashboardUrl(url: string): void;
118
175
  /**
119
176
  * Settle the running debug command
120
177
  */
@@ -126,9 +183,40 @@ export declare class DebugService extends Service<DebugServiceParameters> {
126
183
  private openBrowser;
127
184
  /**
128
185
  * Create and start the debug HTTP server with WebSocket support.
129
- * @param port - Port to listen on
186
+ *
187
+ * One server per loopback address: `127.0.0.1` and `::1`, so that no other
188
+ * local process can squat the IPv6 side of `localhost`. A port already taken
189
+ * on either address is an error; an unavailable IPv6 stack is tolerated.
190
+ * The websocket upgrade is handled by hand so that the `Host`, `Origin` and
191
+ * token checks run before `ws` completes the handshake.
192
+ * @param port - Port to listen on (0 for a random one)
193
+ * @param options - local mode
194
+ */
195
+ startDebugServer(port: number, options?: DebugServerOptions): Promise<void>;
196
+ /**
197
+ * Close every bound server.
198
+ */
199
+ private closeServers;
200
+ /**
201
+ * Start one HTTP server on an address.
202
+ * @param port - the port
203
+ * @param address - the loopback address
204
+ * @returns the listening server
205
+ */
206
+ private listen;
207
+ /**
208
+ * Authenticate a websocket upgrade before handing it to `ws`.
209
+ * @param req - the upgrade request
210
+ * @param socket - the underlying socket
211
+ * @param head - the first packet of the upgraded stream
130
212
  */
131
- startDebugServer(port: number): Promise<void>;
213
+ private handleUpgrade;
214
+ /**
215
+ * Whether an Origin is the hosted dashboard, the dev server or the debug server itself.
216
+ * @param origin - the Origin header value
217
+ * @returns `true` when trusted
218
+ */
219
+ private isTrustedOrigin;
132
220
  /**
133
221
  * Route incoming HTTP requests to introspection handlers.
134
222
  * @param req - Incoming HTTP request
@@ -141,9 +229,32 @@ export declare class DebugService extends Service<DebugServiceParameters> {
141
229
  */
142
230
  private getOpenAPISpec;
143
231
  /**
144
- * Serve a static file from the webui directory.
145
- * Falls back to index.html for SPA-style routing.
232
+ * `POST /api/session`: exchange the one-time bootstrap code for the session token (`--local` only).
233
+ *
234
+ * The code is printed in the local URL fragment; the page sends it once and
235
+ * keeps the token in memory. Accepted only from the debug origin itself (or
236
+ * without Origin, for non-browser clients); 404 in hosted mode so the route
237
+ * reveals nothing there. The code is consumed on a successful match only, and a
238
+ * reuse after it was spent is logged (the link was used by another client).
239
+ * @param req - the request
240
+ * @param res - the response
241
+ */
242
+ private handleSessionExchange;
243
+ /**
244
+ * Security headers of the pages and assets the debug server serves.
245
+ * @param res - the response
246
+ * @param html - whether the response is a page
247
+ */
248
+ private pageHeaders;
249
+ /**
250
+ * Serve a static file from the webui directory (`--local`), or the hosted
251
+ * placeholder page.
252
+ *
253
+ * The page never carries the token: the local page obtains it through the
254
+ * one-time code exchange. Unknown paths fall back to index.html (SPA routing);
255
+ * directories are not found.
146
256
  * @param pathname - Request pathname
257
+ * @param req - Incoming request
147
258
  * @param res - Server response
148
259
  */
149
260
  private serveStaticFile;