feat: add customizable contact double opt-in flow (#350)
* feat: add customizable contact double opt-in flow * test: add double opt-in service coverage * fix: address review comments for double opt-in PR - Make pending status conditional on doubleOptInEnabled flag - Backfill legacy unsubscribeReason for reliable pending detection - Add doubleOptInContent to contact book listing select - Fix duplicate toast on DOI editor subject save failure - Harden searchParams parsing against string[] values - Make default DOI template use link mark for clickable URL - Make public API create+update atomic via transaction - Prevent contact upsert failure when DOI email send fails - Fix empty string template variable replacement Co-authored-by: opencode <opencode@anthropic.com> * fix: harden double opt-in confirmation safeguards Preserve explicit unsubscribe intent in DOI flows and prevent confirmation links from re-subscribing opted-out contacts. Also sanitize subscribe-page error messaging and use timing-safe hash comparison for link verification. * ui stuff * fix: require doubleOptInUrl in double opt-in templates * feat: add configurable from address for double opt-in emails * feat: add resend confirmation flow for pending contacts * fix: move subscribe confirmation to explicit POST flow * test: add contact book public API endpoint coverage * docs: add double opt-in documentation and update OpenAPI spec Add a user guide for the double opt-in feature covering setup, contact statuses, email customization, template variables, and best practices. Update the OpenAPI spec to include doubleOptIn fields in all contactBook request/response schemas. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: opencode <opencode@anthropic.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -15,17 +15,12 @@
|
||||
- `pnpm dev`: Turbo dev for all relevant apps (loads `.env`).
|
||||
- `pnpm start:web:local`: Run only `apps/web` locally on port 3000.
|
||||
- `pnpm build`: Turbo build across the monorepo.
|
||||
- `pnpm lint`: Run ESLint via shared config; fail on warnings.
|
||||
- `pnpm format`: Prettier over ts/tsx/md.
|
||||
- `pnpm dx` / `pnpm dx:up` / `pnpm dx:down`: Spin up/down local infra via Docker Compose, then run migrations.
|
||||
- Database (apps/web filter): `pnpm db:generate` | `db:migrate-dev` | `db:push` | `db:studio`.
|
||||
- Never run migrations unless users explicitly asked
|
||||
|
||||
## Coding Style & Naming Conventions
|
||||
|
||||
- TypeScript-first; 2-space indent; semicolons enabled by Prettier.
|
||||
- Linting: `@usesend/eslint-config`; run `pnpm lint` before PRs.
|
||||
- Formatting: Prettier 3; run `pnpm format`.
|
||||
- Files: React components PascalCase (e.g., `AppSideBar.tsx`); folders kebab/lowercase.
|
||||
- Paths (web): use alias `~/` for src imports (e.g., `import { x } from "~/utils/x"`).
|
||||
- NEVER USE DYNAMIC IMPORTS. ALWAYS IMPORT ON THE TOP
|
||||
|
||||
Reference in New Issue
Block a user