chore(autonomous-session): consolidate uncommitted work from prior session
Bundles the prior autonomous-session output that was sitting unstaged: - Em-dash sweep across src/ + tests/ (en-dash/em-dash to hyphen, ~2280 instances) - country-flag-icons rollout (CountryFlag component, replaces emoji glyphs that never rendered on Windows; lazy-loads the 3x2 SVG index as a single chunk after the per-subpath dynamic-import approach silently failed in webpack) - Admin IA Phase 1+2: 7-domain regroup, 41 to 38 pages, /admin/berths index, redirects (ocr to ai, reports to dashboard, invitations to users), docs/admin-ia-proposal.md - Per-template email tester (registry + endpoint + UI on Email admin page) - Cancel-document mode picker (delete-from-Documenso vs keep-for-audit) - Dashboard PDF report: 25 widgets, SVG charts, date-range picker, 11 resolvers - Customize-widgets per-region sortables at xl+ (charts/rails/feed); single flat sortable below xl when the layout stacks; per-viewport saved orders - Audit doc updates capturing each shipped item - Lint fixes: react-compiler immutability in DonutChart (reduce instead of let-reassign), set-state-in-effect disables in CountryFlag and UploadForSigning preview-bytes effect, unused 'confirm' destructures in interest contract + reservation tabs, unescaped apostrophe in test-template card copy
This commit is contained in:
@@ -32,7 +32,7 @@ function canonicalizeEvent(event: string): string {
|
||||
|
||||
// Discriminated union of every Documenso event we know how to react to.
|
||||
// Adding a new event type forces a compile error in the `match(...)`
|
||||
// below via `.exhaustive()` — so we can't ship a Documenso 2.x bump
|
||||
// below via `.exhaustive()` - so we can't ship a Documenso 2.x bump
|
||||
// without consciously deciding how to handle each new event. Anything
|
||||
// not in this list falls through to the structured-log catch-all below.
|
||||
type KnownDocumensoEvent =
|
||||
@@ -90,7 +90,7 @@ type DocumensoRecipient = {
|
||||
readStatus?: string;
|
||||
signedAt?: string | null;
|
||||
/** Per-recipient signing token Documenso uses as the URL tail.
|
||||
* Present on both v1.13 and v2 payloads under varied field names —
|
||||
* Present on both v1.13 and v2 payloads under varied field names -
|
||||
* we coalesce them below. Phase 2: passed through to the handlers
|
||||
* so they can match against `document_signers.signing_token`
|
||||
* instead of email. */
|
||||
@@ -162,8 +162,8 @@ async function handleDocumensoWebhook(req: NextRequest): Promise<NextResponse> {
|
||||
source: 'webhook',
|
||||
});
|
||||
}
|
||||
// Always return 200 (webhook best-practice — don't leak signal). Body
|
||||
// is intentionally empty/uniform — error-ux-auditor H5 noted the
|
||||
// Always return 200 (webhook best-practice - don't leak signal). Body
|
||||
// is intentionally empty/uniform - error-ux-auditor H5 noted the
|
||||
// literal "Invalid secret" string confirms the endpoint expects a
|
||||
// secret, which is a free reconnaissance hint for enumeration.
|
||||
return NextResponse.json({ ok: false }, { status: 200 });
|
||||
@@ -206,14 +206,14 @@ async function handleDocumensoWebhook(req: NextRequest): Promise<NextResponse> {
|
||||
|
||||
// Every handler accepts an optional `portId` and refuses to mutate when
|
||||
// the lookup is ambiguous across multiple ports without one. Forward
|
||||
// the secret-resolved portId everywhere — not just the expired path —
|
||||
// the secret-resolved portId everywhere - not just the expired path -
|
||||
// so signed/completed/opened/rejected/cancelled events can't flip a
|
||||
// foreign-tenant document via documensoId reuse.
|
||||
const portScope = matchedPortId ? { portId: matchedPortId } : {};
|
||||
|
||||
try {
|
||||
if (!isKnownEvent(event)) {
|
||||
// New / unknown Documenso event — structured log catches the
|
||||
// New / unknown Documenso event - structured log catches the
|
||||
// shape so we can add a handler before the next webhook lands.
|
||||
logger.info({ event }, 'Unhandled Documenso webhook event type');
|
||||
} else {
|
||||
@@ -222,12 +222,12 @@ async function handleDocumensoWebhook(req: NextRequest): Promise<NextResponse> {
|
||||
// v1.13 fires DOCUMENT_SIGNED per recipient sign;
|
||||
// 2.x fires DOCUMENT_RECIPIENT_COMPLETED for the same semantics.
|
||||
// Some 2.x deployments emit RECIPIENT_SIGNED as a v2-flavoured alias
|
||||
// — log when we see it (telemetry) and route to the same handler so
|
||||
// - log when we see it (telemetry) and route to the same handler so
|
||||
// v2 deployments don't silently drop per-recipient signs.
|
||||
if (e === 'RECIPIENT_SIGNED') {
|
||||
logger.info(
|
||||
{ event: e, documensoId },
|
||||
'Documenso v2 RECIPIENT_SIGNED received — routing to recipient-signed handler',
|
||||
'Documenso v2 RECIPIENT_SIGNED received - routing to recipient-signed handler',
|
||||
);
|
||||
}
|
||||
const signedRecipients = recipients.filter(
|
||||
@@ -253,7 +253,7 @@ async function handleDocumensoWebhook(req: NextRequest): Promise<NextResponse> {
|
||||
if (e === 'RECIPIENT_VIEWED') {
|
||||
logger.info(
|
||||
{ event: e, documensoId },
|
||||
'Documenso v2 RECIPIENT_VIEWED received — routing to document-opened handler',
|
||||
'Documenso v2 RECIPIENT_VIEWED received - routing to document-opened handler',
|
||||
);
|
||||
}
|
||||
const openedRecipients = recipients.filter(
|
||||
@@ -293,7 +293,7 @@ async function handleDocumensoWebhook(req: NextRequest): Promise<NextResponse> {
|
||||
await handleDocumentExpired({ documentId: documensoId, ...portScope });
|
||||
})
|
||||
.with('DOCUMENT_REMINDER_SENT', async () => {
|
||||
// Auto-reminder — informational only, no state change.
|
||||
// Auto-reminder - informational only, no state change.
|
||||
logger.info(
|
||||
{
|
||||
documensoId,
|
||||
@@ -313,7 +313,7 @@ async function handleDocumensoWebhook(req: NextRequest): Promise<NextResponse> {
|
||||
} catch (err) {
|
||||
logger.error({ err, event }, 'Error processing Documenso webhook');
|
||||
// The audit caught that webhook handlers were the only API surface
|
||||
// bypassing the platform-error pipeline — admin/errors was silent on
|
||||
// bypassing the platform-error pipeline - admin/errors was silent on
|
||||
// Documenso webhook crashes. Pipe them in so they surface alongside
|
||||
// every other 5xx.
|
||||
void captureErrorEvent({
|
||||
@@ -327,7 +327,7 @@ async function handleDocumensoWebhook(req: NextRequest): Promise<NextResponse> {
|
||||
}
|
||||
|
||||
// Wrap with withPublicContext so the handler runs inside a
|
||||
// runWithRequestContext ALS frame — without it the inline
|
||||
// runWithRequestContext ALS frame - without it the inline
|
||||
// `captureErrorEvent` call in the catch block silently no-ops because
|
||||
// getRequestContext() returns null for unauthenticated routes.
|
||||
export const POST = withPublicContext(handleDocumensoWebhook);
|
||||
|
||||
Reference in New Issue
Block a user