fix(documents): idempotency, perf, contract pipeline, observability

- A1: idempotency gate in handleDocumentCompleted (prevents duplicate files on Documenso retry)
- A3: LEFT JOIN port_id move to outer WHERE (uses idx_docs_signed_file_id)
- G-C5: contract_sent / contract_signed auto-advance triggers in sendDocument + handleDocumentCompleted
- 0-byte signed PDF guard before storage.put
- portId in outer catch + poll worker
- Sanitize storagePath/storageBucket in aggregated files API
- Audit log for handleDocumentCompleted file insert
- Replace em-dashes in aggregated group labels with colons
- G-I6: delete orphaned hub-counts route + getHubTabCounts service fn

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-11 13:56:46 +02:00
parent c0e5af8b92
commit c761b4b911
6 changed files with 138 additions and 123 deletions

View File

@@ -42,12 +42,16 @@ export async function processDocumensoPoll(): Promise<void> {
if (localSigner && localSigner.status !== 'signed') {
logger.info(
{ documentId: doc.id, email: remoteRecipient.email },
{ documentId: doc.id, email: remoteRecipient.email, portId: doc.portId },
'Reconciling signed signer from poll',
);
// Thread portId from the workflow's port context so the webhook
// handlers run port-scoped lookups (resolveWebhookDocument) rather
// than the port-ambiguous fallback.
await handleRecipientSigned({
documentId: doc.documensoId,
recipientEmail: remoteRecipient.email,
portId: doc.portId,
});
}
}
@@ -55,11 +59,11 @@ export async function processDocumensoPoll(): Promise<void> {
// Reconcile document status
if (remoteDoc.status === 'COMPLETED' && doc.status !== 'completed') {
logger.info({ documentId: doc.id }, 'Reconciling completed document from poll');
await handleDocumentCompleted({ documentId: doc.documensoId });
logger.info({ documentId: doc.id, portId: doc.portId }, 'Reconciling completed document from poll');
await handleDocumentCompleted({ documentId: doc.documensoId, portId: doc.portId });
} else if (remoteDoc.status === 'EXPIRED' && doc.status !== 'expired') {
logger.info({ documentId: doc.id }, 'Reconciling expired document from poll');
await handleDocumentExpired({ documentId: doc.documensoId });
logger.info({ documentId: doc.id, portId: doc.portId }, 'Reconciling expired document from poll');
await handleDocumentExpired({ documentId: doc.documensoId, portId: doc.portId });
}
} catch (err) {
logger.error(