feat: phase 5 contact form — hCaptcha, honeypot, rate limit, notify
Working /contact POST flow: honeypot → hCaptcha server-verify → field validation → SlowAPI 3/hr IP rate limit → contact_submissions row → best-effort Resend notification (Reply-To = submitter) → generic success page. Spam paths don't persist and render the same success page (anti-enumeration). Send failures don't break the request path — the row is already durable. New services: HCaptchaService (async httpx + dev fallback), ContactService. EmailService gains send_contact_notification. Production config validator now requires ADMIN_CONTACT_EMAIL, HCAPTCHA_SECRET, HCAPTCHA_SITE_KEY. 23 new tests, all green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -183,11 +183,22 @@ class Settings(BaseSettings):
|
||||
missing.append("RESEND_FROM")
|
||||
if not self.admin_emails or not self.admin_emails_list:
|
||||
missing.append("ADMIN_EMAILS")
|
||||
# Phase 5: the public contact form needs a destination inbox
|
||||
# and a real hCaptcha key pair. Missing any of these would
|
||||
# either drop submissions on the floor (no recipient) or open
|
||||
# the form to unverified bot traffic (no captcha), so we
|
||||
# enforce them at boot.
|
||||
if not self.admin_contact_email:
|
||||
missing.append("ADMIN_CONTACT_EMAIL")
|
||||
if not self.hcaptcha_secret:
|
||||
missing.append("HCAPTCHA_SECRET")
|
||||
if not self.hcaptcha_site_key:
|
||||
missing.append("HCAPTCHA_SITE_KEY")
|
||||
if missing:
|
||||
raise ValueError(
|
||||
"Production configuration is missing required values: "
|
||||
+ ", ".join(missing)
|
||||
+ ". These are needed for magic-link admin auth."
|
||||
+ ". These are needed for admin auth and the contact form."
|
||||
)
|
||||
return self
|
||||
|
||||
|
||||
Reference in New Issue
Block a user