Serverless multi-user mailbox receiver di Cloudflare (receive-only), dengan frontend React dan backend Workers + D1.
MailCF memetakan 1 akun login ke 1 alamat inbox permanen (username@domain) dan menampilkan email masuk dari Cloudflare Email Routing.
flowchart LR
A[Pengirim Email] --> B[Cloudflare Email Routing]
B --> C[Cloudflare Worker Email Handler]
C --> D[(D1 Database)]
E[Frontend React<br/>Cloudflare Pages] --> F[Cloudflare Worker API]
F --> D
D --> F
F --> E
- Auth aman dengan PBKDF2 (WebCrypto), session token hash di D1.
- Single inbox permanen per akun.
- Multi-domain ready via
APP_DOMAINS(aktifkan 1 atau lebih domain). - Ingest email via Cloudflare Email Worker (
postal-mime). - Ownership check ketat di semua endpoint inbox/message.
- Rate limiting untuk auth dan create inbox.
- Viewer pesan HTML aman:
- Sanitasi backend + frontend.
- Render HTML email dalam
iframeterisolasi agar style email tidak merusak UI app.
- UI:
- Inbox list/message list.
- Copy actions + toast notification.
- Delete per message.
- Confirm modal untuk aksi sensitif (delete, logout).
.
├─ frontend/ # React + Vite + Tailwind v4
│ ├─ src/
│ │ ├─ pages/
│ │ ├─ components/
│ │ └─ lib/
│ └─ vite.config.js
├─ backend/ # Cloudflare Worker API + Email handler
│ ├─ src/
│ │ ├─ router.js
│ │ ├─ email.js
│ │ ├─ services/
│ │ ├─ middleware/
│ │ └─ db/schema.sql
│ ├─ wrangler.toml.example
│ └─ wrangler.toml # lokal (tidak untuk commit)
└─ package.json # helper script root
| Layer | Teknologi |
|---|---|
| Frontend | React 19, React Router 7, Vite 7, Tailwind CSS 4 |
| Backend API | Cloudflare Workers, Hono |
| Email Parser | postal-mime |
| Validation | zod |
| Database | Cloudflare D1 (SQLite) |
| Runtime | JavaScript ESM (tanpa TypeScript) |
- Register/Login memakai:
usernamepassworddomain(opsional jika single-domain)
- Email akun disusun otomatis:
username@domain. - Jika
APP_DOMAINSberisi lebih dari 1 domain, UI otomatis menampilkan pilihan domain.
GET /api/healthGET /api/public-configPOST /api/auth/registerbody:{ username, domain?, password }POST /api/auth/loginbody:{ username, domain?, password }POST /api/auth/logout(idempotent, tetap clear cookie)
POST /api/auth/change-passwordbody:{ newPassword, confirmPassword }GET /api/meGET /api/my-inboxGET /api/my-messages?limit=50&offset=0GET /api/inboxesGET /api/inboxes/:idGET /api/inboxes/:id/messages?limit=50&offset=0GET /api/messages/:idDELETE /api/messages/:id
POST /api/inboxes->403DELETE /api/inboxes/:id->403
Skema ada di backend/src/db/schema.sql, tabel:
userssessionsinboxesmessagesrate_limits
Index penting:
idx_inboxes_user_ididx_messages_inbox_received
Contoh lengkap: backend/.env.example
| Variable | Wajib | Keterangan |
|---|---|---|
APP_DOMAIN |
Ya | Domain default/fallback |
APP_DOMAINS |
Ya | Daftar domain dipisah koma |
FRONTEND_ORIGIN |
Ya | Origin frontend production |
FRONTEND_ORIGIN_DEV |
Dev | Origin frontend lokal (CORS dev) |
SESSION_TTL_SECONDS |
Ya | TTL session detik (mis. 604800) |
PBKDF2_ITERATIONS |
Ya | Iterasi hash password |
INBOX_MAX_PER_USER |
Ya | Batas inbox/user (legacy multi-inbox) |
TEXT_MAX_BYTES |
Ya | Max bytes text body email |
HTML_MAX_BYTES |
Ya | Max bytes html body email |
Contoh: frontend/.env.example
| Variable | Wajib | Keterangan |
|---|---|---|
VITE_API_BASE |
Ya | Base API (default /api) |
backend/wrangler.toml.example: template aman untuk commit.backend/wrangler.toml: file lokal aktual (sudah di-ignore).backend/.envdanfrontend/.env: file lokal (sudah di-ignore).
- Node.js + npm
- Cloudflare account + Wrangler login (
npx wrangler login)
npm install
npm --prefix backend install
npm --prefix frontend installcd backend
npm run d1:create
npm run d1:migrate:localcd backend
npm run devcd frontend
# optional jika worker dev bukan default
# export WORKER_DEV_URL=http://127.0.0.1:8787
npm run devCatatan:
frontend/vite.config.jssudah memproxy/apike Worker dev (WORKER_DEV_URLatau default127.0.0.1:8787).
- Copy
backend/wrangler.toml.example->backend/wrangler.toml. - Isi
name,vars, dan bindingd1_databases. - Jalankan migration remote:
cd backend
npm run d1:migrate:remote- Deploy worker:
cd backend
npm run deploy- Build frontend:
cd frontend
npm run build- Deploy ke Pages:
cd frontend
npm run deploy- Pastikan domain Pages yang dipakai cocok dengan
FRONTEND_ORIGINdi backend.
- Buka domain di Cloudflare Dashboard.
- Aktifkan Email Routing.
- Buat route:
*@your-domain.com
- Destination:
- Worker
mailcf-worker(atau nama worker kamu).
- Worker
- Kirim email test ke inbox yang terdaftar.
- Password hash: PBKDF2-HMAC-SHA-256 + random salt.
- Session:
- cookie
__Host-session(HttpOnly,Secure,SameSite=None,Path=/) - yang disimpan di DB hanya hash token.
- cookie
- CORS terbatas ke origin frontend.
- Validasi input via
zod. - Rate limit:
- auth login/register per IP
- create inbox per user
- Sanitasi HTML email untuk cegah XSS.
- Tombol copy menampilkan toast sukses/gagal.
- Konfirmasi delete message dan logout memakai modal custom (
ConfirmModal). - Mobile action button sudah icon-first.
npm run dev:backend
npm run dev:frontend
npm run build:backend
npm run build:frontend
npm run deploy:backend
npm run deploy:frontend- Cek
FRONTEND_ORIGINdanFRONTEND_ORIGIN_DEV. - Pastikan request frontend
credentials: include(sudah default di app). - Pastikan HTTPS di production.
- Username sudah ada pada domain terpilih.
- Cek route Email Routing.
- Pastikan recipient memang inbox yang ada di DB.
- Cek logs worker (
wrangler tail).
- Samakan domain frontend dengan env backend:
FRONTEND_ORIGINFRONTEND_ORIGIN_DEV
cd backend
npx wrangler tailcd backend
npx wrangler d1 execute DB --remote --command "SELECT id,email,created_at FROM users ORDER BY created_at DESC LIMIT 10;"Internal project / private use.