Bohan Jiang
7984606eed
feat(landing): add Contact Sales page and inquiry endpoint (MUL-2493) ( #2988 )
...
* feat(landing): add Contact Sales page and inquiry endpoint (MUL-2493)
Adds a public `/contact-sales` marketing page with a needs-discovery form
modelled on the design reference attached to MUL-2493 — first/last name,
business email (with free-provider rejection), company name + size,
country/region, intended use case, and a free-text goals field, plus the
two consent checkboxes from the reference.
Submissions hit a new public `POST /api/contact-sales` endpoint with
per-IP rate limiting (Redis-backed via the existing RateLimit middleware,
configurable through `RATE_LIMIT_CONTACT_SALES`) and a per-email hourly
cap so a single business address can't be used as a flood channel after
one valid pass. The inquiry is stored in a new `contact_sales_inquiry`
table; analytics fires a `contact_sales_submitted` PostHog event with
only the closed-enum dimensions (size, country, use case) — the free-text
goals stay in the DB and are never broadcast.
The page is linked from the landing header (md+) and the footer's Company
column, in both English and Simplified Chinese. The reserved-slug list is
updated so a workspace named `contact-sales` can't shadow the route.
Co-authored-by: multica-agent <github@multica.ai >
* fix(landing): canonicalize business email and tighten contact-sales form (MUL-2493)
- Parse the submitted email with net/mail and run the free-email
block-list against the canonical addr.Address, so a display-name
form like `Ada <ada@gmail.com >` can no longer slip past the gate
(the raw string had domain `gmail.com>`, which wasn't blocked).
Adds regression tests covering the display-name bypass and the
canonicalization helper.
- Drop noValidate from the contact-sales form so the browser's
native required / email / select checks fire before submit;
the JS-side free-email warning still runs as a UX guard.
- Update success copy ("respond within three business days") in
EN and ZH plus the page metadata.
Co-authored-by: multica-agent <github@multica.ai >
---------
Co-authored-by: multica-agent <github@multica.ai >
2026-05-22 13:22:36 +08:00
..
2026-03-29 00:22:17 +08:00
2026-03-29 00:22:17 +08:00
2026-03-29 16:49:55 +08:00
2026-03-29 16:49:55 +08:00
2026-03-29 16:53:28 +08:00
2026-03-29 16:53:28 +08:00
2026-03-29 17:01:07 +08:00
2026-03-29 17:01:07 +08:00
2026-03-29 17:52:35 +08:00
2026-03-29 17:52:35 +08:00
2026-03-30 03:08:52 +08:00
2026-03-30 03:08:52 +08:00
2026-03-30 13:26:42 +08:00
2026-03-30 13:26:42 +08:00
2026-03-30 16:49:13 +08:00
2026-03-30 16:49:13 +08:00
2026-03-30 22:37:59 +08:00
2026-03-30 22:37:59 +08:00
2026-03-30 22:53:28 +08:00
2026-03-30 22:53:28 +08:00
2026-03-30 22:37:59 +08:00
2026-03-30 22:37:59 +08:00
2026-03-31 13:48:39 +08:00
2026-03-31 13:48:39 +08:00
2026-03-31 15:29:41 +08:00
2026-03-31 15:29:41 +08:00
2026-03-31 16:19:02 +08:00
2026-03-31 16:19:02 +08:00
2026-03-31 16:13:58 +08:00
2026-03-31 16:13:58 +08:00
2026-03-31 15:47:09 +08:00
2026-03-31 15:47:09 +08:00
2026-04-02 17:33:52 +08:00
2026-04-02 17:33:52 +08:00
2026-04-07 22:02:28 +08:00
2026-04-07 22:02:28 +08:00
2026-04-09 12:43:21 +08:00
2026-04-09 14:00:03 +08:00
2026-04-09 13:38:46 +08:00
2026-04-09 13:38:46 +08:00
2026-04-08 13:08:15 +08:00
2026-04-08 13:08:15 +08:00
2026-04-09 14:19:46 +08:00
2026-04-09 17:18:14 +08:00
2026-04-09 12:43:21 +08:00
2026-04-09 14:00:03 +08:00
2026-04-09 14:59:16 +08:00
2026-04-09 14:59:16 +08:00
2026-04-09 16:31:05 +08:00
2026-04-09 16:31:05 +08:00
2026-04-10 14:34:32 +08:00
2026-04-10 14:34:32 +08:00
2026-04-10 14:29:00 +08:00
2026-04-10 14:29:00 +08:00
2026-04-10 16:21:20 +08:00
2026-04-10 16:21:20 +08:00
2026-04-10 19:00:25 +08:00
2026-04-10 19:00:25 +08:00
2026-04-10 20:59:32 +08:00
2026-04-10 20:59:32 +08:00
2026-04-13 16:47:56 +08:00
2026-04-13 16:47:56 +08:00
2026-04-14 18:21:11 +08:00
2026-04-14 18:21:11 +08:00
2026-04-15 14:58:53 +08:00
2026-04-15 14:58:53 +08:00
2026-04-15 00:01:18 +08:00
2026-04-15 00:01:18 +08:00
2026-04-15 04:54:37 +08:00
2026-04-15 04:54:37 +08:00
2026-04-16 13:16:35 +08:00
2026-04-16 13:16:35 +08:00
2026-04-15 13:48:21 +08:00
2026-04-15 13:48:21 +08:00
2026-04-16 13:16:35 +08:00
2026-04-16 13:16:35 +08:00
2026-04-16 13:16:35 +08:00
2026-04-16 13:16:35 +08:00
2026-04-18 01:35:22 +08:00
2026-04-18 01:35:22 +08:00
2026-04-17 13:40:02 +08:00
2026-04-17 13:40:02 +08:00
2026-04-16 18:54:12 +08:00
2026-04-16 18:54:12 +08:00
2026-04-16 21:21:20 +08:00
2026-04-16 21:21:20 +08:00
2026-04-17 15:10:38 +08:00
2026-04-17 15:10:38 +08:00
2026-04-19 23:21:31 -07:00
2026-04-19 23:21:31 -07:00
2026-04-21 20:32:33 +08:00
2026-04-21 20:32:33 +08:00
2026-04-21 00:06:34 +08:00
2026-04-21 00:06:34 +08:00
2026-04-21 14:42:52 +08:00
2026-04-21 14:42:52 +08:00
2026-04-21 20:32:33 +08:00
2026-04-21 20:32:33 +08:00
2026-04-21 20:32:33 +08:00
2026-04-21 20:32:33 +08:00
2026-04-21 20:32:33 +08:00
2026-04-21 20:32:33 +08:00
2026-04-21 20:32:33 +08:00
2026-04-21 20:32:33 +08:00
2026-04-22 13:08:37 +08:00
2026-04-22 13:08:37 +08:00
2026-04-22 15:08:06 +08:00
2026-04-22 15:08:06 +08:00
2026-04-23 10:35:55 +08:00
2026-04-23 10:35:55 +08:00
2026-04-24 11:05:33 +08:00
2026-04-24 11:05:33 +08:00
2026-04-27 14:23:42 +08:00
2026-04-27 14:23:42 +08:00
2026-05-06 16:16:12 +08:00
2026-05-06 16:16:12 +08:00
2026-04-29 14:50:58 +08:00
2026-04-29 14:50:58 +08:00
2026-05-03 10:51:31 +08:00
2026-05-03 10:51:31 +08:00
2026-04-29 14:05:26 +08:00
2026-04-29 14:05:26 +08:00
2026-04-29 14:50:58 +08:00
2026-04-29 14:50:58 +08:00
2026-04-29 18:29:46 +08:00
2026-04-29 18:29:46 +08:00
2026-04-29 18:29:46 +08:00
2026-04-29 18:29:46 +08:00
2026-04-29 22:51:29 +02:00
2026-04-29 22:51:29 +02:00
2026-04-30 16:05:53 +08:00
2026-04-30 16:05:53 +08:00
2026-04-30 14:00:43 +08:00
2026-04-30 14:00:43 +08:00
2026-04-30 14:17:53 +08:00
2026-04-30 14:17:53 +08:00
2026-04-30 15:50:05 +08:00
2026-04-30 15:50:05 +08:00
2026-05-06 16:27:06 +08:00
2026-05-06 16:27:06 +08:00
2026-05-09 05:49:33 +02:00
2026-05-09 05:49:33 +02:00
2026-05-07 15:45:29 +08:00
2026-05-07 15:45:29 +08:00
2026-05-08 15:35:21 +08:00
2026-05-08 15:35:21 +08:00
2026-05-08 15:35:21 +08:00
2026-05-08 15:35:21 +08:00
2026-05-08 15:35:21 +08:00
2026-05-08 15:35:21 +08:00
2026-05-08 15:35:21 +08:00
2026-05-08 15:35:21 +08:00
2026-05-08 15:35:21 +08:00
2026-05-08 15:35:21 +08:00
2026-05-08 15:35:21 +08:00
2026-05-08 15:35:21 +08:00
2026-05-08 15:35:21 +08:00
2026-05-08 15:35:21 +08:00
2026-05-09 15:07:57 +08:00
2026-05-09 15:07:57 +08:00
2026-05-09 14:39:10 +08:00
2026-05-09 14:39:10 +08:00
2026-05-12 13:49:03 +08:00
2026-05-12 13:49:03 +08:00
2026-05-09 15:07:57 +08:00
2026-05-09 15:07:57 +08:00
2026-05-11 14:39:35 +08:00
2026-05-11 14:39:35 +08:00
2026-05-11 14:39:35 +08:00
2026-05-11 14:39:35 +08:00
2026-05-12 10:57:54 +08:00
2026-05-12 10:57:54 +08:00
2026-05-11 22:53:07 +08:00
2026-05-11 22:53:07 +08:00
2026-05-13 18:46:20 +08:00
2026-05-13 18:46:20 +08:00
2026-05-13 12:51:16 +08:00
2026-05-13 12:51:16 +08:00
2026-05-13 18:46:20 +08:00
2026-05-13 18:46:20 +08:00
2026-05-13 18:46:20 +08:00
2026-05-13 18:46:20 +08:00
2026-05-13 18:46:20 +08:00
2026-05-13 18:46:20 +08:00
2026-05-13 18:46:20 +08:00
2026-05-13 18:46:20 +08:00
2026-05-14 14:07:26 +08:00
2026-05-14 14:07:26 +08:00
2026-05-14 15:23:36 +02:00
2026-05-14 15:23:36 +02:00
2026-05-18 12:17:39 +08:00
2026-05-18 12:17:39 +08:00
2026-05-17 15:01:38 +08:00
2026-05-17 15:01:38 +08:00
2026-05-16 21:26:30 +02:00
2026-05-16 21:26:30 +02:00
2026-05-16 21:26:30 +02:00
2026-05-16 21:26:30 +02:00
2026-05-18 14:59:40 +08:00
2026-05-18 14:59:40 +08:00
2026-05-19 05:35:18 +02:00
2026-05-19 05:35:18 +02:00
2026-05-20 12:30:10 +08:00
2026-05-20 12:30:10 +08:00
2026-05-19 18:37:48 +02:00
2026-05-19 18:37:48 +02:00
2026-05-20 05:30:13 +02:00
2026-05-20 05:30:13 +02:00
2026-05-19 19:51:28 +02:00
2026-05-19 19:51:28 +02:00
2026-05-20 15:37:23 +08:00
2026-05-20 15:37:23 +08:00
2026-05-22 13:22:36 +08:00
2026-05-22 13:22:36 +08:00
2026-05-21 19:00:26 +08:00
2026-05-21 19:00:26 +08:00
2026-05-21 15:33:47 +08:00
2026-05-21 15:33:47 +08:00
2026-05-21 15:33:47 +08:00
2026-05-21 15:33:47 +08:00
2026-05-21 15:33:47 +08:00
2026-05-21 15:33:47 +08:00
2026-05-21 15:33:47 +08:00
2026-05-21 16:26:42 +08:00
2026-05-21 15:33:47 +08:00
2026-05-21 15:33:47 +08:00
2026-05-21 16:35:45 +08:00
2026-05-21 16:35:45 +08:00
2026-05-22 12:26:56 +08:00
2026-05-22 12:26:56 +08:00