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:
@@ -490,6 +490,26 @@ a:focus-visible {
|
||||
margin-top: var(--space-2);
|
||||
}
|
||||
|
||||
/* Phase 5: inline field errors + top-level banner. Scoped to the
|
||||
contact form so the red tone does not bleed into other forms. */
|
||||
.contact-form__field-error,
|
||||
.contact-form__error {
|
||||
margin: var(--space-1) 0 0;
|
||||
color: #8b2e2e;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.contact-form__error {
|
||||
padding: var(--space-2) var(--space-3);
|
||||
background-color: #fbe9e7;
|
||||
border: 1px solid #d9a8a1;
|
||||
border-radius: var(--radius);
|
||||
}
|
||||
|
||||
.contact-form__captcha {
|
||||
margin-top: var(--space-2);
|
||||
}
|
||||
|
||||
/* Generic button. */
|
||||
.btn {
|
||||
display: inline-block;
|
||||
|
||||
Reference in New Issue
Block a user