create-safest-tools 0.4.0 → 0.4.1
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 +2 -2
- package/package.json +1 -1
- package/src/cli.mjs +8 -6
- package/src/config.mjs +26 -5
- package/src/update.mjs +32 -2
- package/template/README.md +3 -3
- package/template/console/reports/ReportDrawer.tsx +51 -6
- package/template/console/reports/controller.ts +11 -5
- package/template/console/reports/store.ts +1 -1
- package/template/console/reports/types.ts +23 -0
- package/template/package.json +2 -2
- package/template/public/console/auth-shell.js +4332 -4236
- package/template/public/styles.css +17 -2
- package/template/reports.config.example.json +2 -1
- package/template/reports.schema.json +2 -1
- package/template/scripts/reports-auth-onboarding.mjs +6 -5
- package/template/scripts/reports-plan.mjs +13 -4
- package/template/src/index.ts +12 -3
- package/template/src/installation-admin.ts +2 -1
- package/template/src/report-ai.ts +30 -5
- package/template/src/report-better-auth.ts +5 -14
- package/template/src/report-delivery.ts +4 -3
- package/template/src/report-email.ts +106 -0
- package/template/src/report-workflow.ts +103 -21
- package/template/src/workflow-effects.ts +4 -3
- package/template/worker-configuration.d.ts +5 -3
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { AUDIT_GENESIS_HASH, canonicalJson, hashAuditEntry } from "./audit";
|
|
2
|
+
import { selectParticipantDelivery, type ParticipantDelivery } from "./report-email";
|
|
2
3
|
import { ApiError } from "./report-http";
|
|
3
4
|
import { analystAvatarColor } from "./report-presence";
|
|
4
5
|
import type { DeliveryJob, OperatorSession, ParticipantAudience, ReportState } from "./report-types";
|
|
@@ -6,6 +7,7 @@ import type { AppealDecisionInput, DecisionInput, ReviewerMessageInput } from ".
|
|
|
6
7
|
|
|
7
8
|
interface WorkflowReportRow {
|
|
8
9
|
id: string;
|
|
10
|
+
installation_id: string;
|
|
9
11
|
public_reference: string;
|
|
10
12
|
state: ReportState;
|
|
11
13
|
queue_id: string | null;
|
|
@@ -14,6 +16,8 @@ interface WorkflowReportRow {
|
|
|
14
16
|
target_reference: string;
|
|
15
17
|
owner_reference: string | null;
|
|
16
18
|
reporter_reference: string | null;
|
|
19
|
+
reporter_contact_mode: string;
|
|
20
|
+
reporter_contact_ciphertext: string | null;
|
|
17
21
|
}
|
|
18
22
|
|
|
19
23
|
interface ClaimRow {
|
|
@@ -62,8 +66,9 @@ function auditEvent(
|
|
|
62
66
|
|
|
63
67
|
async function workflowReport(db: D1Database, reportId: string): Promise<WorkflowReportRow | null> {
|
|
64
68
|
return db.prepare(`
|
|
65
|
-
SELECT r.id, r.public_reference, r.state, r.queue_id, r.queue_version_id, r.policy_version_id,
|
|
66
|
-
t.target_reference, t.owner_reference, p.reference AS reporter_reference
|
|
69
|
+
SELECT r.id, r.installation_id, r.public_reference, r.state, r.queue_id, r.queue_version_id, r.policy_version_id,
|
|
70
|
+
t.target_reference, t.owner_reference, p.reference AS reporter_reference,
|
|
71
|
+
p.contact_mode AS reporter_contact_mode, p.contact_ciphertext AS reporter_contact_ciphertext
|
|
67
72
|
FROM reports r
|
|
68
73
|
JOIN report_targets t ON t.report_id = r.id
|
|
69
74
|
JOIN report_participants p ON p.report_id = r.id AND p.audience = 'reporter'
|
|
@@ -160,6 +165,8 @@ export async function loadReportDetail(db: D1Database, reportId: string): Promis
|
|
|
160
165
|
SELECT r.id, r.public_reference AS reference, r.source, r.state, r.priority, r.reason_code AS reasonCode,
|
|
161
166
|
r.locale, r.queue_id AS queueId, r.submitted_at AS submittedAt, r.received_at AS receivedAt,
|
|
162
167
|
r.updated_at AS updatedAt, r.due_at AS dueAt, r.resolved_at AS resolvedAt,
|
|
168
|
+
r.form_version_id AS formVersionId, form.title AS formTitle,
|
|
169
|
+
form.fields_json AS formFieldsJson, form.consent_notice AS consentNotice,
|
|
163
170
|
r.policy_version_id AS policyVersionId, policy.title AS policyTitle,
|
|
164
171
|
policy.rules_json AS policyRulesJson, r.queue_version_id AS queuePolicyVersionId,
|
|
165
172
|
qv.management_mode AS queueManagementMode, qv.reviewer_instructions AS reviewerInstructions,
|
|
@@ -170,6 +177,7 @@ export async function loadReportDetail(db: D1Database, reportId: string): Promis
|
|
|
170
177
|
JOIN report_targets t ON t.report_id = r.id
|
|
171
178
|
JOIN report_participants p ON p.report_id = r.id AND p.audience = 'reporter'
|
|
172
179
|
LEFT JOIN policy_versions policy ON policy.id = r.policy_version_id
|
|
180
|
+
LEFT JOIN report_form_versions form ON form.id = r.form_version_id
|
|
173
181
|
LEFT JOIN queue_versions qv ON qv.id = r.queue_version_id
|
|
174
182
|
WHERE r.id = ?1
|
|
175
183
|
LIMIT 1
|
|
@@ -227,6 +235,37 @@ export async function loadReportDetail(db: D1Database, reportId: string): Promis
|
|
|
227
235
|
LIMIT 500
|
|
228
236
|
`).bind(reportId).all<Record<string, unknown>>(),
|
|
229
237
|
]);
|
|
238
|
+
const formFieldsValue = parsedJson(report.formFieldsJson, []);
|
|
239
|
+
const formFields = Array.isArray(formFieldsValue)
|
|
240
|
+
? formFieldsValue.filter((field): field is Record<string, unknown> => Boolean(field) && typeof field === "object" && !Array.isArray(field))
|
|
241
|
+
: [];
|
|
242
|
+
const answerRows = new Map(answers.results.map((row) => [String(row.fieldKey), row]));
|
|
243
|
+
const projectedAnswers = formFields.flatMap((field) => {
|
|
244
|
+
const fieldKey = typeof field.key === "string" ? field.key : "";
|
|
245
|
+
if (!fieldKey) return [];
|
|
246
|
+
const row = answerRows.get(fieldKey);
|
|
247
|
+
answerRows.delete(fieldKey);
|
|
248
|
+
return [{
|
|
249
|
+
fieldKey,
|
|
250
|
+
label: typeof field.label === "string" ? field.label : fieldKey,
|
|
251
|
+
type: typeof field.type === "string" ? field.type : "unknown",
|
|
252
|
+
required: field.required === true,
|
|
253
|
+
options: Array.isArray(field.options) ? field.options : [],
|
|
254
|
+
answered: Boolean(row),
|
|
255
|
+
value: row ? parsedJson(row.valueJson, null) : null,
|
|
256
|
+
}];
|
|
257
|
+
});
|
|
258
|
+
for (const [fieldKey, row] of answerRows) {
|
|
259
|
+
projectedAnswers.push({
|
|
260
|
+
fieldKey,
|
|
261
|
+
label: fieldKey,
|
|
262
|
+
type: "unknown",
|
|
263
|
+
required: false,
|
|
264
|
+
options: [],
|
|
265
|
+
answered: true,
|
|
266
|
+
value: parsedJson(row.valueJson, null),
|
|
267
|
+
});
|
|
268
|
+
}
|
|
230
269
|
return {
|
|
231
270
|
...report,
|
|
232
271
|
policyRules: (() => {
|
|
@@ -236,7 +275,8 @@ export async function loadReportDetail(db: D1Database, reportId: string): Promis
|
|
|
236
275
|
} catch { return []; }
|
|
237
276
|
})(),
|
|
238
277
|
policyRulesJson: undefined,
|
|
239
|
-
|
|
278
|
+
formFieldsJson: undefined,
|
|
279
|
+
answers: projectedAnswers,
|
|
240
280
|
evidence: evidence.results,
|
|
241
281
|
messages: messages.results.map((row) => ({ ...row, automated: row.automated === 1 })),
|
|
242
282
|
decisions: decisions.results.map((row) => ({
|
|
@@ -278,20 +318,43 @@ export async function loadReportDetail(db: D1Database, reportId: string): Promis
|
|
|
278
318
|
}
|
|
279
319
|
|
|
280
320
|
export interface WorkflowDeliveryOptions {
|
|
321
|
+
emailEnabled: boolean;
|
|
322
|
+
contactEncryptionKey: string;
|
|
323
|
+
recipientHashPepper: string;
|
|
281
324
|
notificationEnabled: boolean;
|
|
282
325
|
}
|
|
283
326
|
|
|
327
|
+
async function reporterDelivery(
|
|
328
|
+
report: WorkflowReportRow,
|
|
329
|
+
options: WorkflowDeliveryOptions,
|
|
330
|
+
): Promise<ParticipantDelivery> {
|
|
331
|
+
return selectParticipantDelivery({
|
|
332
|
+
reportId: report.id,
|
|
333
|
+
audience: "reporter",
|
|
334
|
+
contactMode: report.reporter_contact_mode,
|
|
335
|
+
contactCiphertext: report.reporter_contact_ciphertext,
|
|
336
|
+
reference: report.reporter_reference,
|
|
337
|
+
...options,
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
|
|
284
341
|
export async function addReviewerMessage(
|
|
285
342
|
db: D1Database,
|
|
286
343
|
reportId: string,
|
|
287
344
|
session: OperatorSession,
|
|
288
345
|
input: ReviewerMessageInput,
|
|
289
346
|
options: WorkflowDeliveryOptions,
|
|
290
|
-
): Promise<{ messageId: string; jobs: DeliveryJob[]; idempotentReplay: boolean }> {
|
|
347
|
+
): Promise<{ messageId: string; jobs: DeliveryJob[]; deliveryState: string; deliveryChannel: ParticipantDelivery["channel"]; idempotentReplay: boolean }> {
|
|
291
348
|
const existing = await db.prepare(`
|
|
292
|
-
SELECT id
|
|
293
|
-
|
|
294
|
-
|
|
349
|
+
SELECT m.id, m.delivery_state AS deliveryState,
|
|
350
|
+
CASE
|
|
351
|
+
WHEN EXISTS (SELECT 1 FROM email_deliveries e WHERE e.message_id = m.id) THEN 'email'
|
|
352
|
+
WHEN EXISTS (SELECT 1 FROM notices n JOIN notification_outbox o ON o.notice_id = n.id WHERE n.message_id = m.id) THEN 'customer_webhook'
|
|
353
|
+
ELSE NULL
|
|
354
|
+
END AS deliveryChannel
|
|
355
|
+
FROM case_messages m WHERE m.report_id = ?1 AND m.idempotency_key = ?2 LIMIT 1
|
|
356
|
+
`).bind(reportId, input.idempotencyKey).first<{ id: string; deliveryState: string; deliveryChannel: ParticipantDelivery["channel"] }>();
|
|
357
|
+
if (existing) return { messageId: existing.id, jobs: [], deliveryState: existing.deliveryState, deliveryChannel: existing.deliveryChannel, idempotentReplay: true };
|
|
295
358
|
const createdAt = now();
|
|
296
359
|
await requireActiveClaim(db, reportId, session.actor.id, createdAt);
|
|
297
360
|
const report = await workflowReport(db, reportId);
|
|
@@ -301,24 +364,27 @@ export async function addReviewerMessage(
|
|
|
301
364
|
}
|
|
302
365
|
const messageId = crypto.randomUUID();
|
|
303
366
|
const noticeId = crypto.randomUUID();
|
|
304
|
-
const
|
|
367
|
+
const deliveryId = crypto.randomUUID();
|
|
368
|
+
const delivery = await reporterDelivery(report, options);
|
|
305
369
|
const nextState: ReportState = input.awaitReporter ? "awaiting_reporter" : report.state;
|
|
306
|
-
const jobs: DeliveryJob[] =
|
|
307
|
-
? [{ version: 1, jobId: `
|
|
308
|
-
:
|
|
370
|
+
const jobs: DeliveryJob[] = delivery.channel === "email"
|
|
371
|
+
? [{ version: 1, jobId: `email:${deliveryId}`, type: "deliver_email", outboxId: deliveryId }]
|
|
372
|
+
: delivery.channel === "customer_webhook"
|
|
373
|
+
? [{ version: 1, jobId: `notification:${deliveryId}`, type: "deliver_notification", outboxId: deliveryId }]
|
|
374
|
+
: [];
|
|
309
375
|
const statements: D1PreparedStatement[] = [
|
|
310
376
|
db.prepare(`
|
|
311
377
|
INSERT INTO case_messages (
|
|
312
378
|
id, report_id, audience, direction, sender_type, sender_id, body, template_version_id,
|
|
313
379
|
automated, delivery_state, created_at, idempotency_key
|
|
314
380
|
) VALUES (?1, ?2, 'reporter', 'outbound', 'human', ?3, ?4, 'reporter-follow-up-v1', 0, ?5, ?6, ?7)
|
|
315
|
-
`).bind(messageId, reportId, session.actor.id, input.body,
|
|
381
|
+
`).bind(messageId, reportId, session.actor.id, input.body, delivery.state, createdAt, input.idempotencyKey),
|
|
316
382
|
db.prepare(`
|
|
317
383
|
INSERT INTO notices (
|
|
318
384
|
id, report_id, audience, template_version_id, rendered_body, delivery_state,
|
|
319
385
|
created_at, updated_at, idempotency_key, message_id
|
|
320
386
|
) VALUES (?1, ?2, 'reporter', 'reporter-follow-up-v1', ?3, ?4, ?5, ?5, ?6, ?7)
|
|
321
|
-
`).bind(noticeId, reportId, input.body,
|
|
387
|
+
`).bind(noticeId, reportId, input.body, delivery.state, createdAt, input.idempotencyKey, messageId),
|
|
322
388
|
auditEvent(db, {
|
|
323
389
|
idempotencyKey: `reviewer-message:${reportId}:${input.idempotencyKey}`,
|
|
324
390
|
action: "report.reporter_message_created",
|
|
@@ -326,7 +392,7 @@ export async function addReviewerMessage(
|
|
|
326
392
|
actorId: session.actor.id,
|
|
327
393
|
targetType: "report",
|
|
328
394
|
targetId: reportId,
|
|
329
|
-
details: { messageId, audience: "reporter", nextState },
|
|
395
|
+
details: { messageId, audience: "reporter", nextState, deliveryChannel: delivery.channel, deliveryState: delivery.state },
|
|
330
396
|
createdAt,
|
|
331
397
|
}),
|
|
332
398
|
];
|
|
@@ -339,15 +405,22 @@ export async function addReviewerMessage(
|
|
|
339
405
|
`).bind(crypto.randomUUID(), reportId, report.state, nextState, session.actor.type, session.actor.id, createdAt),
|
|
340
406
|
);
|
|
341
407
|
}
|
|
342
|
-
if (
|
|
408
|
+
if (delivery.channel === "email" && delivery.recipientHash) {
|
|
409
|
+
statements.push(db.prepare(`
|
|
410
|
+
INSERT INTO email_deliveries (
|
|
411
|
+
id, installation_id, message_id, audience, recipient_hash,
|
|
412
|
+
provider, state, attempt_count, next_attempt_at, created_at, updated_at
|
|
413
|
+
) VALUES (?1, ?2, ?3, 'reporter', ?4, 'cloudflare_email_service', 'pending', 0, ?5, ?5, ?5)
|
|
414
|
+
`).bind(deliveryId, report.installation_id, messageId, delivery.recipientHash, createdAt));
|
|
415
|
+
} else if (delivery.channel === "customer_webhook") {
|
|
343
416
|
statements.push(db.prepare(`
|
|
344
417
|
INSERT INTO notification_outbox (
|
|
345
418
|
id, notice_id, idempotency_key, channel, payload_json, status, next_attempt_at, created_at, updated_at
|
|
346
419
|
) VALUES (?1, ?2, ?3, 'customer_webhook', ?4, 'pending', ?5, ?5, ?5)
|
|
347
|
-
`).bind(
|
|
420
|
+
`).bind(deliveryId, noticeId, `notice:${noticeId}`, canonicalJson({
|
|
348
421
|
version: 1,
|
|
349
422
|
event: "report.message.available",
|
|
350
|
-
notification_id:
|
|
423
|
+
notification_id: deliveryId,
|
|
351
424
|
report_reference: report.public_reference,
|
|
352
425
|
...(report.reporter_reference ? { recipient_reference: report.reporter_reference } : {}),
|
|
353
426
|
audience: "reporter",
|
|
@@ -355,7 +428,7 @@ export async function addReviewerMessage(
|
|
|
355
428
|
}), createdAt));
|
|
356
429
|
}
|
|
357
430
|
await db.batch(statements);
|
|
358
|
-
return { messageId, jobs, idempotentReplay: false };
|
|
431
|
+
return { messageId, jobs, deliveryState: delivery.state, deliveryChannel: delivery.channel, idempotentReplay: false };
|
|
359
432
|
}
|
|
360
433
|
|
|
361
434
|
|
|
@@ -416,6 +489,7 @@ export async function recordHumanDecision(
|
|
|
416
489
|
const reporterMessageId = crypto.randomUUID();
|
|
417
490
|
const reporterNoticeId = crypto.randomUUID();
|
|
418
491
|
const reporterNotificationId = crypto.randomUUID();
|
|
492
|
+
const reporterDeliveryPlan = await reporterDelivery(report, options);
|
|
419
493
|
const actionId = input.actionCode ? crypto.randomUUID() : null;
|
|
420
494
|
const jobs: DeliveryJob[] = [];
|
|
421
495
|
const statements: D1PreparedStatement[] = [
|
|
@@ -435,13 +509,13 @@ export async function recordHumanDecision(
|
|
|
435
509
|
id, report_id, audience, direction, sender_type, sender_id, body, template_version_id,
|
|
436
510
|
automated, delivery_state, created_at, idempotency_key
|
|
437
511
|
) VALUES (?1, ?2, 'reporter', 'outbound', 'human', ?3, ?4, 'reporter-outcome-v1', 0, ?5, ?6, ?7)
|
|
438
|
-
`).bind(reporterMessageId, reportId, session.actor.id, input.reporterNotice,
|
|
512
|
+
`).bind(reporterMessageId, reportId, session.actor.id, input.reporterNotice, reporterDeliveryPlan.state, createdAt, `decision:${decisionId}:reporter-message`),
|
|
439
513
|
db.prepare(`
|
|
440
514
|
INSERT INTO notices (
|
|
441
515
|
id, report_id, decision_id, audience, template_version_id, rendered_body,
|
|
442
516
|
delivery_state, created_at, updated_at, idempotency_key, message_id
|
|
443
517
|
) VALUES (?1, ?2, ?3, 'reporter', 'reporter-outcome-v1', ?4, ?5, ?6, ?6, ?7, ?8)
|
|
444
|
-
`).bind(reporterNoticeId, reportId, decisionId, input.reporterNotice,
|
|
518
|
+
`).bind(reporterNoticeId, reportId, decisionId, input.reporterNotice, reporterDeliveryPlan.state, createdAt, `decision:${decisionId}:reporter-notice`, reporterMessageId),
|
|
445
519
|
db.prepare(`
|
|
446
520
|
UPDATE reports SET state = 'resolved_by_human', resolved_at = ?2, updated_at = ?2 WHERE id = ?1
|
|
447
521
|
`).bind(reportId, createdAt),
|
|
@@ -475,7 +549,15 @@ export async function recordHumanDecision(
|
|
|
475
549
|
`).bind(reportId, crypto.randomUUID(), createdAt),
|
|
476
550
|
];
|
|
477
551
|
|
|
478
|
-
if (
|
|
552
|
+
if (reporterDeliveryPlan.channel === "email" && reporterDeliveryPlan.recipientHash) {
|
|
553
|
+
statements.push(db.prepare(`
|
|
554
|
+
INSERT INTO email_deliveries (
|
|
555
|
+
id, installation_id, message_id, audience, recipient_hash,
|
|
556
|
+
provider, state, attempt_count, next_attempt_at, created_at, updated_at
|
|
557
|
+
) VALUES (?1, ?2, ?3, 'reporter', ?4, 'cloudflare_email_service', 'pending', 0, ?5, ?5, ?5)
|
|
558
|
+
`).bind(reporterNotificationId, report.installation_id, reporterMessageId, reporterDeliveryPlan.recipientHash, createdAt));
|
|
559
|
+
jobs.push({ version: 1, jobId: `email:${reporterNotificationId}`, type: "deliver_email", outboxId: reporterNotificationId });
|
|
560
|
+
} else if (reporterDeliveryPlan.channel === "customer_webhook") {
|
|
479
561
|
statements.push(db.prepare(`
|
|
480
562
|
INSERT INTO notification_outbox (
|
|
481
563
|
id, notice_id, idempotency_key, channel, payload_json, status, next_attempt_at, created_at, updated_at
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { canonicalJson } from "./audit";
|
|
2
2
|
import { loadWorkflowExecutionContext } from "./component-executor";
|
|
3
3
|
import { decryptReportContact, sha256 } from "./report-crypto";
|
|
4
|
+
import { reporterEmailBinding, reporterEmailFrom } from "./report-email";
|
|
4
5
|
import type { DeliveryJob } from "./report-types";
|
|
5
6
|
import { evaluateInputMapping } from "./workflow-expressions";
|
|
6
7
|
import type { JsonObject, JsonValue, WorkflowNodeV1 } from "./workflow-platform-types";
|
|
@@ -96,11 +97,11 @@ export async function sendWorkflowMessage(
|
|
|
96
97
|
const deliveryChannels = Array.isArray(input.node.config.delivery_channels)
|
|
97
98
|
? input.node.config.delivery_channels.filter((entry): entry is string => typeof entry === "string")
|
|
98
99
|
: audience === "internal" ? [] : ["customer_webhook", "email"];
|
|
99
|
-
const emailBinding = (env
|
|
100
|
+
const emailBinding = reporterEmailBinding(env);
|
|
100
101
|
const emailAddress = participant?.contactCiphertext
|
|
101
102
|
? (await decryptReportContact(env.CREDENTIAL_ENCRYPTION_KEY, participant.contactCiphertext, run.reportId, audience as "reporter" | "affected_user")).trim().toLowerCase()
|
|
102
103
|
: "";
|
|
103
|
-
const emailFrom = env
|
|
104
|
+
const emailFrom = reporterEmailFrom(env);
|
|
104
105
|
const emailEnabled = Boolean(
|
|
105
106
|
audience !== "internal" && deliveryChannels.includes("email")
|
|
106
107
|
&& participant?.contactMode === "customer_notification"
|
|
@@ -187,7 +188,7 @@ export async function sendWorkflowMessage(
|
|
|
187
188
|
deliveryChannels, emailEnabled, notificationEnabled,
|
|
188
189
|
}), timestamp));
|
|
189
190
|
await env.DB.batch(statements);
|
|
190
|
-
if (job) await env.
|
|
191
|
+
if (job) await env.DELIVERY_JOBS.send(job, { contentType: "json" }).catch(() => {});
|
|
191
192
|
return { port: "sent", messageId, deliveryState: audience === "internal" ? "not_required" : externalDeliveryEnabled ? "queued" : "suppressed", idempotentReplay: false };
|
|
192
193
|
}
|
|
193
194
|
|
|
@@ -12,6 +12,8 @@ interface __BaseEnv_Env {
|
|
|
12
12
|
LOADER: WorkerLoader;
|
|
13
13
|
AI: Ai;
|
|
14
14
|
ASSETS: Fetcher;
|
|
15
|
+
AUTH_EMAIL: SendEmail;
|
|
16
|
+
REPORTER_EMAIL: SendEmail;
|
|
15
17
|
PUBLIC_BASE_URL: string;
|
|
16
18
|
ALLOWED_ORIGINS: string;
|
|
17
19
|
CONTEXT_TOKEN_TTL_SECONDS: string;
|
|
@@ -25,8 +27,8 @@ interface __BaseEnv_Env {
|
|
|
25
27
|
PUBLIC_REPORT_LIMIT_PER_10_MINUTES: string;
|
|
26
28
|
PUBLIC_REPORT_SURGE_PER_5_MINUTES: string;
|
|
27
29
|
OPERATOR_NOTIFICATION_WEBHOOK_URL: string;
|
|
28
|
-
|
|
29
|
-
|
|
30
|
+
REPORTER_EMAIL_FROM: string;
|
|
31
|
+
REPORTER_EMAIL_SUBJECT_PREFIX: string;
|
|
30
32
|
ADMIN_API_KEY: string;
|
|
31
33
|
BETTER_AUTH_SECRET: string;
|
|
32
34
|
AUTH_EMAIL_FROM: string;
|
|
@@ -59,5 +61,5 @@ type StringifyValues<EnvType extends Record<string, unknown>> = {
|
|
|
59
61
|
[Binding in keyof EnvType]: EnvType[Binding] extends string ? EnvType[Binding] : string;
|
|
60
62
|
};
|
|
61
63
|
declare namespace NodeJS {
|
|
62
|
-
interface ProcessEnv extends StringifyValues<Pick<Cloudflare.Env, "PUBLIC_BASE_URL" | "ALLOWED_ORIGINS" | "CONTEXT_TOKEN_TTL_SECONDS" | "CLAIM_TTL_MINUTES" | "REPORT_RETENTION_DAYS" | "MESSAGE_RETENTION_DAYS" | "APPEAL_WINDOW_DAYS" | "ALLOWED_ACTION_CODES" | "OPERATIONS_DLQ_NAME" | "PUBLIC_INTEGRATION_ID" | "PUBLIC_REPORT_LIMIT_PER_10_MINUTES" | "PUBLIC_REPORT_SURGE_PER_5_MINUTES" | "OPERATOR_NOTIFICATION_WEBHOOK_URL" | "
|
|
64
|
+
interface ProcessEnv extends StringifyValues<Pick<Cloudflare.Env, "PUBLIC_BASE_URL" | "ALLOWED_ORIGINS" | "CONTEXT_TOKEN_TTL_SECONDS" | "CLAIM_TTL_MINUTES" | "REPORT_RETENTION_DAYS" | "MESSAGE_RETENTION_DAYS" | "APPEAL_WINDOW_DAYS" | "ALLOWED_ACTION_CODES" | "OPERATIONS_DLQ_NAME" | "PUBLIC_INTEGRATION_ID" | "PUBLIC_REPORT_LIMIT_PER_10_MINUTES" | "PUBLIC_REPORT_SURGE_PER_5_MINUTES" | "OPERATOR_NOTIFICATION_WEBHOOK_URL" | "REPORTER_EMAIL_FROM" | "REPORTER_EMAIL_SUBJECT_PREFIX" | "ADMIN_API_KEY" | "BETTER_AUTH_SECRET" | "AUTH_EMAIL_FROM" | "CONTEXT_SIGNING_SECRET" | "CREDENTIAL_ENCRYPTION_KEY" | "RATE_LIMIT_PEPPER" | "INTEGRATION_API_KEYS_JSON" | "TURNSTILE_SITE_KEY" | "TURNSTILE_SECRET_KEY" | "ACTION_WEBHOOK_URL" | "ACTION_WEBHOOK_SECRET" | "NOTIFICATION_WEBHOOK_URL" | "NOTIFICATION_WEBHOOK_SECRET" | "OPERATOR_NOTIFICATION_WEBHOOK_SECRET" | "EXAMPLE_ENRICHMENT_SECRET" | "AI_LOCAL_FIXTURE_ENABLED" | "AI_LOCAL_FIXTURE_JSON">> {}
|
|
63
65
|
}
|