@replayio-app-building/netlify-recorder 0.34.0 → 0.35.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 +82 -2
- package/dist/index.d.ts +58 -14
- package/dist/index.js +118 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -113,7 +113,63 @@ export default createRecordingRequestHandler(
|
|
|
113
113
|
|
|
114
114
|
> **Note:** Always use the response returned by the wrapper (or `finishRequest`), not your original response object. The wrapper adds the `X-Replay-Request-Id` header to the response it returns.
|
|
115
115
|
|
|
116
|
-
### 4.
|
|
116
|
+
### 4. Expose a recording endpoint for other services
|
|
117
|
+
|
|
118
|
+
Use `createRecordingEndpoint` to create a standalone Netlify function that other services can call to trigger recording creation or check recording status. This is the simplest way to let external services interact with the `backend_requests` table without importing the full package.
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
// netlify/functions/ensure-recording.ts
|
|
122
|
+
import { createRecordingEndpoint } from "@replayio-app-building/netlify-recorder";
|
|
123
|
+
import { neon } from "@neondatabase/serverless";
|
|
124
|
+
|
|
125
|
+
const sql = neon(process.env.DATABASE_URL!);
|
|
126
|
+
|
|
127
|
+
export default createRecordingEndpoint({
|
|
128
|
+
sql,
|
|
129
|
+
recorderUrl: "https://netlify-recorder-bm4wmw.netlify.app",
|
|
130
|
+
// Optional: require callers to authenticate with a shared secret
|
|
131
|
+
secret: process.env.RECORDER_ENDPOINT_SECRET,
|
|
132
|
+
// Optional: receive a webhook when the recording completes
|
|
133
|
+
webhookUrl: "https://my-app.netlify.app/.netlify/functions/recording-webhook",
|
|
134
|
+
});
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
**Calling the endpoint from another service:**
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
// Trigger recording creation (POST)
|
|
141
|
+
const res = await fetch("https://my-app.netlify.app/.netlify/functions/ensure-recording", {
|
|
142
|
+
method: "POST",
|
|
143
|
+
headers: {
|
|
144
|
+
"Content-Type": "application/json",
|
|
145
|
+
// Include if `secret` is configured:
|
|
146
|
+
"Authorization": "Bearer my-shared-secret",
|
|
147
|
+
},
|
|
148
|
+
body: JSON.stringify({ requestId: "a1b2c3d4-..." }),
|
|
149
|
+
});
|
|
150
|
+
const result = await res.json();
|
|
151
|
+
// { status: "queued", requestId: "a1b2c3d4-..." }
|
|
152
|
+
// or { status: "recorded", recordingId: "...", requestId: "..." }
|
|
153
|
+
// or { status: "pending", requestId: "..." }
|
|
154
|
+
|
|
155
|
+
// Check status without triggering (GET)
|
|
156
|
+
const status = await fetch(
|
|
157
|
+
"https://my-app.netlify.app/.netlify/functions/ensure-recording?requestId=a1b2c3d4-...",
|
|
158
|
+
{ headers: { "Authorization": "Bearer my-shared-secret" } },
|
|
159
|
+
);
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
**Response statuses:**
|
|
163
|
+
|
|
164
|
+
| Status | HTTP Code | Meaning |
|
|
165
|
+
|--------|-----------|---------|
|
|
166
|
+
| `recorded` | 200 | Recording exists — `recordingId` is included |
|
|
167
|
+
| `pending` | 200 | Recording is queued or processing — check back later |
|
|
168
|
+
| `queued` | 202 | Recording was just queued by this POST call |
|
|
169
|
+
| `not_found` | 404 | Request ID not found in `backend_requests` |
|
|
170
|
+
| `error` | 4xx/5xx | Validation error, auth failure, or recording failure |
|
|
171
|
+
|
|
172
|
+
### 5. Create recordings programmatically
|
|
117
173
|
|
|
118
174
|
Use `ensureRequestRecording` to turn a captured request into a Replay recording. It checks the `backend_requests` table first — if a recording already exists, it returns the recording ID immediately without calling the service. Otherwise it passes the stored blob data URL to the Netlify Recorder service and updates the row status to `"queued"`.
|
|
119
175
|
|
|
@@ -150,7 +206,7 @@ On failure:
|
|
|
150
206
|
{ "status": "failed", "error": "Error message" }
|
|
151
207
|
```
|
|
152
208
|
|
|
153
|
-
###
|
|
209
|
+
### 6. Manage stored requests
|
|
154
210
|
|
|
155
211
|
Use the `backendRequests*` helpers to query and manage captured requests in your database:
|
|
156
212
|
|
|
@@ -410,6 +466,30 @@ Ensures a Replay recording exists (or is being created) for a backend request. L
|
|
|
410
466
|
|
|
411
467
|
**Throws:** If the request ID is not found in `backend_requests`, or if the service call fails.
|
|
412
468
|
|
|
469
|
+
### `createRecordingEndpoint(options): (req: Request) => Promise<Response>`
|
|
470
|
+
|
|
471
|
+
Creates a Netlify Function v2 handler that other services can call to trigger recording creation or check recording status for entries in the `backend_requests` table.
|
|
472
|
+
|
|
473
|
+
Supports **POST** (trigger recording) and **GET** (check status). When `secret` is provided, all requests must include an `Authorization: Bearer <secret>` header.
|
|
474
|
+
|
|
475
|
+
**Parameters:**
|
|
476
|
+
- `options.sql` — A Neon SQL tagged-template function
|
|
477
|
+
- `options.recorderUrl` — Base URL of the Netlify Recorder service
|
|
478
|
+
- `options.secret` — Shared secret for authentication (optional — when omitted, the endpoint is open)
|
|
479
|
+
- `options.webhookUrl` — URL to POST the recording result to when complete (optional)
|
|
480
|
+
|
|
481
|
+
**Returns:** An async function `(req: Request) => Promise<Response>` suitable as a Netlify Functions v2 default export.
|
|
482
|
+
|
|
483
|
+
**POST body:** `{ "requestId": "<uuid>" }` — triggers recording if needed.
|
|
484
|
+
|
|
485
|
+
**GET query:** `?requestId=<uuid>` — returns current status without triggering.
|
|
486
|
+
|
|
487
|
+
**Response body** (`RecordingEndpointResponse`):
|
|
488
|
+
- `status` — `"recorded"`, `"pending"`, `"queued"`, `"not_found"`, or `"error"`
|
|
489
|
+
- `recordingId` — Present when `status` is `"recorded"`
|
|
490
|
+
- `requestId` — The request ID echoed back
|
|
491
|
+
- `error` — Error message when `status` is `"not_found"` or `"error"`
|
|
492
|
+
|
|
413
493
|
### `createRequestRecording(blobUrlOrData, handlerPath, requestInfo): Promise<RecordingResult>`
|
|
414
494
|
|
|
415
495
|
Called inside a recording container running under `replay-node`. Downloads the captured data blob (or accepts pre-parsed `BlobData`), installs replay-mode interceptors that return pre-recorded responses instead of making real calls, and executes the original handler so `replay-node` can record the execution.
|
package/dist/index.d.ts
CHANGED
|
@@ -235,7 +235,7 @@ interface RecordingResult {
|
|
|
235
235
|
*/
|
|
236
236
|
declare function createRequestRecording(blobUrlOrData: string | BlobData, handlerPath: string, requestInfo: RequestInfo): Promise<RecordingResult>;
|
|
237
237
|
|
|
238
|
-
type SqlFunction$
|
|
238
|
+
type SqlFunction$2 = (...args: any[]) => Promise<any[]>;
|
|
239
239
|
interface BackendRequest {
|
|
240
240
|
id: string;
|
|
241
241
|
blob_data_url: string;
|
|
@@ -256,8 +256,8 @@ interface BackendRequest {
|
|
|
256
256
|
* using this table. The blob data (captured network calls, env reads, etc.)
|
|
257
257
|
* is uploaded to UploadThing at capture time and only the URL is stored.
|
|
258
258
|
*/
|
|
259
|
-
declare function backendRequestsEnsureTable(sql: SqlFunction$
|
|
260
|
-
declare function backendRequestsInsert(sql: SqlFunction$
|
|
259
|
+
declare function backendRequestsEnsureTable(sql: SqlFunction$2): Promise<void>;
|
|
260
|
+
declare function backendRequestsInsert(sql: SqlFunction$2, data: {
|
|
261
261
|
id?: string;
|
|
262
262
|
blobDataUrl: string;
|
|
263
263
|
handlerPath: string;
|
|
@@ -265,13 +265,13 @@ declare function backendRequestsInsert(sql: SqlFunction$1, data: {
|
|
|
265
265
|
branchName: string;
|
|
266
266
|
repositoryUrl?: string | null;
|
|
267
267
|
}): Promise<string>;
|
|
268
|
-
declare function backendRequestsGet(sql: SqlFunction$
|
|
269
|
-
declare function backendRequestsGetBlobUrl(sql: SqlFunction$
|
|
270
|
-
declare function backendRequestsList(sql: SqlFunction$
|
|
268
|
+
declare function backendRequestsGet(sql: SqlFunction$2, id: string): Promise<BackendRequest | null>;
|
|
269
|
+
declare function backendRequestsGetBlobUrl(sql: SqlFunction$2, id: string): Promise<string | null>;
|
|
270
|
+
declare function backendRequestsList(sql: SqlFunction$2, filters?: {
|
|
271
271
|
status?: string;
|
|
272
272
|
limit?: number;
|
|
273
273
|
}): Promise<BackendRequest[]>;
|
|
274
|
-
declare function backendRequestsUpdateStatus(sql: SqlFunction$
|
|
274
|
+
declare function backendRequestsUpdateStatus(sql: SqlFunction$2, id: string, status: string, recordingId?: string, errorMessage?: string): Promise<void>;
|
|
275
275
|
/**
|
|
276
276
|
* Convenience helper: creates `FinishRequestCallbacks` that upload
|
|
277
277
|
* captured request data to UploadThing and store the URL in the
|
|
@@ -279,7 +279,15 @@ declare function backendRequestsUpdateStatus(sql: SqlFunction$1, id: string, sta
|
|
|
279
279
|
*
|
|
280
280
|
* Requires `UPLOADTHING_TOKEN` environment variable and the `uploadthing` package.
|
|
281
281
|
*/
|
|
282
|
-
declare function databaseCallbacks(sql: SqlFunction$
|
|
282
|
+
declare function databaseCallbacks(sql: SqlFunction$2): FinishRequestCallbacks;
|
|
283
|
+
/**
|
|
284
|
+
* Creates `FinishRequestCallbacks` that POST captured request data to a
|
|
285
|
+
* remote Netlify Recorder service's `/api/store-request` endpoint.
|
|
286
|
+
*
|
|
287
|
+
* Use this instead of `databaseCallbacks` when the app does not own the
|
|
288
|
+
* recorder database — the hosted service handles blob upload and storage.
|
|
289
|
+
*/
|
|
290
|
+
declare function remoteCallbacks(recorderUrl: string): FinishRequestCallbacks;
|
|
283
291
|
interface EnsureRequestRecordingOptions {
|
|
284
292
|
/** Base URL of the Netlify Recorder service (e.g. "https://netlify-recorder-bm4wmw.netlify.app"). */
|
|
285
293
|
recorderUrl: string;
|
|
@@ -299,9 +307,9 @@ interface EnsureRequestRecordingOptions {
|
|
|
299
307
|
* This function is idempotent — calling it multiple times for the same request
|
|
300
308
|
* is safe. Once the recording completes, subsequent calls return the recording ID.
|
|
301
309
|
*/
|
|
302
|
-
declare function ensureRequestRecording(sql: SqlFunction$
|
|
310
|
+
declare function ensureRequestRecording(sql: SqlFunction$2, requestId: string, options: EnsureRequestRecordingOptions): Promise<string | null>;
|
|
303
311
|
|
|
304
|
-
type SqlFunction = (...args: any[]) => Promise<any[]>;
|
|
312
|
+
type SqlFunction$1 = (...args: any[]) => Promise<any[]>;
|
|
305
313
|
/**
|
|
306
314
|
* Creates the `audit_log` table and a generic PL/pgSQL trigger function
|
|
307
315
|
* (`audit_trigger_function`) that records INSERT, UPDATE, and DELETE
|
|
@@ -309,20 +317,56 @@ type SqlFunction = (...args: any[]) => Promise<any[]>;
|
|
|
309
317
|
*
|
|
310
318
|
* Call this once during schema initialization.
|
|
311
319
|
*/
|
|
312
|
-
declare function databaseAuditEnsureLogTable(sql: SqlFunction): Promise<void>;
|
|
320
|
+
declare function databaseAuditEnsureLogTable(sql: SqlFunction$1): Promise<void>;
|
|
313
321
|
/**
|
|
314
322
|
* Creates a trigger on the specified table that calls
|
|
315
323
|
* `audit_trigger_function` for INSERT, UPDATE, and DELETE operations.
|
|
316
324
|
*
|
|
317
325
|
* Throws if `tableName` is `'audit_log'` (cannot monitor itself).
|
|
318
326
|
*/
|
|
319
|
-
declare function databaseAuditMonitorTable(sql: SqlFunction, tableName: string, primaryKeyColumn?: string): Promise<void>;
|
|
327
|
+
declare function databaseAuditMonitorTable(sql: SqlFunction$1, tableName: string, primaryKeyColumn?: string): Promise<void>;
|
|
320
328
|
/**
|
|
321
329
|
* Returns all rows from the `audit_log` table, ordered by `performed_at` DESC.
|
|
322
330
|
*/
|
|
323
|
-
declare function databaseAuditDumpLogTable(sql: SqlFunction): Promise<Record<string, unknown>[]>;
|
|
331
|
+
declare function databaseAuditDumpLogTable(sql: SqlFunction$1): Promise<Record<string, unknown>[]>;
|
|
332
|
+
|
|
333
|
+
type SqlFunction = (...args: any[]) => Promise<any[]>;
|
|
334
|
+
interface CreateRecordingEndpointOptions {
|
|
335
|
+
sql: SqlFunction;
|
|
336
|
+
recorderUrl: string;
|
|
337
|
+
secret?: string;
|
|
338
|
+
webhookUrl?: string;
|
|
339
|
+
}
|
|
340
|
+
interface RecordingEndpointResponse {
|
|
341
|
+
status: "recorded" | "pending" | "queued" | "not_found" | "error";
|
|
342
|
+
recordingId?: string;
|
|
343
|
+
requestId?: string;
|
|
344
|
+
error?: string;
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Creates a Netlify Function handler that other services can call to trigger
|
|
348
|
+
* recording creation for a backend request (or retrieve its current status).
|
|
349
|
+
*
|
|
350
|
+
* **POST** with `{ "requestId": "<uuid>" }` — triggers recording creation if
|
|
351
|
+
* needed and returns the current status. Idempotent: re-posting the same
|
|
352
|
+
* request ID will not re-queue an already-queued or recorded request.
|
|
353
|
+
*
|
|
354
|
+
* **GET** with `?requestId=<uuid>` — returns the current recording status
|
|
355
|
+
* without triggering any recording.
|
|
356
|
+
*
|
|
357
|
+
* When `secret` is provided, every request must include an
|
|
358
|
+
* `Authorization: Bearer <secret>` header or receive a 401 response.
|
|
359
|
+
*
|
|
360
|
+
* Response body shape (`RecordingEndpointResponse`):
|
|
361
|
+
* - `{ status: "recorded", recordingId, requestId }` — recording exists
|
|
362
|
+
* - `{ status: "pending", requestId }` — recording is queued or processing
|
|
363
|
+
* - `{ status: "queued", requestId }` — recording was just queued by this call
|
|
364
|
+
* - `{ status: "not_found", error }` — request ID not in backend_requests
|
|
365
|
+
* - `{ status: "error", requestId?, error }` — recording failed or server error
|
|
366
|
+
*/
|
|
367
|
+
declare function createRecordingEndpoint(options: CreateRecordingEndpointOptions): (req: Request) => Promise<Response>;
|
|
324
368
|
|
|
325
369
|
declare function runInRequestContext<T>(requestId: string | null, fn: () => Promise<T>): Promise<T>;
|
|
326
370
|
declare function getCurrentRequestId(): string | null;
|
|
327
371
|
|
|
328
|
-
export { type BackendRequest, type BlobData, type CapturedData, type CreateRecordingRequestHandlerOptions, type EnsureRequestRecordingOptions, type EnvRead, type FinishRequestCallbacks, type FinishRequestOptions, type HandlerResponse$1 as HandlerResponse, type NetlifyEvent, type NetlifyV2Request, type NetworkCall, type RecordingResult, type RequestContext, type RequestInfo, backendRequestsEnsureTable, backendRequestsGet, backendRequestsGetBlobUrl, backendRequestsInsert, backendRequestsList, backendRequestsUpdateStatus, createRecordingRequestHandler, createRequestRecording, databaseAuditDumpLogTable, databaseAuditEnsureLogTable, databaseAuditMonitorTable, databaseCallbacks, ensureRequestRecording, finishRequest, getCurrentRequestId, redactBlobData, runInRequestContext, startRequest };
|
|
372
|
+
export { type BackendRequest, type BlobData, type CapturedData, type CreateRecordingEndpointOptions, type CreateRecordingRequestHandlerOptions, type EnsureRequestRecordingOptions, type EnvRead, type FinishRequestCallbacks, type FinishRequestOptions, type HandlerResponse$1 as HandlerResponse, type NetlifyEvent, type NetlifyV2Request, type NetworkCall, type RecordingEndpointResponse, type RecordingResult, type RequestContext, type RequestInfo, backendRequestsEnsureTable, backendRequestsGet, backendRequestsGetBlobUrl, backendRequestsInsert, backendRequestsList, backendRequestsUpdateStatus, createRecordingEndpoint, createRecordingRequestHandler, createRequestRecording, databaseAuditDumpLogTable, databaseAuditEnsureLogTable, databaseAuditMonitorTable, databaseCallbacks, ensureRequestRecording, finishRequest, getCurrentRequestId, redactBlobData, remoteCallbacks, runInRequestContext, startRequest };
|
package/dist/index.js
CHANGED
|
@@ -974,6 +974,9 @@ async function createRequestRecording(blobUrlOrData, handlerPath, requestInfo) {
|
|
|
974
974
|
headers["content-type"] = "application/json";
|
|
975
975
|
return new ResponseShim(body, { ...init, headers });
|
|
976
976
|
}
|
|
977
|
+
get body() {
|
|
978
|
+
return this._body;
|
|
979
|
+
}
|
|
977
980
|
async text() {
|
|
978
981
|
return this._body ?? "";
|
|
979
982
|
}
|
|
@@ -1280,6 +1283,33 @@ function databaseCallbacks(sql) {
|
|
|
1280
1283
|
}
|
|
1281
1284
|
};
|
|
1282
1285
|
}
|
|
1286
|
+
function remoteCallbacks(recorderUrl) {
|
|
1287
|
+
const baseUrl = recorderUrl.replace(/\/+$/, "");
|
|
1288
|
+
return {
|
|
1289
|
+
storeRequest: async (data) => {
|
|
1290
|
+
const res = await fetch(`${baseUrl}/api/store-request`, {
|
|
1291
|
+
method: "POST",
|
|
1292
|
+
headers: { "Content-Type": "application/json" },
|
|
1293
|
+
body: JSON.stringify({
|
|
1294
|
+
blobData: data.blobData,
|
|
1295
|
+
commitSha: data.commitSha,
|
|
1296
|
+
branchName: data.branchName,
|
|
1297
|
+
repositoryUrl: data.repositoryUrl,
|
|
1298
|
+
handlerPath: data.handlerPath,
|
|
1299
|
+
requestId: data.requestId
|
|
1300
|
+
})
|
|
1301
|
+
});
|
|
1302
|
+
if (!res.ok) {
|
|
1303
|
+
const errBody = await res.text().catch(() => "(unreadable)");
|
|
1304
|
+
throw new Error(
|
|
1305
|
+
`netlify-recorder: remote store-request failed: ${res.status} ${errBody}`
|
|
1306
|
+
);
|
|
1307
|
+
}
|
|
1308
|
+
const result = await res.json();
|
|
1309
|
+
return result.requestId;
|
|
1310
|
+
}
|
|
1311
|
+
};
|
|
1312
|
+
}
|
|
1283
1313
|
async function ensureRequestRecording(sql, requestId, options) {
|
|
1284
1314
|
const request = await backendRequestsGet(sql, requestId);
|
|
1285
1315
|
if (!request) {
|
|
@@ -1405,6 +1435,92 @@ async function databaseAuditDumpLogTable(sql) {
|
|
|
1405
1435
|
const rows = await sql`SELECT * FROM audit_log ORDER BY performed_at DESC`;
|
|
1406
1436
|
return rows;
|
|
1407
1437
|
}
|
|
1438
|
+
|
|
1439
|
+
// src/createRecordingEndpoint.ts
|
|
1440
|
+
function jsonResponse(body, status) {
|
|
1441
|
+
return new Response(JSON.stringify(body), {
|
|
1442
|
+
status,
|
|
1443
|
+
headers: { "Content-Type": "application/json" }
|
|
1444
|
+
});
|
|
1445
|
+
}
|
|
1446
|
+
function formatStatus(request) {
|
|
1447
|
+
if (request.status === "recorded" && request.recording_id) {
|
|
1448
|
+
return { status: "recorded", recordingId: request.recording_id, requestId: request.id };
|
|
1449
|
+
}
|
|
1450
|
+
if (request.status === "queued" || request.status === "processing") {
|
|
1451
|
+
return { status: "pending", requestId: request.id };
|
|
1452
|
+
}
|
|
1453
|
+
if (request.status === "failed") {
|
|
1454
|
+
return { status: "error", requestId: request.id, error: request.error_message ?? "Recording failed" };
|
|
1455
|
+
}
|
|
1456
|
+
return { status: "pending", requestId: request.id };
|
|
1457
|
+
}
|
|
1458
|
+
function createRecordingEndpoint(options) {
|
|
1459
|
+
const { sql, recorderUrl, secret, webhookUrl } = options;
|
|
1460
|
+
return async (req) => {
|
|
1461
|
+
if (secret) {
|
|
1462
|
+
const auth = req.headers.get("authorization");
|
|
1463
|
+
if (auth !== `Bearer ${secret}`) {
|
|
1464
|
+
return jsonResponse({ status: "error", error: "Unauthorized" }, 401);
|
|
1465
|
+
}
|
|
1466
|
+
}
|
|
1467
|
+
try {
|
|
1468
|
+
if (req.method === "GET") {
|
|
1469
|
+
const url = new URL(req.url);
|
|
1470
|
+
const requestId = url.searchParams.get("requestId");
|
|
1471
|
+
if (!requestId) {
|
|
1472
|
+
return jsonResponse({ status: "error", error: "Missing requestId query parameter" }, 400);
|
|
1473
|
+
}
|
|
1474
|
+
const request = await backendRequestsGet(sql, requestId);
|
|
1475
|
+
if (!request) {
|
|
1476
|
+
return jsonResponse({ status: "not_found", error: `Request ${requestId} not found` }, 404);
|
|
1477
|
+
}
|
|
1478
|
+
return jsonResponse(formatStatus(request), 200);
|
|
1479
|
+
}
|
|
1480
|
+
if (req.method === "POST") {
|
|
1481
|
+
const body = await req.json();
|
|
1482
|
+
const requestId = body.requestId;
|
|
1483
|
+
if (!requestId) {
|
|
1484
|
+
return jsonResponse({ status: "error", error: "Missing requestId in request body" }, 400);
|
|
1485
|
+
}
|
|
1486
|
+
const request = await backendRequestsGet(sql, requestId);
|
|
1487
|
+
if (!request) {
|
|
1488
|
+
return jsonResponse({ status: "not_found", error: `Request ${requestId} not found` }, 404);
|
|
1489
|
+
}
|
|
1490
|
+
if (request.status === "recorded" && request.recording_id) {
|
|
1491
|
+
return jsonResponse(
|
|
1492
|
+
{ status: "recorded", recordingId: request.recording_id, requestId },
|
|
1493
|
+
200
|
|
1494
|
+
);
|
|
1495
|
+
}
|
|
1496
|
+
if (request.status === "queued" || request.status === "processing") {
|
|
1497
|
+
return jsonResponse({ status: "pending", requestId }, 200);
|
|
1498
|
+
}
|
|
1499
|
+
try {
|
|
1500
|
+
const recordingId = await ensureRequestRecording(sql, requestId, {
|
|
1501
|
+
recorderUrl,
|
|
1502
|
+
webhookUrl
|
|
1503
|
+
});
|
|
1504
|
+
if (recordingId) {
|
|
1505
|
+
return jsonResponse({ status: "recorded", recordingId, requestId }, 200);
|
|
1506
|
+
}
|
|
1507
|
+
return jsonResponse({ status: "queued", requestId }, 202);
|
|
1508
|
+
} catch (err) {
|
|
1509
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1510
|
+
await backendRequestsUpdateStatus(sql, requestId, "failed", void 0, message).catch(
|
|
1511
|
+
() => {
|
|
1512
|
+
}
|
|
1513
|
+
);
|
|
1514
|
+
return jsonResponse({ status: "error", requestId, error: message }, 502);
|
|
1515
|
+
}
|
|
1516
|
+
}
|
|
1517
|
+
return jsonResponse({ status: "error", error: "Method not allowed" }, 405);
|
|
1518
|
+
} catch (err) {
|
|
1519
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1520
|
+
return jsonResponse({ status: "error", error: message }, 500);
|
|
1521
|
+
}
|
|
1522
|
+
};
|
|
1523
|
+
}
|
|
1408
1524
|
export {
|
|
1409
1525
|
backendRequestsEnsureTable,
|
|
1410
1526
|
backendRequestsGet,
|
|
@@ -1412,6 +1528,7 @@ export {
|
|
|
1412
1528
|
backendRequestsInsert,
|
|
1413
1529
|
backendRequestsList,
|
|
1414
1530
|
backendRequestsUpdateStatus,
|
|
1531
|
+
createRecordingEndpoint,
|
|
1415
1532
|
createRecordingRequestHandler,
|
|
1416
1533
|
createRequestRecording,
|
|
1417
1534
|
databaseAuditDumpLogTable,
|
|
@@ -1422,6 +1539,7 @@ export {
|
|
|
1422
1539
|
finishRequest,
|
|
1423
1540
|
getCurrentRequestId,
|
|
1424
1541
|
redactBlobData,
|
|
1542
|
+
remoteCallbacks,
|
|
1425
1543
|
runInRequestContext,
|
|
1426
1544
|
startRequest
|
|
1427
1545
|
};
|