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:
@@ -1,16 +1,16 @@
|
||||
/**
|
||||
* Phase 6 — IMAP bounce poller.
|
||||
* Phase 6 - IMAP bounce poller.
|
||||
*
|
||||
* Polls the configured IMAP inbox for delivery-status notifications, runs
|
||||
* each through `parseBounce()`, and matches the original recipient against
|
||||
* a recent `document_sends` row. When matched, updates the send row's
|
||||
* bounce_* columns and fires an `email_bounced` notification to the rep
|
||||
* who originated the send (hard/soft only — out-of-office is logged but
|
||||
* who originated the send (hard/soft only - out-of-office is logged but
|
||||
* not surfaced as an actionable alert).
|
||||
*
|
||||
* The job runs globally (no per-port context). IMAP creds are read from
|
||||
* environment variables (`IMAP_HOST` / `IMAP_PORT` / `IMAP_USER` /
|
||||
* `IMAP_PASS`) — when any is missing the poll is a no-op so the worker
|
||||
* `IMAP_PASS`) - when any is missing the poll is a no-op so the worker
|
||||
* boots happily in dev. Run cadence is set in `src/lib/queue/scheduler.ts`
|
||||
* (every 15 minutes).
|
||||
*
|
||||
@@ -31,7 +31,7 @@ import { createNotification } from '@/lib/services/notifications.service';
|
||||
const STATE_KEY = 'bounce_poller_state';
|
||||
const FIRST_RUN_LOOKBACK_HOURS = 24;
|
||||
/** How far back to look for the originating document_sends row. Any send
|
||||
* whose bounce arrives after this window won't be matched — the SMTP
|
||||
* whose bounce arrives after this window won't be matched - the SMTP
|
||||
* protocol guarantees NDRs typically arrive within minutes / hours, so
|
||||
* 7 days is generous. */
|
||||
const SEND_MATCH_WINDOW_DAYS = 7;
|
||||
@@ -81,12 +81,12 @@ export async function processImapBouncePoll(): Promise<void> {
|
||||
// copy-paste with the visual spaces preserved still works.
|
||||
const pass = process.env.IMAP_PASS?.replace(/\s+/g, '');
|
||||
if (!host || !portStr || !user || !pass) {
|
||||
logger.debug('IMAP bounce poll skipped — IMAP_* env not configured');
|
||||
logger.debug('IMAP bounce poll skipped - IMAP_* env not configured');
|
||||
return;
|
||||
}
|
||||
const port = Number.parseInt(portStr, 10);
|
||||
if (!Number.isFinite(port)) {
|
||||
logger.warn({ portStr }, 'IMAP bounce poll skipped — IMAP_PORT not numeric');
|
||||
logger.warn({ portStr }, 'IMAP bounce poll skipped - IMAP_PORT not numeric');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -141,7 +141,7 @@ export async function processImapBouncePoll(): Promise<void> {
|
||||
|
||||
const lookback = new Date(Date.now() - SEND_MATCH_WINDOW_DAYS * 86_400_000);
|
||||
// Most-recent matching send to this recipient; the recipient
|
||||
// may have been sent multiple files in the same window — the
|
||||
// may have been sent multiple files in the same window - the
|
||||
// bounce always refers to the latest.
|
||||
const candidates = await db
|
||||
.select()
|
||||
@@ -174,7 +174,7 @@ export async function processImapBouncePoll(): Promise<void> {
|
||||
.where(eq(documentSends.id, target.id));
|
||||
matched++;
|
||||
|
||||
// Skip OOO — informational, not actionable. Hard/soft notify
|
||||
// Skip OOO - informational, not actionable. Hard/soft notify
|
||||
// the original sender so they can re-send or escalate.
|
||||
if (
|
||||
target.sentByUserId &&
|
||||
@@ -185,7 +185,7 @@ export async function processImapBouncePoll(): Promise<void> {
|
||||
userId: target.sentByUserId,
|
||||
type: 'email_bounced',
|
||||
title: 'Email bounced',
|
||||
description: `Your email to ${parsed.originalRecipient} bounced — ${parsed.reason}`,
|
||||
description: `Your email to ${parsed.originalRecipient} bounced - ${parsed.reason}`,
|
||||
link: target.interestId ? `/interests/${target.interestId}` : undefined,
|
||||
entityType: 'document_send',
|
||||
entityId: target.id,
|
||||
@@ -207,7 +207,7 @@ export async function processImapBouncePoll(): Promise<void> {
|
||||
try {
|
||||
await client.logout();
|
||||
} catch {
|
||||
// Logout failures are non-fatal — the connection will be torn down
|
||||
// Logout failures are non-fatal - the connection will be torn down
|
||||
// by the timeout settings above.
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user