mirror of
https://github.com/mskatoni/ni-mail.git
synced 2026-09-05 07:26:46 +08:00
Add files via upload
This commit is contained in:
@@ -2,16 +2,18 @@
|
|||||||
|
|
||||||
一個極簡的 Cloudflare Worker,用於接收私人域名郵件並提供 HTTP API 讀取。
|
一個極簡的 Cloudflare Worker,用於接收私人域名郵件並提供 HTTP API 讀取。
|
||||||
|
|
||||||
無需資料庫、無需前端、無需 JWT,部署後即可通過 API 取得最新郵件內容。
|
無需前端、無需 JWT,部署後即可通過 API 取得最新郵件內容。
|
||||||
|
|
||||||
## 特性
|
## 特性
|
||||||
|
|
||||||
- 📨 通過 Cloudflare Email Routing 接收郵件
|
- 📨 通過 Cloudflare Email Routing 接收郵件
|
||||||
- 🗄️ 使用 KV 儲存,最多保留 50 封
|
- 🗄️ 使用 Durable Objects + SQLite 儲存郵件 metadata,使用 R2 儲存附件內容
|
||||||
- 🔑 API Key 鑑權
|
- 🔑 API Key 鑑權
|
||||||
- 🌐 支持多個自定義域名(域名需托管在 Cloudflare)
|
- 🌐 支持多個自定義域名(域名需托管在 Cloudflare)
|
||||||
- 📦 僅依賴 `postal-mime`,無其他依賴
|
- 📦 保留原版極簡 API:`/latest`、`/mails`、`/mail/:id`
|
||||||
- 🚫 附件只保留 metadata,不存 base64,避免撞 KV 25MB 限制
|
- 🧵 支持 threaded inbox、thread detail、附件下載
|
||||||
|
- ✉️ 可選支持 `send / reply / forward`(配置 `EMAIL` binding 後啟用)
|
||||||
|
- 🚫 未配置 `EMAIL` binding 時,收信與讀信 API 仍可正常工作
|
||||||
|
|
||||||
## 工作原理
|
## 工作原理
|
||||||
|
|
||||||
@@ -19,18 +21,21 @@
|
|||||||
外部郵件 → 你的域名 MX(Cloudflare Email Routing)
|
外部郵件 → 你的域名 MX(Cloudflare Email Routing)
|
||||||
↓ catch-all 轉發
|
↓ catch-all 轉發
|
||||||
Cloudflare Worker(收信 + HTTP API)
|
Cloudflare Worker(收信 + HTTP API)
|
||||||
↓ 存儲
|
↓
|
||||||
Cloudflare KV(最近 50 封)
|
Mailbox Durable Object(每個 mailbox 一個)
|
||||||
↓ 讀取
|
↓
|
||||||
|
SQLite(郵件 metadata / thread) + R2(附件)
|
||||||
|
↓
|
||||||
curl /latest → 自動化腳本
|
curl /latest → 自動化腳本
|
||||||
```
|
```
|
||||||
|
|
||||||
郵件到達後由 `postal-mime` 解析為結構化 JSON,通過帶鑑權的 HTTP API 按需讀取,無需輪詢、無需訂閱。
|
郵件到達後由 `postal-mime` 解析為結構化 JSON,通過帶鑑權的 HTTP API 按需讀取。
|
||||||
|
|
||||||
## 前置條件
|
## 前置條件
|
||||||
|
|
||||||
- 域名已托管在 Cloudflare
|
- 域名已托管在 Cloudflare
|
||||||
- 已啟用 Cloudflare Email Routing
|
- 已啟用 Cloudflare Email Routing
|
||||||
|
- Node.js 20 或更高版本
|
||||||
|
|
||||||
> ⚠️ 收信地址必須是托管在 Cloudflare 的真實域名(如 `user@yourdomain.com`),
|
> ⚠️ 收信地址必須是托管在 Cloudflare 的真實域名(如 `user@yourdomain.com`),
|
||||||
> `*.workers.dev` 不支持 Email Routing,發往 workers.dev 地址的信不會被收到。
|
> `*.workers.dev` 不支持 Email Routing,發往 workers.dev 地址的信不會被收到。
|
||||||
@@ -39,109 +44,134 @@
|
|||||||
|
|
||||||
### 方式一:Cloudflare 一鍵部署
|
### 方式一:Cloudflare 一鍵部署
|
||||||
|
|
||||||
[](https://deploy.workers.cloudflare.com/?url=https://github.com/mskatoni/ni-mail)
|
上傳到你自己的 GitHub 倉庫後,將下方按鈕中的 `<YOUR_USERNAME>/<YOUR_REPO>` 換成實際倉庫地址即可:
|
||||||
|
|
||||||
點擊按鈕後,Cloudflare 會自動 Fork 此 repo 並完成代碼部署。
|
```markdown
|
||||||
|
[](https://deploy.workers.cloudflare.com/?url=https://github.com/<YOUR_USERNAME>/<YOUR_REPO>)
|
||||||
|
```
|
||||||
|
|
||||||
|
Cloudflare Builds 建議配置:
|
||||||
|
|
||||||
|
- Root directory:留空或 `/`
|
||||||
|
- Build command:留空
|
||||||
|
- Deploy command:`npm run deploy`
|
||||||
|
|
||||||
部署完成後,還需在控制台完成以下配置:
|
部署完成後,還需在控制台完成以下配置:
|
||||||
|
|
||||||
**1. 綁定 KV Namespace**
|
**1. 設定 AUTH_KEY**
|
||||||
|
|
||||||
Cloudflare 控制台 → Workers & Pages → `ni-mail` → Settings → Bindings → 新增 KV Namespace:
|
|
||||||
- 名稱:`MAIL_KV`
|
|
||||||
- 選擇已建立的 KV namespace(若尚未建立,先到 Workers & Pages → KV → Create namespace)
|
|
||||||
|
|
||||||
**2. 設定 AUTH_KEY**
|
|
||||||
|
|
||||||
Settings → Variables and Secrets → 新增:
|
Settings → Variables and Secrets → 新增:
|
||||||
|
|
||||||
- 類型:**Secret(密鑰)**,不要選 Text(明文可見)
|
- 類型:**Secret(密鑰)**,不要選 Text(明文可見)
|
||||||
- 變數名稱:`AUTH_KEY`
|
- 變數名稱:`AUTH_KEY`
|
||||||
- 值:自訂一個密碼
|
- 值:自訂一個密碼
|
||||||
|
|
||||||
儲存後點 **Deploy** 讓設定生效。
|
儲存後點 **Deploy** 讓設定生效。
|
||||||
|
|
||||||
|
**2. 設定可收信域名**
|
||||||
|
|
||||||
|
在 `wrangler.jsonc` 中設定你的域名:
|
||||||
|
|
||||||
|
```jsonc
|
||||||
|
{
|
||||||
|
"vars": {
|
||||||
|
"DOMAINS": "example.com,example.net"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
公開模板預設留空,方便任何 Cloudflare 帳號先完成第一次部署。
|
||||||
|
|
||||||
**3. 設定 Email Routing**
|
**3. 設定 Email Routing**
|
||||||
|
|
||||||
Cloudflare 控制台 → 你的域名 → Email → Email Routing → Routing rules → Catch-all:
|
Cloudflare 控制台 → 你的域名 → Email → Email Routing → Routing rules → Catch-all:
|
||||||
|
|
||||||
- Action:Send to Worker
|
- Action:Send to Worker
|
||||||
- 選擇 `ni-mail`
|
- 選擇 `ni-mail`
|
||||||
|
|
||||||
|
**4. (可選)啟用發信功能**
|
||||||
|
|
||||||
|
若你需要使用發信 / 回覆 / 轉發接口,請為 Worker 新增一個名為 `EMAIL` 的 `send_email` binding。
|
||||||
|
|
||||||
|
未配置 `EMAIL` binding 時,收信和讀信 API 仍可正常工作,只是發信接口會回 `501`。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 方式二:本地 CLI 部署
|
### 方式二:本地 CLI 部署
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/mskatoni/ni-mail.git
|
git clone https://github.com/<YOUR_USERNAME>/<YOUR_REPO>.git
|
||||||
cd ni-mail
|
cd ni-mail
|
||||||
npm install
|
npm ci
|
||||||
|
npm run check:deploy
|
||||||
# 建立 KV Namespace
|
npm exec -- wrangler secret put AUTH_KEY
|
||||||
wrangler kv:namespace create MAIL_KV
|
npm run deploy
|
||||||
```
|
```
|
||||||
|
|
||||||
複製輸出的 ID,在 Cloudflare 控制台綁定,或直接加進 `wrangler.toml`:
|
如果你是本地部署,通常只需要:
|
||||||
|
|
||||||
```toml
|
1. 修改 `wrangler.jsonc` 中的:
|
||||||
[[kv_namespaces]]
|
- `name`
|
||||||
binding = "MAIL_KV"
|
- `vars.DOMAINS`
|
||||||
id = "你的 KV ID"
|
- `r2_buckets[*].bucket_name`
|
||||||
```
|
2. 在 Cloudflare 後台設置 `AUTH_KEY`
|
||||||
|
3. 配置 Email Routing 的 Send to Worker 規則
|
||||||
```bash
|
|
||||||
wrangler deploy
|
|
||||||
```
|
|
||||||
|
|
||||||
## 自定義域名(可選)
|
## 自定義域名(可選)
|
||||||
|
|
||||||
> 域名必須已托管在 Cloudflare,無需手動建立 DNS 記錄,Cloudflare 會自動處理並簽發 SSL。
|
> 域名必須已托管在 Cloudflare,無需手動建立 DNS 記錄,Cloudflare 會自動處理並簽發 SSL。
|
||||||
|
|
||||||
在 `wrangler.toml` 中取消注釋,支持多個:
|
首次部署成功後,可以在 Cloudflare 控制台為 Worker 添加 Custom Domain。
|
||||||
|
|
||||||
```toml
|
如需用 Wrangler 管理,在 `wrangler.jsonc` 中添加你自己的域名:
|
||||||
[[routes]]
|
|
||||||
pattern = "mail.domain-a.com"
|
|
||||||
custom_domain = true
|
|
||||||
|
|
||||||
[[routes]]
|
```jsonc
|
||||||
pattern = "mail.domain-b.com"
|
{
|
||||||
custom_domain = true
|
"routes": [
|
||||||
|
{
|
||||||
|
"pattern": "mail.example.com",
|
||||||
|
"custom_domain": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
重新部署後即可通過自定義域名訪問 API。多個域名收到的郵件共用同一個 inbox,`to` 欄位可用於區分來源域名。
|
不要把不屬於部署者 Cloudflare 帳號的域名提交到公開模板中。
|
||||||
|
|
||||||
## API
|
## API
|
||||||
|
|
||||||
所有請求需帶上 Header:`X-Auth-Key: 你的密碼`
|
所有請求需帶上 Header:`X-Auth-Key: 你的密碼`
|
||||||
|
|
||||||
|
### 與原版兼容的路由
|
||||||
|
|
||||||
| 方法 | 路徑 | 說明 |
|
| 方法 | 路徑 | 說明 |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| GET | `/latest` | 取得最新一封完整郵件 |
|
| GET | `/latest?mailbox=user@example.com` | 取得最新一封完整郵件 |
|
||||||
| GET | `/mails?limit=10` | 取得最近 N 封郵件列表(不含正文) |
|
| GET | `/mails?mailbox=user@example.com&limit=10` | 取得最近 N 封郵件列表(不含正文) |
|
||||||
| GET | `/mail/:id` | 取得單封完整郵件(含 html/text) |
|
| GET | `/mail/:id?mailbox=user@example.com` | 取得單封完整郵件(含 html/text) |
|
||||||
| DELETE | `/mails` | 清空收件匣 |
|
| DELETE | `/mails?mailbox=user@example.com&folder=inbox` | 清空收件匣 |
|
||||||
|
|
||||||
|
### 新增路由
|
||||||
|
|
||||||
|
| 方法 | 路徑 | 說明 |
|
||||||
|
|---|---|---|
|
||||||
|
| GET | `/api/mailboxes/:mailboxId/latest` | 讀取指定 mailbox 最新郵件 |
|
||||||
|
| GET | `/api/mailboxes/:mailboxId/emails` | 列表 / 搜索 / 排序 |
|
||||||
|
| GET | `/api/mailboxes/:mailboxId/emails/:id` | 讀取單封郵件 |
|
||||||
|
| POST | `/api/mailboxes/:mailboxId/emails/:id/read` | 標記已讀 |
|
||||||
|
| GET | `/api/mailboxes/:mailboxId/threads/:threadId` | 讀取整個 thread |
|
||||||
|
| GET | `/api/attachments/:attachmentId?mailbox=user@example.com` | 下載附件 |
|
||||||
|
| POST | `/api/mailboxes/:mailboxId/send` | 發新郵件(需 `EMAIL` binding) |
|
||||||
|
| POST | `/api/mailboxes/:mailboxId/reply` | 回覆郵件(需 `EMAIL` binding) |
|
||||||
|
| POST | `/api/mailboxes/:mailboxId/forward` | 轉發郵件(需 `EMAIL` binding) |
|
||||||
|
|
||||||
**範例**
|
**範例**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl https://your-worker.workers.dev/latest \
|
curl "https://your-worker.workers.dev/latest?mailbox=user@example.com" \
|
||||||
-H "X-Auth-Key: 你的密碼"
|
-H "X-Auth-Key: 你的密碼"
|
||||||
```
|
```
|
||||||
|
|
||||||
**成功回應(有郵件)**
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"id": "7eb63a8d-1195-4124-9eb3-fb4c2673e90c",
|
|
||||||
"receivedAt": "2026-03-22T10:28:01.506Z",
|
|
||||||
"from": "[email protected]",
|
|
||||||
"to": "[email protected]",
|
|
||||||
"subject": "beta",
|
|
||||||
"text": "beta\n\n",
|
|
||||||
"html": "<div dir=\"ltr\">beta</div>\n\n",
|
|
||||||
"attachments": []
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**無郵件時(HTTP 404)**
|
**無郵件時(HTTP 404)**
|
||||||
|
|
||||||
```json
|
```json
|
||||||
@@ -154,19 +184,28 @@ curl https://your-worker.workers.dev/latest \
|
|||||||
{ "error": "unauthorized" }
|
{ "error": "unauthorized" }
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**未配置發信 binding 而調用發信接口時(HTTP 501)**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{ "error": "outbound email is not configured; add an EMAIL send_email binding after deployment" }
|
||||||
|
```
|
||||||
|
|
||||||
## 常見問題
|
## 常見問題
|
||||||
|
|
||||||
### error code: 1101
|
### error code: 1101
|
||||||
|
|
||||||
Worker 運行時拋出未捕獲異常,最常見原因是 **KV Namespace 沒有正確綁定**。
|
Worker 運行時拋出未捕獲異常,常見原因包括:
|
||||||
|
|
||||||
確認步驟:控制台 → Workers & Pages → `ni-mail` → Settings → Bindings,確認有一條:
|
- `AUTH_KEY` 沒有設置
|
||||||
|
- Email Routing 沒有正確指到這個 Worker
|
||||||
|
- `DOMAINS` 沒有包含當前 mailbox 的域名
|
||||||
|
- R2 / Durable Object 綁定不完整
|
||||||
|
|
||||||
| 類型 | 名稱 | 值 |
|
先確認:
|
||||||
|---|---|---|
|
|
||||||
| KV Namespace | `MAIL_KV` | 你建立的 namespace |
|
|
||||||
|
|
||||||
如果是空的,重新新增並點 **Save** 後重新部署即可。
|
- Worker 已成功部署
|
||||||
|
- `wrangler.jsonc` 中的 `MAILBOX` 與 `BUCKET` binding 存在
|
||||||
|
- 控制台裡 `AUTH_KEY` 已設為 Secret
|
||||||
|
|
||||||
### AUTH_KEY 建議使用 Secret 而非 Text
|
### AUTH_KEY 建議使用 Secret 而非 Text
|
||||||
|
|
||||||
@@ -175,15 +214,6 @@ Settings → Variables and Secrets 新增 `AUTH_KEY` 時,類型請選 **Secret
|
|||||||
- **Secret**:值加密儲存,部署後不可見,適合密碼類資訊
|
- **Secret**:值加密儲存,部署後不可見,適合密碼類資訊
|
||||||
- **Text**:明文儲存,任何有控制台權限的人都能看到
|
- **Text**:明文儲存,任何有控制台權限的人都能看到
|
||||||
|
|
||||||
## Star History
|
|
||||||
|
|
||||||
[](https://star-history.com/#mskatoni/ni-mail&Date)
|
|
||||||
|
|
||||||
## 社區
|
|
||||||
|
|
||||||
<a href="https://v2ex.com"><img src="https://user-images.githubusercontent.com/80169337/122051970-cd075b80-ce02-11eb-9653-0b8702377727.png" width="24" height="24" alt="V2EX" /></a>
|
|
||||||
<a href="https://www.nodeseek.com/post-659586-1"><img src="https://github.com/user-attachments/assets/0c6db696-769c-4d79-997c-9bc014cc6895" width="24" height="24" alt="NodeSeek" /></a>
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Apache License 2.0
|
Apache License 2.0
|
||||||
|
|||||||
Reference in New Issue
Block a user