159 lines
9.2 KiB
Markdown
159 lines
9.2 KiB
Markdown
# Mebbling
|
||
|
||
自架的 Memos 公開貼文 Hub。將朋友各自 Memos 中的公開貼文集中展示,同時保留 Hub 內的留言、表情回應與發文功能。
|
||
|
||
目前開發版本:`v0.8.0`(尚未發布)。版本變更請見 [CHANGELOG.md](CHANGELOG.md)。
|
||
|
||
## 功能
|
||
|
||
- 匯入多個 Memos 來源的 `PUBLIC` 貼文、標籤與附件。
|
||
- 圖片在首頁與文章頁直接顯示;點擊後以站內全螢幕燈箱檢視。
|
||
- Hub 使用者可留言、表情回應,並可將新貼文推送到已連接的 Memos。
|
||
- 同一個「Memos 網址 + Memos 帳號」只建立一個共享來源,避免重複同步及重複貼文。
|
||
- 使用者可加入共享來源並手動同步或發文;僅來源建立者能管理 webhook URL。
|
||
- Webhook URL 採不可猜測的隨機密鑰路徑、雜湊保存與簡易速率限制。
|
||
- 控制台會顯示最近一次收到 webhook 的時間及最後同步時間。
|
||
- 來源建立者可重新命名、停用、刪除或轉移所有權;共享成員可自行離開來源。
|
||
- 同步工作具去重、重試、觸發來源與歷史紀錄;管理員可集中檢視異常。
|
||
- 內建 SQLite 與附件備份腳本,以及可追蹤的 schema migration。
|
||
- 可依內容、標籤、來源、作者、日期與附件篩選公開貼文,並支援分頁、標籤/來源頁、RSS 與 Atom。
|
||
- 提供安全 Markdown、程式碼高亮、收藏、稍後閱讀、閱讀紀錄與互動通知。
|
||
- 每個來源可設定標籤、日期與附件類型同步規則,並在貼文頁保留可回到原始 Memos 貼文的連結。
|
||
- 控制台可測試 Token/Memos 連線、顯示遠端名稱與頭像,並提示 webhook 長時間未收到事件的狀態。
|
||
- 同源請求保護、SQLite 共用登入/webhook 限流、附件白名單與可選掃毒服務。
|
||
- 管理員可審核檢舉、隱藏貼文、停權帳號與協助重設密碼;提供健康檢查與 JSON 結構化日誌。
|
||
- 文章以底部標籤為主,避免內文 hashtag 重複;提供時間範圍標籤雲、草稿自動儲存與預覽。
|
||
- 遠端附件預設直連;來源可選擇只快取圖片或完整快取,並受每來源配額限制。
|
||
|
||
## 快速啟動(WSL/Docker)
|
||
|
||
```bash
|
||
cp .env.example .env
|
||
# 編輯 .env,至少設定 SESSION_SECRET、TOKEN_ENCRYPTION_KEY、ADMIN_USERNAME、ADMIN_PASSWORD
|
||
docker compose up --build -d
|
||
```
|
||
|
||
預設網址為 [http://localhost:8088](http://localhost:8088)。停止服務:
|
||
|
||
```bash
|
||
docker compose down
|
||
```
|
||
|
||
查看服務狀態與日誌:
|
||
|
||
```bash
|
||
docker compose ps
|
||
docker compose logs -f web worker
|
||
```
|
||
|
||
部署後可執行公開端點冒煙測試(若 metrics 有設 token,先匯出同一個 `METRICS_TOKEN`):
|
||
|
||
```bash
|
||
./scripts/smoke-test.sh https://你的網域
|
||
```
|
||
|
||
## 環境變數
|
||
|
||
以 `.env.example` 為範本。正式環境請更換所有 secret,且不要將 `.env` 加入 Git。
|
||
|
||
| 變數 | 用途 |
|
||
| --- | --- |
|
||
| `SESSION_SECRET` | 登入 session 的簽章密鑰。請使用長隨機字串。 |
|
||
| `TOKEN_ENCRYPTION_KEY` | Memos Token 的 AES-256-GCM 加密金鑰,必須為 64 個十六進位字元。 |
|
||
| `ADMIN_USERNAME` / `ADMIN_PASSWORD` | 首次啟動時建立的管理員帳號。 |
|
||
| `NEXT_PUBLIC_APP_URL` | Hub 的對外 HTTPS 網址,例如 `https://mebbling.example.com`。Webhook URL 以此組成。 |
|
||
| `UPLOAD_MAX_BYTES` | Hub 發文上傳附件的單檔上限,預設 10 MiB。 |
|
||
| `UPLOAD_ALLOWED_TYPES` | 逗號分隔的 Hub 附件 MIME 白名單。 |
|
||
| `VIRUS_SCAN_URL` / `VIRUS_SCAN_REQUIRED` | 選用的 HTTP 掃毒服務;服務需回傳 `{ "clean": true }`。若 required 為 `1`,掃毒不可用時拒絕上傳。 |
|
||
| `SYNC_INTERVAL_MINUTES` | 背景校正同步的間隔,預設 60 分鐘。 |
|
||
| `ALERT_WEBHOOK_URL` | 選填的 HTTPS Discord webhook 或 ntfy topic;同步及 webhook 異常會保存後投遞,失敗最多重試 5 次。 |
|
||
| `METRICS_TOKEN` | 選填的 `/api/metrics` Bearer Token;未設定時務必由網路/反向代理限制存取。 |
|
||
| `SEED_MEMOS_*` | 選填;首次啟動時自動建立管理員的第一個 Memos 來源。 |
|
||
|
||
## 系統架構
|
||
|
||
```mermaid
|
||
flowchart LR
|
||
Visitor[訪客/Hub 使用者] --> Web[Next.js Web\nport 8088]
|
||
Web --> DB[(SQLite\ndata/hub.db)]
|
||
Web --> Uploads[附件\npublic/uploads]
|
||
Memos[Memos 來源] -->|Webhook| Web
|
||
Web -->|建立同步工作| Jobs[同步佇列\nsync_jobs]
|
||
Worker[背景 Worker] --> Jobs
|
||
Worker --> DB
|
||
Worker <-->|Memos API| Memos
|
||
Worker --> Uploads
|
||
```
|
||
|
||
Web 接收使用者操作和 webhook,將同步需求寫入 SQLite 的 `sync_jobs`。背景 Worker 每 5 秒處理一項工作,負責:
|
||
|
||
- **Pull**:從 Memos 取得公開貼文,更新 Hub 鏡像;已刪除或非公開的遠端貼文會在 Hub 隱藏。
|
||
- **Push**:把 Hub 建立的貼文與本機附件上傳/回寫到選定的 Memos 來源。
|
||
- **排程校正**:依 `SYNC_INTERVAL_MINUTES` 定期建立 Pull 工作,避免 webhook 遺漏造成資料不同步。
|
||
|
||
在來源管理中設定的同步規則會套用到 Pull:多個標籤採「同時符合」篩選,日期以 Memos 貼文建立日為準;附件可選擇全部保留、只保留圖片,或不同步附件。貼文後續在遠端被修改、刪除、改為非公開或不再符合規則時,下一次 Pull 會更新或隱藏 Hub 鏡像。
|
||
|
||
## 正式營運與監控
|
||
|
||
- `GET /api/health`:供反向代理或監控工具檢查服務與 SQLite 狀態,也會回傳失敗同步工作數與版本。
|
||
- Web、Worker 的事件輸出為 JSON;同步錯誤同時保存於管理頁的「最近系統錯誤」。告警 webhook 會以 SQLite 佇列投遞、指數退避重試五次,並以 `mebbling_alert_deliveries` metrics 暴露狀態。
|
||
- 登入在 15 分鐘內最多嘗試 8 次;webhook 與登入限流資料存於 SQLite,同一份資料庫的多個 Web 容器會共用計數。
|
||
- 所有會改變帳號或內容的瀏覽器 POST 都檢查 `Origin`,Webhook 則使用密鑰 URL 驗證,不適用此規則。
|
||
- Gitea Actions 工作流程會在推送/標籤時執行型別檢查、測試與 Docker 建置;若設定 `DEPLOY_WEBHOOK_URL` secret,建立 `v*` tag 時會通知部署端。
|
||
- 工作流程也會對 production dependencies 執行高風險漏洞檢查,並產生 SPDX SBOM artifact;發布、升級與日後映像簽章的流程見 [發布文件](docs/RELEASING.md)。
|
||
|
||
## Webhook 設定與驗證
|
||
|
||
1. 來源建立者登入「控制台」。
|
||
2. 在來源卡片按「產生 webhook URL」,立即複製完整網址。
|
||
3. 在該 Memos 帳號的 webhook 設定中貼上網址。
|
||
4. 在 Memos 發布或更新一篇公開貼文。
|
||
5. 回到 Hub:顯示「最近收到」代表 Hub 確實收到 webhook;「上次同步」更新則代表同步已完成。
|
||
|
||
若 webhook 已設定但超過 7 天未收到事件,控制台會顯示提醒;這不會中斷定期校正同步。來源建立者也可按「測試 Memos 連線」檢查 Token 是否有效,同時更新遠端顯示名稱與頭像。
|
||
|
||
網址格式如下;`來源 ID` 與 `隨機密鑰` 都由系統產生,請勿自行修改:
|
||
|
||
```text
|
||
https://你的網域/api/sync/webhook/來源ID/隨機密鑰
|
||
```
|
||
|
||
重新產生 webhook URL 會立即使舊 URL 失效。共享來源的其他成員可查看接收狀態,但沒有產生或輪替密鑰的權限。
|
||
|
||
## 專案結構
|
||
|
||
```text
|
||
.
|
||
├── app/ # Next.js App Router:頁面、元件與 API
|
||
│ ├── api/ # 登入、來源、發文、同步、webhook 等路由
|
||
│ ├── components/ # 可重用 UI,例如附件圖片燈箱
|
||
│ ├── dashboard/ # 來源管理、發文與 webhook 控制台
|
||
│ ├── posts/[id]/ # 單篇貼文頁
|
||
│ ├── page.tsx # 公開貼文首頁
|
||
│ ├── layout.tsx # 全站版型與導覽
|
||
│ └── styles.css # 全站樣式
|
||
├── lib/ # 共用伺服器邏輯
|
||
│ ├── auth.ts # Session 與權限
|
||
│ ├── crypto.ts # Token 加解密
|
||
│ ├── db.ts # SQLite schema 與輕量遷移
|
||
│ ├── memos.ts # Memos API 封裝
|
||
│ ├── webhook.ts # Webhook 密鑰產生、雜湊與驗證
|
||
│ └── rate-limit.ts # Webhook 簡易速率限制
|
||
├── worker/ # 背景同步 worker
|
||
├── public/uploads/ # Hub 上傳附件的持久化資料
|
||
├── data/ # SQLite 資料庫持久化資料
|
||
├── Dockerfile # Web/Worker 共用映像檔
|
||
├── docker-compose.yml # web + worker 服務與 volume 掛載
|
||
├── scripts/ # 備份、驗證、還原與效能基準工具
|
||
├── docs/ # 維運與擴展文件
|
||
└── .env.example # 環境變數範本
|
||
```
|
||
|
||
`data/` 與 `public/uploads/` 是正式資料,備份時請一併備份。`.next/` 與 `node_modules/` 是可重新產生的建置/依賴資料,不需備份。
|
||
|
||
備份、還原及資料庫 migration 的操作請見 [維運文件](docs/OPERATIONS.md)。
|
||
|
||
## 正式部署
|
||
|
||
將 `NEXT_PUBLIC_APP_URL` 設成實際 HTTPS 網域,並以反向代理將該網域導向 Web 容器的 3000 連接埠(或主機的 8088 對應埠)。務必確保外部可連到 webhook URL,否則仍會由定期校正同步補回資料,但不會即時更新。
|