Snapshot of uncommitted work that had accumulated in the tree alongside
the Turnstile changes:
- marketing pages, SEO helpers (lib/seo.ts, lib/marketing/) and
structured data
- admin billing actions and a per-user portfolio view, plus an admin
error boundary
- rate limiting (lib/rate-limit.ts) applied across the /api/v1 surface
- CSP and proxy adjustments, accounting/webhook lib updates
- Playwright config and an e2e/unit test suite
- next bumped to ^16.3.4 with the lockfile regenerated
- generated AGENTS.md / CLAUDE.md
Authored by other sessions working in this tree; committed here so the
Turnstile work could be pushed without leaving the tree dirty.
Typecheck passes.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Turnstile protected sign-in, sign-up and password-reset, but three gaps
remained:
- updatePassword had no verification and its page had no widget, so the
final step of the reset flow was unprotected. The reset token is now
carried through the failure redirect so a failed challenge doesn't
strand the user on a form whose emailed link can't be replayed.
- /api/auth/[...all] exposed better-auth's handler directly, accepting
unlimited credential guesses and email sends with no bot protection --
a full bypass of the page-level checks. Credential-bearing POSTs now
require a verified token. The gate lives in the route handler, so the
server actions (which call auth.api.* in-process) are unaffected. GET
is untouched for OAuth callbacks and verify-email links, and
/sign-in/social stays open since it only redirects to the provider.
- Turnstile tokens expire after ~5 minutes and the widget never reset,
so a form left open submitted a stale token and failed with "complete
the verification challenge" despite the challenge visibly passing.
Verified with Cloudflare's test keys: all four auth forms block an
invalid token, pass a valid one through to real auth logic, and the API
gate returns 403 without a token and 401 with one.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- Data export (Art. 15/20): GET /api/gdpr/export serves a full JSON export
of the user's data (credentials/tokens excluded, exclusions declared)
- Right to erasure (Art. 17): self-service deletion with 30-day grace
period (Settings -> Privacy & Data), cancellable; daily /api/cron/gdpr
drain cancels Stripe billing, purges Spaces files, cascade-deletes the
account, anonymizes consent rows, and writes audit evidence
- Migration 0011: account_deletion_requests (partial unique index = one
pending per user) + FK-less consent_log (survives erasure)
- Consent: terms/privacy acceptance logged at signup (email + Google);
cookie banner with analytics opt-out (umami.disabled), choices logged
server-side for signed-in users via POST /api/gdpr/consent
- Admin deleteUser upgraded to the same full purge (was leaving Spaces
files and Stripe subscriptions orphaned)
- /gdpr legal page now points at the self-service tools
- scripts/verify-gdpr.ts: end-to-end verification vs live dev DB (22/22)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Deploy config:
- .do/app.yaml: build the Dockerfile directly from GitHub (deploy_on_push) instead
of a pre-built DOCR image; NEXT_PUBLIC_* set RUN_AND_BUILD_TIME with the
propertymanagement.network domain so they bake into the client bundle; add
custom domains block (apex + www); wire Sentry DSN (server + browser).
Included pending work from the audit-fixes branch:
- AI provider abstraction (OpenAI/Anthropic, admin-selectable; Anthropic default)
- Per-landlord e-signature (DocuSign OAuth + Dropbox Sign) + migration 0010
- Outbound webhooks / Zapier integration
- PayPal removal (Stripe-only billing)
- Storage hardening (fail-loud when Spaces unconfigured), security fixes
Verified: full production Docker build (same build-args as DO) passes clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Batch commit of the pending working tree on security/audit-fixes-2026-07.
Major areas:
- Outbound webhooks / Zapier: schema + signed delivery with retries, public
v1 API (REST-hook subscribe/unsubscribe), settings UI, cron drain.
- Deploy hardening: email via SMTP2GO (Resend fully removed), verified DB TLS
(DATABASE_SSL=require + DATABASE_CA), storage fails loud in production when
Spaces is unconfigured instead of silently using ephemeral disk.
- Integrations & features (concurrent work): accounting (QuickBooks/Xero),
e-signature (DocuSign/Dropbox Sign), PayPal, geocoding/maps, onboarding,
expanded legal pages.
- DB migrations 0006–0009.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>