Refine workspaces and event publishing; harden uploads and email delivery
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
# Email delivery
|
||||
|
||||
Manyangles uses the RaceTix-style branded shell for verification, password resets,
|
||||
account invitations, and gallery-ready messages. HTML has plain-text alternatives,
|
||||
escaped dynamic content, and system-font fallbacks. People previews the actual
|
||||
gallery-ready HTML before queueing.
|
||||
|
||||
## Production Resend configuration
|
||||
|
||||
Set `EMAIL_PROVIDER=resend`, `RESEND_API_KEY`, `EMAIL_FROM` (a verified sender), and
|
||||
`NEXT_PUBLIC_APP_URL` (the public HTTPS origin). Run the image/email worker with
|
||||
the same environment as the web process. Configuration changes do not migrate
|
||||
pending emails between providers. No production test sends are needed.
|
||||
|
||||
## Completion outbox
|
||||
|
||||
Organizer actions enqueue one immutable payload per event/normalized email
|
||||
address. A unique database constraint deduplicates concurrent requests. Only
|
||||
opted-in guests qualify. Before sending, the worker checks consent and gallery
|
||||
visibility again. Messages whose guest link changed are held for review.
|
||||
|
||||
The worker records provider acceptance, attempts and sanitized errors. “Sent”
|
||||
means accepted by Resend/SMTP, not delivered to an inbox. No automatic event
|
||||
schedule sends email.
|
||||
|
||||
Configure a Resend webhook at `/api/webhooks/resend` and set
|
||||
`RESEND_WEBHOOK_SECRET` in the web process. Subscribe to `email.sent`,
|
||||
`email.delivered`, `email.delivery_delayed`, `email.bounced`, `email.complained`,
|
||||
and `email.failed`. The endpoint verifies the bounded raw body with the Resend
|
||||
SDK and deduplicates by signed `svix-id`. It stores only message identifiers,
|
||||
outcomes and timestamps, never raw recipient payloads. Failed database writes
|
||||
return 503 for provider retry. Callbacks arriving before the worker saves its
|
||||
provider reference are retained and matched when email history is read.
|
||||
|
||||
People shows provider outcomes separately from queue status. Complaints,
|
||||
bounces and failures take precedence over delivered/delayed/sent regardless of
|
||||
callback order. Delivered means acceptance by the recipient mail server, not
|
||||
inbox placement. This tracks gallery-ready outbox messages; auth email outcomes
|
||||
do not have a management UI. Tracking does not change guest consent or implement
|
||||
a cross-event suppression list. Mailpit does not produce Resend callbacks.
|
||||
|
||||
Resend retries reuse `gallery-ready/<delivery-id>` and the original payload.
|
||||
Retries back off, stop after five attempts, and stay inside a conservative
|
||||
23-hour window (Resend retains idempotency keys for 24 hours). Expired/uncertain
|
||||
deliveries require provider review. The People tab permits safe Resend retries
|
||||
inside that window. SMTP ambiguity is not automatically retried.
|
||||
|
||||
Account verification, password resets and account invitations remain synchronous
|
||||
transactional sends. They also use stable, recipient-specific idempotency keys.
|
||||
|
||||
## Local verification
|
||||
|
||||
Keep `EMAIL_PROVIDER=mailpit`, `SMTP_HOST=127.0.0.1`, `SMTP_PORT=1027`.
|
||||
Open Mailpit at http://localhost:8027. Test scripts reject nonlocal Mailpit settings.
|
||||
|
||||
`bun run email:preview` sends all four templates to the local inbox, like RaceTix's
|
||||
preview workflow. Authentication/invitation links in these samples are deliberately
|
||||
nonfunctional; the gallery sample links to `/e/demo`.
|
||||
|
||||
Docker Compose already supplies Postgres (5439), Mailpit (SMTP 1027 / inbox 8027),
|
||||
and Garage (3900 / 3903). Use `bun run docker:up`, `bun run docker:logs`, and
|
||||
`bun run docker:down`. The web app and worker run on the host with Bun, just as
|
||||
RaceTix's development setup does. Stopping this project's Compose stack does not
|
||||
stop Colima or disrupt other projects.
|
||||
|
||||
```sh
|
||||
bun run typecheck
|
||||
bun test
|
||||
WEBHOOK_INTEGRATION=1 bun --env-file=.env test packages/email/src/webhooks.test.ts
|
||||
cd apps/web
|
||||
NOTIFICATIONS_INTEGRATION=1 PUBLISHING_INTEGRATION=1 bun --env-file=../../.env test src/server/guest-notifications.integration.test.ts src/server/publishing.integration.test.ts
|
||||
```
|
||||
|
||||
With the local worker running, add `REAL_UPLOAD_INTEGRATION=1` to the publishing
|
||||
test command to copy one existing `/e/demo` original through a presigned PUT,
|
||||
verify a single queued job and smaller generated variants, then remove only the
|
||||
temporary event and its uploaded objects. The original demo assets are untouched.
|
||||
|
||||
Run `bun --env-file=.env packages/database/src/verify-migrations.ts` from the repo
|
||||
root to migrate a temporary local database; the script removes that database
|
||||
after verification. It never migrates a remote database.
|
||||
Reference in New Issue
Block a user