Files
Mebbling/README.md
T

159 lines
9.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 重複;提供時間範圍標籤雲、草稿自動儲存與預覽。
- 遠端附件預設直連;來源可選擇只快取圖片或完整快取,並受每來源配額限制。
## 快速啟動(WSLDocker
```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 # WebWorker 共用映像檔
├── 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,否則仍會由定期校正同步補回資料,但不會即時更新。