Replace the current “anonymous root URL → login/admin app” experience with a developer-first public marketing homepage (Resend / Mailjet-inspired). Unauthenticated visitors see product value, API/SMTP/Webhook integration paths, and bilingual CTAs; authenticated users still reach the admin console at /.
| Item | Choice |
|---|---|
| Audience | Developers first |
| Scope (MVP) | Single-page landing with in-page anchors |
| Language | zh-CN / en-US toggle (localStorage) |
| Pricing | None |
| Implementation | Static landing entry (Vite landing build), decoupled from React admin bundle |
| Visual system | Same Modern SaaS indigo tokens as admin (#4F46E5, canvas, ink) |
/login./ maps to public/index.html (admin app).| Path | Unauthenticated | Authenticated |
|---|---|---|
GET / |
Serve landing (landing.html) |
Serve admin (index.html) |
/login, /register, /forgot-password, /resend-verification, /reset-password |
login.html |
Optional redirect to admin if already logged in (existing) |
/app (optional alias) |
Redirect to /login |
Admin console |
src/server.js)/login.html.GET / (or mapped /index root):
getRequestUser is present → index.html (admin).landing.html./ to /login./api/* auth unchanged.GET / landing vs admin) MUST be non-cacheable: e.g. Cache-Control: private, no-store (and prefer the same for landing.html / index.html HTML documents) so proxies/browsers do not stick a visitor on the wrong shell after login./app alias: out of MVP; primary CTAs use /register and /login only./register#api (scroll)POST /api/send samplesent / bounced / failed)| Column | Content |
|---|---|
| API | Token auth, POST /api/send, field list, link to register |
| SMTP | Host/ports aligned with product submission config, example snippet |
| Webhooks | Event types + signature headers summary, “configure after login” |
X-MailHub-Signature one-linerLanding is fully static (no authenticated API on first paint).
https://mail.example.com, host mail.example.com (or “your MailHub host” in prose).25 (smtp), 587 (smtp/STARTTLS), 465 (smtps), 2525 (smtp). Do not hardcode a specific production hostname from env..env into landing assets.from, to, subject, text; header Authorization: Bearer <USER_API_TOKEN>.sent | bounced | failed; payload type email.sent / email.bounced / email.failed; header X-MailHub-Signature: t=…,v1=….--mh-primary: #4F46E5zh-CN and en-USlocalStorage (key e.g. mailhub-landing-locale)navigator.language starts with zh → zh-CN, else en-USdata-i18n nodes and document.documentElement.langVite multi-page entry only (same pipeline as index / login):
landing.html + src/frontend/landing/main.ts + landing.css + i18n dictionaryvite.config.ts input key: landingpublic/landing.html + hashed assets under public/assets/Do not maintain a parallel hand-written-only public/landing.html source of truth outside the Vite build.
GET / body/title or content-type indicates landing (or path resolution helper unit test)GET / resolves to admin index| Path | Role |
|---|---|
landing.html (source) |
Landing markup |
src/frontend/landing/main.ts |
i18n, interactions |
src/frontend/landing/landing.css |
Styles |
src/frontend/landing/i18n.js |
Copy dictionary |
vite.config.ts |
landing input |
src/server.js |
Root routing + public asset allowlist |
test/server-*.test.js |
Route tests |
public/landing.html + assets |
Build output |
/ and assets/ shows marketing landing, not login form as the only chrome/ still loads admin consolenpm test and npm run build pass/docs site