@replayio-app-building/netlify-recorder 0.15.3 → 0.15.4
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 +75 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +4 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -155,6 +155,79 @@ const { status, recordingId } = await res.json();
|
|
|
155
155
|
// recordingId: string | null
|
|
156
156
|
```
|
|
157
157
|
|
|
158
|
+
### 4. Access control with secrets
|
|
159
|
+
|
|
160
|
+
You can restrict access to captured requests by setting a `secret` string. When a secret is set, the request is only accessible via API calls that provide the same secret value. This lets each app isolate its requests from other apps sharing the same Netlify Recorder service.
|
|
161
|
+
|
|
162
|
+
#### Setting a secret
|
|
163
|
+
|
|
164
|
+
Pass `secret` in the options when wrapping your handler:
|
|
165
|
+
|
|
166
|
+
```typescript
|
|
167
|
+
export default createRecordingRequestHandler(
|
|
168
|
+
async (req) => {
|
|
169
|
+
const result = await myBusinessLogic();
|
|
170
|
+
return { statusCode: 200, body: JSON.stringify(result) };
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
callbacks: remoteCallbacks(RECORDER_URL),
|
|
174
|
+
handlerPath: "netlify/functions/my-handler",
|
|
175
|
+
secret: process.env.NETLIFY_RECORDER_SECRET,
|
|
176
|
+
}
|
|
177
|
+
);
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
#### Listing requests by secret
|
|
181
|
+
|
|
182
|
+
Use the `list-by-secret` endpoint to retrieve all requests associated with a secret, with optional filtering by status, handler path, and time range:
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
const res = await fetch(
|
|
186
|
+
`${RECORDER_URL}/api/list-by-secret?secret=${secret}&status=recorded&handlerPath=netlify/functions/my-handler&after=2025-01-01T00:00:00Z&before=2025-12-31T23:59:59Z`
|
|
187
|
+
);
|
|
188
|
+
const { rows, total, page, limit } = await res.json();
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Query parameters:
|
|
192
|
+
|
|
193
|
+
| Parameter | Description |
|
|
194
|
+
|---|---|
|
|
195
|
+
| `secret` | **(required)** The secret string used when creating the requests |
|
|
196
|
+
| `status` | Filter by status: `captured`, `queued`, `processing`, `recorded`, `failed` |
|
|
197
|
+
| `handlerPath` | Filter by handler path (exact match) |
|
|
198
|
+
| `after` | Only include requests created at or after this ISO timestamp |
|
|
199
|
+
| `before` | Only include requests created at or before this ISO timestamp |
|
|
200
|
+
| `page` | Page number (default 1) |
|
|
201
|
+
| `limit` | Page size (default 20, max 100) |
|
|
202
|
+
|
|
203
|
+
#### Checking request status with a secret
|
|
204
|
+
|
|
205
|
+
When a request was created with a secret, you must include the secret when polling its status:
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
const res = await fetch(
|
|
209
|
+
`${RECORDER_URL}/api/get-request?requestId=${requestId}&secret=${secret}`
|
|
210
|
+
);
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Requests created without a secret remain accessible without one (backward compatible).
|
|
214
|
+
|
|
215
|
+
#### Managing your secret
|
|
216
|
+
|
|
217
|
+
Store your secret as a `NETLIFY_RECORDER_SECRET` environment variable. To keep it secure:
|
|
218
|
+
|
|
219
|
+
1. **Netlify site environment:** Add `NETLIFY_RECORDER_SECRET` in your Netlify site's environment variables (Site settings → Environment variables). This makes it available to all deployed functions.
|
|
220
|
+
|
|
221
|
+
2. **Branch-level secret** (for CI/development): If you're using the Netlify Recorder agent infrastructure, store the secret as a branch secret so deploy scripts and background agents can access it:
|
|
222
|
+
|
|
223
|
+
```bash
|
|
224
|
+
set-branch-secret NETLIFY_RECORDER_SECRET "your-secret-value"
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
3. **Local development:** Add `NETLIFY_RECORDER_SECRET` to your `.env` file (make sure `.env` is in `.gitignore`).
|
|
228
|
+
|
|
229
|
+
Use a strong random string (e.g. `openssl rand -base64 32`) and rotate it if compromised. All requests created under the old secret remain accessible only with the old secret value — there is no migration mechanism, so plan rotations during low-traffic windows.
|
|
230
|
+
|
|
158
231
|
---
|
|
159
232
|
|
|
160
233
|
## Option B: Self-Hosted
|
|
@@ -406,6 +479,7 @@ When `context.waitUntil` is not available (v1 handlers or missing context), the
|
|
|
406
479
|
- `options.commitSha` — Override `COMMIT_SHA` env var
|
|
407
480
|
- `options.branchName` — Override `BRANCH_NAME` env var
|
|
408
481
|
- `options.repositoryUrl` — Override `REPLAY_REPOSITORY_URL` env var
|
|
482
|
+
- `options.secret` — Optional secret string. When set, the stored request is only accessible via API calls that provide the same secret value.
|
|
409
483
|
|
|
410
484
|
**Returns:** A wrapped handler function with the same signature.
|
|
411
485
|
|
|
@@ -443,6 +517,7 @@ Logs a `console.warn` when the total duration exceeds 2 seconds or when individu
|
|
|
443
517
|
- `options.branchName` — Override `BRANCH_NAME` env var
|
|
444
518
|
- `options.repositoryUrl` — Override `REPLAY_REPOSITORY_URL` env var
|
|
445
519
|
- `options.requestId` — Pre-generated request ID (used by `createRecordingRequestHandler` in the `waitUntil` flow)
|
|
520
|
+
- `options.secret` — Optional secret string. When set, the stored request is only accessible via API calls that provide the same secret value.
|
|
446
521
|
|
|
447
522
|
### `remoteCallbacks(serviceUrl): FinishRequestCallbacks`
|
|
448
523
|
|
package/dist/index.d.ts
CHANGED
|
@@ -111,6 +111,8 @@ interface FinishRequestCallbacks {
|
|
|
111
111
|
handlerPath: string;
|
|
112
112
|
/** Pre-generated request ID. Use as the row ID when provided. */
|
|
113
113
|
requestId?: string;
|
|
114
|
+
/** Optional secret that restricts access to this request. */
|
|
115
|
+
secret?: string;
|
|
114
116
|
}) => Promise<string>;
|
|
115
117
|
}
|
|
116
118
|
/**
|
|
@@ -217,6 +219,11 @@ interface FinishRequestOptions {
|
|
|
217
219
|
* the response is returned before `finishRequest` runs.
|
|
218
220
|
*/
|
|
219
221
|
requestId?: string;
|
|
222
|
+
/**
|
|
223
|
+
* Optional secret string. When set, the stored request is only
|
|
224
|
+
* accessible via API calls that provide the same secret value.
|
|
225
|
+
*/
|
|
226
|
+
secret?: string;
|
|
220
227
|
}
|
|
221
228
|
/**
|
|
222
229
|
* Called at the end of the handler execution.
|
package/dist/index.js
CHANGED
|
@@ -458,7 +458,8 @@ async function finishRequest(requestContext, callbacks, response, options) {
|
|
|
458
458
|
branchName,
|
|
459
459
|
repositoryUrl,
|
|
460
460
|
handlerPath,
|
|
461
|
-
requestId: options?.requestId
|
|
461
|
+
requestId: options?.requestId,
|
|
462
|
+
secret: options?.secret
|
|
462
463
|
});
|
|
463
464
|
const storeDuration = Date.now() - storeStart;
|
|
464
465
|
if (storeDuration > SLOW_STEP_THRESHOLD_MS) {
|
|
@@ -564,7 +565,8 @@ function remoteCallbacks(serviceUrl) {
|
|
|
564
565
|
commitSha: metadata.commitSha,
|
|
565
566
|
branchName: metadata.branchName,
|
|
566
567
|
repositoryUrl: metadata.repositoryUrl,
|
|
567
|
-
requestId: metadata.requestId
|
|
568
|
+
requestId: metadata.requestId,
|
|
569
|
+
secret: metadata.secret
|
|
568
570
|
})
|
|
569
571
|
}
|
|
570
572
|
);
|