From 676c711e7a73241d12fe2f4ff649b03011cf5e6b Mon Sep 17 00:00:00 2001 From: ogt Date: Sun, 19 Apr 2026 02:03:45 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20AI=20=E6=B2=BB=E7=90=86=E5=AE=8C?= =?UTF-8?q?=E5=82=99=20V10.3=20=E2=80=94=20=E6=8A=80=E8=A1=93=E5=82=B5?= =?UTF-8?q?=E6=B8=85=E9=9B=B6=20+=20DB=20=E5=82=99=E4=BB=BD=E6=A9=9F?= =?UTF-8?q?=E5=88=B6=20+=20=E5=82=99=E4=BB=BD=20AI=20=E7=9B=A3=E6=8E=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 技術債清零 (2026-04-19): - migrations/010: ai_insights 補 decay_exempt/avg_quality/status/ai_model/feedback 欄位 - migrations/011: embedding_retry_queue 持久化表 (ADR-009) - migrations/012: backup_log 備份記錄表 - services/openclaw_learning_service: 記憶體 Queue → DB retry queue,時間衰減 RAG - services/nemoton_dispatcher_service: 三個 tool 強制雙寫 ai_insights (_sink_insight_to_km) - services/import_service: Excel 前置欄位防禦(商品名稱類 + 業績金額類) - services/ollama_service: generate_embedding 新增 EMBEDDING_HOST env,embedding 永遠走 192.168.0.111 - SYSTEM_VERSION: V9.4 → V10.3 DB 備份機制: - scripts/pg_backup.sh: host-level pg_dump 備份腳本,cron 每日 02:00,保留 7 天,Telegram 通知 - services/db_backup_service.py: Python 備份 service,寫入 backup_log - scheduler: run_db_backup_task (02:00) + run_backup_monitor_task (每 6h AI Agent 監控) - Dockerfile: 加入 postgresql-client 文件: - CLAUDE.md: 環境架構依 ADR-008 實地重寫,含完整 SSH/Docker 部署 SOP - PROJECT_CONSTITUTION.md: 內容已整合入 CLAUDE.md,刪除重複檔案 Co-Authored-By: Claude Sonnet 4.6 --- CLAUDE.md | 5297 +-------------------------------- CONSTITUTION.md | 15 +- Dockerfile | 1 + PROJECT_CONSTITUTION.md | 599 ---- migrations/012_backup_log.sql | 19 + run_scheduler.py | 8 + scheduler.py | 153 + scripts/pg_backup.sh | 72 + services/db_backup_service.py | 187 ++ services/ollama_service.py | 27 +- 10 files changed, 494 insertions(+), 5884 deletions(-) delete mode 100644 PROJECT_CONSTITUTION.md create mode 100644 migrations/012_backup_log.sql create mode 100644 scripts/pg_backup.sh create mode 100644 services/db_backup_service.py diff --git a/CLAUDE.md b/CLAUDE.md index bbabe1b..937cb95 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,5283 +1,34 @@ -# EwoooC — Claude 專案記憶(原 MOMO Pro System) +# EwoooC (MOMO Pro System) — 核心索引 -> 此檔案供 Claude Code 自動讀取,記錄專案的關鍵資訊與待辦事項。 -> **專案正式名稱**:EwoooC(ADR-006,2026-04-18 決策) -> **範圍**:本目錄唯一範圍為 `momo-pro-system`,**不含 AWOOOI / WOOO AIOps SaaS**。 +> **版本**: V10.3 | **目標**: AI 驅動 MOMO 商品監控、業績分析與策略自動化 ---- +## 治理 +- **憲法**: [CONSTITUTION.md](CONSTITUTION.md) — 所有開發必須遵守 +- **ADR**: [docs/adr/](docs/adr/) +- **AI 策略師**: `services/openclaw_strategist_service.py` (Gemini 2.0) -## 🏛️ 第零章:遊戲規則憲法(Claude Code 官方規範 — 2026-04-18 訂立) - -> **這是本系統的元憲法。所有 AI 助手(Claude / Antigravity)在任何 Session 開始前必須先讀此章。** - -### 📐 記憶架構分層(四層缺一不可) - -| 層次 | 位置 | 用途 | 生命週期 | -|---|---|---|---| -| **Memory(持久記憶)** | `~/.claude/projects/.../memory/*.md` | 統帥偏好、技術決策、協作 context | 永久,跨 Session | -| **ADR(架構決策記錄)** | `docs/adr/ADR-XXX-*.md` | 重大架構決策,含背景/替代方案/後果 | 永久,只增不刪 | -| **SOT(單一事實來源)** | `docs/AI_INTELLIGENCE_MODULE_SOT.md` | 當前 AI 模組架構狀態(可更新) | 隨架構演進 | -| **Skills(執行流程)** | `.claude/skills/*.py` | Claude 執行複雜任務的 SOP checklist | 隨流程成熟迭代 | - -### 📋 失憶防範三大鐵律(絕對禁止違反) - -#### 鐵律一:每次決策必沉澱 -- ✅ 任何架構決策 → 立刻建 `ADR-XXX.md` + 更新 `docs/adr/README.md` -- ✅ 任何統帥偏好/風格 → 立刻寫入 `memory/user_profile.md` 或新 memory 文件 -- ✅ 任何技術債/待辦 → 立刻寫入 `memory/project_tech_debt_backlog.md` -- ❌ **禁止**:口頭決定不留文字記錄(下個 Session 必失憶) - -#### 鐵律二:CLAUDE.md 是憲法主幹 -- ✅ 每次 Session 開始,先讀 CLAUDE.md → memory/ → 相關 ADR -- ✅ 重大架構章節必須同步更新到 CLAUDE.md(SOT 章節) -- ✅ 新憲法條款必須更新 CONSTITUTION.md -- ❌ **禁止**:依賴 Claude 對話 Session 記憶(context window 有截斷) - -#### 鐵律三:範圍邊界 -- ✅ 本 CLAUDE.md 範圍 = **Momo Pro System(EwoooC)唯一** -- ❌ **禁止**:將 AWOOOI / WOOO AIOps SaaS 的決策混入本文件 -- ❌ **禁止**:跨專案邊界做架構決策 - -### 🔄 Session 開始 SOP(每次 Claude 啟動必做) - -``` -1. 讀 CLAUDE.md 頭部(此章 + 環境章) -2. 讀 memory/MEMORY.md 索引 → 依需求讀具體 memory -3. 讀 docs/adr/README.md 索引 → 依任務讀相關 ADR -4. 確認當前任務屬於哪個 Agent 層(Hermes/NemoTron/OpenClaw) -5. 確認任務結束後需更新哪些記憶層 -``` - -### 📝 記憶更新 SOP(每次 Session 結束前必做) - -``` -□ 有架構決策?→ 新建或更新 docs/adr/ADR-XXX.md + README.md 索引 -□ 有統帥偏好/回饋?→ 更新 memory/user_profile.md 或 feedback_*.md -□ 有技術債?→ 更新 memory/project_tech_debt_backlog.md -□ 有 SOT 變更?→ 更新 docs/AI_INTELLIGENCE_MODULE_SOT.md -□ 有憲法新條款?→ 更新 CONSTITUTION.md + 本 CLAUDE.md -□ 有技能流程變更?→ 更新 .claude/skills/ 對應 skill 文件 -□ 更新 memory/MEMORY.md 索引(加入新 memory 文件連結) -``` - ---- - -## 專案概覽 - -| 項目 | 說明 | -|-----|------| -| 專案名稱 | EwoooC(原 MOMO Pro System,WOOO TECH) | -| 技術棧 | Flask (gunicorn) + PostgreSQL (pgvector) + Docker Compose V12.0 | -| 主要功能 | 商品看板、每日業績報表、廠商缺貨通知、EDM 爬蟲、AI 知識庫(KM) | -| 正式運行主機 | **`ollama@192.168.0.188`**(Docker Compose,非 K8s) | -| 對外域名 | `mo.wooo.work` / `momo.wooo.work`(皆透過 110 VM Nginx 反向代理) | - ---- - -## 🌐 環境架構總覽(2026-04-19 依 SSH 實地審計重寫,見 ADR-008) - -> **重要**: 先前文件誤記為「K3s / kubectl / 192.168.0.110 運行應用」。實地審計 (2026-04-18) 確認 EwoooC 從未遷移到 K8s,188 始終是 **Docker Compose** 架構。本章以實際現況為準。 - -| 主機 | 角色 | 承載內容 | -|------|------|---------| -| **`192.168.0.188` (`ollama@`)** | **EwoooC 正式運行主機** | `momo-pro-system` / `momo-db` / `momo-scheduler` / `openclaw` / `n8n`(Docker) / Ollama | -| `192.168.0.110` (`wooo@`) | 周邊服務主機 | VM Nginx 反向代理、Harbor(仍在運行)、Sentry self-hosted、SigNoz、Gitea (+ runner)、Langfuse、監控 exporter | -| `192.168.0.112` (`kali@`) | 安全掃描工作站 | Nmap / Bandit / Trivy / WireGuard | - -> ⚠️ 110 的 `/home/wooo/momo_pro_system/` 實際已空置(僅剩 `docker/` + `venv/`),不是 EwoooC 的運行目錄。 -> ⚠️ 110 載載 load avg ≈ 14.5,負責 monitor.wooo.work / registry.wooo.work / Harbor / Sentry / SigNoz 等周邊堆疊。 - -### EwoooC 運行主機細節(`ollama@192.168.0.188`) - -| 項目 | 值 | -|------|----| -| **專案路徑** | `/home/ollama/momo-pro/` | -| **部署方式** | Docker Compose V12.0(無 Git 版控,純檔案系統) | -| **主容器** | `momo-pro-system` (Flask + gunicorn) / `momo-db` (pgvector/pgvector:pg14) / `momo-scheduler` / `openclaw` (clawbot-v5) | -| **容器啟動指令** | `gunicorn --bind 0.0.0.0:80 --workers 4 --timeout 300 app:app` | -| **主容器端口** | `127.0.0.1:5003:80`(gunicorn 內部聽 80,host 僅本地 5003) | -| **Volume 掛載** | `app.py` / `scheduler.py` / `config.py` / `auth.py` / `config/` / `database/` / `templates/` / `services/` / `routes/` / `data/` / `logs/` / `backups/`(皆為 host 目錄掛到容器 `/app`,修改即時生效) | -| **未掛載** | `docs/`(CLAUDE.md / ADR 純屬本地參考文件) | -| **資料庫** | `momo-db` 使用 **pgvector/pgvector:pg14**,為 AI KM 模組(ADR-002/003/007)提供 `vector(1024)` 欄位 | - -### SSH 連線 SOP(必須經 110 跳板) +## 環境 +| 主機 | IP | 角色 | +|------|----|------| +| 110 (Gateway) | `192.168.0.110` | Nginx, Gitea, n8n, Superset | +| 188 (App/AI) | `192.168.0.188` | EwoooC App, DB, Ollama | +## 常用指令 ```bash -# 先跳 110 -ssh wooo@192.168.0.110 -# 再進 188(SSH Key 已預先建立) -ssh ollama@192.168.0.188 -``` +# 本地開發 +source venv/bin/activate && python app.py -Claude 工具層可直接執行:`ssh -J wooo@192.168.0.110 ollama@192.168.0.188 ""`。 - -### Docker Compose 管理指令(在 188 上) - -```bash -cd /home/ollama/momo-pro - -# 查看所有容器 -docker ps - -# 即時日誌(Flask 主應用) -docker logs -f momo-pro-system --tail 100 - -# 重啟主應用(Python 檔案 volume 掛載已更新時使用) -docker restart momo-pro-system - -# 完全重建(新增 Python 套件/更改 Dockerfile 時) -docker compose build momo-app && docker compose up -d momo-app - -# 進入資料庫 -docker exec -it momo-db psql -U momo -d momo_analytics -``` - ---- - -## 網路架構圖(2026-04-19 實地重繪) - -``` - ┌────────────────────────────────────────────────┐ - Internet │ 110 (wooo@192.168.0.110) │ - │ │ 周邊服務主機 + VM Nginx 反向代理 │ - ▼ │ │ - ┌──────────┐ │ VM Nginx (systemd, port 80/443, SSL) │ - │ DNS │ │ ├─ mo.wooo.work ──┐ │ - │ Records │ │ ├─ momo.wooo.work ──┤ ⚠️ TODO:需統帥確認 │ - └────┬─────┘ │ ├─ registry.wooo.work ─┤ 這兩個域名目前 │ - │ │ ├─ monitor.wooo.work │ 實際 upstream 為何?│ - ▼ │ └─ ollama.wooo.work ─┘ 檔案 /etc/nginx/ │ - ┌──────────┐ │ sites-enabled/momo│ - │DNS → 110 │ │ (110 本地的周邊容器:) │ - └────┬─────┘ │ Harbor (11 個 harbor-* 容器,仍在運行) │ - │ │ Sentry self-hosted (30+ 容器) │ - │ │ SigNoz / Gitea + runner / Langfuse │ - │ │ Docker Registry / Superset │ - │ │ Prometheus exporters / Fail2Ban exporter │ - │ (VM Nginx 反向代理) └────────────────────────────┬───────────────────┘ - │ │ (proxy_pass) - ▼ ▼ - ┌────────────────────────────────────────────────────────────────────┐ - │ 188 (ollama@192.168.0.188) EwoooC 正式運行主機 │ - │ /home/ollama/momo-pro/ (Docker Compose V12.0) │ - ├────────────────────────────────────────────────────────────────────┤ - │ │ - │ ┌────────────────────┐ ┌───────────────────┐ │ - │ │ momo-pro-system │──▶│ momo-db │ │ - │ │ Flask + gunicorn │ │ pgvector/pg14 │ │ - │ │ 127.0.0.1:5003:80 │ │ (DB + KM vector) │ │ - │ └────────────────────┘ └───────────────────┘ │ - │ ┌────────────────────┐ ┌───────────────────┐ │ - │ │ momo-scheduler │ │ openclaw │ │ - │ │ (爬蟲排程) │ │ (clawbot-v5) │ │ - │ └────────────────────┘ └───────────────────┘ │ - │ ┌────────────────────┐ ┌───────────────────┐ │ - │ │ n8n (Docker) │ │ Ollama + WebUI │ │ - │ └────────────────────┘ └───────────────────┘ │ - └────────────────────────────────────────────────────────────────────┘ -``` - -### 服務端口對照表 - -| 服務 | 主機 | 類型 | 本地綁定 | 外部訪問 | 說明 | -|------|------|------|---------|---------|------| -| **momo-pro-system** | 188 | Docker | `127.0.0.1:5003:80` | `mo.wooo.work` / `momo.wooo.work` (經 110 VM Nginx) | Flask + gunicorn (4 workers) | -| **momo-db** | 188 | Docker | 內部 network | 僅容器網路 | pgvector/pg14 | -| **momo-scheduler** | 188 | Docker | — | — | 爬蟲排程 | -| **openclaw** | 188 | Docker | — | — | clawbot-v5 | -| **n8n** | 188 | Docker | `127.0.0.1:5678:5678` | `monitor.wooo.work/n8n/` (經 110 代理) | Webhook 自動化 | -| **Ollama** | 188 | Docker | 內部 network | `ollama.wooo.work` (經 110) | AI 模型服務 | -| **Registry** | 110 | Docker | `127.0.0.1:5000` | `registry.wooo.work` (經 VM Nginx) | Container Registry | -| **Prometheus** | 110 | Docker | 內部 | `monitor.wooo.work/prometheus/` | 監控指標收集 | -| **Grafana** | 110 | Docker | 內部 | `monitor.wooo.work/grafana/` | 監控儀表板 | -| **Alertmanager** | 110 | Docker | 內部 | `monitor.wooo.work/alertmanager/` | 告警路由 | -| **Harbor** | 110 | Docker | 內部 | 待定 | 尚未撤除(文件先前誤報已移除) | - -### VM Nginx 反向代理(110 上) - -Nginx 配置位置:`/etc/nginx/sites-enabled/momo` - -`mo.wooo.work` / `momo.wooo.work` 的流量路徑(現況推測,待驗證): -``` -用戶 → {mo|momo}.wooo.work:443 → 110 VM Nginx (SSL 終結) → ??? → 188:5003 momo-pro-system -``` - -> ⚠️ **TODO: 需統帥確認** — 110 上 `proxy_pass` 的具體 upstream。若為 `http://192.168.0.188:5003`,要求 188 的 5003 對 110 開放(非純 127.0.0.1 綁定);若 110 本機另跑一個 reverse proxy daemon 聽 `127.0.0.1:5001` 再轉 188,需補查。此段待 SSH 到 110 檢視 `sites-enabled/momo` 實際內容後更新。 - -### 監控堆疊(Docker,分佈於 110 + 188) - -| 組件 | 主機 | 說明 | -|------|------|------| -| Prometheus | 110 Docker | 抓取 110 + 188 兩邊的 exporter | -| Grafana | 110 Docker | `monitor.wooo.work/grafana/` | -| Alertmanager | 110 Docker | 告警 → Telegram | -| Node Exporter | 110 + 188 | 各自主機指標 | -| cAdvisor | 110 + 188 | 容器資源指標 | -| n8n | 188 Docker | 自動化 Webhook(容器名 `n8n`) | - -**Grafana 登入**: -- URL: `http://192.168.0.110:30030`(若已改內部綁定,需透過 monitor.wooo.work) -- 帳號 / 密碼: `admin` / `Wooo_Grafana_2026` - -**Telegram 告警配置**: -- Bot: `@wooowooowooobot` -- Chat ID: `5619078117` - -### 故障排除:服務 502 / 容器停止 - -> K3s / kubectl / PVC / StatefulSet 相關故障排除流程已作廢(188 無 K8s)。改用 Docker Compose 操作: - -```bash -# 在 188 上 -docker ps -a | grep momo # 查看容器狀態 -docker logs -f momo-pro-system --tail 100 # 看日誌 -docker restart momo-pro-system # 單純重啟 -cd /home/ollama/momo-pro && docker compose up -d # 全部重新拉起 - -# 在 110 上(VM Nginx 若異常) -sudo systemctl reload nginx -sudo nginx -t -``` - -### 回滾(Docker Compose 原生機制) - -188 無 K8s,無所謂「回滾到 Docker Compose」。版本回退方式是: -1. 重建 Docker image 至舊版本並 `docker compose up -d` -2. 若 Python 檔案損壞,可從 `/home/ollama/momo-pro/backups/` 取回 - -> **過往 `/home/wooo/backups/pre-k8s-migration/quick_rollback.sh` 已失去意義(188 從未 K8s 化),保留檔案但不再作為回滾途徑。** - ---- - -## 🔐 服務帳號密碼總覽 (2026-04-18 更新) - -> **單一監控中心**: 所有服務集中於 UAT 主機 - -### 應用服務 - -| 環境 | 服務 | URL | 帳號 | 密碼 | 版本 | -|------|------|-----|------|------|------| -| 🟢 正式 | **EwoooC(MOMO Pro)** | https://mo.wooo.work、https://momo.wooo.work | - | `0936223270` | V9.4(Docker Compose 於 188) | -| 🟢 正式 | **CI/CD Dashboard** | https://mo.wooo.work/cicd | - | - | - | -| 🟢 正式 | **PostgreSQL (momo-db, pgvector)** | 容器內部 network on 188 | `momo` | `wooo_pg_2026` | pgvector/pg14,含 KM `vector(1024)` | - -### 監控服務 (集中於 UAT) - -| 服務 | URL | 帳號 | 密碼 | 說明 | -|------|-----|------|------|------| -| **監控入口頁面** | https://monitor.wooo.work/ | - | - | 所有監控服務入口 (110) | -| **Grafana** | http://192.168.0.110:30030 或 monitor.wooo.work/grafana/ | `admin` | `Wooo_Grafana_2026` | 監控儀表板 (110 Docker) | -| **Prometheus** | monitor.wooo.work/prometheus/ | - | - | 指標收集 (110 Docker) | -| **Alertmanager** | monitor.wooo.work/alertmanager/ | - | - | 告警路由 → Telegram (110 Docker) | -| **n8n** | http://192.168.0.188:5678 (內部) | `admin@wooo.work` | `Wooo_N8n_2026` | 自動化工作流程 (188 Docker, 容器名 `n8n`) | - -### 開發工具 - -| 服務 | URL | 帳號 | 密碼 | 說明 | -|------|-----|------|------|------| -| **Registry** | https://registry.wooo.work | `admin` | `Wooo_Registry_2026` | Container Registry | - -### BI 分析平台 - -| 服務 | URL | 帳號 | 密碼 | 說明 | -|------|-----|------|------|------| -| **Apache Superset** | https://monitor.wooo.work/superset/ | `admin` | `Wooo_Superset_2026` | BI 分析儀表板 | -| **Superset DB (readonly)** | 10.42.0.199:5432 | `superset_readonly` | `Wooo_Superset_RO_2026` | 唯讀資料庫用戶 | - -### 管理工具 - -| 服務 | URL | 帳號 | 密碼 | 說明 | -|------|-----|------|------|------| -| **Portainer** | https://monitor.wooo.work/portainer/ | `admin` | `Wooo_Portainer_2026` | Docker 容器管理 | - -### AI 服務 - -| 服務 | URL | API Key | 說明 | -|------|-----|---------|------| -| **Ollama AI** | https://ollama.wooo.work | `0df8b4f247a4497998248f013ce92a17` | AI 模型服務 | -| **Gemini AI** | API | `AIzaSyCqv7TY2iTGi2wa91d2irwH08VYXjT9YUk` | 文案生成 | - -### 指標收集器 (Exporters) - -| 服務 | URL | 說明 | -|------|-----|------| -| **Node Exporter** | 110 + 188 (127.0.0.1:9100) | 各主機 CPU/記憶體/磁碟 | -| **cAdvisor** | 110 + 188 (127.0.0.1:8080) | 容器資源使用 | -| **Postgres Exporter** | 188 Docker (針對 momo-db pgvector) | PostgreSQL 監控 | -| **Blackbox Exporter** | 110 (127.0.0.1:9115) | 外部服務探測 | - -### SSH 連線 - -| 伺服器 | 指令 | 密碼 | 說明 | -|--------|------|------|------| -| **EwoooC 正式主機** | `ssh -J wooo@192.168.0.110 ollama@192.168.0.188` | `0936223270` | 運行 Flask 主應用 / momo-db / scheduler / openclaw / n8n / Ollama | -| **周邊服務主機** | `ssh wooo@192.168.0.110` | `0936223270` | VM Nginx 反代 / Harbor / Sentry / SigNoz / Gitea / 監控堆疊 | -| **Kali DevSecOps** | `ssh kali@192.168.0.112` | - | 安全掃描工作站 | - -### Telegram 告警 - -| 項目 | 值 | -|------|-----| -| Bot Token | `8075645931:AAH-EGKMo8ZC4QJs-Nc1_0s92xHrGdQvdpg` | -| Chat ID | `5619078117` | -| 告警來源 | Alertmanager + n8n 工作流程 | -| 告警類型 | Pod OOMKilled、重啟過多、記憶體不足等 | - -### API Keys 總覽 - -| 服務 | Key | 用途 | -|------|-----|------| -| **Gemini AI** | `AIzaSyCqv7TY2iTGi2wa91d2irwH08VYXjT9YUk` | Google Gemini AI 文案生成 | -| **YouTube API** | `AIzaSyBA9n7-rYIQVMq8rSF7kz486avBAfFzJ0s` | YouTube 趨勢爬蟲 | -| **n8n API** | `wooo_n8n_api_2026` | n8n 工作流 API 存取 | -| **Ollama API** | `0df8b4f247a4497998248f013ce92a17.vqSWDEK0RppTZIwcdT-ei-Sz` | Ollama AI 模型服務 | -| **LINE Channel** | `nD6MSXjB2FyB111zpT6Yik5B275mi6olHjjf94VnqN1ljUcqzcA7KtSSslxsOCEG6pERzmidNJFdzol6h+9V+t1x3j4Q8ljAacqC+i0627RuwbkiLxoHTJ/9HbIdehhoSJoeuNJHLraE721iDDfIuQdB04t89/1O/w1cDnyilFU=` | LINE 訊息推送 | -| **Telegram Bot** | `8075645931:AAH-EGKMo8ZC4QJs-Nc1_0s92xHrGdQvdpg` | Telegram 告警通知 | - ---- - -## 🔄 運維架構(2026-04-19 依 SSH 實地審計重寫) - -> **架構**: EwoooC 正式服務跑在 **188 Docker Compose**,監控告警堆疊跑在 **110 Docker**,n8n Webhook 引擎跑在 **188 Docker** (容器名 `n8n`)。 - -### 架構圖 - -``` -┌──────────────────────────────────────────────┐ ┌─────────────────────────────────────────────┐ -│ 110 (wooo@192.168.0.110) 周邊服務主機 │ │ 188 (ollama@192.168.0.188) EwoooC 正式主機 │ -│ │ │ │ -│ ┌────────────────────────────────────────┐ │ │ ┌───────────────────────────────────────┐ │ -│ │ Prometheus + Grafana + Alertmanager │ │ │ │ /home/ollama/momo-pro/ (Compose V12) │ │ -│ │ (Docker) │──┼────┼─▶│ momo-pro-system / momo-db (pgvector) │ │ -│ │ 每 15s 抓取 110 + 188 exporter 指標 │ │ │ │ momo-scheduler / openclaw / n8n │ │ -│ └────────────────────────────────────────┘ │ │ └───────────────────────────────────────┘ │ -│ ┌────────────────────────────────────────┐ │ │ ┌───────────────────────────────────────┐ │ -│ │ VM Nginx (systemd) SSL 終結 + 反代 │──┼────┼─▶│ 127.0.0.1:5003 momo-pro-system │ │ -│ │ mo.wooo.work / momo.wooo.work → 188 │ │ │ │ (⚠️ TODO 待確認 5003 對 110 開放路徑) │ │ -│ └────────────────────────────────────────┘ │ │ └───────────────────────────────────────┘ │ -│ │ │ ┌───────────────────────────────────────┐ │ -│ Harbor / Sentry / SigNoz / Gitea / Langfuse │ │ │ n8n Docker (容器名 `n8n`) │ │ -│ Docker Registry / Superset │ │ │ 自動化工作流程引擎 │ │ -│ (monitor.wooo.work 反代入口) │ │ └───────────────────────────────────────┘ │ -└──────────────────────────────────────────────┘ └─────────────────────────────────────────────┘ -``` - -### 監控 → 告警 → 自動修復 流程 - -``` -Prometheus(110) 每 15s 抓取 - │ - ├─ 110 cAdvisor/Node exporter - ├─ 188 cAdvisor/Node exporter - ├─ momo-db pg_exporter (188) - └─ Blackbox: mo.wooo.work / momo.wooo.work / monitor.wooo.work - │ - ▼ - Alertmanager(110) 告警路由 - │ - ├─ Telegram 通知(@wooowooowooobot) - └─ n8n webhook 觸發自動修復 - │ - ▼ - ssh 188 → docker restart -``` - -### 域名健康監控腳本(2026-02-09 / 2026-04-19 修正) - -**腳本位置**: `/home/wooo/scripts/domain-health-monitor.sh`(110 上執行,因為 110 是 VM Nginx 所在,也能 SSH 到 188) -**Cron 排程**: 每 5 分鐘執行 - -**監控域名清單**: - -| 域名 | 預期狀態碼 | 服務名稱 | 自動修復命令 | -|------|-----------|---------|-------------| -| `https://mo.wooo.work/health` | 200 | EwoooC App (正式) | `ssh ollama@188 "docker restart momo-pro-system"` | -| `https://momo.wooo.work/health` | 200 | EwoooC App (momo 域名別名) | `ssh ollama@188 "docker restart momo-pro-system"` | -| `https://monitor.wooo.work/` | 200 | Monitor 首頁 (110) | `sudo systemctl reload nginx` | -| `https://registry.wooo.work/v2/` | 401 | Docker Registry (110) | `cd /home/wooo/devops/registry && docker compose restart` | -| `http://192.168.0.188:5678/` | 200 | n8n (188) | `ssh ollama@188 "docker restart n8n"` | -| `https://monitor.wooo.work/superset/login/` | 200 | Superset (110) | `docker compose restart` | - -**Cron 設定**: -```bash -*/5 * * * * /home/wooo/scripts/domain-health-monitor.sh >> /home/wooo/logs/domain_health_monitor.log 2>&1 -``` - -**日誌檔案**: `/home/wooo/logs/domain_health_monitor.log` - -### 基礎設施清單 - -| 項目 | 主機 | 狀態 | 備註 | -|------|------|------|------| -| **Docker Compose** | 188 | ✅ V12.0 | 承載 EwoooC 正式服務 | -| **pgvector DB** | 188 Docker | ✅ pg14 | AI KM `vector(1024)` | -| **Prometheus** | 110 Docker | ✅ | 指標收集 | -| **Grafana** | 110 Docker | ✅ | 儀表板 | -| **Alertmanager** | 110 Docker | ✅ | Telegram 告警 | -| **n8n** | 188 Docker | ✅ | 容器名 `n8n`,自動化任務 | -| **PostgreSQL Exporter** | 188 Docker | ✅ | 對 momo-db 做指標 | -| **自動修復** | 110 Cron | ✅ | domain-health-monitor.sh via SSH | - ---- - -## n8n 自動化工作流程 (2026-02-07 更新) - -### 概述 - -n8n 作為 **UAT 監控管理平台**,負責: -- UAT 服務監控與自動修復 -- 系統告警通知 (Telegram) - -| 類型 | Python Scheduler | n8n | -|------|-----------------|-----| -| 系統健康監控 | ❌ | ✅ 每 5 分鐘 | -| 資料爬蟲 | ✅ 專業爬蟲邏輯 | ❌ | -| Google Drive 匯入 | ✅ 複雜資料處理 | ✅ 匯入監控 | -| Telegram/LINE 通知 | ✅ 業績通知 | ✅ 系統告警 | -| 自動修復 | ❌ | ✅ 本地 kubectl | - -### 已部署工作流程 - -> **2026-04-18 更新**: 單一 UAT 環境監控 + 自動修復 - -#### 系統監控類 (12 個) - -| # | 工作流程 | 頻率 | 自動修復 | 說明 | -|---|----------|------|----------|------| -| 01 | 磁碟空間監控 | 每小時 | ✅ 自動清理 | 警告 80%、緊急 90% | -| 02 | SSL 證書監控 | 每日 09:00 | ❌ | 30 天內到期告警 | -| 03 | CI/CD Pipeline 通知 | Webhook | ❌ | CI/CD 成功/失敗通知(webhook 來源待定,GitLab 已撤除) | -| 04 | 資料庫備份監控 | 每日 10:00 | ❌ | 檢查最新備份狀態 | -| 05 | 爬蟲執行監控 | 每 2 小時 | ✅ 重啟 scheduler | 檢查爬蟲排程執行 | -| 06 | K8s Pod 狀態監控 | 每 10 分鐘 | ✅ 自動重啟 Pod | 應用健康檢查 | -| 07 | Docker Registry 健康檢查 | 每 30 分鐘 | ❌ | Registry 可用性 | -| 11 | Registry 健康監控 | 每 10 分鐘 | ❌ | 詳細 Registry 狀態 | -| 12 | Google Drive 匯入監控 | 每 30 分鐘 | ❌ | 自動匯入狀態追蹤 | -| 15 | Ollama/WebUI 健康監控 | 每 5 分鐘 | ✅ 自動重啟 | AI 服務監控 | -| 16 | K8s MOMO App 健康監控 | 每 5 分鐘 | ✅ 自動重啟 | 主應用深度監控 | -| 18 | Clawdbot 健康監控 | 每 5 分鐘 | ✅ 自動修復 | Bot 服務監控 | - -#### 環境監控類 - -| # | 工作流程 | 頻率 | 自動修復 | 說明 | -|---|----------|------|----------|------| -| 17 | UAT 健康監控 | 每 5 分鐘 | ❌ | 服務可用性檢查 | -| 18 | PostgreSQL 資料庫監控 | 每 15 分鐘 | ❌ | 資料庫狀態 | -| 20 | UAT 自動修復機制 | 每 5 分鐘 | ✅ 自動重啟 | 服務自我修復 | - -#### 定期報告類 (3 個) - -| # | 工作流程 | 頻率 | 說明 | -|---|----------|------|------| -| 08 | 每日系統狀態報告 | 每日 09:00 | 應用、備份、爬蟲狀態彙報 | -| 09 | 每週業績摘要 | 每週一 09:00 | 週業績、訂單數、成長率 | -| 10 | 月初作業提醒 | 每月 1 日 09:00 | 月結待辦事項提醒 | - -#### 其他工作流程 - -| # | 工作流程 | 說明 | -|---|----------|------| -| 13 | PostgreSQL 慢查詢監控 | 慢查詢告警 + 自動 VACUUM | -| 14 | Windows 遠端控制 | Telegram Bot 遠端控制 | -| 19 | UAT 頁面健康監控 | 關鍵頁面可用性檢查 | -| - | EwoooC 系統健康監控 | 整體系統狀態 | - -#### 工作流程檔案位置 - -``` -n8n-workflows/ -├── 01-disk-space-monitor.json # 磁碟空間監控 -├── 02-ssl-certificate-monitor.json # SSL 證書監控 -├── 03-cicd-pipeline-notify.json # CI/CD 通知 (Webhook) -├── 04-backup-monitor.json # 備份監控 -├── 05-crawler-monitor.json # 爬蟲監控 -├── 06-k8s-pod-monitor.json # K8s Pod 監控 -├── 07-registry-health.json # Registry 健康檢查 -├── 08-daily-system-report.json # 每日系統報告 -├── 09-weekly-sales-summary.json # 每週業績摘要 -├── 10-monthly-reminder.json # 月初提醒 -├── 11-harbor-health-monitor.json # Harbor 健康監控 -├── 12-google-drive-import-monitor.json # Google Drive 匯入監控 -├── 13-slow-query-monitor.json # PostgreSQL 慢查詢監控 -├── 14-ollama-health-monitor.json # Ollama 健康監控 -├── 17-uat-health-monitor.json # UAT 健康監控 -├── 18-postgres-health-monitor.json # PostgreSQL 資料庫監控 -└── 20-auto-repair-uat.json # UAT 自動修復機制 -``` - -#### 系統管理 API (供 n8n 呼叫) - -| 端點 | 方法 | 說明 | -|------|------|------| -| `/api/system/cleanup` | POST | 自動清理 (Docker prune, 日誌輪替) | -| `/api/system/k8s/restart` | POST | 重啟 K8s Deployment | -| `/api/system/crawler/status` | GET | 爬蟲執行狀態 | -| `/api/system/backup/status` | GET | 備份狀態檢查 | -| `/api/system/ssl/check` | GET | SSL 證書檢查 | -| `/api/system/registry/health` | GET | Registry 健康檢查 | - ---- - -### 通知模板管理系統 - -#### 功能說明 - -提供 Web UI 管理 Telegram/LINE 通知訊息內容,可自訂 emoji、標題、內容格式。 - -#### 管理頁面 - -- URL: `/notification_templates` -- 功能: 編輯模板、預覽訊息、初始化預設模板 - -#### API 端點 - -| 端點 | 方法 | 說明 | -|------|------|------| -| `/api/notification/templates` | GET | 取得所有模板 | -| `/api/notification/templates/` | GET | 取得單一模板 | -| `/api/notification/templates/` | PUT | 更新模板 | -| `/api/notification/render` | POST | 渲染模板 (n8n 呼叫) | - -#### 預設模板列表 (14 個) - -| 分類 | 模板代碼 | 說明 | -|------|----------|------| -| system | `disk_warning` | 磁碟空間警告 (80%) | -| system | `disk_critical` | 磁碟空間嚴重不足 (90%) | -| system | `cleanup_complete` | 自動清理完成 | -| system | `ssl_warning` | SSL 證書即將到期 | -| system | `pod_unhealthy` | K8s Pod 異常 | -| system | `pod_restart_result` | Pod 重啟結果 | -| system | `crawler_warning` | 爬蟲執行警告 | -| system | `registry_unhealthy` | Registry 異常 | -| system | `backup_warning` | 備份監控警告 | -| system | `cicd_success` | CI/CD 成功 | -| system | `cicd_failed` | CI/CD 失敗 | -| report | `daily_report` | 每日系統狀態報告 | -| business | `weekly_sales` | 每週業績摘要 | -| business | `monthly_reminder` | 月初作業提醒 | - -#### n8n 整合方式 - -在 n8n 中呼叫 `/api/notification/render` 取得格式化訊息後發送: - -```json -POST /api/notification/render -{ - "code": "disk_warning", - "variables": { - "usage_percent": 85, - "free_gb": 15, - "total_gb": 100 - } -} -``` - ---- - -### n8n 管理指令 - -```bash -# SSH 到 UAT -ssh wooo@192.168.0.110 - -# 查看 n8n 容器狀態 -docker ps | grep n8n - -# 查看 n8n 日誌 -docker logs momo-n8n --tail 50 - -# 列出所有工作流程 -docker exec momo-n8n n8n list:workflow - -# 手動觸發工作流程 -docker exec momo-n8n n8n execute --id=H7O3cl6sUPSLM7UD -``` - -### n8n API 存取 - -```bash -# 列出工作流程 -curl -H "X-N8N-API-KEY: wooo_n8n_api_2026" \ - http://192.168.0.110:5678/api/v1/workflows - -# 執行工作流程 -curl -X POST -H "X-N8N-API-KEY: wooo_n8n_api_2026" \ - http://192.168.0.110:5678/api/v1/workflows/H7O3cl6sUPSLM7UD/activate -``` - ---- - -### Docker Registry 詳細資訊 - -| 項目 | 值 | -|-----|-----| -| **外部 URL** | `https://registry.wooo.work` (HTTPS, 推薦) | -| 內部 URL | `http://127.0.0.1:5000` (僅本機可存取) | -| 帳號 | `admin` | -| 密碼 | `Wooo_Registry_2026` | -| 映像位置 | `registry.wooo.work/wooo/momo-pro-system:latest` | - -> ⚠️ **安全提醒**: Port 5000 已改為僅綁定 127.0.0.1,外部訪問必須透過 HTTPS 域名。 - -### Rancher (K8s 管理平台) - -| 項目 | 值 | -|-----|-----| -| URL | `https://192.168.0.110:8443` | -| 帳號 | `admin` | -| 密碼 | `0B5-z2m0yxle6HtTq5WA` | -| 功能 | K3s 集群管理、監控、日誌 | -| K3s 集群 | `momo-k3s` (已匯入) | -| Cluster ID | `c-52mz8` | -| K8s 版本 | `v1.34.3+k3s1` | - -### Registry 映像管理 -```bash -# 登入 Registry (使用 HTTPS 域名) -echo 'Wooo_Registry_2026' | docker login registry.wooo.work -u admin --password-stdin - -# 映像位置 (使用域名) -registry.wooo.work/wooo/momo-pro-system:latest -``` - -### CI/CD 流程(GitLab 已撤除,待定新方案) - -> **2026-04-18 更新**: GitLab CE + GitLab CI/CD 已從基礎設施移除,新的 CI/CD 方案尚未定案。 -> 目前部署以「本地 build + SCP + kubectl rollout restart」為暫時流程,相關腳本待 CI/CD 選型後重寫。 - -### 手動觸發 K8s 更新 -```bash -# SSH 到 UAT -ssh wooo@192.168.0.110 - -# 重啟 K8s Deployment(會自動拉取最新 Image) -kubectl rollout restart deployment/momo-app deployment/momo-scheduler -n momo - -# 查看更新狀態 -kubectl rollout status deployment/momo-app -n momo -``` - -### Docker 容器(DevOps 服務) -| 容器名稱 | 說明 | Port | -|---------|------|------| -| registry | Docker Registry 容器 | 5000 | -| watchtower | Docker 映像自動更新 | - | -| n8n | Webhook 自動化 | 5678 | - -### K8s Pod(應用服務) -| Pod | 說明 | 資源限制 | -|-----|------|---------| -| momo-app | Flask 主應用 | 512Mi-4Gi / 200m-2000m | -| momo-postgres | PostgreSQL 資料庫 | 256Mi-1Gi / 100m-500m | -| momo-scheduler | 爬蟲排程服務 | 256Mi-2Gi / 100m-1000m | - -### K8s 配置檔案 -| 檔案 | 說明 | -|------|------| -| `k8s/02-configmap.yaml` | 環境變數配置(Ollama AI、密碼策略等) | -| `k8s/04-momo-app.yaml` | Flask 主應用 Deployment + Google Drive 認證 | -| `k8s/05-scheduler.yaml` | 爬蟲排程 Deployment + Google Drive 認證 | -| `k8s/08-google-drive-secret.yaml` | Google OAuth 認證 Secret | -| `k8s/nginx/monitor.conf` | VM Nginx 配置(含 Rancher 代理) | - -### Nginx 代理路徑(monitor.wooo.work) -| 路徑 | 後端服務 | Port | -|------|----------|------| -| `/grafana/` | Docker Grafana | 3000 | -| `/prometheus/` | Prometheus | 9090 | -| `/alertmanager/` | Alertmanager | 9093 | -| `/portainer/` | Portainer | 9000 | -| `/n8n/` | n8n | 5678 | -| `/pgadmin/` | pgAdmin | 8088 | -| `/loki/` | Loki | 3100 | -| `/rancher/` | Rancher | 8443 (HTTPS) | -| `/k8s-grafana/` | K8s Grafana | 30030 | - ---- - -## 關鍵路徑 - -| 用途 | 路徑 | -|-----|------| -| 主資料庫 | `data/momo_database.db` | -| UAT 資料庫 | `data/momo_database_uat.db` | -| Google Drive 認證 | `config/google_credentials.json` | -| 排程器程式 | `run_scheduler.py` | -| 排程統計 | `data/scheduler_stats.json` | -| 系統日誌 | `logs/system.log` | - ---- - -## Ollama AI 服務 - -| 項目 | 值 | -|-----|-----| -| Host | `http://192.168.0.188:11434` | -| 預設模型 | `llama3:70b-instruct-q2_K` | -| API Key | `0df8b4f247a4497998248f013ce92a17.vqSWDEK0RppTZIwcdT-ei-Sz` | -| 超時設定 | 基本 180 秒 / 文案生成 240 秒 | - -### K8s ConfigMap 設定 -```yaml -# k8s/02-configmap.yaml -OLLAMA_HOST: "http://192.168.0.188:11434" -OLLAMA_MODEL: "llama3:70b-instruct-q2_K" -``` - -### 環境變數 -```bash -OLLAMA_HOST=http://192.168.0.188:11434 -OLLAMA_MODEL=llama3:70b-instruct-q2_K -OLLAMA_API_KEY=0df8b4f247a4497998248f013ce92a17.vqSWDEK0RppTZIwcdT-ei-Sz -``` - -### Ollama 伺服器健康監控 (2026-01-28 新增) - -**監控腳本**: `/home/ollama/scripts/ollama_health_monitor.sh` -**排程**: 每 5 分鐘執行 (cron) -**n8n 工作流程**: `14-ollama-health-monitor.json` - -**監控項目**: -| 項目 | 端點 | 說明 | -|------|------|------| -| Nginx | 進程檢查 | 確保代理服務正常 | -| Ollama API | http://127.0.0.1:11434/api/tags | AI 模型服務 | -| Open WebUI | http://192.168.0.188/ | Web 介面 | - -**自動修復機制**: -| 問題 | 修復動作 | -|------|---------| -| Nginx 停止 | `sudo systemctl restart nginx` | -| Ollama 停止 | `systemctl restart ollama` | -| Open WebUI 502 (Docker 網路失效) | `sudo systemctl restart docker` | -| Open WebUI 容器卡住 | `docker restart open-webui` | - -**SSH 連線**: -```bash -ssh ollama@192.168.0.188 -# 密碼: 0936223270 -``` - -**手動檢查**: -```bash -# 執行健康檢查 -/home/ollama/scripts/ollama_health_monitor.sh - -# 查看日誌 -tail -f /var/log/ollama_health_monitor.log -``` - -### Ollama 伺服器安全加固 (2026-02-02 新增) - -**安全腳本**: `scripts/security/harden_ollama_server.sh` - -**已安裝防護**: -| 服務 | 配置 | 說明 | -|------|------|------| -| Fail2Ban | SSH 3 次失敗封鎖 1 小時 | 防暴力破解 | -| UFW 防火牆 | 預設拒絕入站 | 網路存取控制 | - -**UFW 規則**: -```bash -# 公開服務 -22/tcp # SSH -80/tcp # HTTP -443/tcp # HTTPS - -# 僅限內網 (192.168.0.0/24) -11434 # Ollama API -3000 # Open WebUI -5678 # n8n -8080 # SearXNG -``` - -**管理指令**: -```bash -# 查看 Fail2Ban 封鎖狀態 -sudo fail2ban-client status sshd - -# 查看防火牆規則 -sudo ufw status verbose -``` - ---- - -## Kali Linux DevSecOps 工作站 - -| 項目 | 值 | -|-----|-----| -| IP | `192.168.0.112` | -| 用戶 | `kali` | -| 用途 | 集中式安全掃描、滲透測試、弱點管理 | - -### SSH 連線 -```bash -ssh kali@192.168.0.112 -``` - -### 目錄結構 -``` -/home/kali/ -├── projects/ # 專案程式碼 (Git clone) -│ └── momo-pro-system/ # 主專案 -└── scripts/ # 自動化掃描腳本 - ├── port_monitor.py # 端口監控 - ├── port_baseline.json # 端口基線 - ├── code_security_scan.py # 程式碼安全掃描 (Bandit) - ├── registry_image_scan.py # 容器映像掃描 (Trivy) - └── logs/ # 掃描日誌 -``` - -### 自動化掃描工具 - -#### 1. 端口監控 (port_monitor.py) -- **執行頻率**: 每小時 -- **監控目標**: `192.168.0.110`, `192.168.0.188` -- **功能**: Nmap 掃描,與基線比對,發現新端口時發送 Telegram 告警 -- **Cron**: `0 * * * * /usr/bin/python3 /home/kali/scripts/port_monitor.py` - -#### 2. 程式碼安全掃描 (code_security_scan.py) -- **執行頻率**: 每日 08:00 -- **工具**: Bandit (Python 安全分析器) -- **功能**: 掃描 ~/projects 下所有專案,發送 Telegram 報告 -- **Cron**: `0 8 * * * /usr/bin/python3 /home/kali/scripts/code_security_scan.py` - -#### 3. Registry 映像掃描 (registry_image_scan.py) -- **執行頻率**: 每週日 09:00 -- **工具**: Trivy (容器弱點掃描器) -- **功能**: 掃描 Docker Registry 中的映像,發送 Telegram 報告 -- **Cron**: `0 9 * * 0 /usr/bin/python3 /home/kali/scripts/registry_image_scan.py` -- **掃描目標**: `registry.wooo.work/wooo/momo-pro-system:latest` - -### 基線端口清單 (2026-01-25 更新) - -**192.168.0.110** (外部可見): -| Port | Service | -|------|---------| -| 22 | SSH | -| 80 | HTTP | -| 443 | HTTPS (SSL, Registry/mo.wooo.work) | - -> ⚠️ Port 5050, 5678, 9100 已隱藏,僅本地可存取 - -**192.168.0.188** (外部可見): -| Port | Service | -|------|---------| -| 22 | SSH | -| 80 | HTTP | -| 443 | HTTPS | - -### 手動執行掃描 -```bash -# 端口掃描 -python3 ~/scripts/port_monitor.py - -# 程式碼安全掃描 (Bandit) -python3 ~/scripts/code_security_scan.py - -# Registry 映像掃描 (Trivy) -python3 ~/scripts/registry_image_scan.py -``` - -### WireGuard VPN (WG-Easy) - -| 項目 | 值 | -|-----|-----| -| 管理介面 | `http://192.168.0.112:51821` | -| 外部端點 | `114.32.151.246:51820/udp` | -| 密碼 | `Wooo_VPN_2026` | -| 允許網段 | `192.168.0.0/24`, `10.0.0.0/8` | -| DNS | `8.8.8.8`, `8.8.4.4` | - -**使用方式**: -1. 開啟管理介面 `http://192.168.0.112:51821` -2. 輸入密碼登入 -3. 點擊「+ New」建立新用戶 -4. 手機下載 WireGuard App,掃描 QR Code 即可連線 - -**Docker 容器管理**: -```bash -# 查看狀態 -docker ps --filter name=wg-easy - -# 重啟服務 -docker restart wg-easy - -# 查看日誌 -docker logs wg-easy -``` - -> ⚠️ **安全提醒**: 需要在路由器設定 Port Forwarding: 51820/UDP → 192.168.0.112:51820 - ---- - -## 🛡️ 服務變更安全協議 (2026-02-08 新增) - -> **核心原則**: 避免「改一個壞一個」,每次變更前必須確認所有相關服務狀態 - -### 服務依賴關係圖 - -``` -┌─────────────────────────────────────────────────────────────────────────────┐ -│ UAT 服務依賴架構圖 │ -├─────────────────────────────────────────────────────────────────────────────┤ -│ │ -│ ┌─────────────────┐ │ -│ │ VM Nginx │ ← 所有外部流量入口 │ -│ │ (Port 80/443) │ │ -│ └────────┬────────┘ │ -│ │ │ -│ ┌────────┴────────────────────────────────────────────────────────┐ │ -│ │ │ │ -│ ▼ ▼ ▼ │ │ -│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ -│ │mo.wooo │ │monitor. │ │registry. │ │ │ -│ │.work │ │wooo.work │ │wooo.work │ │ │ -│ └────┬─────┘ └────┬─────┘ └────┬─────┘ │ │ -│ │ │ │ │ │ -│ ▼ ▼ ▼ │ │ -│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ -│ │K8s │ │Superset │ │Registry │ │ │ -│ │momo-app │ │Grafana │ │Docker │ │ │ -│ │ │ │Prometheus│ │ │ │ │ -│ └────┬─────┘ │n8n │ └──────────┘ │ │ -│ │ │Portainer │ │ │ -│ │ └──────────┘ │ │ -│ ▼ │ │ -│ ┌──────────┐ │ │ -│ │momo- │ │ │ -│ │postgres │ │ │ -│ └──────────┘ │ │ -│ │ -└─────────────────────────────────────────────────────────────────────────────┘ - -⚠️ 重要依賴關係: -1. Nginx 故障 → 所有外部服務不可訪問 -2. Docker 故障 → Superset、n8n、Registry 全停 -3. K8s 故障 → momo-app、momo-scheduler、momo-postgres 全停 -4. Registry 故障 → K8s 無法拉取新映像 -``` - -### 變更前必做檢查清單 ✅ - -**在進行任何配置變更之前,必須執行以下檢查:** - -```bash -# 1. 記錄當前所有服務狀態(變更前快照) -ssh wooo@192.168.0.110 " -echo '=== 變更前服務狀態快照 ===' -echo '時間:' \$(date '+%Y-%m-%d %H:%M:%S') -echo '' -echo '--- Docker 容器 ---' -docker ps --format 'table {{.Names}}\t{{.Status}}' | head -20 -echo '' -echo '--- K8s Pods ---' -kubectl get pods -n momo --no-headers 2>/dev/null -echo '' -echo '--- 健康檢查 ---' -curl -s -o /dev/null -w 'mo.wooo.work: %{http_code}\n' https://mo.wooo.work/health -curl -s -o /dev/null -w 'Superset: %{http_code}\n' https://monitor.wooo.work/superset/login/ -curl -s -o /dev/null -w 'n8n: %{http_code}\n' http://127.0.0.1:5678/ 2>/dev/null || echo 'n8n: 連線失敗' -" | tee /tmp/pre_change_status.txt -``` - -### 服務恢復 SOP - -#### 1. Docker 容器停止 (Exit 128/137) - -**常見原因**: 系統重啟、Docker 重啟、OOM - -```bash -# 檢查停止的容器 -ssh wooo@192.168.0.110 "docker ps -a --filter status=exited" - -# 恢復 n8n (如有衝突需先刪除) -ssh wooo@192.168.0.110 "docker rm -f momo-n8n; cd /home/wooo && docker compose up -d n8n" - -# 恢復 Superset -ssh wooo@192.168.0.110 "cd /home/wooo/momo_pro_system/docker/superset && docker compose down && docker compose up -d" - -# 恢復 Registry -ssh wooo@192.168.0.110 "cd /home/wooo/devops/registry && docker compose up -d" -``` - -#### 2. Nginx 502 Bad Gateway - -**診斷流程**: -```bash -# Step 1: 確認後端容器狀態 -docker ps | grep - -# Step 2: 測試容器內部 -docker exec curl -s http://127.0.0.1:/health - -# Step 3: 如果內部正常但外部 502 = Docker 網路問題 -sudo systemctl restart docker -``` - -#### 3. Superset 路徑問題 (/superset/superset/) - -**根本原因**: Superset 內部路由已是 `/superset/...`,Nginx `proxy_redirect / /superset/` 會重複添加 - -**正確的 Nginx 配置**: -```nginx -location /superset/ { - proxy_pass http://127.0.0.1:8088/; - - # 關鍵:Superset 內部已是 /superset/,不需要再添加 - proxy_redirect /superset/ /superset/; - proxy_redirect ~^/(?!superset)(.*)$ /superset/$1; - - # 只重寫 static 和 api 路徑 - sub_filter '"/static/' '"/superset/static/'; - sub_filter "'/static/" "'/superset/static/"; - sub_filter_once off; -} -``` - -**Superset 配置 (superset_config.py)**: -```python -ENABLE_PROXY_FIX = True -PROXY_FIX_CONFIG = { - "x_for": 1, - "x_proto": 1, - "x_host": 1, - "x_prefix": 0, # 必須為 0!Nginx 已處理前綴 -} -``` - -#### 4. K8s Pod 問題 - -```bash -# 查看 Pod 狀態 -kubectl get pods -n momo - -# 查看 Pod 詳情 -kubectl describe pod -n momo - -# 重啟 Deployment -kubectl rollout restart deployment/momo-app deployment/momo-scheduler -n momo - -# 查看日誌 -kubectl logs -f deployment/momo-app -n momo --tail=100 -``` - -### 變更後驗證清單 ✅ - -**每次變更完成後,必須執行以下驗證:** - -```bash -# 完整服務健康檢查 -ssh wooo@192.168.0.110 " -echo '=== 變更後服務驗證 ===' -echo '時間:' \$(date '+%Y-%m-%d %H:%M:%S') -echo '' - -# Docker 容器 -echo '--- Docker 容器狀態 ---' -docker ps --format 'table {{.Names}}\t{{.Status}}' | head -15 - -# K8s -echo '' -echo '--- K8s Pods ---' -kubectl get pods -n momo --no-headers 2>/dev/null - -# 健康檢查 -echo '' -echo '--- 健康檢查 ---' -curl -s -o /dev/null -w 'mo.wooo.work: %{http_code}\n' https://mo.wooo.work/health -curl -s -o /dev/null -w 'Superset 登入: %{http_code}\n' https://monitor.wooo.work/superset/login/ -curl -s -o /dev/null -w 'n8n: %{http_code}\n' http://127.0.0.1:5678/ 2>/dev/null || echo 'n8n: 連線失敗' -curl -s -o /dev/null -w 'Registry: %{http_code}\n' https://registry.wooo.work/v2/ 2>/dev/null || echo 'Registry: 連線失敗' -curl -s -o /dev/null -w 'CI/CD Dashboard: %{http_code}\n' https://mo.wooo.work/cicd 2>/dev/null - -echo '' -echo '=== 驗證完成 ===' -" -``` - -### Claude 協作改進方案 - -> **2026-02-08 新增** - 回應用戶「改一個壞一個」的反饋 - -**1. 變更前自動快照** -- 在進行任何配置變更前,Claude 應主動執行「變更前必做檢查清單」 -- 將結果保存以便變更後對比 - -**2. 相關服務連帶檢查** -- 修改 Nginx → 同時檢查所有代理的服務 -- 修改 Docker Compose → 同時檢查相關容器 -- 修改 K8s YAML → 同時檢查 Pod 狀態 - -**3. 變更後完整驗證** -- 不只驗證目標服務,還要驗證所有可能受影響的服務 -- 使用「變更後驗證清單」確保沒有遺漏 - -**4. 文檔記錄** -- 每次修復重要問題後,立即更新 CLAUDE.md -- 記錄問題症狀、根本原因、解決方案 - ---- - -## 常見問題與解法 - -### 1. Scheduler 無法匯入 Google Drive 檔案 -**症狀**: 日誌顯示 `找不到認證檔案: config/google_credentials.json` - -**K8s 解法**: 使用 initContainer 從 Secret 複製認證到可寫入目錄 -```yaml -# k8s/04-momo-app.yaml & k8s/05-scheduler.yaml -initContainers: - - name: copy-google-credentials - image: busybox:1.36 - command: ['sh', '-c', 'cp /secrets/* /config/ && chmod 644 /config/*'] - volumeMounts: - - name: google-drive-secrets - mountPath: /secrets - readOnly: true - - name: momo-config-volume - mountPath: /config -volumes: - - name: google-drive-secrets - secret: - secretName: google-drive-credentials - - name: momo-config-volume - emptyDir: {} -``` - -**Docker 解法**: 確認 docker-compose.yml 中 scheduler 有掛載 config 目錄 -```yaml -scheduler: - volumes: - - ./config:/app/config:ro # 必須有這行 -``` - -### 2. 資料沒有同步到 realtime_sales_monthly -**症狀**: `daily_sales_snapshot` 有資料但 `realtime_sales_monthly` 沒有 - -**解法**: 檢查 `services/import_service.py` 中的同步邏輯 (約 line 404-459) - -### 3. LINE 通知發送失敗 -**症狀**: `You have reached your monthly limit` - -**原因**: LINE Notify 免費方案每月 1000 則限制,需等待下月重置 - -### 4. 502 Bad Gateway (Docker 網路問題) - -> **2026-01-28 新增** - 詳細排查過程與自動修復方案 - -**症狀**: -- 訪問 `https://mo.wooo.work/` 返回 `502 Bad Gateway` -- Nginx 無法連接到後端服務 - -**診斷步驟**: - -1. **檢查 Docker 容器狀態** - ```bash - ssh wooo@192.168.0.110 "sudo docker ps | grep momo" - ``` - 結果:容器顯示 `Up About a minute (healthy)`,看起來正常 - -2. **檢查外部端口連通性** - ```bash - curl -v http://127.0.0.1:5001/health - ``` - 結果:`Connection reset by peer` 或超時(約 30 秒) - -3. **檢查容器內部健康** - ```bash - docker exec momo-pro-system curl -s http://127.0.0.1:80/health - ``` - 結果:`{"status": "healthy"}` - 容器內部正常! - -4. **診斷結論** - - ✅ 容器內部服務正常運行 - - ❌ Docker 端口映射/網路轉發失效 - - Docker-proxy 進程存在但不轉發流量 - -**根本原因**: Docker 網路棧故障,通常發生在: -- 系統重啟後 Docker 網路未正確初始化 -- iptables 規則被其他服務清除 -- Docker daemon 內部狀態不一致 - -**解決方案**: -```bash -# 重啟 Docker 服務(會重建網路棧) -sudo systemctl restart docker - -# 等待容器自動啟動(約 30 秒) -sleep 30 - -# 驗證服務恢復 -curl -s https://mo.wooo.work/health -``` - -**自動修復腳本**: 見 `scripts/docker_health_monitor.sh` - ---- - -## 🔧 自動化監控與修復 - -### Docker 網路健康監控 (Cron) - -**腳本位置**: `/home/wooo/scripts/docker_health_monitor.sh` - -**功能**: -1. 每 5 分鐘檢查 mo.wooo.work 健康狀態 -2. 檢測到 502/超時時自動執行診斷 -3. 確認是 Docker 網路問題後自動重啟 Docker -4. 發送 Telegram 通知 - -**Cron 設定**: -```bash -# 每 5 分鐘執行健康檢查 -*/5 * * * * /home/wooo/scripts/docker_health_monitor.sh >> /var/log/docker_health_monitor.log 2>&1 -``` - -**監控邏輯**: -``` -外部健康檢查 (curl mo.wooo.work) - │ - ├── 成功 (HTTP 200) → 結束 - │ - └── 失敗 (502/超時) - │ - ├── 檢查容器內部 (docker exec curl) - │ │ - │ ├── 內部正常 → Docker 網路問題 → 重啟 Docker - │ │ - │ └── 內部失敗 → 應用問題 → 重啟容器 - │ - └── 發送 Telegram 告警 -``` - -**Telegram 告警範例**: -``` -🔴 MOMO Pro 服務異常 - -症狀: 502 Bad Gateway -診斷: Docker 網路轉發失效 -動作: 已自動重啟 Docker 服務 -時間: 2026-01-28 15:30:00 -``` - -### 5. K8s ImagePullBackOff (Registry 服務中斷) - -> **2026-01-28 新增** - Docker Registry 服務中斷導致 K8s 無法拉取映像檔 - -**症狀**: -- K8s Pods 顯示 `ImagePullBackOff` 或 `ErrImagePull` -- Google Drive 自動匯入停止運作 -- Scheduler 和 App 服務中斷 - -**診斷步驟**: - -1. **檢查 K8s Pod 狀態** - ```bash - sudo kubectl get pods -n momo - ``` - 結果:`momo-app` 和 `momo-scheduler` 顯示 `ImagePullBackOff` - -2. **查看詳細錯誤** - ```bash - sudo kubectl describe pod -n momo - ``` - 結果:`Failed to pull image: dial tcp 192.168.0.110:5000: connect: connection refused` - -3. **檢查 Registry 服務** - ```bash - docker ps | grep registry - ``` - 結果:Registry 容器未運行 - -**根本原因**: -- Docker Registry 服務停止(可能因系統重啟) -- Registry 端口配置改為 127.0.0.1:5000(安全配置)導致 K8s 無法連線 - -**解決方案**: - -```bash -# 1. 啟動 Registry -cd /home/wooo/devops/registry -docker compose up -d - -# 2. 更新 K8s 使用 HTTPS 域名 -# 修改 k8s/04-momo-app.yaml 和 k8s/05-scheduler.yaml -# 將 image: 192.168.0.110:5000/wooo/momo-pro-system:latest -# 改為 image: registry.wooo.work/wooo/momo-pro-system:latest - -# 3. 更新 registry secret -kubectl delete secret registry-secret -n momo -kubectl create secret docker-registry registry-secret \ - --docker-server=registry.wooo.work \ - --docker-username=admin \ - --docker-password=Wooo_Registry_2026 \ - -n momo - -# 4. 套用配置並重啟 -kubectl apply -f k8s/04-momo-app.yaml -f k8s/05-scheduler.yaml -kubectl rollout restart deployment/momo-app deployment/momo-scheduler -n momo -``` - -**自動修復腳本**: 見 `scripts/registry_health_monitor.sh` - -### 6. Scheduler CrashLoopBackOff (run_scheduler.py 遺失) - -> **2026-02-02 新增** - Scheduler Pod 重啟 387 次 - -**症狀**: -- momo-scheduler Pod 顯示 CrashLoopBackOff -- 日誌:`python: can't open file '/app/run_scheduler.py': [Errno 2] No such file or directory` - -**原因**: `run_scheduler.py` 檔案在某次部署中遺失 - -**解決方案**: -```bash -# 1. 確認檔案存在 -ls /Users/ogt/momo-pro-system/run_scheduler.py - -# 2. 重建 Docker 映像 -cd /home/wooo/momo_pro_system -docker build -t registry.wooo.work/wooo/momo-pro-system:latest . -docker push registry.wooo.work/wooo/momo-pro-system:latest - -# 3. 重啟 scheduler -kubectl rollout restart deployment/momo-scheduler -n momo -``` - -### 7. vendor-stockout 500 錯誤 (模板路徑錯誤) - -> **2026-02-02 新增** - 模板檔案位置不正確 - -**症狀**: -- 訪問 `/vendor-stockout/` 返回 Internal Server Error -- 日誌:`jinja2.exceptions.TemplateNotFound: vendor_stockout/index.html` - -**原因**: 模板檔案放在根目錄 (`vendor_stockout_index.html`),而非正確路徑 (`templates/vendor_stockout/index.html`) - -**解決方案**: -```bash -# 建立正確目錄並移動模板 -mkdir -p templates/vendor_stockout -mv vendor_stockout_*.html templates/vendor_stockout/ - -# 重新命名為正確名稱 -mv templates/vendor_stockout/vendor_stockout_index.html templates/vendor_stockout/index.html -# ... 其他檔案同理 -``` - -### 8. Alertmanager 無法發送 Telegram 告警 - -> **2026-02-02 新增** - 訊息模板語法錯誤 - -**症狀**: -- Alertmanager 日誌持續報錯 -- 錯誤:`template: :12:27: can't evaluate field StartsAt in type *template.Data` - -**原因**: Telegram 訊息模板中 `.StartsAt` 放在 `{{ range .Alerts }}` 區塊外,而 `StartsAt` 是每個 Alert 的屬性 - -**解決方案**: -```yaml -# k8s/monitoring/values-prometheus.yaml - -# ❌ 錯誤寫法 -{{ range .Alerts }} -📝 摘要: {{ .Annotations.summary }} -{{ end }} -⏰ 時間: {{ .StartsAt.Local.Format "..." }} # 在 range 外,.StartsAt 不存在 - -# ✅ 正確寫法 -{{ range .Alerts }} -📝 摘要: {{ .Annotations.summary }} -⏰ 時間: {{ .StartsAt.Local.Format "..." }} # 在 range 內 -{{ end }} -``` - -**套用修復**: -```bash -# 更新 Helm values -scp k8s/monitoring/values-prometheus.yaml wooo@192.168.0.110:/home/wooo/momo_pro_system/k8s/monitoring/ - -# 升級 Prometheus stack -helm upgrade prometheus prometheus-community/kube-prometheus-stack \ - -n monitoring \ - -f /home/wooo/momo_pro_system/k8s/monitoring/values-prometheus.yaml - -# 重啟 Alertmanager -kubectl rollout restart statefulset/alertmanager-prometheus-kube-prometheus-alertmanager -n monitoring -``` - ---- - -## 🔧 Registry 自動監控 - -### Registry 健康監控 (Cron) - -**腳本位置**: `/home/wooo/scripts/registry_health_monitor.sh` - -**功能**: -1. 每 5 分鐘檢查 Registry 服務狀態 -2. 檢測到 Registry 停止時自動啟動 -3. 發送 Telegram 告警通知 - -**Cron 設定**: -```bash -# 每 5 分鐘執行 Registry 健康檢查 -*/5 * * * * /home/wooo/scripts/registry_health_monitor.sh >> /var/log/registry_health_monitor.log 2>&1 -``` - ---- - -## 排程任務設定 - -| 任務 | 頻率 | 說明 | -|-----|------|------| -| Google Drive 自動匯入 | 每 30 分鐘 | 匯入 `即時業績_當日.xlsx` | -| 商品看板爬蟲 | 每 1 小時 | 爬取 momo 商品資料 | -| EDM 限時搶購爬蟲 | 每 1 小時 | 爬取 EDM 活動 | -| 購物節活動爬蟲 | 每 6 小時 | 爬取節慶活動 | - ---- - -## 🔄 系統重開機自動啟動機制 (2026-01-31 新增) - -### 概述 - -系統重開機後,透過 systemd 服務自動啟動所有必要服務,確保服務可用性。 - -### 服務啟動順序 - -``` -重開機完成 - ↓ (30 秒延遲,等待系統穩定) -1. 確認 Docker 服務啟動 - ↓ -2. 停止 Supabase 非必要服務 (減少資源競爭) - ↓ -3. 清理並啟動 Docker Registry (等待 healthy) - ↓ -4. 重啟 K8s momo-app/scheduler (等待就緒) - ↓ -5. 啟動監控服務 - ↓ -6. 健康檢查 + Telegram 通知 -``` - -> ⚠️ **2026-04-18 備註(ADR-008)**: 以下 systemd 服務設定與路徑為 110 主機早期架構,188 的 Docker Compose 自帶 `restart: unless-stopped` 即可自動恢復,**目前 188 不需要此 systemd 腳本**。保留本章以供 110 周邊服務(Harbor/Sentry 等)啟動順序參考。 - -### 相關檔案(110 周邊服務用,若仍存在) - -| 檔案 | 位置 | 說明 | -|------|------|------| -| 啟動腳本 | `/home/wooo/momo_pro_system/scripts/tools/system_startup.sh` ⚠️(110 上路徑已空置,腳本存在與否需確認) | 舊啟動腳本 | -| systemd 服務 | `/etc/systemd/system/momo-startup.service` | 開機自動執行 | -| 日誌檔案 | `/var/log/momo_startup.log` | 啟動日誌 | - -### 188 主機啟動策略(實況) - -188 不需要客製 systemd 服務 — Docker Compose 的 `restart: unless-stopped` 已足夠: -```yaml -# docker-compose.yml(V12.0 已設定) -services: - momo-app: - restart: unless-stopped - momo-scheduler: - restart: unless-stopped - momo-db: - restart: unless-stopped -``` -開機後 Docker daemon 啟動 → 容器自動恢復 → 無需額外腳本介入。 - -### 故障排除 - -| 問題 | 解決方法 | -|------|---------| -| Registry 啟動失敗 | 手動執行 `cd /home/wooo/devops/registry && docker compose down && docker compose up -d` | -| K8s Pod 卡在 Init | 檢查 Registry 是否健康,重啟 Pod | -| 服務未自動啟動 | 確認 `systemctl is-enabled momo-startup.service` 返回 `enabled` | -| 查看詳細日誌 | `cat /var/log/momo_startup.log` 或 `journalctl -u momo-startup.service -n 100` | - -### 模組化輔助腳本庫 - -``` -deploy/lib/ -├── registry.sh # Registry 管理函數 -├── k8s.sh # K8s 管理函數 -├── monitoring.sh # 監控與 Telegram 通知 -└── systemd.sh # Systemd 服務管理 -``` - ---- - -## 已完成功能 (2026-02) - -- [x] SSL 憑證配置 (Let's Encrypt) -- [x] UAT 服務自動啟動腳本優化 -- [x] 服務依賴關係文檔建立 -- [x] **Harbor 移除,改用 Docker Registry** (registry.wooo.work) -- [x] **CI/CD Dashboard 完整版** (`/cicd` 頁面) -- [x] **監控告警環境標籤** - ---- - -## 已完成功能 (2026-01) - -- [x] 廠商缺貨郵件系統(含自訂模板編輯) -- [x] 缺貨清單分頁 UI(待發送/已發送分開) -- [x] 郵件發送歷史頁面(含內容預覽) -- [x] 每日業績 Telegram/LINE 通知(含趨勢 emoji) -- [x] 修復 scheduler config 目錄掛載問題 -- [x] PostgreSQL 資料庫支援(雙資料庫架構) -- [x] 商品看板效能優化(索引 + FileLock 快取鎖) -- [x] K8s Google Drive 認證(initContainer 架構) -- [x] Ollama AI 服務配置(192.168.0.188) -- [x] Rancher K8s 管理平台部署 -- [x] K3s 集群匯入 Rancher(momo-k3s) -- [x] CI/CD 安全掃描 (Bandit + Trivy) — 原 GitLab CI 已撤除,待新方案重建 -- [x] Kali Linux DevSecOps 工作站(192.168.0.112) -- [x] 自動化端口監控與程式碼掃描 (Cron) -- [x] Registry HTTPS 安全配置(隱藏 5000 端口) -- [x] WireGuard VPN (WG-Easy) 遠端存取 -- [x] 系統重開機自動啟動機制 (systemd + 健康檢查) - ---- - -## 更新記錄 (2026-01-22 ~ 02-13) - -> ⚠️ **2026-04-18 補正說明(ADR-008)**: 以下歷史紀錄多處提到「K8s 遷移」「kubectl rollout restart」「`/home/wooo/momo_pro_system/` 路徑」等,**經實地 SSH 審計確認 EwoooC 從未切換到 K3s/K8s**,實際始終是 188 上的 Docker Compose。相關歷史段落保留為「文件考古」,**不可據此執行任何部署動作**。最新部署 SOP 見本文件開頭「🌐 環境架構總覽」與「部署指令」章節。 - -### 2026-02-13:重開機服務修復與 Clawdbot 整合 - -#### 1. 系統啟動腳本更新 (v2.0) - -**問題**: 每次重開機後服務異常,Harbor 已移除但腳本仍嘗試啟動 - -**修復內容**: -- 更新 `scripts/tools/system_startup.sh` 為 v2.0 版本 -- 移除 Harbor 相關啟動邏輯 -- 改用 docker-registry 作為容器倉庫 -- 簡化啟動流程:Docker → Registry → n8n → Superset → K8s - -**啟動腳本新流程**: -``` -1. 檢查 Docker 服務 -2. 啟動 Docker Registry (docker-registry) -3. 啟動 n8n (momo-n8n) -4. 啟動 Superset (docker compose) -5. 重啟 K8s 應用 (momo-app, momo-scheduler) -6. 健康檢查 + Telegram 通知 -``` - -#### 2. Monitor 首頁更新 - -**問題**: monitor.wooo.work 顯示許多不存在的服務 (Grafana Docker, Portainer, Loki, Metabase 等) - -**修復**: -- 重新設計 Monitor 首頁,只顯示實際部署的服務 -- 移除不存在的服務連結:Docker Grafana、Portainer、Loki、Metabase、Nextcloud、Grist、pgAdmin、cAdvisor - -**目前顯示的服務**: -| 分類 | 服務 | -|------|------| -| 應用服務 | EwoooC (mo.wooo.work / momo.wooo.work) | -| 開發工具 | Docker Registry、n8n | -| 監控服務 | Grafana (K8s)、Prometheus、Alertmanager | -| BI 分析 | Apache Superset | - -#### 3. Harbor 完全移除 - -- 清理所有 Harbor 相關容器 -- 更新域名健康監控腳本移除 Harbor 檢查 -- 改用 docker-registry 容器 - -#### 4. Clawdbot 整合腳本 (Ollama Server) - -**新增腳本** (位於 `192.168.0.188:~/scripts/`): - -| 腳本 | 功能 | 使用方式 | -|------|------|---------| -| `health_check.sh` | 服務健康檢查 | `/ssh ~/scripts/health_check.sh` | -| `k8s_manage.sh` | K8s 管理 | `/ssh ~/scripts/k8s_manage.sh [pods\|logs\|restart]` | -| `backup_manage.sh` | 備份管理 | `/ssh ~/scripts/backup_manage.sh [status\|run]` | -| `sales_query.sh` | 業績查詢 | `/ssh ~/scripts/sales_query.sh [today\|status]` | -| `gemini_cost_report.sh` | Gemini 費用 | 每日 9:00 Cron 自動發送 | - -**SSH Key 設定**: -- Ollama → UAT SSH Key 已添加到 authorized_keys -- 允許 Clawdbot 透過 SSH 執行 K8s 管理命令 - -#### 5. 域名監控更新 - -**檔案**: `scripts/domain-health-monitor.sh` -- 移除 Metabase 監控(未部署) -- 目前監控服務:EwoooC App、Monitor、Registry、n8n、Superset - -#### 6. 監控告警異常修復 (2026-02-13 06:30) - -**發現的問題**: -1. **n8n 容器不存在** - 容器未設置自動重啟或被意外刪除 -2. **Superset 容器未啟動** - 容器意外停止且未自動重啟 -3. **監控腳本配置錯誤** - n8n 和 Superset 監控路徑錯誤(寫成 K8s 但實際是 Docker) - -**服務修復記錄**: -| 服務 | 問題 | 修復方式 | 修復時間 | -|------|------|---------|---------| -| n8n | 容器不存在 | `docker run -d --name momo-n8n ...` | 06:32 | -| Superset | 容器停止 | `cd docker/superset && docker compose up -d` | 06:33 | -| Clawdbot Gateway | 服務停止 | `systemctl --user start clawdbot-gateway` | 06:33 | - -**監控腳本重大更新** (`scripts/domain-health-monitor.sh`): - -```diff -# 舊配置(錯誤) -- ["https://monitor.wooo.work/n8n/healthz"]="200|n8n (K8s)|kubectl rollout restart ..." -- ["https://monitor.wooo.work/superset/health"]="200|Superset (K8s)|kubectl rollout restart ..." -- 固定等待時間 30 秒 - -# 新配置(正確) -+ ["http://127.0.0.1:5678/"]="200|n8n|docker start momo-n8n ..."|30" -+ ["http://127.0.0.1:8088/health"]="200|Superset|cd docker/superset && docker compose up -d|60" -+ 加入重試機制(最多 3 次,每次間隔 10 秒) -``` - -**服務優先級定義**: -| 優先級 | 服務 | 說明 | -|--------|------|------| -| 1 (最高) | EwoooC App | 核心業務 | -| 2 | Registry | 映像倉庫 | -| 3 | n8n | 自動化工作流程 | -| 4 | Superset | BI 分析平台 | -| 5 | Monitor 頁面 | 監控入口 | - -**最終服務狀態** (06:38 驗證): -| 服務 | 狀態碼 | 狀態 | -|------|--------|------| -| EwoooC App (mo) | 200 | ✅ 正常 | -| EwoooC App (momo) | 200 | ✅ 正常 | -| Registry | 401 | ✅ 正常 (需認證) | -| n8n | 200 | ✅ 正常 | -| Superset | 200 | ✅ 正常 | -| Monitor | 200 | ✅ 正常 | - -**變更檔案**: -- `scripts/domain-health-monitor.sh` - 修正監控路徑、增加等待時間、加入重試機制 - -#### 7. 下午修復 (2026-02-13 17:00) - -**Clawdbot Gateway 穩定性優化**: -- **問題**:Clawdbot Gateway 經常異常,出現鎖定檔案衝突 -- **原因**:舊進程未正確終止,留下 `~/.clawdbot/gateway.lock` -- **修復**:在 systemd service 添加 `ExecStartPre` 清理鎖定檔案 -- **位置**:`~/.config/systemd/user/clawdbot-gateway.service` (192.168.0.188) - -```ini -[Service] -ExecStartPre=/bin/bash -c 'pkill -9 clawdbot 2>/dev/null || true; rm -f ~/.clawdbot/gateway.lock 2>/dev/null || true; sleep 2' -ExecStart=/usr/bin/clawdbot gateway -Restart=always -RestartSec=15 -``` - -**已修復問題**: -| 問題 | 狀態 | 修復方式 | -|------|------|---------| -| Monitor Logo 破圖 | ✅ 已修復 | 創建 `/var/www/monitor/monitor-static/images/` 並複製 logo | -| Superset 404 | ✅ 已修復 | Nginx 添加 `/superset/` 重定向和 `/superset/login/` 代理規則 | -| Clawdbot Linger | ✅ 已修復 | `loginctl enable-linger ollama` 已啟用 | - -**Superset Nginx 配置修復**: -```nginx -# 根路徑重定向 -location /superset/ { - if ($request_uri = /superset/) { - return 302 /superset/welcome/; - } - # ... 其他配置 -} - -# 登入頁面代理到正確路徑 -location = /superset/login/ { - proxy_pass http://superset_backend/login/; - # ... 其他 headers -} -``` - -**待修復問題**: -| 問題 | 狀態 | 說明 | -|------|------|------| -| Clawdbot Telegram 衝突 | ⏳ 待修復 | Bot Token 與監控告警系統共用,需創建新的 Telegram Bot 給 Clawdbot | - -**最終服務狀態** (17:09 驗證): -| 服務 | 狀態碼 | 狀態 | -|------|--------|------| -| Monitor Logo | 200 | ✅ 正常 | -| Superset 根路徑 | 302 | ✅ 重定向正常 | -| Superset Login | 200 | ✅ 正常 | -| MOMO App | 200 | ✅ 正常 | - -#### 8. 晚間修復 (2026-02-13 19:30-20:00) - -**Superset 登入轉圈問題修復**: -- **問題**:Superset 登入頁面一直轉圈,無法登入 -- **原因**:`/static/` 靜態資源路徑返回 302 重定向而非正確的代理 -- **修復**:修改 Nginx `/static/` location 為直接代理到 Superset backend - -```nginx -# 修正後 -location ^~ /static/ { - proxy_pass http://superset_backend/static/; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; -} -``` - -**n8n 工作流程重新匯入**: -- **問題**:n8n 只有 1 個工作流程(應有 29 個) -- **原因**:n8n 容器重建後資料遺失 -- **修復**:從 `n8n-workflows/` 目錄重新匯入所有 27+ 個工作流程 - -**Alertmanager 路由規則更新**: -- **問題**:Telegram 告警未正確發送到 momo namespace 的告警 -- **修復**:Helm upgrade prometheus-stack 套用正確的路由規則 - -**n8n executeCommand 節點問題**: -- **問題**:n8n 2.6.4 版本不支援 `executeCommand` 節點類型 -- **影響工作流程**:UAT 自動修復、Clawdbot 健康監控、UAT 頁面健康監控 -- **修復**:停用有問題的工作流程,改用 Cron 腳本自動修復(已正常運作) - -**自動修復機制確認**: -| 機制 | 狀態 | 說明 | -|------|------|------| -| Cron domain-health-monitor.sh | ✅ 運作中 | 每 5 分鐘檢查 7 個服務,自動修復 | -| n8n 磁碟空間監控 | ✅ 運作中 | 自動清理磁碟 | -| n8n 健康監控 | ✅ 運作中 | HTTP 檢查 UAT 服務 | -| Prometheus Alertmanager | ✅ 運作中 | K8s momo namespace 告警 → Telegram | -| n8n 自動修復工作流程 | ❌ 已停用 | executeCommand 不支援,改用 Cron | - -**最終驗證** (20:00): -- ✅ Superset 登入成功 -- ✅ 所有 7 個監控服務正常 -- ✅ Telegram 告警測試成功 -- ✅ Cron 自動修復機制運作正常 -- ✅ 29 個 n8n 工作流程已載入 - -#### 9. 進階自動修復系統部署 (2026-02-13 20:15) - -**新增自動修復腳本** (`scripts/auto-repair/`): - -| 腳本 | 功能 | 執行頻率 | -|------|------|---------| -| `oom-handler.sh` | OOM 檢測,自動增加記憶體 +50% 並重啟 | 每 15 分鐘 | -| `postgres-repair.sh` | 連線檢測、死鎖處理、VACUUM、週備份 | 每 30 分鐘 | -| `auto-rollback.sh` | 連續 5 次健康檢查失敗,自動回滾版本 | 每 5 分鐘 | -| `master-auto-repair.sh` | 主協調腳本 | 每 5 分鐘 | - -**Cron 設定**: -```bash -*/5 * * * * /home/wooo/scripts/auto-repair/master-auto-repair.sh >> /var/log/master_auto_repair.log 2>&1 -``` - -**自動修復能力總覽**: - -| 問題類型 | 自動修復方式 | 狀態 | -|---------|-------------|------| -| OOM 記憶體不足 | 自動增加限制 +50%,重啟 Pod | ✅ 已實作 | -| PostgreSQL 連線失敗 | 自動重啟 Pod | ✅ 已實作 | -| PostgreSQL 死鎖 | 終止長時間查詢 | ✅ 已實作 | -| 表膨脹/Dead Tuples | 自動 VACUUM ANALYZE | ✅ 已實作 | -| 程式碼 Bug (5次失敗) | 自動回滾到上一版本 | ✅ 已實作 | -| 服務無回應 | Cron 腳本自動重啟 | ✅ 已實作 | - -**新增 Prometheus 告警規則** (`k8s/monitoring/complete-alerting-rules.yaml`): - -完整的 Prometheus → Alertmanager → Telegram → 自動修復 流程配置: - -```yaml -# 告警規則群組 -- name: memory-alerts # OOM 相關告警 -- name: postgres-alerts # PostgreSQL 告警 -- name: app-health-alerts # 應用健康告警 -- name: infrastructure-alerts # 基礎設施告警 -``` - -| 告警名稱 | 觸發條件 | 告警等級 | 自動修復標籤 | -|---------|---------|---------|-------------| -| `PodOOMKilled` | OOMKilled 事件 | critical | `oom-handler` | -| `HighMemoryUsage` | 記憶體 > 85% | warning | 無 | -| `MemoryNearLimit` | 記憶體 > 95% | critical | `oom-handler` | -| `PostgresDown` | pg_up == 0 | critical | `postgres-repair` | -| `PostgresHighConnections` | 連線 > 80% | warning | `postgres-repair` | -| `PostgresDeadlocks` | 死鎖發生 | warning | `postgres-repair` | -| `PostgresSlowQueries` | 查詢 > 5 分鐘 | warning | `postgres-repair` | -| `MomoAppDown` | 健康檢查失敗 | critical | `auto-rollback` | -| `HighHTTP5xxRate` | 5xx > 5% | warning | `auto-rollback` | -| `PodRestartTooMany` | 1小時重啟>5次 | warning | `auto-rollback` | -| `DiskSpaceLow` | 磁碟 < 15% | warning | `disk-cleanup` | -| `DiskSpaceCritical` | 磁碟 < 5% | critical | `disk-cleanup` | -| `MomoSchedulerDown` | Scheduler 停止 | critical | `domain-health-monitor` | - -**監控 → 告警 → 自動修復 完整流程圖**: - -``` -┌─────────────────────────────────────────────────────────────────────────┐ -│ 完整監控與自動修復架構 │ -├─────────────────────────────────────────────────────────────────────────┤ -│ │ -│ 監控層 告警層 修復層 │ -│ ─────── ─────── ─────── │ -│ │ -│ ┌─────────┐ ┌─────────┐ ┌─────────────────┐ │ -│ │Prometheus│ ──────▶│Alertmgr │ ──Telegram──▶│ Cron 自動修復 │ │ -│ │ 指標收集 │ │告警路由 │ │ master-auto- │ │ -│ └─────────┘ └─────────┘ │ repair.sh │ │ -│ │ │ └────────┬────────┘ │ -│ ▼ ▼ │ │ -│ PrometheusRule webhook (預留) ▼ │ -│ (告警規則) └── n8n 整合 ┌────────────┐ │ -│ │ oom-handler │ │ -│ ┌─────────┐ ┌─────────┐ │ postgres- │ │ -│ │域名健康 │ ──────▶│Telegram │ ◀─────────────│ repair │ │ -│ │監控腳本 │ │告警通知 │ │ auto- │ │ -│ └─────────┘ └─────────┘ │ rollback │ │ -│ (每5分鐘Cron) └────────────┘ │ -│ │ -└─────────────────────────────────────────────────────────────────────────┘ -``` - -**n8n 工作流程更新** (2026-02-13 20:30): - -- 重新匯入所有 27+ 個工作流程到 n8n -- 停用使用 `executeCommand` 節點的工作流程 (n8n 2.6.4 不支援) -- 停用的工作流程改用 Cron 自動修復機制代替: - - UAT 自動修復 → Cron: domain-health-monitor.sh - - Clawdbot 健康監控 → Cron: 已整合 - -**Git 提交記錄** (2026-02-13 21:00): - -``` -feat: 完整監控告警與自動修復系統 - -新增自動修復腳本 (scripts/auto-repair/): -- oom-handler.sh: OOM 自動增加記憶體 -- postgres-repair.sh: PostgreSQL 自動維護 -- auto-rollback.sh: 健康檢查失敗自動回滾 -- master-auto-repair.sh: 主協調腳本 - -新增 Prometheus 告警規則: -- k8s/monitoring/complete-alerting-rules.yaml -- 14 個告警規則覆蓋所有核心服務 -- 每個告警都有對應的自動修復標籤 -``` - -**已提交至 Git 版控**: ✅ - ---- - -### 2026-02-09:域名健康監控與服務修復 - -#### 1. 新增域名健康監控腳本 - -**腳本**: `scripts/domain-health-monitor.sh` -**Cron**: 每 5 分鐘執行 - -**功能**: -- 監控 8 個核心域名健康狀態 -- 自動發送 Telegram 告警 -- 嘗試自動修復(重啟容器/K8s Pod) -- 等待 30 秒驗證修復結果 - -#### 2. 更新 Nginx 配置 - -**檔案**: `docker/nginx/monitor-clean.conf` - -**變更**: -- 移除 Harbor 相關配置(Harbor 已廢棄) -- 新增 Docker Registry 反向代理(port 5002) -- 更新 Prometheus 使用 K8s ClusterIP (10.43.25.78:9090) -- 更新 Alertmanager 使用 K8s ClusterIP (10.43.79.187:9093) - -#### 3. Docker 服務修復 - -**n8n 容器重建**: -- 問題:Task Runner 403 錯誤 + 端口綁定問題 -- 修復:添加 `N8N_RUNNERS_DISABLED=true` 環境變數 -- 重建命令: -```bash -docker run -d --name momo-n8n --restart unless-stopped \ - -p 5678:5678 \ - -e N8N_BASIC_AUTH_USER=admin \ - -e N8N_BASIC_AUTH_PASSWORD=Wooo_N8n_2026 \ - -e N8N_RUNNERS_DISABLED=true \ - -v n8n_data:/home/node/.n8n \ - n8nio/n8n:latest -``` - -**Superset 容器修復**: -- 問題:Docker 重啟後容器停止 -- 修復:`cd /home/wooo/momo_pro_system/docker/superset && docker compose up -d` - -#### 4. 當前服務狀態 (2026-02-09 驗證) - -**正常運作的服務**: - -| 服務 | URL | 狀態碼 | -|------|-----|-------| -| EwoooC App (mo) | https://mo.wooo.work/health | 200 ✅ | -| EwoooC App (momo) | https://momo.wooo.work/health | 200 ✅ | -| Monitor 首頁 | https://monitor.wooo.work/ | 200 ✅ | -| Superset | https://monitor.wooo.work/superset/login/ | 200 ✅ | -| Metabase | https://monitor.wooo.work/metabase/ | 200 ✅ | -| n8n | http://192.168.0.110:5678/ | 200 ✅ | -| Registry | https://registry.wooo.work/v2/ | 401 ✅ | -| K8s Grafana | https://monitor.wooo.work/k8s-grafana/ | 302 ✅ | -| Prometheus | https://monitor.wooo.work/prometheus/ | 302 ✅ | -| Alertmanager | https://monitor.wooo.work/alertmanager/ | 200 ✅ | - -**未部署的服務**: -- Nextcloud: 無容器 -- Grist: 無容器 -- Docker Grafana: 無容器(使用 K8s Grafana) -- Portainer: 無容器 -- Loki: 無容器 -- cAdvisor: 無容器 -- Blackbox Exporter: 無容器 - -**已知問題**: -- `harbor-core` 容器卡在 restart loop(需 root 權限移除) -- `cattle-cluster-agent` CrashLoopBackOff(Rancher 相關,不影響核心服務) - ---- - -### 2026-02-08:Superset 子路徑代理修復 - -#### 1. 修復 Superset 雙重前綴問題 - -**問題**:訪問 `/superset/` 會跳轉到 `/superset/superset/welcome/` - -**根本原因**: -- Superset 內部 Flask blueprints 已使用 `/superset/...` 路由 -- `superset_config.py` 中 `x_prefix=1` 會再次添加前綴 -- Nginx `proxy_redirect / /superset/;` 也會重複添加 - -**修復方案**: -1. `superset_config.py`: 設置 `x_prefix=0` 禁用從 X-Forwarded-Prefix 讀取前綴 -2. `nginx-superset.conf`: 使用智能正則表達式保留已有 `/superset/` 前綴 - ```nginx - proxy_redirect /superset/ /superset/; - proxy_redirect ~^/(?!superset)(.*)$ /superset/$1; - ``` - -#### 2. 修復 Superset 登入重定向問題 - -**問題**:訪問 `/superset/login/` 後跳轉到 `/login/` → 404 - -**根本原因**: -- Superset JavaScript bootstrap 資料中 `user_login_url: "/login/"` 沒有前綴 -- 瀏覽器直接訪問 `/login/` 時 Nginx 無法匹配到 Superset - -**修復方案**:在 Nginx 添加多個重定向規則 -```nginx -location = /login/ { - return 302 /superset/login/; -} -location = /logout/ { - return 302 /superset/logout/; -} -location ^~ /lang/ { - return 302 /superset$request_uri; -} -location ^~ /users/ { - return 302 /superset$request_uri; -} -``` - -**修改檔案**: -- `docker/superset/superset_config.py` - 設置 `x_prefix=0` -- `docker/superset/nginx-superset.conf` - 更新代理規則和重定向 - -**驗證結果**: -| 路徑 | 狀態 | -|------|------| -| `/superset/` | ✅ 302 → `/superset/welcome/` | -| `/superset/login/` | ✅ 200 (登入頁面) | -| `/login/` | ✅ 302 → `/superset/login/` | -| `/logout/` | ✅ 302 → `/superset/logout/` | - ---- - -### 2026-02-07:CI/CD Dashboard + 移除 Harbor - -#### 1. Harbor 完全移除,改用 Docker Registry - -**變更原因**:Harbor 資源佔用大、穩定性問題,改用輕量級 Docker Registry - -| 項目 | 舊配置 | 新配置 | -|------|--------|--------| -| Registry URL | `harbor.wooo.work` | `registry.wooo.work` | -| Port | 5050 | 5002 (內部) → Nginx 反向代理 | -| 認證 | Harbor 內建 | Nginx Basic Auth | -| 映像路徑 | `harbor.wooo.work/wooo/momo-pro-system:latest` | `registry.wooo.work/wooo/momo-pro-system:latest` | - -**新增/修改檔案**: -- `docker/registry/docker-compose.yml` - Docker Registry 配置 -- `docker/registry/config.yml` - Registry 儲存配置 -- `config/nginx/sites-available/registry` - Nginx 反向代理配置 - -#### 2. CI/CD Dashboard 完整版 - -**URL**: `https://mo.wooo.work/cicd` - -**功能**: -- 即時 Pipeline 流程視覺化(Test → Build → Deploy) -- UAT/PROD 環境健康狀態卡片 -- Pod 狀態監控(透過 SSH 查詢) -- 快速操作按鈕(部署 UAT/PROD、回滾) -- 成功率統計、今日部署次數 -- 10 秒自動刷新 - -**新增檔案**: -- `routes/cicd_routes.py` - CI/CD API 路由 (1354 行) -- `templates/cicd_dashboard.html` - Dashboard 前端頁面 - -**API 端點**: -| 端點 | 方法 | 說明 | -|------|------|------| -| `/api/cicd/status` | GET | 取得完整 CI/CD 狀態 | -| `/api/cicd/pipelines` | GET | 取得最近 Pipeline 列表 | -| `/api/cicd/environments` | GET | 取得所有環境狀態 | -| `/api/cicd/deploy/` | POST | 觸發部署 | -| `/api/cicd/rollback/` | POST | 回滾部署 | - -#### 3. CI/CD UAT 自動部署(歷史紀錄) - -> **2026-04-18 備註**: 當時的 CI/CD 建構在 GitLab CI 之上,GitLab 已撤除,以下內容僅作歷史參考。 - -**當時流程**:push main → UAT 自動部署 - -**當時 Pipeline 階段**: -``` -push main - │ - ├─ test (pytest) - ├─ security-scan (Bandit) - │ - ├─ build (docker build + push to registry.wooo.work) - │ - └─ deploy-uat (SSH → git pull → kubectl restart) -``` - -#### 4. 監控告警標籤 - -**Alertmanager (Prometheus) 告警模板**: -``` -🏢 環境: UAT (mo.wooo.work) -``` - -**n8n 頁面健康監控**: -- UAT 監控:`n8n-workflows/17-page-health-monitor.json` - - 告警標題:`🔴 *EwoooC 頁面異常*` - - 標籤:`(mo.wooo.work / momo.wooo.work)` - -#### 5. wooo 用戶 kubectl 無密碼訪問設定 - -```bash -# 設定 kubeconfig -sudo cp /etc/rancher/k3s/k3s.yaml /home/wooo/.kube/config -sudo chown wooo:wooo /home/wooo/.kube/config - -# 加入 bashrc -echo 'export KUBECONFIG=/home/wooo/.kube/config' >> ~/.bashrc -``` - -**修改檔案清單**: -- `app.py` - 註冊 CI/CD Blueprint -- `routes/cicd_routes.py` - 新增 -- `templates/cicd_dashboard.html` - 新增 -- `n8n-workflows/17-page-health-monitor.json` - 環境標籤 -- `k8s/monitoring/values-prometheus.yaml` - Alertmanager 環境標籤 - -#### 6. PostgreSQL 監控部署 (新增) - -**部署內容**: -- `k8s/monitoring/postgres-exporter.yaml` - PostgreSQL Exporter Deployment + ServiceMonitor -- `k8s/monitoring/postgres-alerting-rules.yaml` - 資料庫告警規則 - -**監控指標**: -| 指標 | 閾值 | 告警等級 | -|------|------|----------| -| 連線數 | > 80 | Warning | -| 連線數 | > 95 | Critical | -| 資料庫停機 | pg_up == 0 | Critical | -| 資料庫大小 | > 5GB | Warning | -| 資料庫大小 | > 8GB | Critical | -| 死鎖 | > 0 | Warning | -| 表膨脹 (Dead Tuples) | > 100,000 | Warning | - -#### 7. n8n 工作流程匯入完成 (22個) - -**原有工作流程 (20個)**: -- 系統監控:磁碟空間、SSL 證書、CI/CD 通知、備份、爬蟲、K8s Pod、Harbor -- 定期報告:每日系統報告、每週業績、月初提醒 -- AI 監控:Ollama 健康、K8s 健康、慢查詢 - -**新增工作流程 (2個)**: -- `17-uat-health-monitor.json` - UAT 健康監控 -- `18-postgres-health-monitor.json` - PostgreSQL 資料庫監控 - -**工作流程匯入方式**: -```bash -docker exec momo-n8n n8n import:workflow --input=/home/node/n8n-workflows/.json -``` - -#### 8. 集中式監控架構 - -**架構決策**:所有監控告警統一由 UAT 主機管理 - -``` -┌─────────────────────────────────────────────────────────────────┐ -│ UAT 主機 (192.168.0.110) │ -│ 統一監控與排程中心 │ -├─────────────────────────────────────────────────────────────────┤ -│ Prometheus → Alertmanager → Telegram │ -│ n8n 工作流程 │ -│ • 監控 EwoooC App (mo.wooo.work / momo.wooo.work) │ -│ • PostgreSQL 健康檢查 │ -└─────────────────────────────────────────────────────────────────┘ -``` - -**分工原則**: -| 類型 | 工具 | 位置 | -|------|------|------| -| 監控告警 | Prometheus + n8n | UAT 集中管理 | -| 業務排程 | momo-scheduler | K8s 內執行 | - ---- - -### 2026-02-02:監控系統修復與安全加固 - -**修復項目**: - -#### 1. K8s Scheduler CrashLoopBackOff 修復 -- **問題**:momo-scheduler Pod 重啟 387 次,持續 CrashLoopBackOff -- **原因**:`run_scheduler.py` 檔案遺失 -- **解決**:建立新的 `run_scheduler.py` 獨立排程腳本 -- **新增檔案**:`run_scheduler.py` - -```python -# run_scheduler.py 排程任務 -- 每小時:主站爬蟲、EDM 爬蟲 -- 每 6 小時:購物節爬蟲 -- 每 30 分鐘:Google Drive 自動匯入、網頁白頁監控 -``` - -#### 2. vendor-stockout 500 錯誤修復 -- **問題**:訪問 `/vendor-stockout/` 返回 Internal Server Error -- **原因**:模板位置錯誤 (`vendor_stockout_index.html` 應為 `templates/vendor_stockout/index.html`) -- **解決**:建立 `templates/vendor_stockout/` 目錄並移動所有模板 - -``` -templates/vendor_stockout/ -├── index.html -├── list.html -├── history.html -├── import.html -├── send_email.html -└── vendor_management.html -``` - -#### 3. Alertmanager 無法發送 Telegram 告警修復 -- **問題**:Alertmanager 日誌持續報錯 `can't evaluate field StartsAt in type *template.Data` -- **原因**:Telegram 訊息模板錯誤,`.StartsAt` 放在 `{{ range .Alerts }}` 區塊外 -- **解決**:修正模板,將 `.StartsAt` 移入 range 區塊內 -- **修改檔案**:`k8s/monitoring/values-prometheus.yaml` - -```yaml -# 修正前(錯誤) -{{ range .Alerts }}...{{ end }} -⏰ 時間: {{ .StartsAt.Local.Format "..." }} # 錯誤:在 range 外 - -# 修正後(正確) -{{ range .Alerts }} -... -⏰ 時間: {{ .StartsAt.Local.Format "..." }} # 正確:在 range 內 -{{ end }} -``` - -#### 4. n8n 配置損壞修復 -- **問題**:n8n 容器持續重啟,配置檔案損壞 -- **解決**:重新寫入正確的 JSON 配置 - -#### 5. 伺服器安全加固 - -**192.168.0.188 (Ollama Server)**: -- ✅ 安裝 Fail2Ban (SSH 3 次失敗封鎖 1 小時) -- ✅ 安裝 UFW 防火牆 -- ✅ AI 服務限制內網存取 (192.168.0.0/24) - -**192.168.0.110 (UAT Server)**: -- ✅ Fail2Ban 已運行 (確認配置正確) -- ✅ SSH + Nginx 雙重保護 - -**UFW 規則 (192.168.0.188)**: -| 端口 | 服務 | 存取範圍 | -|------|------|---------| -| 22 | SSH | 公開 | -| 80/443 | HTTP/HTTPS | 公開 | -| 11434 | Ollama API | 僅內網 | -| 3000 | Open WebUI | 僅內網 | -| 5678 | n8n | 僅內網 | -| 8080 | SearXNG | 僅內網 | - -**監控系統現況**: -``` -Prometheus (抓取指標) → PrometheusRule (告警規則) → Alertmanager → Telegram Bot -``` - -目前告警規則: -- PodOOMKilled, PodRestartTooMany, PodNotReady, PodPending -- HighMemoryUsage, MemoryNearLimit, HighCPUUsage -- PostgresDown, PVCSpaceLow -- MomoAppDown, HighHTTP5xxRate -- NodeMemoryLow, NodeDiskLow, NodeNotReady - ---- - -### 2026-01-31:系統重開機自動啟動機制 - -**問題發現**: -- 伺服器重開機後 Registry 容器未正常啟動 (Exit 128) -- K8s Pod 顯示 `ImagePullBackOff` 無法拉取映像 -- mo.wooo.work 返回 502 Bad Gateway -- 系統負載過高 (load average 112) - -**根本原因**: -1. Registry 容器重開機後未自動啟動 -2. Supabase 不必要服務佔用大量資源 -3. 沒有統一的服務啟動順序管理 - -**解決方案**: -1. 建立 `scripts/tools/system_startup.sh` 統一啟動腳本 -2. 建立 `momo-startup.service` systemd 服務 -3. 啟動順序:Docker → 停止 Supabase 多餘服務 → Registry → K8s → 監控 -4. 每個服務啟動後進行健康檢查 -5. 啟動完成後發送 Telegram 通知 - -**新增檔案**: -- `scripts/tools/system_startup.sh` - 系統啟動腳本 -- `scripts/tools/momo-startup.service` - systemd 服務定義 -- `deploy/lib/registry.sh` - Registry 管理函數庫 -- `deploy/lib/k8s.sh` - K8s 管理函數庫 -- `deploy/lib/monitoring.sh` - 監控與通知函數庫 -- `deploy/lib/systemd.sh` - Systemd 服務管理函數庫 - -**部署步驟**: -```bash -# 1. 上傳腳本 -scp scripts/tools/system_startup.sh wooo@192.168.0.110:/home/wooo/momo_pro_system/scripts/tools/ - -# 2. 安裝 systemd 服務 -sudo cp /tmp/momo-startup.service /etc/systemd/system/ -sudo systemctl daemon-reload -sudo systemctl enable momo-startup.service -``` - -**驗證方式**: -```bash -# 確認服務已啟用 -systemctl is-enabled momo-startup.service # 應返回 enabled - -# 查看啟動日誌 -sudo journalctl -u momo-startup.service -``` - ---- - -### 2026-01-28:502 Bad Gateway 排查與自動修復 - -**問題發現**: -- mo.wooo.work 返回 502 Bad Gateway -- Nginx 無法連接到後端 Docker 容器 - -**排查過程**: -1. 檢查 Docker 容器狀態 → 顯示 healthy -2. 測試外部端口 `curl 127.0.0.1:5001` → Connection reset by peer -3. 測試容器內部 `docker exec curl` → HTTP 200 正常 -4. 診斷結論:Docker 網路轉發失效 - -**解決方案**: -```bash -sudo systemctl restart docker -``` - -**新增功能**: -- 新增自動化監控腳本 `scripts/docker_health_monitor.sh` -- 每 5 分鐘自動檢查服務健康狀態 -- 檢測到 Docker 網路問題時自動修復 -- 發送 Telegram 告警通知 - -**修改檔案**: -- `CLAUDE.md` - 新增問題 #4, #5 和自動修復文檔 -- `scripts/docker_health_monitor.sh` - Docker 網路監控腳本 -- `scripts/registry_health_monitor.sh` - Registry 服務監控腳本 -- `k8s/04-momo-app.yaml` - 改用 registry.wooo.work 域名 -- `k8s/05-scheduler.yaml` - 改用 registry.wooo.work 域名 - ---- - -### 2026-01-28:Registry 服務中斷與 K8s 映像拉取修復 - -**問題發現**: -- Google Drive 自動匯入功能停止 -- K8s Pods 顯示 `ImagePullBackOff` - -**根本原因**: -1. Docker Registry 服務停止 -2. Registry 端口綁定 127.0.0.1:5000(安全配置) -3. K8s 嘗試從 192.168.0.110:5000 拉取映像失敗 - -**解決方案**: -1. 啟動 Registry: `cd /home/wooo/devops/registry && docker compose up -d` -2. K8s 配置改用 HTTPS 域名 `registry.wooo.work` -3. 重建 registry secret 使用新域名 -4. 重啟 K8s Deployments - -**預防措施**: -- 建立 Registry 健康監控腳本 -- 加入 cron 排程自動檢查 -- UAT crontab - 新增排程任務 - ---- - -### 2026-01-28:當日業績匯入 OOM 問題修復 - -**問題發現**: -- 自動匯入通知發送成功,但 `/daily_sales` 頁面顯示「資料表為空」 -- 匯入任務卡在 60% 進度 - -**排查過程**: -1. 檢查 `import_jobs` 表發現任務狀態為 `importing`,進度 60% -2. 查看 Docker 日誌發現 `Worker was sent SIGKILL! Perhaps out of memory?` -3. 確認 OOM 導致匯入進程被殺死 - -**解決方案**: -1. 找到暫存檔案 `/app/data/temp/即時業績_當日.xlsx`(匯入中途失敗但已下載) -2. 手動觸發匯入: - ```python - from services.import_service import import_service - import os - file_path = '/app/data/temp/即時業績_當日.xlsx' - job_id = import_service.create_import_job('daily_sales', None, '即時業績_當日.xlsx', os.path.getsize(file_path)) - import_service.process_daily_sales_import(job_id, file_path) - ``` -3. 成功匯入 19,099 筆資料(2026-01-01 ~ 2026-01-27) - -**資料流向說明(重要)**: -``` -Excel 檔案 → import_service.process_daily_sales_import() - │ - ├→ daily_sales_snapshot (主資料表,77欄位,含 snapshot_date) - │ - └→ realtime_sales_monthly (副本,28欄位,供業績分析儀表板) -``` -- **正確方向**:`daily_sales_snapshot` → `realtime_sales_monthly`(同步寫入) -- **禁止反向操作**:不可從 `realtime_sales_monthly` 同步回 `daily_sales_snapshot` - -**修改檔案**: -- `services/import_service.py` - 新增日期範圍追蹤 -- `scheduler.py` - 增強匯入通知格式 -- `scripts/google_drive_monitor.sh` - Google Drive 認證監控 -- `scripts/system_health_monitor.sh` - 統一健康監控腳本 - ---- - -### 2026-01-28:Ollama 502 Bad Gateway 修復與監控機制 - -**問題發現**: -- `http://192.168.0.188/` 返回 502 Bad Gateway -- Ollama API `http://192.168.0.188:11434/api/tags` 正常回應 - -**診斷過程**: -1. 檢查 nginx 進程 → 正常運行 -2. 檢查 nginx error.log → `recv() failed (104: Unknown error)` 指向 `127.0.0.1:3000` -3. 發現 nginx 代理到 **Open WebUI** 容器 (port 3000),非直接代理 Ollama -4. 檢查 Open WebUI 容器 → 容器內部正常,但外部無法連接 -5. 診斷結論:**Docker 端口映射失效** - -**解決方案**: -```bash -ssh ollama@192.168.0.188 -sudo systemctl restart docker -# 等待 60 秒後所有容器恢復 -``` - -**新增監控機制**: -- **監控腳本**: `scripts/ollama_health_monitor.sh` -- **n8n 工作流程**: `n8n-workflows/14-ollama-health-monitor.json` -- **排程**: 每 5 分鐘執行 (cron) -- **自動修復**: - - Open WebUI 502 → 重啟 Docker daemon - - Open WebUI 容器卡住 → 重啟容器 - - Nginx 停止 → 重啟 nginx - - Ollama 停止 → 重啟 Ollama - -**部署到 192.168.0.188**: -```bash -# 1. 上傳監控腳本 -scp scripts/ollama_health_monitor.sh ollama@192.168.0.188:/home/ollama/scripts/ - -# 2. 設定 cron -*/5 * * * * /home/ollama/scripts/ollama_health_monitor.sh >> /var/log/ollama_health_monitor.log 2>&1 - -# 3. 設定 sudoers(無密碼重啟) -ollama ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart docker, /usr/bin/systemctl restart nginx, /usr/bin/systemctl restart ollama -``` - -**修改檔案**: -- `scripts/ollama_health_monitor.sh` - 新增 Ollama/Open WebUI 監控腳本 -- `n8n-workflows/14-ollama-health-monitor.json` - 新增 n8n 工作流程 -- `CLAUDE.md` - 更新 Ollama 監控文檔 - ---- - -### 2026-01-25:DevSecOps 安全強化 + WireGuard VPN - -**Registry 安全配置**: -- 端口 5000 改為僅綁定 127.0.0.1 (內部) -- 外部訪問改用 `registry.wooo.work` (HTTPS) -- CI/CD 腳本更新使用域名 -- 減少攻擊面 - -**WireGuard VPN (WG-Easy)**: -- 部署於 Kali Linux (192.168.0.112) -- 管理介面:`http://192.168.0.112:51821` -- 外部端點:`114.32.151.246:51820/udp` -- QR Code 掃描連線 - -**CI/CD 安全掃描(已退役,待新方案重建)**: -- 曾在 GitLab CI 上設置 `security-code-scan`(每次 Push 執行 Bandit)與 `container-security-scan`(每週 Trivy 容器弱點掃描) -- 掃描結果自動發送 Telegram 通知 -- 隨 GitLab 撤除而停用,後續需在新 CI/CD 平台重建 - -**SSL/TLS 安全審核 (SSLyze)**: -- mo.wooo.work 掃描結果:✅ 全部通過 -- SSL 2.0/3.0, TLS 1.0/1.1 已禁用 -- TLS 1.2/1.3 啟用強加密套件 -- HSTS 已啟用,無 Heartbleed/CRIME 弱點 - -**Trivy 容器掃描結果**: -- 掃描 `192.168.0.110:5050/wooo/momo-pro-system:latest` -- HIGH: 56 (Debian 基礎映像) -- CRITICAL: 0 -- Python 套件: 0 弱點 - -**Kali Linux DevSecOps 工作站 (192.168.0.112)**: -- 建立集中式安全掃描環境 -- 部署 `port_monitor.py`:Nmap 端口監控,每小時執行 -- 部署 `code_security_scan.py`:Bandit 程式碼掃描,每日執行 -- 建立端口基線:9 個端口 (192.168.0.110: 7, 192.168.0.188: 2) -- 程式碼掃描結果:16 HIGH, 65 MEDIUM, 72 LOW - ---- - -### 2026-01-24:K8s 配置優化 + Rancher 部署 - -**K8s Google Drive 認證修復**: -- 問題:K8s Secret 為唯讀,OAuth Token 無法刷新 -- 解法:使用 initContainer 將認證複製到 emptyDir(可寫入) -- 修改檔案:`k8s/04-momo-app.yaml`、`k8s/05-scheduler.yaml` -- 新增 Secret:`google-drive-credentials` - -**Ollama AI 配置更新**: -- Host 更新為 `http://192.168.0.188:11434`(原本錯誤指向 192.168.0.110) -- 模型更新為 `llama3:70b-instruct-q2_K` -- 修改檔案:`k8s/02-configmap.yaml` - -**n8n 白頁修復**: -- 問題:子路徑 `/n8n/` 顯示空白頁面 -- 解法:新增環境變數 `N8N_PATH_PREFIX=/n8n` -- 修改檔案:UAT `docker-compose.yml` - -**Rancher 部署 + K3s 匯入**: -- 安裝 Rancher v2.13.1 via Docker -- URL:`https://192.168.0.110:8443` -- K3s 集群 `momo-k3s` 已成功匯入 -- Cluster ID:`c-52mz8` -- 修改檔案:`k8s/nginx/monitor.conf`(新增 `/rancher/` 代理) - ---- - -### 2026-01-24:K8s 遷移完成 - -**重大變更**: -- ✅ **K3s 部署完成** - 版本 v1.34.3+k3s1 -- ✅ **PostgreSQL 資料遷移完成** - 資料庫: `momo_analytics` -- ✅ **VM Nginx 配置更新** - 指向 K8s ClusterIP - -**K8s 資源**: -| 資源 | 說明 | -|------|------| -| momo-app | Flask 應用 Deployment | -| momo-postgres | PostgreSQL StatefulSet | -| momo-scheduler | 爬蟲排程 Deployment | -| momo-data-pvc | 應用資料 PVC (5Gi) | -| postgres-pvc | 資料庫 PVC (10Gi) | - -**網路架構**: -``` -用戶 → mo.wooo.work:443 → VM Nginx (SSL) → K8s ClusterIP 10.43.238.49:80 → momo-app -``` - -**備份位置**: -- `/home/wooo/backups/pre-k8s-migration/` -- 回滾腳本: `quick_rollback.sh` - ---- - -### 2026-01-22:CI/CD 初版部署(已退役) - -> **2026-04-18 備註**: 原本以 GitLab CE + Docker Registry + Watchtower 為核心的 CI/CD 架構已撤除,此段僅作歷史紀錄。Docker Registry 與 Watchtower 仍保留在 UAT 運作。 - -**當時架構重點**: -- 自建 Git 伺服器 + CI/CD 平台(已撤除) -- Docker Registry: 私有 Container Registry(保留,位於 registry.wooo.work) -- Watchtower: 自動偵測 Registry 映像更新並部署(保留) -- 部署位置: UAT Server (192.168.0.110) - -**當時 CI/CD 流程**: -``` -git push → CI 測試 → 構建 Docker Image → 推送到 Registry → Watchtower 自動更新容器 -``` - ---- - -### 2026-01-22:PostgreSQL 相容性修復 - -**問題**:`sales_analysis` 頁面的「星期幾」和「時段」篩選功能在 PostgreSQL 環境下失效 - -**原因**:SQLite 使用 `strftime('%w', timestamp)` 取得星期幾,PostgreSQL 使用 `EXTRACT(dow FROM timestamp)` - -**修復內容**: -1. 更新 `app.py` 中的 `get_sales_table_data()` 函數 -2. 根據 `DATABASE_TYPE` 動態選擇正確的 SQL 語法 -3. 修復 SQLite 日期字串格式問題(PostgreSQL 返回 `datetime` 物件) - -**修改檔案**: -- `app.py` (約 line 2800-2900) -- `config.py` - 新增 `DATABASE_TYPE` 變數 - -**驗證**:API `/api/sales_table?dow=1&hour=14` 在 PostgreSQL 環境正常運作 - ---- - -### 2026-01-23:商品看板效能優化 - -**問題**:商品看板首頁載入緩慢(約 3-4 秒) - -**診斷結果**: -- 資料庫有 5,510 商品、609,173 價格記錄 -- `price_records` 表缺少 `product_id` 索引 -- 多個 gunicorn worker 同時重建快取 - -**優化措施**: - -#### 1. PostgreSQL 索引優化 -```sql -CREATE INDEX CONCURRENTLY idx_price_records_product_id ON price_records(product_id); -CREATE INDEX CONCURRENTLY idx_price_records_product_timestamp ON price_records(product_id, timestamp DESC); -CREATE INDEX idx_products_status ON products(status); -``` - -#### 2. 跨進程快取鎖定(FileLock) -- 將 `threading.Lock()` 替換為 `fcntl.flock()` 檔案鎖 -- 解決多 gunicorn worker 同時重建快取的問題 -- 鎖定檔案位置:`data/.dashboard_cache.lock` - -**修改檔案**: -- `routes/dashboard_routes.py` - 新增 `FileLock` 類別,更新 `get_full_dashboard_data()` 函數 - -**效能改善**: -| 情境 | 優化前 | 優化後 | -|------|--------|--------| -| 快取命中 | ~1 秒 | ~1 秒 | -| 快取重建(單一 worker)| ~3-4 秒 | ~3-4 秒 | -| 快取重建(多 worker)| 每個各 3-4 秒 | 只有一個重建 | - ---- - -### FileLock 實作細節 - -```python -import fcntl - -class FileLock: - """簡單的檔案鎖,用於 gunicorn 多進程環境""" - def __init__(self, lock_file): - self.lock_file = lock_file - self.fd = None - - def acquire(self, blocking=True): - try: - self.fd = open(self.lock_file, 'w') - if blocking: - fcntl.flock(self.fd, fcntl.LOCK_EX) - else: - fcntl.flock(self.fd, fcntl.LOCK_EX | fcntl.LOCK_NB) - return True - except (IOError, OSError): - if self.fd: - self.fd.close() - self.fd = None - return False - - def release(self): - if self.fd: - fcntl.flock(self.fd, fcntl.LOCK_UN) - self.fd.close() - self.fd = None -``` - -**使用方式**: -```python -_DASHBOARD_FILE_LOCK = FileLock('/path/to/data/.dashboard_cache.lock') - -# 取得鎖(非阻塞) -if not _DASHBOARD_FILE_LOCK.acquire(blocking=False): - # 其他 worker 正在重建,等待 - _DASHBOARD_FILE_LOCK.acquire() - _DASHBOARD_FILE_LOCK.release() - return cached_data - -try: - # 重建快取... -finally: - _DASHBOARD_FILE_LOCK.release() -``` - ---- - -## 資料庫表結構 - -### 核心業務表 -- `daily_sales_snapshot` - 每日銷售快照 (snapshot_date 欄位) -- `realtime_sales_monthly` - 業績分析儀表板資料 (日期 欄位) -- `products` - 商品資料 -- `price_records` - 價格記錄 - -### 廠商缺貨系統 -- `vendor_stockout` - 缺貨商品清單 -- `vendor_list` - 廠商資料 -- `vendor_emails` - 廠商郵件地址 -- `email_send_log` - 郵件發送記錄 - -### 系統表 -- `users` - 用戶帳號 -- `permissions` - 權限定義 -- `user_permissions` - 用戶權限關聯 -- `import_jobs` - 匯入任務記錄 -- `import_config` - 匯入設定 - ---- - -## 部署指令(Docker-on-188,見 ADR-008) - -### SSH 進入 188(必經 110 跳板) -```bash -ssh -J wooo@192.168.0.110 ollama@192.168.0.188 -``` - -### 同步 Python 檔案到 188(volume mount,即時生效) -```bash -# 經 110 跳板 -scp -o ProxyJump=wooo@192.168.0.110 app.py ollama@192.168.0.188:/home/ollama/momo-pro/ -scp -o ProxyJump=wooo@192.168.0.110 -r services/ ollama@192.168.0.188:/home/ollama/momo-pro/ -``` - -### 重啟主應用(volume mount 後) -```bash +# 部署 → 188(via 110 跳板) +scp -o ProxyJump=wooo@192.168.0.110 ollama@192.168.0.188:/home/ollama/momo-pro/ ssh -J wooo@192.168.0.110 ollama@192.168.0.188 "docker restart momo-pro-system" ``` -### 重建映像(新增套件 / Dockerfile 變更) -```bash -ssh -J wooo@192.168.0.110 ollama@192.168.0.188 "cd /home/ollama/momo-pro && docker compose build momo-app && docker compose up -d momo-app" -``` - -### 查看日誌 -```bash -# momo-pro-system -ssh -J wooo@192.168.0.110 ollama@192.168.0.188 "docker logs -f momo-pro-system --tail 100" - -# scheduler -ssh -J wooo@192.168.0.110 ollama@192.168.0.188 "docker logs -f momo-scheduler --tail 100" - -# PostgreSQL (pgvector) -ssh -J wooo@192.168.0.110 ollama@192.168.0.188 "docker logs -f momo-db --tail 100" -``` - -### 進入容器 Shell -```bash -# 進入主應用 -ssh -J wooo@192.168.0.110 ollama@192.168.0.188 "docker exec -it momo-pro-system /bin/bash" - -# 進入 PostgreSQL(pgvector/pg14) -ssh -J wooo@192.168.0.110 ollama@192.168.0.188 "docker exec -it momo-db psql -U momo -d momo_analytics" -``` - ---- - -# 待辦:細粒度權限系統實作計畫 - -## 需求總結 - -將現有的三級角色權限系統 (admin/manager/user) 升級為細粒度權限配置系統: -- **保留角色概念**:作為「預設權限模板」,新增用戶時快速套用 -- **權限粒度**:分為「查看」和「操作」兩層 -- **不需要權限群組功能** - ---- - -## 現有系統分析 - -### 已有的基礎設施 - -| 檔案 | 說明 | -|------|-----| -| `database/user_models.py` | User 和 LoginHistory 模型已存在 | -| `services/user_service.py` | 用戶 CRUD 服務已存在 | -| `services/password_service.py` | 密碼服務已存在 | -| `routes/user_routes.py` | 用戶管理路由已存在 | -| `templates/user_management.html` | 用戶管理頁面已存在 | -| `auth.py` | login_required, role_required 裝飾器已存在 | - -### 需要擴展的部分 - -- User 模型需新增 permissions 欄位或關聯表 -- 新增 Permission 模型和 UserPermission 關聯表 -- 新增 permission_required 裝飾器 -- 更新用戶管理介面,增加權限勾選區塊 -- 更新導航列,根據權限動態顯示選單 - ---- - -## 資料庫設計 - -### 新增表: permissions (權限定義表) - -```python -class Permission(db.Model): - """權限定義表 - 系統預設,不可刪除""" - __tablename__ = 'permissions' - - id = Column(Integer, primary_key=True) - code = Column(String(50), unique=True, nullable=False) # 如 'dashboard.view' - name = Column(String(100), nullable=False) # 顯示名稱 '查看首頁看板' - category = Column(String(50), nullable=False) # 分類 '首頁/看板' - description = Column(String(200)) # 詳細說明 - sort_order = Column(Integer, default=0) # 排序順序 -``` - -### 新增表: user_permissions (用戶權限關聯表) - -```python -class UserPermission(db.Model): - """用戶權限關聯表""" - __tablename__ = 'user_permissions' - - id = Column(Integer, primary_key=True) - user_id = Column(Integer, ForeignKey('users.id'), nullable=False) - permission_code = Column(String(50), nullable=False) - granted_by = Column(Integer, ForeignKey('users.id')) - granted_at = Column(DateTime, default=datetime.utcnow) - - # 複合唯一索引 - __table_args__ = ( - UniqueConstraint('user_id', 'permission_code', name='uq_user_permission'), - ) -``` - ---- - -## 權限清單定義 - -### 首頁/看板 -| 權限代碼 | 名稱 | 說明 | -|---------|------|------| -| `dashboard.view` | 查看首頁看板 | 訪問 `/` 首頁 | -| `dashboard.export` | 匯出看板資料 | 匯出商品資料 Excel | - -### 報表 -| 權限代碼 | 名稱 | 說明 | -|---------|------|------| -| `report.daily_sales.view` | 查看每日銷售 | 訪問 `/daily_sales` | -| `report.daily_sales.export` | 匯出每日銷售 | 匯出每日銷售 Excel | -| `report.monthly_summary.view` | 查看月度總結 | 訪問 `/monthly_summary_analysis` | -| `report.monthly_summary.import` | 匯入月度資料 | 匯入月度總結 Excel | -| `report.sales_analysis.view` | 查看銷售分析 | 訪問 `/sales_analysis` | -| `report.growth_analysis.view` | 查看成長分析 | 訪問 `/growth_analysis` | -| `report.abc_analysis.view` | 查看 ABC 分析 | 訪問 `/abc_analysis/detail` | - -### 活動看板 -| 權限代碼 | 名稱 | 說明 | -|---------|------|------| -| `edm.view` | 查看 EDM 看板 | 訪問 `/edm` | -| `edm.trigger` | 觸發 EDM 爬蟲 | 手動觸發爬蟲按鈕 | -| `festival.view` | 查看節慶看板 | 訪問 `/festival` | -| `festival.trigger` | 觸發節慶爬蟲 | 手動觸發爬蟲按鈕 | - -### 廠商缺貨 -| 權限代碼 | 名稱 | 說明 | -|---------|------|------| -| `vendor.index.view` | 查看廠商缺貨首頁 | 訪問 `/vendor_stockout/` | -| `vendor.import` | 匯入缺貨資料 | 匯入缺貨 Excel | -| `vendor.list.view` | 查看缺貨清單 | 訪問 `/vendor_stockout/list` | -| `vendor.list.edit` | 編輯缺貨資料 | 編輯/刪除缺貨記錄 | -| `vendor.management.view` | 查看廠商管理 | 訪問 `/vendor_stockout/vendor-management` | -| `vendor.management.edit` | 管理廠商資料 | 新增/編輯/刪除廠商 | -| `vendor.email.view` | 查看郵件發送 | 訪問 `/vendor_stockout/send-email` | -| `vendor.email.send` | 發送廠商郵件 | 發送缺貨通知郵件 | -| `vendor.history.view` | 查看歷史記錄 | 訪問 `/vendor_stockout/history` | - -### 匯入 -| 權限代碼 | 名稱 | 說明 | -|---------|------|------| -| `import.auto.view` | 查看自動匯入 | 訪問 `/auto_import` | -| `import.auto.manage` | 管理匯入任務 | 新增/編輯/刪除任務 | -| `import.manual` | 手動匯入資料 | 系統設定頁匯入按鈕 | - -### 系統 -| 權限代碼 | 名稱 | 說明 | -|---------|------|------| -| `system.settings.view` | 查看系統設定 | 訪問 `/settings` | -| `system.settings.edit` | 修改系統設定 | 儲存設定變更 | -| `system.advanced.view` | 查看進階設定 | 訪問 `/system_settings` | -| `system.advanced.edit` | 修改進階設定 | 分類管理等 | -| `system.logs.view` | 查看系統日誌 | 訪問 `/logs` | -| `system.crawler.view` | 查看爬蟲管理 | 訪問 `/crawler_management` | -| `system.crawler.manage` | 管理爬蟲設定 | 修改爬蟲設定 | -| `system.backup` | 備份資料庫 | 執行備份操作 | -| `system.users.view` | 查看用戶管理 | 訪問 `/user_management` | -| `system.users.manage` | 管理用戶帳號 | 新增/編輯/刪除用戶 | - -### 其他 -| 權限代碼 | 名稱 | 說明 | -|---------|------|------| -| `brand_assets.view` | 查看品牌素材 | 訪問 `/brand_assets` | - -**總計: 31 個權限項目** - ---- - -## 角色預設權限模板 - -### admin (管理員) -- 全部 31 個權限 - -### manager (管理者) -- 所有 `.view` 權限 -- `dashboard.export` -- `report.daily_sales.export` -- `report.monthly_summary.import` -- `vendor.import`, `vendor.list.edit`, `vendor.management.edit`, `vendor.email.send` -- `import.auto.manage`, `import.manual` -- `system.settings.edit` -- **排除**: `system.users.*`, `system.backup`, `system.advanced.edit`, `system.crawler.manage` - -### user (一般用戶) -- `dashboard.view` -- `report.daily_sales.view`, `report.monthly_summary.view`, `report.sales_analysis.view`, `report.growth_analysis.view`, `report.abc_analysis.view` -- `edm.view`, `festival.view` -- `vendor.index.view`, `vendor.list.view`, `vendor.history.view` - ---- - -## 修改檔案清單 - -### 新增檔案 - -| 檔案 | 說明 | -|------|-----| -| `database/permission_models.py` | Permission 和 UserPermission 模型 | -| `services/permission_service.py` | 權限 CRUD 服務、角色模板應用 | -| `templates/user_permissions.html` | 用戶權限編輯頁面 (獨立頁面或 Modal) | - -### 修改檔案 - -| 檔案 | 修改內容 | -|------|---------| -| `auth.py` | 新增 `permission_required` 裝飾器、更新 `get_current_user()` | -| `routes/user_routes.py` | 新增權限 CRUD API | -| `templates/user_management.html` | 新增「編輯權限」按鈕 | -| `app.py` | 註冊權限 context_processor、初始化權限資料 | -| `database/manager.py` | 新增權限表初始化 | - -### 需更新權限檢查的路由檔案 - -| 檔案 | 說明 | -|------|-----| -| `routes/dashboard_routes.py` | 首頁看板 | -| `routes/daily_sales_routes.py` | 每日銷售 | -| `routes/monthly_routes.py` | 月度總結 | -| `routes/sales_routes.py` | 銷售分析、成長分析 | -| `routes/edm_routes.py` | EDM、節慶看板 | -| `routes/system_routes.py` | 系統設定、日誌 | -| `vendor_routes.py` | 廠商缺貨相關 | -| `auto_import_routes.py` | 自動匯入 | -| `crawler_management_routes.py` | 爬蟲管理 | - ---- - -## 實作步驟 - -### Phase 1: 資料庫模型 (預計 1 步) - -1. 建立 `database/permission_models.py` - - Permission 模型 - - UserPermission 模型 - - 權限常數定義 (PERMISSIONS 字典) - - 角色預設權限模板 (ROLE_DEFAULT_PERMISSIONS) - -2. 更新 `database/manager.py` - - 在 `init_db()` 中初始化權限表資料 - -### Phase 2: 服務層 (預計 1 步) - -1. 建立 `services/permission_service.py` - - `get_all_permissions()` - 取得所有權限定義 (分類顯示) - - `get_user_permissions(user_id)` - 取得用戶權限列表 - - `set_user_permissions(user_id, permission_codes, granted_by)` - 設定用戶權限 - - `apply_role_template(user_id, role)` - 套用角色預設權限 - - `has_permission(user_id, permission_code)` - 檢查是否有權限 - - `has_any_permission(user_id, *permission_codes)` - 檢查是否有任一權限 - -### Phase 3: 認證與裝飾器 (預計 1 步) - -1. 更新 `auth.py` - - 新增 `permission_required(*permissions)` 裝飾器 - - 更新 `get_current_user()` 增加 permissions 欄位 - - 登入時載入用戶權限到 session (或每次查詢) - -### Phase 4: API 路由 (預計 1 步) - -1. 更新 `routes/user_routes.py` - - `GET /api/permissions` - 取得所有權限定義 - - `GET /api/users//permissions` - 取得用戶權限 - - `PUT /api/users//permissions` - 更新用戶權限 - - `POST /api/users//apply_role_template` - 套用角色模板 - -### Phase 5: 前端介面 (預計 1 步) - -1. 建立用戶權限編輯介面 - - 可在 `user_management.html` 新增 Modal 或獨立頁面 - - 按分類顯示權限勾選框 - - 「套用角色模板」下拉選單 - - 「全選」/「清除」按鈕 - -### Phase 6: 套用權限檢查 (預計 2-3 步) - -1. 更新各路由檔案,將 `@role_required` 改為 `@permission_required` -2. 更新導航列模板,根據權限動態顯示選單項目 -3. 更新頁面內的操作按鈕,根據權限顯示/隱藏 - -### Phase 7: 測試驗證 (預計 1 步) - -1. 建立測試用戶並配置不同權限 -2. 驗證各頁面和功能的權限控制 -3. 確認角色模板套用正確 - ---- - -## 權限裝飾器設計 - -```python -def permission_required(*permissions): - """權限檢查裝飾器 - - 使用範例: - @permission_required('dashboard.view') - @permission_required('vendor.list.view', 'vendor.list.edit') - """ - def decorator(f): - @wraps(f) - def decorated_function(*args, **kwargs): - if not session.get('logged_in'): - return redirect(url_for('login')) - - user_id = session.get('user_id') - user_role = session.get('role') - - # admin 擁有所有權限 - if user_role == 'admin': - return f(*args, **kwargs) - - # 檢查用戶是否有任一所需權限 - from services.permission_service import PermissionService - if not PermissionService.has_any_permission(user_id, *permissions): - abort(403) - - return f(*args, **kwargs) - return decorated_function - return decorator -``` - ---- - -## 用戶權限編輯介面設計 - -``` -┌─────────────────────────────────────────────────┐ -│ 編輯權限: peter (Peter Chen) │ -├─────────────────────────────────────────────────┤ -│ 快速設定: [套用角色模板 ▼] [全選] [清除] │ -├─────────────────────────────────────────────────┤ -│ │ -│ 📊 首頁/看板 │ -│ ├─ ☑ 查看首頁看板 │ -│ └─ ☐ 匯出看板資料 │ -│ │ -│ 📈 報表 │ -│ ├─ ☑ 查看每日銷售 │ -│ ├─ ☐ 匯出每日銷售 │ -│ ├─ ☑ 查看月度總結 │ -│ ├─ ☐ 匯入月度資料 │ -│ ├─ ☑ 查看銷售分析 │ -│ ├─ ☑ 查看成長分析 │ -│ └─ ☐ 查看 ABC 分析 │ -│ │ -│ 📣 活動看板 │ -│ ├─ ☑ 查看 EDM 看板 │ -│ ├─ ☐ 觸發 EDM 爬蟲 │ -│ ├─ ☑ 查看節慶看板 │ -│ └─ ☐ 觸發節慶爬蟲 │ -│ │ -│ 🏭 廠商缺貨 │ -│ ├─ ☐ 查看廠商缺貨首頁 │ -│ ├─ ☐ 匯入缺貨資料 │ -│ ├─ ☐ 查看缺貨清單 │ -│ ├─ ☐ 編輯缺貨資料 │ -│ ├─ ☐ 查看廠商管理 │ -│ ├─ ☐ 管理廠商資料 │ -│ ├─ ☐ 查看郵件發送 │ -│ ├─ ☐ 發送廠商郵件 │ -│ └─ ☐ 查看歷史記錄 │ -│ │ -│ 📥 匯入 │ -│ ├─ ☐ 查看自動匯入 │ -│ ├─ ☐ 管理匯入任務 │ -│ └─ ☐ 手動匯入資料 │ -│ │ -│ ⚙️ 系統 │ -│ ├─ ☐ 查看系統設定 │ -│ ├─ ☐ 修改系統設定 │ -│ ├─ ☐ 查看進階設定 │ -│ ├─ ☐ 修改進階設定 │ -│ ├─ ☐ 查看系統日誌 │ -│ ├─ ☐ 查看爬蟲管理 │ -│ ├─ ☐ 管理爬蟲設定 │ -│ ├─ ☐ 備份資料庫 │ -│ ├─ ☐ 查看用戶管理 │ -│ └─ ☐ 管理用戶帳號 │ -│ │ -│ 📦 其他 │ -│ └─ ☑ 查看品牌素材 │ -│ │ -├─────────────────────────────────────────────────┤ -│ [取消] [儲存權限] │ -└─────────────────────────────────────────────────┘ -``` - ---- - -## 導航列動態顯示 - -更新 `templates/components/_navbar.html`,根據權限決定顯示哪些選單項目: - -```jinja2 -{% if has_permission('dashboard.view') %} -首頁 -{% endif %} - -{% if has_permission('report.daily_sales.view') %} -每日銷售 -{% endif %} - -{# 廠商缺貨子選單 - 有任一權限就顯示 #} -{% if has_any_permission('vendor.index.view', 'vendor.list.view', 'vendor.management.view') %} - -{% endif %} -``` - ---- - -## 驗證計畫 - -1. **資料庫測試** - - 確認 permissions 和 user_permissions 表正確建立 - - 確認初始權限資料正確載入 - -2. **API 測試** - - 取得權限列表 API - - 更新用戶權限 API - - 套用角色模板 API - -3. **權限控制測試** - - 建立測試用戶,僅給予 `dashboard.view` 和 `report.daily_sales.view` - - 確認可以訪問首頁和每日銷售 - - 確認無法訪問廠商缺貨、系統設定等頁面 (403) - - 確認導航列只顯示有權限的選單 - -4. **角色模板測試** - - 套用 manager 模板,確認權限正確 - - 套用 user 模板,確認權限正確 - ---- - -## 關鍵檔案路徑 - -- 新增: `database/permission_models.py` -- 新增: `services/permission_service.py` -- 修改: `auth.py` (新增裝飾器) -- 修改: `routes/user_routes.py` (新增 API) -- 修改: `templates/user_management.html` (新增權限編輯) -- 修改: `app.py` (context_processor) -- 修改: 各路由檔案 (套用權限檢查) - ---- - -# 專案憲法 (CONSTITUTION.md) - -> 本文件定義專案開發的核心準則與不可違反的規範 -> **建立日期**: 2026-01-12 -> **當前版本**: V9.4 - ---- - -## 第一章:資料庫與模型層規範 - -### 第 1 條:商品 ID 命名規範(絕對禁止違反) -- ✅ **正確**: 使用 `product.i_code` 作為商品唯一識別碼 -- ❌ **禁止**: 使用 `product.momo_id`(此屬性不存在) - -### 第 2 條:時間戳處理規範(絕對禁止違反) -- ✅ **正確**: 使用 `datetime.now(TAIPEI_TZ).replace(tzinfo=None)` -- ❌ **禁止**: 直接使用 `datetime.now()` 或保留 tzinfo -- **理由**: SQLite 不支援時區感知的 datetime - -### 第 3 條:價格變動邏輯(絕對禁止違反) -- ✅ **正確**: 比對「今日最新價格」與「今日之前的最後一筆價格」 -- ❌ **禁止**: 僅查詢「昨天 00:00-23:59」的價格記錄 - -### 第 4 條:查詢效能優化(強制要求) -- ✅ **正確**: 使用批次查詢(如 `yesterday_prices_map`) -- ❌ **禁止**: N+1 查詢模式 - ---- - -## 第二章:爬蟲與資料採集規範 - -### 第 5 條:商品圖片 URL 構造(絕對禁止違反) -- ✅ **正確**: 使用 CDN 直接構造 - ```python - image_url = f"https://m.momoshop.com.tw/moscdn/goods/{i_code}_m.webp" - ``` -- ❌ **禁止**: 使用複雜的 DOM 查詢 - -### 第 6 條:爬蟲頻率與禮貌性(強制要求) -- ✅ **正確**: 每次請求間隔至少 1 秒 -- ❌ **禁止**: 高頻率無間隔爬取 - ---- - -## 第三章:API 設計規範 - -### 第 8 條:API 邏輯一致性(絕對禁止違反) -- ✅ **正確**: API 的資料處理邏輯必須與儀表板**完全一致** -- ❌ **禁止**: API 與前端使用不同的計算邏輯 - -### 第 9 條:API 錯誤處理(強制要求) -- ✅ **正確**: 所有 API 必須使用 try-except-finally 結構 -- ✅ **正確**: 錯誤時返回 JSON 格式: `{'products': []}, 500` -- ❌ **禁止**: 返回 HTML 錯誤頁面或純文字錯誤 - ---- - -## 第四章:前端 UI/UX 規範 - -### 第 11 條:設計系統色彩(絕對禁止違反) -- ✅ **主題色**: 紫色漸變 `#667eea` → `#764ba2` -- ✅ **漲價**: 紅色 `#dc3545` / `#ff6b6b` -- ✅ **降價**: 綠色 `#28a745` / `#51cf66` - -### 第 12 條:響應式設計(強制要求) -- ✅ **正確**: 所有頁面必須支援手機版(< 768px) -- ✅ **正確**: 使用 Bootstrap 5.3.3 - ---- - -## 第五章:系統架構規範 - -### 第 15 條:服務端口(絕對禁止違反) -- ✅ **正確**: Flask 使用 **Port 80** -- ❌ **禁止**: 使用 5888 或其他端口(已廢棄) - -### 第 16 條:資料庫路徑(絕對禁止違反) -- ✅ **正確**: `data/momo_database.db` - -### 第 17 條:時區設定(絕對禁止違反) -- ✅ **正確**: 使用 `TAIPEI_TZ = pytz.timezone('Asia/Taipei')` - -### 第 18 條:日誌系統(強制要求) -- ✅ **正確**: 使用 `sys_log.info()` / `sys_log.error()` 記錄關鍵操作 -- ❌ **禁止**: 使用 `print()` 輸出日誌 - ---- - -## 第十章:安全性規範 - -### 第 31 條:敏感資訊保護(絕對禁止違反) -- ✅ **正確**: 所有 API Token、密碼存放於 `config.py` -- ❌ **禁止**: 硬編碼敏感資訊於程式碼中 - -### 第 32 條:SQL 注入防護(絕對禁止違反) -- ✅ **正確**: 使用 SQLAlchemy ORM 的參數化查詢 -- ❌ **禁止**: 使用字串拼接建構 SQL 查詢 - ---- - -## 附錄 A:常見錯誤與解決方案 - -| 錯誤 | 原因 | 解決 | -|-----|------|------| -| 'Product' object has no attribute 'momo_id' | 使用錯誤的屬性名稱 | 改用 `product.i_code` | -| API 返回空資料 | 價格比對邏輯錯誤(只查昨天) | 使用「今日之前最後一筆」邏輯 | -| 時間戳比對失敗 | 時區感知 datetime 與 naive datetime 混用 | 使用 `.replace(tzinfo=None)` 統一為 naive | - ---- - -# 專案憲法 - 擴充版 (PROJECT_CONSTITUTION.md) - -**版本:** 1.3 -**最後更新:** 2026-01-14 - ---- - -## 第一章:溝通規範 - -### 第 1 條:語言使用 -- **所有溝通一律使用繁體中文** -- 包含:程式碼註解、文檔說明、Commit 訊息、錯誤訊息、日誌輸出 - ---- - -## 第二章:安全政策 - -### 第 4 條:敏感資訊管理 -- **禁止在程式碼中硬編碼任何敏感資訊** -- 所有憑證、API 金鑰、密碼必須使用環境變數(`.env`) - -### 第 5 條:密碼安全 -- 所有密碼必須使用 `pbkdf2:sha256` 雜湊儲存 -- 密碼長度至少 8 個字元 -- 登入失敗 5 次後鎖定帳號 5 分鐘 - -### 第 7 條:CSRF 防護 -- 所有 POST/PUT/DELETE/PATCH 請求必須包含 CSRF token -- HTML 表單使用: `` -- AJAX 請求使用: `'X-CSRFToken': getCSRFToken()` - ---- - -## 第四章:數據爬取規範 - -### 第 13 條:爬蟲程式碼穩定性原則 -- **爬蟲程式碼屬於核心業務邏輯,修改時必須格外謹慎** -- 修改前必須備份現有可運作的版本 -- 修改後必須驗證所有爬蟲任務正常執行 - -### 第 14 條:爬蟲選擇器維護 -- **CSS 選擇器和 XPath 是脆弱的依賴** -- 修改選擇器前必須記錄修改原因 -- 保留舊選擇器作為註解備份 - -### 第 15 條:爬蟲錯誤處理 -- **所有爬蟲函數必須包含完整的錯誤處理** -- 必須處理:網路連線失敗、頁面載入超時、元素找不到、資料格式異常、反爬蟲機制觸發 - ---- - -## 第六章:部署與維運 - -### 第 27 條:環境管理規範(2026-04-18 依 ADR-008 改寫) - -**環境分層**: - -| 環境 | 位置 | 用途 | -|------|------|------| -| 開發環境 | `/Users/ooo/Library/Mobile Documents/com~apple~CloudDocs/momo-pro-system/` (macOS,iCloud) | 程式碼開發、快速測試 | -| 正式環境 | `/home/ollama/momo-pro/` 於 `192.168.0.188`(Docker Compose) | 生產服務、24/7 運行 | - -**嚴格禁止**: -- ❌ 直接在 188 生產環境隨意改檔案(應走 scp 同步 SOP) -- ❌ 跳過本地測試直接部署到 188 -- ❌ 將 `.env` 或資料庫密碼檔上傳到公開 Git - -### 第 28 條:變更前強制備份原則 ⚠️ - -**核心原則**: -- **所有涉及嚴重影響的變更、修改操作,變更前必須先進行完整備份** - -### 第 29 條:部署強制規範(2026-04-18 依 ADR-008 改寫)⚠️ - -**核心原則**: -- **188 正式環境為 Docker Compose + volume mount,目前無 Git 版控(本地 Mac 有),CI/CD 方案待定** -- **程式碼變更必先於本地測試,再以 scp(經 110 跳板)同步到 `ollama@188:~/momo-pro/`** -- **重大結構變更同步記入 ADR + CLAUDE.md 更新記錄** - -**官方唯一部署流程**: -1. 本地 Mac 修改程式碼 + 本地測試通過 -2. 經 110 跳板 scp 到 188: - ```bash - scp -o ProxyJump=wooo@192.168.0.110 <檔案> ollama@192.168.0.188:/home/ollama/momo-pro/<目標> - ``` -3. 依檔案類型處理: - - Python 檔案(app.py/services/routes/templates/config.py/database/) → volume mount 即時生效,只需 `docker restart momo-pro-system` - - 新增套件 / Dockerfile 變更 → `docker compose build momo-app && docker compose up -d momo-app` - - SQL schema 變更 → `docker exec -i momo-db psql -U momo -d momo_analytics < migration.sql` - -**禁止行為**: -- ❌ 直接在 188 上用 `vim` 改線上檔案(繞過 scp + 本地審視) -- ❌ 用 `docker cp` 改容器內檔案(volume mount 下無意義且混亂) -- ❌ 把未測試的 commit scp 上去 -- ❌ 在 110 做 `kubectl *` 類操作(本系統無 K8s) -- ❌ 修改 `~/momo_pro_system/` 於 110(該路徑已空置,不是運行目錄) - -**唯一例外**: -- 熱修復(Hotfix)若時間緊迫可直接在 188 改,但事後必須把 diff 補回本地並記入 CLAUDE.md 更新記錄 - -**違反後果**: -- 版本混亂(本地與 188 漂移)、無從追蹤 -- 容器重啟後可能丟失(若改了非 mount 的目錄) -- 未來導入 Git/CI 時衝突 - -**適用範圍**: -1. 資料庫結構變更(ALTER TABLE, DROP, CREATE) -2. 大量資料修改或刪除(影響 >100 筆) -3. 系統配置檔案修改 -4. 爬蟲核心邏輯修改 -5. 認證和安全模組修改 -6. 生產環境程式碼更新 -7. **K8s ConfigMap 環境變數變更** -8. **K8s Deployment YAML 變更** -9. **Nginx 配置變更** - -**備份命名規範**: -``` -資料庫:momo_backup_YYYYMMDD_HHMMSS.db -程式碼:momo_code_backup_YYYYMMDD_HHMMSS.tar.gz -``` - -### 第 30 條:所有變更必須入 Git 版控(絕對禁止違反)⚠️ - -**核心原則**: -- **任何對專案的修改,無論大小,都必須提交到 Git 版本控制** -- **禁止只在伺服器端修改而不同步到 Git** - -**適用範圍**: -1. 程式碼變更(Python, JavaScript, HTML, CSS) -2. Docker/K8s 配置變更(docker-compose.yml, Deployment YAML) -3. n8n 工作流變更(n8n-workflows/*.json) -4. Nginx 配置變更 -5. 安全性修復 -6. 環境變數變更(ConfigMap) -7. 任何系統配置修改 - -**強制流程**: -```bash -# 1. 本地修改或從伺服器同步變更 -# 2. 提交到 Git -git add -git commit -m "描述性訊息" - -# 3. 推送到遠端 Git 版控(remote 名稱依新 CI/CD 方案而定) -git push main -``` - -**Claude 助手責任**: -- 每次完成修改任務後,必須主動詢問或執行 Git 提交 -- 若在伺服器端直接修改,必須將變更同步回本地並提交 -- 禁止回覆「已完成」但未提交到 Git 的情況 - -**違反後果**: -- 本地與伺服器版本不一致 -- 下次 CI/CD 部署會覆蓋未提交的變更 -- 無法追蹤變更歷史和回滾 - -### 第 31 條:端口安全規範(絕對禁止違反)⚠️ - -> **2026-01-25 Kali 滲透測試後新增** - -**核心原則**: -- **所有內部服務端口必須綁定到 127.0.0.1,禁止暴露到公網 (0.0.0.0)** -- **只有必要的公開服務才能綁定到 0.0.0.0** - -**強制本地綁定的服務**: - -| 服務 | 端口 | 正確配置 | 風險等級 | -|------|------|----------|----------| -| PostgreSQL | 5432 | `127.0.0.1:5432:5432` | 🔴 極高 | -| Portainer | 9000/9443 | `127.0.0.1:9000:9000` | 🔴 極高 | -| Rancher | 8880/8443 | `127.0.0.1:8880:80` | 🔴 極高 | -| MOMO App | 5001 | `127.0.0.1:5001:80` | 🟠 高 | -| Prometheus | 9090 | `127.0.0.1:9090:9090` | 🟠 高 | -| Alertmanager | 9093 | `127.0.0.1:9093:9093` | 🟠 高 | -| Node Exporter | 9100 | `127.0.0.1:9100:9100` | 🟡 中 | -| Postgres Exporter | 9187 | `127.0.0.1:9187:9187` | 🟡 中 | -| Loki | 3100 | `127.0.0.1:3100:3100` | 🟡 中 | -| cAdvisor | 8080 | `127.0.0.1:8080:8080` | 🟡 中 | - -**允許公開的服務**: -- Port 80/443 (Nginx - HTTPS 反向代理) -- Port 22 (SSH - 需要密鑰認證) -- Port 5000 (Registry - 需要認證) - -**docker-compose.yml 配置範例**: -```yaml -# ❌ 錯誤 - 暴露到公網 -ports: - - "5432:5432" - -# ✅ 正確 - 僅本地連線 -ports: - - "127.0.0.1:5432:5432" -``` - -**存取內部服務方式**: -```bash -# SSH Tunnel 存取 Portainer -ssh -L 9000:127.0.0.1:9000 wooo@192.168.0.110 -# 然後瀏覽器開啟 http://localhost:9000 -``` - -### 第 32 條:Nginx 安全配置規範(絕對禁止違反)⚠️ - -> **2026-01-25 Kali 滲透測試後新增** - -**核心原則**: -- **必須隱藏伺服器版本資訊** -- **必須設定安全 Headers** -- **必須透過 HTTPS 反向代理,禁止直接存取後端服務** - -**強制配置項目**: - -```nginx -server { - # 1. 隱藏 Nginx 版本 - server_tokens off; - - # 2. 安全 Headers(必須全部加入) - add_header X-Content-Type-Options "nosniff" always; - add_header X-Frame-Options "SAMEORIGIN" always; - add_header X-XSS-Protection "1; mode=block" always; - - # 3. HSTS - 強制 HTTPS(防止中間人攻擊) - add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; - add_header Referrer-Policy "strict-origin-when-cross-origin" always; - add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always; - - # 3. 反向代理設定 - location / { - proxy_pass http://127.0.0.1:5001; # 後端只綁定本地 - - # 4. 隱藏後端版本資訊 - proxy_hide_header X-Powered-By; - proxy_hide_header Server; - } -} -``` - -**Rate Limiting 配置**(防止暴力掃描和 DDoS): - -```nginx -# 在 nginx.conf 的 http {} 區塊中 -limit_req_zone $binary_remote_addr zone=general:10m rate=10r/s; -limit_req_zone $binary_remote_addr zone=login:10m rate=3r/s; -limit_conn_zone $binary_remote_addr zone=conn_limit:10m; - -# 在 server {} 區塊中 -limit_req zone=general burst=20 nodelay; # 一般請求 -limit_conn conn_limit 50; # 最大同時連線數 - -# 登入頁面更嚴格 -location /login { - limit_req zone=login burst=5 nodelay; # 每秒 3 個請求 -} -``` - -**禁止行為**: -- ❌ 暴露 Nginx 版本號 (`nginx/1.x.x`) -- ❌ 暴露後端框架版本 (`gunicorn`, `Python`) -- ❌ 允許直接存取後端端口 (5001) -- ❌ 使用 HTTP 傳輸登入憑證 -- ❌ 無限制接受請求(易遭 DDoS) - -**驗證指令**: -```bash -# 檢查是否隱藏版本 -curl -sI https://mo.wooo.work | grep -i server - -# 應該只顯示 "server: nginx" 不帶版本號 -``` - -### 第 33 條:資料庫安全規範(絕對禁止違反)⚠️ - -> **2026-01-25 Kali 滲透測試後新增** - -**核心原則**: -- **資料庫端口絕對禁止暴露到公網** -- **必須使用強密碼** -- **連線必須透過內部網路或 SSH Tunnel** - -**PostgreSQL 安全配置**: -```yaml -# docker-compose.yml -postgres: - ports: - - "127.0.0.1:5432:5432" # 僅本地連線 - environment: - - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} # 從環境變數讀取 -``` - -**禁止行為**: -- ❌ 資料庫密碼寫死在程式碼中 -- ❌ 使用弱密碼(如 `postgres`, `123456`) -- ❌ 允許遠端直接連線資料庫 -- ❌ 在錯誤訊息中暴露資料庫結構 - -### 第 34 條:容器管理平台安全規範(絕對禁止違反)⚠️ - -> **2026-01-25 Kali 滲透測試後新增** - -**核心原則**: -- **Portainer、Rancher 等容器管理平台屬於最高權限入口** -- **必須限制為本地連線,透過 SSH Tunnel 存取** -- **必須使用強密碼和雙因素認證(如支援)** - -**風險說明**: -- 攻擊者一旦獲得容器管理平台存取權,可以: - - 查看所有容器的環境變數(包含密碼) - - 執行任意容器命令 - - 部署惡意容器 - - 讀取資料庫內容 - - 完全控制整個系統 - -**強制配置**: -```yaml -# Portainer -ports: - - "127.0.0.1:9000:9000" - - "127.0.0.1:9443:9443" -``` - -```bash -# Rancher(docker run 方式) -docker run -d --name rancher \ - -p 127.0.0.1:8880:80 \ - -p 127.0.0.1:8443:443 \ - rancher/rancher:latest -``` - ---- - -### 第 35 條:SSH 安全規範(絕對禁止違反)⚠️ - -> **2026-01-25 Kali 滲透測試後新增** - -**核心原則**: -- **SSH 服務是伺服器的最重要入口,必須嚴格保護** -- **必須安裝 Fail2Ban 防止暴力破解攻擊** -- **建議使用 SSH Key 認證取代密碼認證** - -**Fail2Ban 強制配置** (`/etc/fail2ban/jail.local`): -```ini -[DEFAULT] -bantime = 3600 # 封鎖 1 小時 -findtime = 600 # 10 分鐘內 -maxretry = 3 # 最多 3 次失敗 -ignoreip = 127.0.0.1/8 192.168.0.0/24 # 白名單 - -[sshd] -enabled = true -port = ssh -filter = sshd -logpath = /var/log/auth.log -maxretry = 3 -``` - -**常用指令**: -```bash -# 查看封鎖狀態 -sudo fail2ban-client status sshd - -# 手動解封 IP -sudo fail2ban-client set sshd unbanip - -# 查看日誌 -sudo tail -f /var/log/fail2ban.log -``` - ---- - -### 第 36 條:持續性安全監控規範(強制要求)⚠️ - -> **2026-01-25 Kali 滲透測試後新增** - -**核心原則**: -- **安全不是一次性工作,必須持續監控** -- **所有安全事件必須有日誌和告警機制** -- **定期執行滲透測試,發現新漏洞立即修復** - -**強制監控項目**: - -| 監控項目 | 工具 | 告警方式 | -|---------|------|---------| -| SSH 暴力破解 | Fail2Ban + Prometheus Exporter | Grafana 儀表板 | -| 端口掃描偵測 | CI/CD Nmap 掃描(待新 CI/CD 方案重建) | Telegram 通知 | -| 系統資源 | Prometheus + Node Exporter | Grafana 告警 | -| 容器狀態 | cAdvisor + Prometheus | Grafana 告警 | -| 網站可用性 | Blackbox Exporter | Telegram 通知 | - -**Fail2Ban Prometheus Exporter**: -```bash -# 服務位置 -/usr/local/bin/fail2ban_exporter.py -systemctl status fail2ban-exporter - -# Prometheus 指標 -fail2ban_currently_banned{jail="sshd"} -fail2ban_total_banned{jail="sshd"} -``` - -**每週自動安全掃描(待重建)**: -```yaml -# 原本在 GitLab CI 上以排程(每週一 09:00)執行 security-scan job -# 掃描:nmap -Pn --top-ports 100 192.168.0.110 -# 通知:Telegram -# GitLab 已撤除,排程掃描需在新 CI/CD 方案或 cron 上重建 -``` - -**發現漏洞處理流程**: -1. 立即評估風險等級(極高/高/中/低) -2. 極高風險:1 小時內修復 -3. 高風險:24 小時內修復 -4. 中/低風險:1 週內修復 -5. 修復後更新憲法和 CLAUDE.md -6. 提交到 Git 版控 - ---- - -### 第 37 條:程式碼安全掃描規範(強制要求)⚠️ - -> **2026-01-25 新增** - -**核心原則**: -- **每次 Push 必須執行自動化程式碼安全掃描** -- **使用 Bandit 工具檢測 Python 常見安全漏洞** -- **高危漏洞必須立即修復後才能部署** - -**掃描工具**: -| 工具 | 用途 | 說明 | -|------|------|------| -| Bandit | Python 安全掃描 | OWASP Top 10、SQL 注入、XSS、密碼硬編碼等 | - -**CI/CD Job**: `security-code-scan`(原 GitLab CI Job,待新 CI/CD 方案重建) - -**漏洞嚴重度與處理**: -| 嚴重度 | 圖示 | 處理方式 | 通知 | -|--------|------|---------|------| -| 高危 (HIGH) | 🔴 | 必須修復後才能合併 | Telegram 警告 | -| 中危 (MEDIUM) | 🟠 | 應在 24 小時內修復 | Telegram 提醒 | -| 低危 (LOW) | 🟡 | 可選擇修復 | 僅記錄 | - -**常見檢測項目**: -- `B101` - assert 在生產環境中使用 -- `B105` - 硬編碼密碼 -- `B303` - 使用 MD5 或 SHA1 -- `B320` - XML 外部實體注入 (XXE) -- `B501` - 禁用 SSL 驗證 -- `B608` - SQL 注入 - -**Bandit 報告位置**:(GitLab 已撤除,待新 CI/CD 方案定案後更新路徑) - ---- - -## 🔒 資安修復記錄 - -> **2026-01-25 Kali Linux 滲透測試修復** - -| 漏洞 | 風險等級 | 修復方式 | 狀態 | -|------|----------|----------|------| -| PostgreSQL 5432 暴露公網 | 🔴 極高 | 改為 127.0.0.1:5432 | ✅ 已修復 | -| Portainer 9000/9443 暴露公網 | 🔴 極高 | 改為 127.0.0.1 | ✅ 已修復 | -| Rancher 8880/8443 暴露公網 | 🔴 極高 | 改為 127.0.0.1 | ✅ 已修復 | -| pgAdmin 8088 暴露公網 | 🔴 極高 | 改為 127.0.0.1:8088 | ✅ 已修復 | -| MOMO App 5001 暴露公網 | 🟠 高 | 改為 127.0.0.1,透過 Nginx 代理 | ✅ 已修復 | -| Prometheus 9090 暴露公網 | 🟠 高 | 改為 127.0.0.1:9090 | ✅ 已修復 | -| Grafana 3000 暴露公網 | 🟠 高 | 改為 127.0.0.1,透過 mon.wooo.work | ✅ 已修復 | -| cAdvisor 8080 無認證暴露 | 🟠 高 | 改為 127.0.0.1:8080 | ✅ 已修復 | -| n8n 5678 暴露 API Keys/Sentry | 🔴 極高 | 改為 127.0.0.1:5678 | ✅ 已修復 | -| Registry 5000 HTTP 公開存取 | 🟠 高 | 改為 127.0.0.1:5000,透過 HTTPS | ✅ 已修復 | -| Nginx 暴露版本資訊 | 🟡 中 | server_tokens off | ✅ 已修復 | -| 後端暴露 Gunicorn/Python 版本 | 🟡 中 | proxy_hide_header | ✅ 已修復 | -| 缺少安全 Headers | 🟡 中 | 新增 X-Content-Type-Options 等 | ✅ 已修復 | -| Nextcloud 8081 暴露公網 | 🟠 高 | 改為 127.0.0.1:8081,透過 Nginx 代理 | ✅ 已修復 | -| SSH 22 暴力破解風險 | 🟠 高 | 安裝 Fail2Ban (3次失敗封鎖1小時) | ✅ 已修復 | -| Node Exporter 9100 洩漏硬體資訊 | 🟡 中 | 改為 127.0.0.1:9100 | ✅ 已修復 | -| SSL/TLS 未啟用 HSTS | 🟡 中 | 新增 Strict-Transport-Security 標頭 | ✅ 已修復 | -| 無請求速率限制 (DDoS 風險) | 🟡 中 | 新增 Rate Limiting (10r/s + burst 20) | ✅ 已修復 | - ---- - -## 🛡️ 安全監控機制 - -### Fail2Ban 監控 (Prometheus + Grafana) - -**Exporter 位置**: `/usr/local/bin/fail2ban_exporter.py` -**服務**: `systemctl status fail2ban-exporter` -**端口**: `0.0.0.0:9191` (內部使用) - -**可用指標**: -```promql -fail2ban_currently_failed{jail="sshd"} # 目前失敗嘗試數 -fail2ban_total_failed{jail="sshd"} # 歷史總失敗數 -fail2ban_currently_banned{jail="sshd"} # 目前封鎖 IP 數 -fail2ban_total_banned{jail="sshd"} # 歷史總封鎖數 -``` - -### 每週自動安全掃描 (Nmap) — 待新 CI/CD 方案重建 - -> 原本建構在 GitLab CI 排程之上(每週一 09:00 執行 `security-scan` job,掃描 PostgreSQL/MySQL/MongoDB/Redis 等常見危險端口並發送 Telegram 通知)。GitLab 撤除後此排程掃描已停用,需在新 CI/CD 方案或 cron 上重建。 - -### 程式碼安全掃描 (Bandit) — 待新 CI/CD 方案重建 - -> 原本建構在 GitLab CI 上(`security-code-scan` job,每次 Push 和手動觸發;Bandit 掃描全部 Python 檔案,排除 tests/, venv/, node_modules/;結果依嚴重度發 Telegram 通知)。GitLab 撤除後此掃描需在新 CI/CD 方案上重建。 - -### 容器映像檔安全掃描 (Trivy) — 待新 CI/CD 方案重建 - -> 原本建構在 GitLab CI 上(`container-security-scan` job,每週一排程 + 手動觸發;Trivy 掃描 Debian 基礎映像與 Python 套件 CVE,僅報告 HIGH/CRITICAL 等級並發送 Telegram 通知)。GitLab 撤除後此掃描需在新 CI/CD 方案上重建。 - ---- - -## 快速檢查清單 - -### ✅ 新功能開發檢查 -- [ ] 程式碼和註解使用繁體中文 -- [ ] 無硬編碼敏感資訊 -- [ ] 所有輸入經過驗證 -- [ ] POST 請求包含 CSRF token -- [ ] 錯誤處理完整 -- [ ] 日誌記錄完整 - -### 🕷️ 爬蟲修改檢查 -- [ ] 備份現有可運作版本 -- [ ] 記錄修改原因 -- [ ] 保留舊選擇器作為註解 -- [ ] 測試新選擇器正確性 -- [ ] 驗證資料完整性 -- [ ] 更新爬蟲文檔 -- [ ] 監控 24 小時確保穩定 - ---- - -# 開發測試部署流程(2026-04-18 依 ADR-008 改寫) - -**版本:** 3.0 -**制定日期:** 2026-04-18 - ---- - -## 環境架構 - -| 環境 | 位置 | 用途 | 網址 | -|------|------|------|------| -| **開發環境** | `/Users/ooo/.../momo-pro-system/` (macOS + iCloud) | 本地開發 | `http://127.0.0.1:5000` | -| **正式環境** | `/home/ollama/momo-pro/` 於 `ollama@192.168.0.188`(Docker Compose) | 生產服務 | `https://mo.wooo.work` / `https://momo.wooo.work` | - ---- - -## 完整開發部署流程 - -### 階段 1:本地開發 -```bash -cd "/Users/ooo/Library/Mobile Documents/com~apple~CloudDocs/momo-pro-system" -source venv/bin/activate -python app.py -``` - -### 階段 2:本地測試 -```bash -./run_security_tests.sh # 若有 -# 手動測試主要流程 / UI -``` - -### 階段 3:同步到 188(經 110 跳板) -```bash -# 單檔 Python -scp -o ProxyJump=wooo@192.168.0.110 app.py ollama@192.168.0.188:/home/ollama/momo-pro/ - -# 整個目錄(routes / services / templates 等) -scp -o ProxyJump=wooo@192.168.0.110 -r routes/ ollama@192.168.0.188:/home/ollama/momo-pro/ -``` - -### 階段 4:重啟容器(volume mount 後) -```bash -ssh -J wooo@192.168.0.110 ollama@192.168.0.188 "docker restart momo-pro-system" -``` -若新增套件或改 Dockerfile: -```bash -ssh -J wooo@192.168.0.110 ollama@192.168.0.188 "cd /home/ollama/momo-pro && docker compose build momo-app && docker compose up -d momo-app" -``` - -### 階段 5:驗證部署 -```bash -# 容器狀態 -ssh -J wooo@192.168.0.110 ollama@192.168.0.188 "docker ps --filter name=momo" - -# 應用日誌 -ssh -J wooo@192.168.0.110 ollama@192.168.0.188 "docker logs -f momo-pro-system --tail 50" - -# 對外健康檢查 -curl -s https://mo.wooo.work/health -curl -s https://momo.wooo.work/health -``` - ---- - -## 常用命令速查 - -### Docker 管理(188) -```bash -# 跳板登入 -ssh -J wooo@192.168.0.110 ollama@192.168.0.188 - -# 容器清單 -docker ps - -# 即時日誌 -docker logs -f momo-pro-system --tail 100 - -# 進入容器 -docker exec -it momo-pro-system /bin/bash - -# 重啟(volume mount 更新後) -docker restart momo-pro-system - -# 重建映像 -cd /home/ollama/momo-pro && docker compose build momo-app && docker compose up -d momo-app -``` - ---- - -## 回滾流程 - -### 方法:以檔案版本回滾(因為是 volume mount,無需 K8s rollout) -```bash -# 在本地 Mac 用 git 找回舊版本 -git log -- app.py -git show :app.py > /tmp/app.py.old - -# 推回 188 -scp -o ProxyJump=wooo@192.168.0.110 /tmp/app.py.old ollama@192.168.0.188:/home/ollama/momo-pro/app.py -ssh -J wooo@192.168.0.110 ollama@192.168.0.188 "docker restart momo-pro-system" -``` - ---- - -# Google Drive API 設定指南 (GOOGLE_DRIVE_SETUP.md) - -## 功能說明 - -系統會: -1. **每 30 分鐘**自動檢查 Google Drive 指定資料夾 -2. **自動下載** Excel 檔案到本地 -3. **自動匯入**資料到資料庫 -4. **自動刪除** Google Drive 上的原始檔案 - ---- - -## 目前認證狀態 - -| 項目 | 值 | -|-----|-----| -| Google Cloud 專案 | `wooo-481204` | -| OAuth Client ID | `132823079326-h9cvj5eahigm8hp9q0b7t5rk77bhu3gp.apps.googleusercontent.com` | -| 目前認證帳號 | `owen.tsai@gmail.com` (BA O) | -| 目標資料夾 | `當日業績匯入` | -| 檔案匹配模式 | `即時業績_當日` | -| 匯入後移至 | `當日業績匯入/已匯入` | - -✅ **狀態**: Google Drive 自動匯入已正常運作(2026-01-24 驗證) - ---- - -## K8s 環境設定(當前使用) - -### 認證檔案位置 -K8s Scheduler Pod 使用以下架構掛載 Google 認證: - -``` -┌─────────────────────────────────────────────────────────┐ -│ K8s Secret: google-drive-credentials │ -│ ├── google_credentials.json (OAuth 設定) │ -│ └── google_token.pickle (已授權的 Token) │ -└─────────────────────────────────────────────────────────┘ - │ - ▼ initContainer 複製到 -┌─────────────────────────────────────────────────────────┐ -│ emptyDir: /app/config/ (可讀寫) │ -│ ├── google_credentials.json │ -│ └── google_token.pickle │ -└─────────────────────────────────────────────────────────┘ -``` - -### 相關 K8s 檔案 -- `k8s/08-google-drive-secret.yaml` - Google 認證 Secret -- `k8s/05-scheduler.yaml` - Scheduler Deployment(含 initContainer) - -### 更換 Google 帳號步驟 - -1. **本地重新認證**(取得新的 token.pickle) - ```bash - cd /Users/ogt/momo-pro-system - # 刪除舊的 token - rm -f config/google_token.pickle - # 重新認證(會開啟瀏覽器) - python3 -c " - from services.google_drive_service import drive_service - drive_service.authenticate() - " - ``` - -2. **更新 K8s Secret** - ```bash - # 將新的 token 編碼為 base64 - CREDENTIALS_B64=$(base64 -i config/google_credentials.json) - TOKEN_B64=$(base64 -i config/google_token.pickle) - - # 更新 k8s/08-google-drive-secret.yaml - # 將 base64 值更新到 data 區段 - - # 套用新的 Secret - kubectl apply -f k8s/08-google-drive-secret.yaml - - # 重啟 Scheduler(讓 initContainer 重新複製) - kubectl rollout restart deployment/momo-scheduler -n momo - ``` - -3. **驗證認證** - ```bash - kubectl exec -n momo deploy/momo-scheduler -c scheduler -- \ - sh -c 'cd /app && python -c " - from services.google_drive_service import drive_service - drive_service.authenticate() - service = drive_service.service - about = service.about().get(fields=\"user\").execute() - print(\"認證帳號:\", about[\"user\"][\"emailAddress\"]) - "' - ``` - ---- - -## 設定步驟摘要(本地/Docker 環境) - -### 1. 建立 Google Cloud 專案 -- 前往 [Google Cloud Console](https://console.cloud.google.com/) -- 建立新專案(例如:`momo-auto-import`) - -### 2. 啟用 Google Drive API -- 前往「API 和服務」→「程式庫」 -- 搜尋並啟用「Google Drive API」 - -### 3. 建立 OAuth 2.0 憑證 -- 前往「API 和服務」→「憑證」 -- 建立 OAuth 用戶端 ID(桌面應用程式) -- 下載 JSON 並放到 `config/google_credentials.json` - -### 4. 首次認證 -```bash -python3 -c " -from services.google_drive_service import drive_service -drive_service.authenticate() -" -``` - -### 5. 設定 Google Drive 資料夾 -**必須**在認證帳號的 Google Drive 建立以下結構: -``` -我的雲端硬碟/ -└── 業績報表/ - └── 當日業績/ - └── 即時業績_當日.xlsx ← 放置要匯入的檔案 -``` - ---- - -## 常見問題 - -| 問題 | 解決方法 | -|-----|---------| -| 認證失敗,找不到認證檔案 | 確認 `config/google_credentials.json` 存在 | -| 找不到 Google Drive 資料夾 | 確認路徑正確(區分大小寫),確認用正確的 Google 帳號 | -| 重新授權 | 刪除 `config/google_token.pickle` 並重新認證 | -| K8s 認證錯帳號 | 依照「更換 Google 帳號步驟」重新設定 | - ---- - -# Docker 部署指南(2026-04-18 依 ADR-008 改寫) - -## 部署架構 - -``` -本機 (Mac/iCloud) 188 伺服器 (ollama@192.168.0.188) -┌──────────────────────┐ scp 經110 ┌──────────────────────────────┐ -│ 開發環境 │ 跳板 │ Docker Compose (V12.0) │ -│ momo-pro-system/ │ ────────────► │ ┌──────────────────────────┐│ -│ │ │ │ momo-pro-system ││ -│ app.py / services/ │ │ │ Gunicorn 4 workers :80 ││ -│ routes/ templates/ │ │ │ (host 127.0.0.1:5003) ││ -│ database/ config.py │ │ └──────────────────────────┘│ -└──────────────────────┘ │ momo-db (pgvector/pg14) │ - │ momo-scheduler / openclaw │ - └──────────────────────────────┘ -``` - ---- - -## 同步檔案到 188 - -```bash -# 單檔 -scp -o ProxyJump=wooo@192.168.0.110 app.py ollama@192.168.0.188:/home/ollama/momo-pro/ - -# 整個目錄 -scp -o ProxyJump=wooo@192.168.0.110 -r services ollama@192.168.0.188:/home/ollama/momo-pro/ -scp -o ProxyJump=wooo@192.168.0.110 -r routes ollama@192.168.0.188:/home/ollama/momo-pro/ -scp -o ProxyJump=wooo@192.168.0.110 -r templates ollama@192.168.0.188:/home/ollama/momo-pro/ -``` - ---- - -## 重啟 / 重建 - -```bash -# Python 檔案熱更新(最常用) -ssh -J wooo@192.168.0.110 ollama@192.168.0.188 "docker restart momo-pro-system" - -# 新套件 / Dockerfile 變更 -ssh -J wooo@192.168.0.110 ollama@192.168.0.188 \ - "cd /home/ollama/momo-pro && docker compose build momo-app && docker compose up -d momo-app" - -# 完整重啟所有 momo-* 容器 -ssh -J wooo@192.168.0.110 ollama@192.168.0.188 \ - "cd /home/ollama/momo-pro && docker compose restart momo-app momo-scheduler" -``` - ---- - -## Volume Mount 檔案清單(這些 host 目錄會即時反映到容器) - -**ro(唯讀)掛載 — 改完 scp + docker restart 即生效**: -- `app.py`, `scheduler.py`, `config.py`, `auth.py` -- `services/`, `routes/`, `database/`, `templates/` - -**rw(讀寫)掛載 — 容器與 host 雙向**: -- `config/`, `data/`, `logs/`, `backups/` - -**未掛載(修改 host 無效)**: -- `docs/`(CLAUDE.md / ADR 為人工參考,不影響 runtime) -- `static/`, `web/`(編譯進 image,改動需重建) - ---- - -# app.py 重構計畫 (docs/APP_REFACTOR_PLAN.md) - -## 現況分析 - -- **總行數**: 7,253 行 -- **路由數量**: 49 個 -- **函數數量**: 約 70 個 - -### 最大函數(需優先拆分) - -| 排名 | 函數名稱 | 行數 | 建議模組 | -|------|----------|------|----------| -| 1 | `sales_analysis()` | 1,020 行 | sales_routes.py | -| 2 | `get_monthly_summary_data()` | 388 行 | monthly_routes.py | -| 3 | `import_excel()` | 288 行 | import_routes.py | -| 4 | `get_sales_table_data()` | 267 行 | sales_routes.py | -| 5 | `edm_dashboard()` | 244 行 | edm_routes.py | - ---- - -## 目標目錄結構 - -``` -momo-pro/ # (188: /home/ollama/momo-pro/) -├── app.py # 主應用程式 (精簡後約 500 行) -├── routes/ # 🆕 路由模組目錄 -│ ├── __init__.py # Blueprint 註冊中心 -│ ├── dashboard_routes.py # 商品看板 (首頁) -│ ├── sales_routes.py # 業績分析相關 -│ ├── daily_sales_routes.py # 當日業績相關 -│ ├── monthly_routes.py # 月結分析相關 -│ ├── edm_routes.py # EDM 儀表板 -│ ├── export_routes.py # 匯出功能 -│ ├── import_routes.py # 匯入功能 -│ ├── system_routes.py # 系統管理 -│ └── api_routes.py # 通用 API -├── services/ # 服務層 -│ ├── cache_service.py # 🆕 快取服務 -│ ├── sales_service.py # 🆕 業績計算服務 -│ └── ... -└── utils/ # 工具函數 - ├── validators.py # 🆕 驗證函數 - └── ... -``` - ---- - -## 重構執行步驟 - -| 順序 | 模組 | 複雜度 | -|------|------|--------| -| 1 | system_routes.py | 低 | -| 2 | api_routes.py | 低 | -| 3 | export_routes.py | 中 | -| 4 | import_routes.py | 中 | -| 5 | edm_routes.py | 中 | -| 6 | monthly_routes.py | 中 | -| 7 | dashboard_routes.py | 高 | -| 8 | daily_sales_routes.py | 高 | -| 9 | sales_routes.py | 高 | - ---- - -## 預期效益 - -| 指標 | 重構前 | 重構後 | -|------|--------|--------| -| app.py 行數 | 7,253 | ~500 | -| 最大函數行數 | 1,020 | ~200 | -| 模組數量 | 1 | 10 | -| 可測試性 | 低 | 高 | - ---- - -# Google Drive 自動匯入功能 (AUTO_IMPORT_README.md) - -## 功能概述 - -- 🔄 **自動化流程**:每 30 分鐘自動檢查 Google Drive -- 📥 **自動下載**:發現新檔案立即下載到本地 -- 💾 **自動匯入**:解析 Excel 並匯入到資料庫 -- 🗑️ **自動清理**:匯入完成後刪除 Google Drive 原檔 - ---- - -## 新增檔案清單 - -### 後端服務 -- `services/google_drive_service.py` - Google Drive API 服務 -- `services/import_service.py` - 自動匯入服務邏輯 -- `database/import_models.py` - 匯入任務與配置資料表 -- `auto_import_routes.py` - API 路由(Blueprint) - -### 前端介面 -- `web/auto_import_index.html` - 自動匯入管理頁面 - ---- - -## API 端點 - -| 端點 | 方法 | 說明 | -|-----|------|------| -| `/api/import_jobs` | GET | 查詢任務清單 | -| `/api/import_jobs/{id}` | GET | 查詢單一任務 | -| `/api/import_config` | GET/POST | 取得/設定配置 | -| `/api/test_drive_connection` | POST | 測試連接 | -| `/api/list_drive_files` | POST | 列出檔案 | -| `/api/manual_import` | POST | 手動觸發匯入 | - ---- - -## 資料表結構 - -### import_jobs(匯入任務) -- `id`, `job_type`, `status`, `drive_file_id`, `drive_file_name` -- `progress_percent`, `current_step`, `total_rows`, `success_rows` -- `created_at`, `started_at`, `completed_at`, `error_message` - -### import_config(匯入配置) -- `config_key`, `config_value`, `config_type`, `description` - ---- - -## 工作流程 - -``` -Google Drive 新檔案上傳 - ↓ -排程任務執行 (每 30 分鐘) - ↓ -建立匯入任務 (import_jobs) - ↓ -下載到本地 (data/temp/) - ↓ -解析 Excel 並匯入資料庫 - ↓ -刪除 Drive 原檔 - ↓ -任務完成,更新狀態記錄 -``` - ---- - -## 故障排除 - -| 問題 | 解決方法 | -|-----|---------| -| 認證失敗 | 確認 `config/google_credentials.json` 存在 | -| 找不到資料夾 | 確認路徑正確(區分大小寫) | -| 檔案沒有被刪除 | 檢查任務狀態是否為「已完成」 | -| 匯入失敗 | 檢查 Excel 格式是否正確 | - ---- - -# 一鍵部署自動化腳本 (2026-01-26 新增) - -## 功能概述 - -完整的部署自動化系統,支援將 MOMO Pro System 一鍵部署到新的 VM 環境。 - -### 支援的部署模式 - -| 模式 | 說明 | 使用場景 | -|------|------|---------| -| **本地部署** | 在當前主機執行部署 | 開發/測試環境 | -| **SSH 遠端部署** | 透過 SSH 部署到遠端主機 | UAT/正式環境 | -| **匯出部署包** | 生成可攜式 tar.gz 壓縮包 | 無網路直連環境 | - ---- - -## 檔案結構 - -``` -deploy/ -├── deploy.sh # 主入口腳本 -├── README.md # 部署說明文檔 -├── configs/ -│ └── .env.template # 環境變數模板 -└── lib/ - ├── common.sh # 通用函數 (日誌、進度條) - ├── check.sh # 環境檢查 (Docker、記憶體、端口) - ├── config.sh # 配置生成 (互動式 + 模板) - ├── docker.sh # Docker 操作 (構建、啟動、同步) - ├── database.sh # 資料庫備份/還原 (完整+CSV) - ├── ssl.sh # SSL 憑證 (Let's Encrypt) - └── health.sh # 健康檢查 (容器、端點、資料庫) -``` - ---- - -## 常用命令 - -### 部署 - -```bash -# 本地部署(互動式配置) -./deploy/deploy.sh deploy - -# 使用自訂配置部署 -./deploy/deploy.sh -e .env.production deploy - -# SSH 遠端部署 -./deploy/deploy.sh --ssh -h 192.168.1.100 -u root deploy - -# 匯出部署包(含資料) -./deploy/deploy.sh --export --with-data -``` - -### 備份與還原 - -```bash -# 完整備份(資料庫 + 配置 + n8n 工作流) -./deploy/deploy.sh backup -# 輸出: backups/momo_backup_20260126_143000.tar.gz - -# 從備份還原 -./deploy/deploy.sh -b backups/momo_backup_20260126.tar.gz restore -``` - -### SSL 憑證 - -```bash -# 申請 Let's Encrypt 憑證 -./deploy/deploy.sh -d momo.example.com ssl - -# 健康檢查 -./deploy/deploy.sh health -``` - ---- - -## 命令選項 - -| 選項 | 說明 | +## 文檔索引 +| 類型 | 路徑 | |------|------| -| `--local` | 本地部署(預設) | -| `--ssh` | SSH 遠端部署 | -| `--export` | 匯出部署包 | -| `-h, --host` | SSH 目標主機 | -| `-u, --user` | SSH 用戶名 | -| `-p, --path` | 遠端部署路徑 | -| `-e, --env-file` | 自訂環境變數檔案 | -| `-d, --domain` | 域名(SSL 用) | -| `-b, --backup` | 備份檔案路徑 | -| `--no-monitoring` | 不部署監控服務 | -| `--with-data` | 包含資料庫備份 | -| `-y, --yes` | 跳過確認提示 | - ---- - -## 資料庫備份特點 - -- **完整 SQL 備份**: 結構 + 資料 (pg_dump) -- **CSV 分表匯出**: 每個資料表獨立 CSV 檔案,方便檢視 -- **備份摘要報告**: 資料表行數、大小統計 -- **自動壓縮**: 生成 `.tar.gz` 壓縮包 - -### 備份內容 - -``` -backups/momo_backup_20260126_143000/ -├── database/ -│ ├── postgres_full_20260126_143000.sql # 完整 SQL -│ ├── backup_summary.txt # 備份摘要 -│ └── tables/ -│ ├── users.csv -│ ├── products.csv -│ ├── daily_sales_snapshot.csv -│ └── ... # 所有資料表 -├── configs/ -│ ├── .env -│ ├── docker-compose.yml -│ └── config/ -└── n8n/ - └── *.json # 所有工作流程 -``` - ---- - -## 環境需求 - -### 硬體需求 - -| 項目 | 最低需求 | 建議配置 | -|------|----------|----------| -| CPU | 4 核心 | 8 核心 | -| RAM | 8 GB | 16 GB | -| 硬碟 | 50 GB SSD | 100 GB SSD | - -### 端口需求 - -| 端口 | 服務 | 必要 | -|------|------|------| -| 5001 | Flask App | ✓ | -| 5432 | PostgreSQL | ✓ | -| 80/443 | Nginx | ✓ | -| 3000 | Grafana | 選填 | -| 9090 | Prometheus | 選填 | -| 5678 | n8n | 選填 | - ---- - -# PostgreSQL 慢查詢監控 (2026-01-26 新增) - -## 功能概述 - -新增 PostgreSQL 慢查詢監控 API 及 n8n 自動修復工作流程。 - -### API 端點 - -| 端點 | 方法 | 說明 | -|------|------|------| -| `/api/system/db/slow_queries` | GET | 取得慢查詢列表 | -| `/api/system/db/optimize` | POST | 執行 VACUUM ANALYZE | -| `/api/system/db/stats` | GET | 資料庫統計資訊 | -| `/api/system/monitor/overview` | GET | 監控總覽(整合所有狀態) | - -### n8n 工作流程 - -**名稱**: `PostgreSQL 慢查詢監控 (含自動修復)` -**檔案**: `n8n-workflows/13-slow-query-monitor.json` - -**工作流程**: -1. 每 15 分鐘執行 -2. 呼叫 `/api/system/db/slow_queries` 取得慢查詢 -3. 分析是否需要優化(≥3 個慢查詢或重複模式) -4. 發送 Telegram 告警 -5. 自動執行 VACUUM ANALYZE(如需要) -6. 通知優化結果 - -### 慢查詢檢測邏輯 - -```javascript -// 閾值設定 -const threshold = 5; // 秒 - -// 觸發優化條件 -if (slowQueries.length >= 3) { - needsOptimize = true; -} - -// 重複慢查詢檢測(可能需要索引) -if (repeatedSlowQueries.length > 0) { - needsOptimize = true; -} -``` - ---- - -## Monitor 頁面更新 - -**檔案**: `docker/nginx/html/monitor-index.html` - -新增功能: -- 即時服務狀態面板(Registry、Grafana、Prometheus、n8n、MOMO App、Database) -- Prometheus 告警顯示面板 -- n8n 工作流程狀態面板 -- 每 30 秒自動刷新 - ---- - -# n8n 監控工作流程清單 - -| 檔案 | 名稱 | 排程 | 功能 | -|------|------|------|------| -| `11-registry-health-monitor.json` | Registry 健康監控 | 每 10 分鐘 | Docker Registry 健康檢查 | -| `12-google-drive-import-monitor.json` | Google Drive 匯入監控 | 每 30 分鐘 | 自動匯入失敗告警 | -| `13-slow-query-monitor.json` | PostgreSQL 慢查詢監控 | 每 15 分鐘 | 慢查詢告警 + 自動 VACUUM | - ---- - -# CI/CD 與一鍵部署自動化 (2026-02-06 新增) - -## 概述 - -完整的部署自動化系統,包含: -- **CI/CD 流程**: GitLab 已撤除,新方案待定 -- **環境自動安裝**: 在全新主機上自動安裝所有必要套件 -- **一鍵完整部署**: 環境安裝 + 應用部署一次完成 - ---- - -## CI/CD 流程(待定) - -> **2026-04-18 備註**: 原 GitLab CI/CD(含 `.gitlab-ci.yml`、`.gitlab-ci-simple.yml`)已隨 GitLab 撤除而退役。下列 Pipeline 骨架保留作為新 CI/CD 方案的參考需求。 - -### 預期階段骨架 - -``` -git push main (remote 待定) - │ - ▼ -╔═══════════════════════════════════════╗ -║ CI/CD Pipeline (待新方案定案) ║ -╠═══════════════════════════════════════╣ -║ Stage: test ║ -║ • pytest 測試 (允許失敗) ║ -╠═══════════════════════════════════════╣ -║ Stage: deploy ║ -║ • rsync 同步程式碼到 K3s 主機 ║ -║ • docker build (在 K3s 主機建置) ║ -║ • k3s ctr images import (匯入映像) ║ -║ • kubectl rollout restart (重啟) ║ -║ • 健康檢查 ║ -║ • Telegram 通知 ║ -╚═══════════════════════════════════════╝ -``` - -### 關鍵特點(設計需求) - -| 特點 | 說明 | -|------|------| -| **不依賴 Registry** | 本地建置 + K3s containerd 直接匯入(可選) | -| **自動健康檢查** | 部署後自動驗證服務狀態 | -| **Telegram 通知** | 部署成功/失敗即時通知 | -| **自動回滾** | 部署失敗可快速回滾 | - -### 相關檔案 - -| 檔案 | 說明 | -|------|------| -| `scripts/deploy/build-and-deploy.sh` | 快速部署腳本(手動執行) | -| `scripts/verify-cicd.sh` | CI/CD 驗證腳本(原 GitLab CI 用,待改寫) | - ---- - -## 一鍵部署到新主機 - -### 完整部署命令 - -```bash -# 1. 複製專案到新主機 -scp -r momo-pro-system root@新主機IP:/opt/ - -# 2. SSH 到新主機執行完整部署 -ssh root@新主機IP -cd /opt/momo-pro-system -sudo ./deploy/scripts/full-deploy.sh --domain mo.example.com --ssl -``` - -**一個命令完成:環境安裝 + K8s 部署 + Nginx + SSL + 監控 + 自動啟動** - -### 部署流程 - -``` -執行 full-deploy.sh - │ - ▼ -Phase 1: 環境安裝 ───────────────────────────────────────────── - • 檢測 OS (Ubuntu/Debian) - • 安裝基礎套件 (curl, git, vim, htop, jq...) - • 安裝 Docker + Docker Compose - • 安裝 K3s + Helm - • 安裝 Nginx + 安全配置 - • 安裝 Certbot (Let's Encrypt) - • 設定 UFW 防火牆 - • 設定 Fail2Ban - • 系統優化 (核心參數、檔案限制) - │ - ▼ -Phase 2: K8s 配置 ───────────────────────────────────────────── - • 建立 momo namespace - • 部署 Secrets + ConfigMap - • 部署 PostgreSQL StatefulSet - • 部署 momo-app Deployment - • 部署 momo-scheduler Deployment - │ - ▼ -Phase 3: 映像建置 ───────────────────────────────────────────── - • docker build -t momo-pro-system:local - • docker save | k3s ctr images import - • kubectl rollout restart - │ - ▼ -Phase 4-5: 網路配置 ─────────────────────────────────────────── - • Nginx 反向代理配置 - • Let's Encrypt SSL 證書 - │ - ▼ -Phase 6: 監控系統 ───────────────────────────────────────────── - • Prometheus + Grafana (Helm) - • Alertmanager 告警 - │ - ▼ -Phase 7-8: 自動化 ───────────────────────────────────────────── - • systemd 開機自動啟動 - • 健康檢查 - • Telegram 通知 -``` - ---- - -## 自動安裝的套件清單 - -### 基礎套件 - -| 套件 | 用途 | -|------|------| -| curl, wget | 檔案下載 | -| git | 版本控制 | -| vim, htop, iotop | 編輯器與系統監控 | -| jq | JSON 處理 | -| rsync | 檔案同步 | -| unzip, tar, gzip | 壓縮解壓縮 | -| net-tools, dnsutils | 網路工具 | -| build-essential | 編譯工具 | -| cron, logrotate | 排程與日誌管理 | - -### 容器與 Kubernetes - -| 套件 | 版本 | 用途 | -|------|------|------| -| Docker CE | 最新 | 容器運行環境 | -| Docker Compose | Plugin | 多容器編排 | -| K3s | 最新 | 輕量級 Kubernetes | -| Helm | v3 | K8s 套件管理 | - -### Python - -| 套件 | 用途 | -|------|------| -| python3 | Python 執行環境 | -| pip | Python 套件管理 | -| venv | 虛擬環境 | - -### Web 伺服器與 SSL - -| 套件 | 用途 | -|------|------| -| Nginx | 反向代理 + 負載均衡 | -| Certbot | Let's Encrypt SSL 自動證書 | - -### 安全 - -| 套件 | 用途 | -|------|------| -| Fail2Ban | 防暴力破解 (SSH 3次失敗封鎖1小時) | -| UFW | 防火牆 | - -### 監控 - -| 套件 | 用途 | -|------|------| -| Node Exporter | 主機指標收集 | -| Prometheus | 指標儲存與查詢 (Helm) | -| Grafana | 監控儀表板 (Helm) | - -### 資料庫 - -| 套件 | 用途 | -|------|------| -| postgresql-client | PostgreSQL 客戶端工具 | - ---- - -## 部署腳本說明 - -### 1. 環境安裝腳本 - -**檔案**: `deploy/scripts/setup-environment.sh` - -只安裝環境,不部署應用: - -```bash -sudo ./deploy/scripts/setup-environment.sh [選項] - -選項: - --user 部署用戶(預設: wooo) - --domain 域名 - --no-docker 不安裝 Docker - --no-k3s 不安裝 K3s - --no-nginx 不安裝 Nginx - --no-certbot 不安裝 Certbot - --no-monitoring 不安裝監控工具 - --no-firewall 不設定防火牆 -``` - -### 2. 完整部署腳本 - -**檔案**: `deploy/scripts/full-deploy.sh` - -環境安裝 + 應用部署: - -```bash -sudo ./deploy/scripts/full-deploy.sh [選項] - -選項: - --user 部署用戶(預設: wooo) - --domain 域名 - --ssl 設定 SSL 證書 - --skip-env 跳過環境安裝(環境已準備好時使用) -``` - -### 3. 快速部署腳本 - -**檔案**: `scripts/deploy/build-and-deploy.sh` - -日常更新用(環境已準備好): - -```bash -./scripts/deploy/build-and-deploy.sh - -# 功能: -# 1. 本地 Docker 建置 -# 2. 映像傳輸到 K3s 主機 -# 3. 匯入 K3s containerd -# 4. 重啟服務 -# 5. 健康檢查 -# 6. Telegram 通知 -``` - -### 4. CI/CD 驗證腳本 - -**檔案**: `scripts/verify-cicd.sh` - -驗證 CI/CD 流程完整性: - -```bash -./scripts/verify-cicd.sh - -# 驗證項目: -# 1. 本地環境 (Docker, Git, SSH) -# 2. SSH 連線 (K3s 主機) -# 3. K8s 服務狀態 -# 4. 應用健康檢查 -# 5. CI/CD 檔案完整性 -# 6. 自動啟動設定 -# 7. 端對端部署測試 -# 8. 監控系統 -``` - ---- - -## 日常更新流程 - -環境已安裝後,日常更新只需: - -```bash -# 方法 1: 使用快速部署腳本 -./scripts/deploy/build-and-deploy.sh - -# 方法 2: 手動步驟 -docker build -t momo-pro-system:local . -docker save momo-pro-system:local | sudo k3s ctr images import - -kubectl rollout restart deployment/momo-app deployment/momo-scheduler -n momo -``` - ---- - -## 系統需求 - -### 硬體需求 - -| 項目 | 最低需求 | 建議配置 | -|------|----------|----------| -| CPU | 2 核心 | 4+ 核心 | -| RAM | 4 GB | 8+ GB | -| 硬碟 | 30 GB | 50+ GB SSD | - -### 支援的作業系統 - -| OS | 版本 | 狀態 | -|------|------|------| -| Ubuntu | 22.04, 24.04 | ✅ 完全支援 | -| Debian | 11, 12 | ✅ 完全支援 | - -### 防火牆規則 (自動設定) - -| 端口 | 服務 | 存取範圍 | -|------|------|---------| -| 22 | SSH | 公開 | -| 80 | HTTP | 公開 | -| 443 | HTTPS | 公開 | -| 6443 | K3s API | 僅內網 | - ---- - -## 部署檔案清單 - -``` -deploy/ -├── deploy.sh # 進階部署主腳本 -├── README.md # 部署說明文檔 -├── QUICK_START.md # 快速部署指南 -├── configs/ -│ └── .env.template # 環境變數模板 -├── lib/ -│ ├── common.sh # 通用函數 -│ ├── check.sh # 環境檢查 -│ ├── config.sh # 配置生成 -│ ├── docker.sh # Docker 操作 -│ ├── database.sh # 資料庫備份還原 -│ ├── ssl.sh # SSL 憑證 -│ ├── health.sh # 健康檢查 -│ ├── k8s.sh # K8s 管理 -│ ├── monitoring.sh # 監控部署 -│ ├── systemd.sh # Systemd 服務 -│ └── registry.sh # Registry 管理 -└── scripts/ - ├── setup-environment.sh # 🆕 環境自動安裝 - ├── full-deploy.sh # 🆕 完整一鍵部署 - └── setup-firewall.sh # 防火牆設定 - -scripts/ -├── deploy/ -│ └── build-and-deploy.sh # 快速部署腳本 -├── verify-cicd.sh # 🆕 CI/CD 驗證腳本 -└── tools/ - ├── system_startup_complete.sh # 系統啟動腳本 - └── momo-startup-complete.service # systemd 服務 -``` - ---- - -# 🚀 WOOO AIOps - 智能運維 SaaS 產品 (2026-02-13 新增) - -## 產品概覽 - -> **WOOO AIOps** 是基於 MOMO Pro System 監控與自動修復經驗打造的 SaaS 產品, -> 讓新網站能夠**即時接入**監控、告警、自動修復與一鍵部署功能。 - -| 項目 | 說明 | -|------|------| -| 產品名稱 | WOOO AIOps (AI Operations Platform) | -| 目標客戶 | 需要快速建立監控的中小型網站 | -| 核心價值 | 10 分鐘內完成監控 + 自動修復配置 | -| 技術棧 | FastAPI + React/Next.js + K8s + Prometheus | - -### 產品特點 - -| 功能 | 說明 | -|------|------| -| **一鍵部署** | 支援 FastAPI、Flask、Express、Next.js 等主流框架 | -| **智能監控** | 自動整合 Prometheus + Grafana | -| **AI 告警** | 基於歷史資料的智能告警 | -| **自動修復** | 重啟、擴容、回滾、記憶體調整等 8 種修復策略 | -| **多租戶** | 完整的用戶隔離與計費系統 | - ---- - -## 架構圖 - -``` -┌─────────────────────────────────────────────────────────────────────────────┐ -│ WOOO AIOps Platform │ -├─────────────────────────────────────────────────────────────────────────────┤ -│ │ -│ Web Portal (React/Next.js) │ -│ ├── Dashboard # 總覽儀表板 │ -│ ├── Apps Management # 應用管理 │ -│ ├── Monitoring # 監控中心 │ -│ ├── Repairs History # 修復記錄 │ -│ └── Settings # 系統設定 │ -│ │ -│ ┌─────────────────────────────────────────────────────────────────────┐ │ -│ │ FastAPI Backend │ │ -│ │ /api/auth/* # 認證 (JWT) │ │ -│ │ /api/apps/* # 應用 CRUD │ │ -│ │ /api/deployments/* # 部署管理 │ │ -│ │ /api/monitoring/* # 監控資料 │ │ -│ │ /api/repairs/* # 修復記錄 │ │ -│ │ /api/users/* # 用戶管理 │ │ -│ └─────────────────────────────────────────────────────────────────────┘ │ -│ │ -│ ┌───────────────┐ ┌───────────────┐ ┌───────────────┐ │ -│ │ Deploy Engine │ │ Monitor Engine│ │ Repair Engine │ │ -│ │ │ │ │ │ │ │ -│ │ • Template │ │ • Prometheus │ │ • 8 種策略 │ │ -│ │ Renderer │ │ Client │ │ • 自動觸發 │ │ -│ │ • K8s Client │ │ • Alertmanager│ │ • 執行追蹤 │ │ -│ └───────────────┘ └───────────────┘ └───────────────┘ │ -│ │ -│ ┌─────────────────────────────────────────────────────────────────────┐ │ -│ │ Kubernetes Cluster │ │ -│ │ │ │ -│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ -│ │ │ User App A │ │ User App B │ │ User App C │ │ │ -│ │ │ (FastAPI) │ │ (Next.js) │ │ (Express) │ │ │ -│ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │ -│ │ │ │ -│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ -│ │ │ Prometheus │ │ Grafana │ │Alertmanager │ │ │ -│ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │ -│ └─────────────────────────────────────────────────────────────────────┘ │ -│ │ -└─────────────────────────────────────────────────────────────────────────────┘ -``` - ---- - -## 目錄結構 - -``` -aiops-core/ -├── README.md # 產品說明文檔 -├── requirements.txt # Python 依賴 -│ -├── api/ # FastAPI 後端 -│ ├── main.py # FastAPI 入口 -│ └── routers/ -│ ├── auth.py # JWT 認證 -│ ├── apps.py # 應用 CRUD -│ ├── deployments.py # 部署管理 -│ ├── monitoring.py # 監控 API -│ ├── repairs.py # 修復記錄 -│ └── users.py # 用戶管理 -│ -├── database/ # 資料庫層 -│ ├── models.py # SQLAlchemy 模型 -│ └── session.py # 資料庫連線 -│ -├── deploy_engine/ # 部署引擎 -│ ├── deploy_service.py # 部署服務 -│ ├── template_renderer.py # Jinja2 模板渲染 -│ └── k8s_client.py # kubectl 封裝 -│ -├── monitor_engine/ # 監控引擎 -│ ├── monitor_service.py # 監控服務 -│ ├── prometheus_client.py # Prometheus API -│ └── alert_manager.py # Alertmanager API -│ -├── repair_engine/ # 修復引擎 -│ ├── repair_service.py # 修復服務 -│ ├── repair_executor.py # 修復執行器 -│ └── repair_strategies.py # 修復策略 -│ -├── templates/ # K8s 模板 (Jinja2) -│ ├── base/ -│ │ ├── namespace.yaml.j2 -│ │ ├── service.yaml.j2 -│ │ └── ingress.yaml.j2 -│ └── frameworks/ -│ ├── fastapi/deployment.yaml.j2 -│ ├── flask/deployment.yaml.j2 -│ ├── express/deployment.yaml.j2 -│ └── nextjs/deployment.yaml.j2 -│ -└── web/ # React/Next.js 前端 - ├── package.json - ├── tailwind.config.js - └── src/ - ├── lib/api.ts # API 客戶端 - └── pages/ - ├── index.tsx # Dashboard - ├── monitoring.tsx # 監控中心 - ├── repairs.tsx # 修復記錄 - └── apps/ - ├── index.tsx # 應用列表 - └── new.tsx # 創建應用精靈 -``` - ---- - -## 資料庫模型 - -### 核心表 - -| 表名 | 說明 | 主要欄位 | -|------|------|---------| -| `users` | 用戶帳號 | id, email, password_hash, plan, company | -| `apps` | 應用配置 | id, user_id, name, framework, status, domain | -| `deployments` | 部署記錄 | id, app_id, version, status, started_at | -| `alerts` | 告警記錄 | id, app_id, name, severity, status | -| `repairs` | 修復記錄 | id, app_id, action, status, attempt | -| `api_keys` | API 金鑰 | id, user_id, key_hash, scopes | - -### 列舉類型 - -```python -class PlanType(str, Enum): - FREE = "free" # 最多 2 個應用 - STARTER = "starter" # 最多 5 個應用 - PRO = "pro" # 最多 20 個應用 - ENTERPRISE = "enterprise" # 無限制 - -class Framework(str, Enum): - FASTAPI = "fastapi" - FLASK = "flask" - EXPRESS = "express" - NEXTJS = "nextjs" - DJANGO = "django" - NESTJS = "nestjs" - -class AppStatus(str, Enum): - PENDING = "pending" - DEPLOYING = "deploying" - RUNNING = "running" - STOPPED = "stopped" - ERROR = "error" - -class RepairAction(str, Enum): - RESTART = "restart" - SCALE_UP = "scale_up" - SCALE_DOWN = "scale_down" - ROLLBACK = "rollback" - INCREASE_MEMORY = "increase_memory" - INCREASE_CPU = "increase_cpu" - VACUUM_DB = "vacuum_db" - CLEAR_CACHE = "clear_cache" -``` - ---- - -## API 端點 - -### 認證 API - -| 端點 | 方法 | 說明 | -|------|------|------| -| `/api/auth/login` | POST | 登入取得 JWT | -| `/api/auth/register` | POST | 註冊新用戶 | -| `/api/auth/refresh` | POST | 刷新 Token | -| `/api/auth/me` | GET | 取得當前用戶 | - -### 應用 API - -| 端點 | 方法 | 說明 | -|------|------|------| -| `/api/apps` | GET | 列出所有應用 | -| `/api/apps` | POST | 創建新應用 | -| `/api/apps/{id}` | GET | 取得應用詳情 | -| `/api/apps/{id}` | PUT | 更新應用 | -| `/api/apps/{id}` | DELETE | 刪除應用 | -| `/api/apps/{id}/start` | POST | 啟動應用 | -| `/api/apps/{id}/stop` | POST | 停止應用 | -| `/api/apps/{id}/restart` | POST | 重啟應用 | - -### 部署 API - -| 端點 | 方法 | 說明 | -|------|------|------| -| `/api/deployments` | GET | 列出部署記錄 | -| `/api/deployments` | POST | 觸發新部署 | -| `/api/deployments/{id}` | GET | 部署詳情 | -| `/api/deployments/{id}/cancel` | POST | 取消部署 | -| `/api/deployments/{id}/rollback` | POST | 回滾到此版本 | - -### 監控 API - -| 端點 | 方法 | 說明 | -|------|------|------| -| `/api/monitoring/dashboard` | GET | 總覽資料 | -| `/api/monitoring/apps/{id}/metrics` | GET | 應用指標 | -| `/api/monitoring/apps/{id}/health` | GET | 健康狀態 | -| `/api/monitoring/alerts` | GET | 告警列表 | - -### 修復 API - -| 端點 | 方法 | 說明 | -|------|------|------| -| `/api/repairs` | GET | 修復記錄列表 | -| `/api/repairs/stats` | GET | 修復統計 | -| `/api/repairs/apps/{id}/trigger` | POST | 手動觸發修復 | - ---- - -## 自動修復策略 - -### 告警到修復動作映射 - -| 告警類型 | 修復動作 | 說明 | -|---------|---------|------| -| `HighCpuUsage` | scale_up | CPU > 80%,自動擴容 | -| `HighMemoryUsage` | increase_memory | 記憶體 > 85%,增加限制 | -| `PodCrashLoopBackOff` | restart | Pod 崩潰,嘗試重啟 | -| `HighErrorRate` | rollback | 錯誤率 > 5%,回滾版本 | -| `PodOOMKilled` | increase_memory | OOM,增加 50% 記憶體 | -| `HighResponseTime` | scale_up | P95 > 2s,擴容 | -| `DatabaseHighConnections` | vacuum_db | 連線 > 80%,執行 VACUUM | -| `DatabaseDeadlock` | kill_query | 發現死鎖,終止查詢 | - -### 修復執行流程 - -``` -告警觸發 - │ - ▼ -repair_service.process_alert() - │ - ├─ 檢查是否符合自動修復條件 - │ - ├─ 建立 Repair 記錄 - │ - ├─ repair_executor.execute() - │ ├─ restart: kubectl rollout restart - │ ├─ scale_up: kubectl scale --replicas +1 - │ ├─ rollback: kubectl rollout undo - │ ├─ increase_memory: kubectl patch +50% - │ ├─ vacuum_db: VACUUM ANALYZE - │ └─ kill_query: pg_terminate_backend() - │ - └─ 更新修復狀態 + 發送通知 -``` - ---- - -## 支援的框架 - -### Python 框架 - -| 框架 | 版本 | 預設配置 | -|------|------|---------| -| FastAPI | 0.100+ | Gunicorn + Uvicorn, Port 8000 | -| Flask | 2.0+ | Gunicorn, Port 5000 | -| Django | 4.0+ | Gunicorn, Port 8000 | - -### JavaScript 框架 - -| 框架 | 版本 | 預設配置 | -|------|------|---------| -| Express | 4.18+ | Node.js, Port 3000 | -| Next.js | 14+ | Node.js, Port 3000 | -| NestJS | 10+ | Node.js, Port 3000 | - -### K8s 模板變數 - -| 變數 | 說明 | 範例 | -|------|------|------| -| `app.name` | 應用名稱 | `my-api` | -| `app.namespace` | K8s 命名空間 | `user-123-my-api` | -| `app.framework` | 框架類型 | `fastapi` | -| `app.replicas` | 副本數 | `2` | -| `app.port` | 容器端口 | `8000` | -| `app.domain` | 自訂域名 | `api.example.com` | -| `resources.cpu_request` | CPU 請求 | `100m` | -| `resources.memory_request` | 記憶體請求 | `256Mi` | - ---- - -## 運行方式 - -### 後端 (FastAPI) - -```bash -cd aiops-core - -# 安裝依賴 -pip install -r requirements.txt - -# 設定環境變數 -export DATABASE_URL="postgresql://user:pass@localhost/aiops" -export JWT_SECRET="your-secret-key" -export PROMETHEUS_URL="http://prometheus:9090" -export ALERTMANAGER_URL="http://alertmanager:9093" - -# 啟動服務 -uvicorn api.main:app --host 0.0.0.0 --port 8000 --reload -``` - -### 前端 (Next.js) - -```bash -cd aiops-core/web - -# 安裝依賴 -npm install - -# 設定環境變數 -echo 'NEXT_PUBLIC_API_URL=http://localhost:8000/api' > .env.local - -# 開發模式 -npm run dev - -# 生產建置 -npm run build -npm start -``` - ---- - -## 開發狀態 - -### 已完成 ✅ - -| 模組 | 檔案數 | 狀態 | -|------|--------|------| -| Deploy Engine | 4 | ✅ 完成 | -| Monitor Engine | 4 | ✅ 完成 | -| Repair Engine | 4 | ✅ 完成 | -| FastAPI Backend | 8 | ✅ 完成 | -| Database Models | 3 | ✅ 完成 | -| K8s Templates | 7 | ✅ 完成 | -| Web Portal | 8 | ✅ 完成 | -| **總計** | **38** | **✅** | - -### 待開發 ⏳ - -| 功能 | 優先級 | 說明 | -|------|--------|------| -| 計費系統整合 | 高 | Stripe/金流整合 | -| 多租戶隔離 | 高 | K8s namespace 隔離 | -| 自訂告警規則 | 中 | 用戶自定義閾值 | -| Slack/Discord 通知 | 中 | 多渠道告警 | -| 日誌聚合 | 中 | Loki 整合 | -| 效能分析 | 低 | APM 功能 | - ---- - -## 更新記錄 - -- **2026-02-13**: - - 🆕 新增 WOOO AIOps 產品核心模組 (38 個檔案) - - 🆕 Deploy Engine: 支援 4 種框架的 K8s 部署 - - 🆕 Monitor Engine: Prometheus + Grafana + Alertmanager 整合 - - 🆕 Repair Engine: 8 種自動修復策略 - - 🆕 FastAPI Backend: 6 個 API 路由模組 - - 🆕 Web Portal: Dashboard、應用管理、監控、修復記錄頁面 - - 🆕 Database Models: User、App、Deployment、Alert、Repair - -- **2026-02-06**: - - 新增 CI/CD 完整驗證腳本 (`scripts/verify-cicd.sh`) - - 新增環境自動安裝腳本 (`deploy/scripts/setup-environment.sh`) - - 新增完整一鍵部署腳本 (`deploy/scripts/full-deploy.sh`) - - 新增快速部署腳本 (`scripts/deploy/build-and-deploy.sh`) - - K8s 優化配置 (`k8s/optimized/`) - - 完整 K8s 配置檔案 (`k8s/00-namespace.yaml`, `k8s/01-secrets.yaml`, `k8s/03-postgres.yaml`) - - 系統自動啟動機制 (systemd) - - 完整部署文檔更新 - -- **2026-01-26**: - - 新增一鍵部署自動化腳本系統 - - 新增 PostgreSQL 慢查詢監控 API - - 新增 n8n 慢查詢自動修復工作流程 - - 更新 Monitor 頁面整合所有監控狀態 - ---- - -## ⚖️ 第十三章:AI 架構完整憲法(2026-04-18 加入 + 統帥批准) - -### 第 40 條:AI 學習數據雙寫規範(絕對禁止違反) -- ✅ **正確**:所有 AI 產出(PPT 洞察、競品分析、對話記錄)必須**雙寫** PostgreSQL `ai_insights` + pgvector embedding -- ❌ **禁止**:只寫 DB 不寫 KM(RAG 無法語意搜尋) -- ❌ **禁止**:只寫 KM 不寫 DB(精確 period/sku 查詢無法命中) -- **理由**:DB 是精準命中,KM 是語意搜尋,兩者互補缺一不可(ADR-007) -- **實作**:NemoTron `store_insight` tool call 觸發雙寫;Hermes 非同步補 embedding - -### 第 41 條:架構決策 ADR 規範(強制要求) -- ✅ **正確**:任何重大架構決策(引入新依賴、改資料庫 schema、改 AI 路由邏輯)必須建立 `docs/adr/ADR-XXX-*.md` -- ✅ **正確**:ADR 建立後同步更新 `docs/adr/README.md` 索引 + 相關 SOT + Memory -- ❌ **禁止**:口頭決策不留文字記錄 -- **格式**:Context → Decision → Alternatives Considered → Consequences -- **目錄**:`docs/adr/`(2026-04-18 已建立 ADR-001 ~ ADR-007) - -### 第 42 條:Memory 跨 Session 持久化規範(強制要求) -- ✅ **正確**:重大協作 context、統帥偏好、技術決策必須寫入 Memory 目錄 -- ✅ **目錄**:`~/.claude/projects/.../memory/` -- ✅ **索引**:`MEMORY.md` 必須保持最新索引 -- ❌ **禁止**:依賴 Claude 對話 session 記憶(必有 context window 截斷問題) -- **類型分類**:`user`(統帥偏好)、`feedback`(決策回饋)、`project`(專案技術)、`reference`(地圖參考) - -### 第 43 條:Embedding 必須本地化到 Hermes(強制要求) -- ✅ **正確**:使用 `bge-m3`(或 `nomic-embed-text`)掛載在 `192.168.0.111` Ollama -- ❌ **禁止**:呼叫外部 Embedding API(成本 + 數據隱私雙重問題) -- **維度**:`vector(1024)`(ADR-003) -- **理由**:統帥明確要求「Hermes 機器徹底包辦所有苦力運算,把數據隱私與成本降到絕對的零」 - -### 第 44 條:NemoTron 配額耗盡 Fallback 鏈(強制要求) -- ✅ **正確**:`nemoton_dispatcher_service.py` 必須有 try-except,HTTP 429 → 立刻切換 `_hermes_rule_fallback()` -- ❌ **禁止**:配額耗盡時讓告警管線中斷(雙 11 等節慶高峰時風險最高) -- **標記**:降級模式告警需帶 `🟡` 前綴,讓統帥識別(ADR-004) - -### 第 45 條:KM 品質分數時間衰減(強制要求) -- ✅ **公式**:`effective_score = base_score × e^(-decay_rate × days_passed)` -- **預設 decay_rate**:`0.005`;`decay_exempt=True` 用於結構性/憲法類知識 -- **理由**:確保 RAG 優先抓取最新適用洞察,避免歷史偏誤(ADR-005) - ---- - -> **📌 遊戲規則全文見第零章(文件最頂部)。本章條款是第零章的 AI 專項細化。** - -## 🏷️ 專案正名記錄 - -- **2026-04-18**:正式更名為 **EwoooC**(ADR-006) -- Docker image tag 維持 `momo-pro-system`(延後處理) -- Domain 維持 `mo.wooo.work` 與 `momo.wooo.work`(皆指向 UAT) +| 部署 SOP | [docs/guides/deployment_sop.md](docs/guides/deployment_sop.md) | +| DevOps 手冊 | [docs/guides/devops_handbook.md](docs/guides/devops_handbook.md) | +| Google Drive | [docs/guides/google_drive_setup.md](docs/guides/google_drive_setup.md) | +| 歷史日誌 | [docs/memory/history_logs.md](docs/memory/history_logs.md) | +| 憑證對照表 | [docs/memory/credentials_passbook.md](docs/memory/credentials_passbook.md) | +| AIOps 存檔 | [docs/external/aiops_saas.md](docs/external/aiops_saas.md) | diff --git a/CONSTITUTION.md b/CONSTITUTION.md index 071eaae..2281f31 100644 --- a/CONSTITUTION.md +++ b/CONSTITUTION.md @@ -2,8 +2,19 @@ > 本文件定義專案開發的核心準則與不可違反的規範 > **建立日期**: 2026-01-12 -> **當前版本**: V10.1 -> **最後更新**: 2026-04-18(加入第十三、十四章 AI 架構與 Claude Code 官方規範) +> **當前版本**: V10.2 (治理與安全重疊整合版) +> **最後更新**: 2026-04-18 + +--- + +## 🗣️ 第零章:溝通與語法原則 +### 第 0.1 條:語言使用 +- **所有溝通一律使用繁體中文**。包含:程式碼註解、文檔說明、Commit 訊息、錯誤訊息、日誌輸出、使用者介面文字。 + +### 第 0.2 條:文檔規範 +- 所有文檔使用 Markdown 格式。 +- 檔案名稱優先使用英文大寫加底線(例:`CONSTITUTION.md`)。 +- 重要變更需記錄在 `CLAUDE.md` 或 `walkthrough.md` 中。 --- diff --git a/Dockerfile b/Dockerfile index e06a296..e32cdc4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,6 +10,7 @@ RUN apt-get update && apt-get install -y \ g++ \ curl \ libpq-dev \ + postgresql-client \ # Chrome/Selenium 依賴 wget \ gnupg \ diff --git a/PROJECT_CONSTITUTION.md b/PROJECT_CONSTITUTION.md deleted file mode 100644 index a9ec03d..0000000 --- a/PROJECT_CONSTITUTION.md +++ /dev/null @@ -1,599 +0,0 @@ -# MOMO 監控系統 - 專案憲法 - -**版本:** 1.3 -**制定日期:** 2026-01-12 -**最後更新:** 2026-01-14 - ---- - -## 📜 專案基本原則 - -本憲法定義了 MOMO 監控系統的開發規範、溝通原則、安全政策及技術標準。所有參與者(開發人員、AI 助手、維護人員)必須遵守以下規範。 - ---- - -## 🗣️ 第一章:溝通規範 - -### 第 1 條:語言使用 -- **所有溝通一律使用繁體中文** -- 包含但不限於: - - 程式碼註解 - - 文檔說明 - - Commit 訊息 - - 錯誤訊息 - - 日誌輸出 - - 使用者介面文字 - - README 和文檔 - -### 第 2 條:文檔規範 -- 所有文檔檔案使用 Markdown 格式(`.md`) -- 檔案名稱使用英文大寫加底線(例:`PROJECT_CONSTITUTION.md`) -- 文檔內容必須包含版本號和最後更新日期 -- 重要變更需記錄在 CHANGELOG 中 - -### 第 3 條:註解規範 -- Python 函數必須包含繁體中文 docstring -- 複雜邏輯必須添加行內註解說明 -- 註解應說明「為什麼」而非「做什麼」 - ---- - -## 🔒 第二章:安全政策 - -### 第 4 條:敏感資訊管理 -- **禁止在程式碼中硬編碼任何敏感資訊** -- 所有憑證、API 金鑰、密碼必須使用環境變數(`.env`) -- `.env` 檔案必須列入 `.gitignore` -- 提供 `.env.example` 作為範本 - -### 第 5 條:密碼安全 -- 所有密碼必須使用 `pbkdf2:sha256` 雜湊儲存 -- 禁止使用明文密碼(僅過渡期允許,需發出警告) -- 密碼長度至少 8 個字元,包含英文字母和數字 -- 登入失敗 5 次後鎖定帳號 5 分鐘 - -### 第 6 條:輸入驗證 -- **所有使用者輸入必須經過驗證** -- SQL 查詢必須使用參數化查詢或白名單驗證 -- 檔案上傳必須驗證副檔名和檔案大小 -- 路徑操作必須使用 `safe_join()` 防止路徑遍歷 - -### 第 7 條:CSRF 防護 -- 所有 POST/PUT/DELETE/PATCH 請求必須包含 CSRF token -- HTML 表單使用 hidden input: `` -- AJAX 請求使用 header: `'X-CSRFToken': getCSRFToken()` - -### 第 8 條:Session 安全 -- Session cookie 必須設定 `HttpOnly=True` -- Session cookie 必須設定 `SameSite=Lax` -- 生產環境必須設定 `Secure=True`(HTTPS) -- Session 有效期設定為 2 小時 - ---- - -## 💻 第三章:程式碼規範 - -### 第 9 條:檔案上傳 -- 僅允許上傳:`.xlsx`, `.xls`, `.csv` -- 檔案大小限制:10 MB -- 使用 `secure_filename_unicode()` 清理檔名(支援中文) -- 檢查路徑遍歷攻擊(`..`, `/`, `\`) - -### 第 10 條:SQL 安全 -- 表名驗證:僅允許英文字母、數字、底線 -- 欄位名驗證:允許中文、英文字母、數字、底線 -- 時間戳驗證:嚴格遵守 `YYYY-MM-DD HH:MM:SS` 格式 -- 使用 `safe_read_sql()` 進行安全的 SQL 查詢 - -### 第 11 條:路徑安全 -- 所有路徑拼接使用 `safe_join(base, *paths)` -- 檢查 Windows 反斜線、連續點、雙點模式 -- 驗證最終路徑在基礎目錄內 -- 偵測到攻擊時記錄安全日誌 - -### 第 12 條:日誌規範 -- 使用結構化日誌格式:`[模組] [級別] 訊息 | 詳細資訊` -- 安全事件使用 `[Security]` 標籤 -- 記錄所有失敗的驗證嘗試 -- 日誌級別: - - `ERROR`: 系統錯誤 - - `WARNING`: 安全警告、失敗的攻擊嘗試 - - `INFO`: 重要操作成功 - - `DEBUG`: 詳細除錯資訊 - ---- - -## 🕷️ 第四章:數據爬取規範 - -### 第 13 條:爬蟲程式碼穩定性原則 -- **爬蟲程式碼屬於核心業務邏輯,修改時必須格外謹慎** -- 任何修改必須經過完整測試,確認不影響現有爬取功能 -- 修改前必須備份現有可運作的版本 -- 修改後必須驗證所有爬蟲任務正常執行 - -### 第 14 條:爬蟲選擇器維護 -- **CSS 選擇器和 XPath 是脆弱的依賴** -- 修改選擇器前必須: - 1. 記錄修改原因(網站改版、元素變更等) - 2. 測試新選擇器是否正確抓取目標資料 - 3. 保留舊選擇器作為註解備份 - 4. 記錄網站結構變更日期 -- 建議使用多層次選擇器備援(主選擇器 + 備用選擇器) - -### 第 15 條:爬蟲錯誤處理 -- **所有爬蟲函數必須包含完整的錯誤處理** -- 必須處理的情況: - 1. 網路連線失敗 - 2. 頁面載入超時 - 3. 元素找不到(選擇器失效) - 4. 資料格式異常 - 5. 反爬蟲機制觸發 -- 錯誤發生時: - - 記錄詳細錯誤日誌(包含 URL、選擇器、錯誤訊息) - - 發送通知給管理員 - - 不中斷其他爬蟲任務 - - 保存最後成功的資料作為備援 - -### 第 16 條:爬蟲測試要求 -- **修改爬蟲程式碼後必須執行完整測試** -- 測試項目: - 1. 單一商品資料爬取 - 2. 列表頁面分頁爬取 - 3. 多執行緒/並發爬取 - 4. 錯誤處理機制 - 5. 資料儲存完整性 -- 測試環境應模擬生產環境(網路延遲、並發請求) -- 使用測試資料集驗證爬取結果準確性 - -### 第 17 條:爬蟲依賴管理 -- **爬蟲依賴的套件版本必須固定** -- `requirements.txt` 中爬蟲相關套件必須指定版本號: - - `selenium==4.x.x` (具體版本) - - `requests==2.x.x` - - `beautifulsoup4==4.x.x` -- 升級套件前必須: - 1. 在測試環境驗證相容性 - 2. 檢查 changelog 確認無破壞性變更 - 3. 執行完整爬蟲測試套件 - 4. 記錄升級原因和影響 - -### 第 18 條:網站結構變更應對 -- **定期檢查目標網站結構是否變更** -- 建立網站結構監控機制: - 1. 記錄關鍵元素的 HTML 結構 - 2. 定期比對結構變化 - 3. 發現變更時立即通知 - 4. 建立選擇器失效告警 -- 保存網站結構快照(HTML samples)供除錯使用 - -### 第 19 條:爬蟲效能與禮節 -- **遵守網站的 robots.txt 規範** -- 設定合理的請求間隔(建議 1-3 秒) -- 使用 User-Agent 識別身份 -- 避免在網站高峰時段進行大量爬取 -- 實作請求失敗的退避重試機制(Exponential Backoff) - -### 第 20 條:資料驗證與清洗 -- **爬取的資料必須經過驗證** -- 驗證項目: - 1. 價格範圍合理性(不可為 0 或異常大) - 2. 日期格式正確性 - 3. 必填欄位完整性 - 4. 資料型別正確性 -- 發現異常資料時: - - 記錄到錯誤日誌 - - 標記為「需人工審核」 - - 不自動儲存到資料庫 - - 發送通知給管理員 - -### 第 21 條:爬蟲版本控制 -- **爬蟲程式碼每次修改必須建立 Git commit** -- Commit 訊息格式: - - `[Crawler] [網站名稱] 修改描述` - - 例:`[Crawler] [MOMO] 修復商品價格選擇器失效問題` -- 重大修改應建立分支,測試通過後才合併 -- 保留至少最近 3 個可運作版本的備份 - -### 第 22 條:爬蟲文檔要求 -- **每個爬蟲模組必須包含詳細文檔** -- 必須記錄: - 1. 爬取目標(網站 URL、資料類型) - 2. 執行頻率(每小時/每日) - 3. 關鍵選擇器說明 - 4. 已知問題和限制 - 5. 最後修改日期和原因 - 6. 聯絡人/負責人 -- 文檔應隨程式碼更新 - ---- - -## 🧪 第五章:測試與品質保證 - -### 第 23 條:測試覆蓋 -- 所有安全功能必須有對應的測試 -- 測試必須包含正常情況和攻擊情境 -- 使用 `test_*.py` 命名測試檔案 -- 執行 `./run_security_tests.sh` 必須全部通過 - -### 第 24 條:安全測試項目 -必須測試以下項目: -1. 環境變數與憑證管理 -2. SQL 注入防護 -3. 路徑遍歷防護 -4. 檔案上傳驗證 -5. CSRF 防護 -6. 登入驗證強化 -7. Flask 安全配置 - -### 第 25 條:爬蟲測試項目 -必須測試以下項目: -1. 選擇器有效性測試 -2. 資料完整性測試 -3. 錯誤處理測試 -4. 並發爬取測試 -5. 效能壓力測試 - -### 第 26 條:程式碼審查 -- 所有涉及安全的程式碼變更必須經過審查 -- 所有涉及爬蟲的程式碼變更必須經過審查 -- 檢查是否符合本憲法規範 -- 驗證是否通過完整測試 -- 確認日誌和錯誤處理完整 - ---- - -## 📦 第六章:部署與維運 - -### 第 27 條:環境管理規範 - -#### 27.1 環境分層 -本系統採用三層環境架構: - -1. **開發環境 (Development)** - - 位置:`/Users/ogt/momo_pro_system` (macOS Local) - - 用途:程式碼開發、快速測試、UI/UX 調整 - - 運行方式:直接執行 `python app.py` - - 特性:即時修改、快速迭代 - -2. **測試環境 (Testing)** - - 位置:同開發環境 - - 用途:功能測試、安全測試、回歸測試 - - 運行方式:執行測試腳本 - -3. **正式環境 (Production)** - - 位置:`/home/ogt/momo_pro_system` (GCP VM) - - 用途:生產服務、24/7 運行 - - 運行方式:systemd service - - 網址:`https://momo.wooo.work` - -#### 27.2 環境同步原則 - -**嚴格禁止**: -- ❌ 直接在正式環境修改程式碼 -- ❌ 跳過測試直接部署到正式環境 -- ❌ 混用不同環境的資料庫 -- ❌ 將 `.env` 檔案上傳到 Git - -**必須遵守**: -- ✅ 所有修改必須在開發環境完成 -- ✅ 完整測試通過後才能部署 -- ✅ 部署前必須備份正式環境 -- ✅ 部署後必須驗證功能正常 -- ✅ 監控 24 小時確保穩定 - -#### 27.3 標準部署流程 - -參照 `DEPLOYMENT_WORKFLOW.md` 文檔,嚴格遵守以下流程: - -``` -開發 → 測試 → 備份 → 部署 → 驗證 → 監控 -``` - -**階段性檢查**: -1. **開發階段**:程式碼符合規範、Git commit 完成 -2. **測試階段**:功能測試、安全測試、爬蟲測試通過 -3. **部署前**:備份正式環境、確認修改檔案清單 -4. **部署中**:使用標準部署腳本或手動部署 -5. **部署後**:服務狀態正常、功能驗證通過 -6. **監控期**:持續監控 24 小時 - -#### 27.4 部署方法選擇 - -**方法 A:完整部署**(推薦用於大改動) -```bash -cd /Users/ogt/momo_pro_system -``` -適用於: -- Python 程式碼修改 -- 依賴套件更新 -- 配置檔案變更 -- 資料庫結構變更 - -**方法 B:快速更新**(用於小改動) -```bash -gcloud compute scp --zone=asia-east1-a 修改的檔案 momo-server:~/momo_pro_system/ -``` -適用於: -- HTML/CSS/JS 檔案修改 -- 模板檔案更新 -- 靜態資源更新 - -**重要**: -- HTML/CSS/JS 修改:不需重啟服務(Flask 自動重載模板) -- Python 檔案修改:必須重啟服務 -- 配置檔案修改:必須重啟服務 - -### 第 28 條:變更前強制備份原則 ⚠️ - -**重要性:最高優先級** - -**核心原則**: -- **所有涉及嚴重影響的變更、修改操作,變更前必須先進行完整備份** -- **備份完成並確認無誤後,才可進行變更** -- 違反此原則的變更操作視為嚴重違規 - -**適用範圍**: -以下操作在執行前必須完整備份: - -1. **資料庫相關** - - 資料庫結構變更(ALTER TABLE, DROP, CREATE) - - 大量資料修改或刪除(UPDATE, DELETE 影響 >100 筆) - - 資料庫升級或遷移 - - 索引重建或優化 - -2. **系統配置** - - 系統配置檔案修改(config.py, .env) - - Nginx/Apache 配置變更 - - Systemd service 配置修改 - - 排程任務(crontab, scheduler)變更 - -3. **核心程式碼** - - 爬蟲核心邏輯修改 - - 資料庫連線和 ORM 修改 - - 認證和安全模組修改 - - API 端點的破壞性變更 - -4. **部署操作** - - 生產環境程式碼更新 - - Python 依賴套件升級 - - 系統套件升級(Python, Node.js 等) - - 伺服器遷移或重啟 - -5. **資料處理** - - Excel 匯入覆蓋現有資料 - - 批次資料清理或轉換 - - 歷史資料歸檔或刪除 - -**備份要求**: - -**必須備份的內容**: -- 完整資料庫檔案(momo.db 或 PostgreSQL dump) -- 所有程式碼檔案(Git commit + 檔案副本) -- 配置檔案(config.py, .env, nginx.conf 等) -- 重要的資料檔案(Excel, CSV 等) - -**備份驗證**: -- 檢查備份檔案完整性(檔案大小、MD5 校驗) -- 確認備份可讀取(嘗試開啟資料庫) -- 記錄備份時間和檔案位置 -- 確保備份檔案有足夠的磁碟空間 - -**備份命名規範**: -``` -資料庫:momo_backup_YYYYMMDD_HHMMSS.db -程式碼:momo_code_backup_YYYYMMDD_HHMMSS.tar.gz -配置:config_backup_YYYYMMDD_HHMMSS.tar.gz -``` - -**復原計畫**: -- 每次重大變更必須準備復原步驟文件 -- 測試復原流程的可行性 -- 記錄復原所需時間 -- 確保有回滾機制 - -**違規處理**: -- 未備份就執行重大變更:視為一級違規 -- 備份不完整或無法復原:視為二級違規 -- 必須立即停止變更,進行損害評估 -- 記錄事件並更新操作規範 - -**例外情況**: -僅以下情況可豁免備份要求: -- 純前端 HTML/CSS/JS 修改(不影響資料) -- 日誌檔案查看(唯讀操作) -- 系統監控和狀態查詢 -- 測試環境的實驗性變更 - -### 第 29 條:環境配置 -- 開發環境使用 `.env` -- 生產環境使用環境變數注入 -- 不同環境使用不同的 `SECRET_KEY` -- 定期輪換敏感憑證 - -### 第 30 條:備份策略 -- 資料庫每日自動備份 -- 備份檔案加密保存 -- 保留最近 7 天備份 -- 使用 `safe_join()` 處理備份路徑 - -### 第 31 條:更新流程 -1. **評估變更影響**(是否需要備份,參照第 28 條) -2. **完整備份**(若屬於重大變更,必須先備份) -3. 更新程式碼 -4. 執行完整測試套件(安全測試 + 爬蟲測試) -5. 檢查安全日誌 -6. 重啟服務 -7. 驗證功能正常(包含爬蟲任務) -8. 監控 24 小時確保穩定 -9. 確認備份可刪除或歸檔 - ---- - -## 🚨 第七章:事件處理 - -### 第 32 條:安全事件 -發現安全漏洞時: -1. 立即記錄詳細資訊 -2. 評估風險等級(Critical/High/Medium/Low) -3. 優先處理 Critical 和 High 級別 -4. 修復後執行完整測試 -5. 更新 `SECURITY_FIX_SUMMARY.md` - -### 第 33 條:爬蟲異常事件 -發現爬蟲異常時: -1. 記錄詳細錯誤資訊(URL、選擇器、錯誤訊息) -2. 檢查是否為網站結構變更 -3. 若為選擇器失效,立即修復並測試 -4. 發送通知給管理員 -5. 記錄在爬蟲維護日誌中 - -### 第 34 條:錯誤處理 -- 所有錯誤必須妥善處理,不得暴露敏感資訊 -- 使用者看到的錯誤訊息應簡潔明確 -- 詳細錯誤資訊記錄在日誌中 -- 開發環境可顯示詳細錯誤,生產環境僅顯示通用訊息 - ---- - -## 🔧 第八章:開發工具與依賴 - -### 第 35 條:Python 依賴 -核心依賴套件(見 `requirements.txt`): -- Flask (Web 框架) -- Flask-WTF (CSRF 防護) -- SQLAlchemy (ORM) -- pandas (資料處理) -- selenium (網頁自動化) -- werkzeug (安全工具) - -### 第 36 條:版本控制 -- 使用 Git 進行版本控制 -- Commit 訊息使用繁體中文 -- Commit 格式:`[模組] 簡短描述` -- 例:`[Security] 修復路徑遍歷漏洞` -- 例:`[Crawler] [MOMO] 更新商品價格選擇器` - -### 第 37 條:開發環境 -- Python 3.8+ -- 使用虛擬環境 (venv) -- IDE 建議:VSCode, PyCharm -- 測試環境與生產環境分離 -- 爬蟲測試使用獨立環境 - ---- - -## 📊 第九章:監控與維護 - -### 第 38 條:系統監控 -- 定期檢查安全日誌 -- 監控登入失敗次數 -- 追蹤異常 API 請求 -- 定期執行安全測試 - -### 第 39 條:爬蟲監控 -- 監控爬蟲執行成功率 -- 追蹤選擇器失效次數 -- 檢查資料品質(異常值、缺失值) -- 監控爬取耗時變化 -- 定期檢查目標網站結構 - -### 第 40 條:效能監控 -- 資料庫查詢效能 -- API 回應時間 -- 記憶體使用量 -- 磁碟空間 -- 爬蟲執行效率 - ---- - -## 📋 第十章:憲法修訂 - -### 第 41 條:修訂流程 -- 本憲法可隨專案需求修訂 -- 修訂需說明原因和影響範圍 -- 更新版本號和修訂日期 -- 記錄在文檔歷史中 - -### 第 42 條:解釋權 -- 本憲法條款如有疑義,以最新版本為準 -- 技術決策以穩定性和安全性優先 -- 爬蟲修改以不影響現有功能為原則 -- 使用者體驗和效能次之 - ---- - -## 📚 附錄:快速檢查清單 - -### ✅ 新功能開發檢查 -- [ ] 程式碼和註解使用繁體中文 -- [ ] 無硬編碼敏感資訊 -- [ ] 所有輸入經過驗證 -- [ ] POST 請求包含 CSRF token -- [ ] 路徑操作使用 `safe_join()` -- [ ] 檔案上傳經過驗證 -- [ ] 錯誤處理完整 -- [ ] 日誌記錄完整 -- [ ] 通過安全測試 -- [ ] 更新相關文檔 - -### ⚠️ 重大變更前備份檢查 -- [ ] 評估變更影響範圍(資料庫/配置/核心程式碼/部署) -- [ ] 確認符合第 28 條適用範圍 -- [ ] 完整備份資料庫檔案 -- [ ] 備份所有程式碼(Git commit + 檔案副本) -- [ ] 備份配置檔案 -- [ ] 驗證備份檔案完整性(檔案大小、可讀取) -- [ ] 記錄備份時間和位置 -- [ ] 準備復原計畫文件 -- [ ] 測試復原流程可行性 -- [ ] 確保有回滾機制 - -### 🔒 安全審查檢查 -- [ ] SQL 查詢使用參數化或白名單 -- [ ] 無明文密碼 -- [ ] Session 配置正確 -- [ ] CSRF 防護啟用 -- [ ] 路徑遍歷防護 -- [ ] 檔案上傳限制 -- [ ] 登入失敗鎖定 -- [ ] 敏感操作有日誌 - -### 🕷️ 爬蟲修改檢查 -- [ ] 備份現有可運作版本 -- [ ] 記錄修改原因和網站變更資訊 -- [ ] 保留舊選擇器作為註解 -- [ ] 測試新選擇器正確性 -- [ ] 執行單一商品爬取測試 -- [ ] 執行列表頁面爬取測試 -- [ ] 驗證資料完整性和格式 -- [ ] 檢查錯誤處理機制 -- [ ] 更新爬蟲文檔 -- [ ] 記錄在維護日誌中 -- [ ] 建立 Git commit -- [ ] 監控 24 小時確保穩定 - ---- - -## 🎯 結語 - -本憲法旨在確保 MOMO 監控系統的安全性、穩定性、可維護性和一致性。所有參與者應: - -1. **遵守規範**:嚴格遵守本憲法所有條款 -2. **持續改進**:隨著專案發展適時修訂 -3. **穩定優先**:爬蟲修改以不影響現有功能為原則 -4. **安全第一**:任何決策以安全為最高優先 -5. **謹慎測試**:修改後必須完整測試並監控 -6. **文檔完整**:保持文檔與程式碼同步更新 - -**核心原則:** -- **安全不是功能,而是基礎** -- **爬蟲是核心業務,修改需格外謹慎** -- **測試是品質的保證,不可省略** - ---- - -**版本歷史:** -- v1.3 (2026-01-14): 新增第六章第 28 條「變更前強制備份原則」⚠️,明確規定所有涉及嚴重影響的變更操作前必須完整備份,定義適用範圍、備份要求、驗證流程、復原計畫及違規處理機制 -- v1.2 (2026-01-13): 擴充第六章第 27 條「環境管理規範」,明確定義開發/測試/正式三層環境架構、環境同步原則、標準部署流程,並新增 `DEPLOYMENT_WORKFLOW.md` 完整部署文檔 -- v1.1 (2026-01-12): 新增第四章「數據爬取規範」(第 13-22 條),定義爬蟲程式碼穩定性、選擇器維護、錯誤處理、測試要求等 10 項規範 -- v1.0 (2026-01-12): 初版發布,定義核心規範 diff --git a/migrations/012_backup_log.sql b/migrations/012_backup_log.sql new file mode 100644 index 0000000..6b569ce --- /dev/null +++ b/migrations/012_backup_log.sql @@ -0,0 +1,19 @@ +-- Migration 012: backup_log 備份記錄表 +-- 用於 AI Agent 監控資料庫備份執行狀況 + +CREATE TABLE IF NOT EXISTS backup_log ( + id SERIAL PRIMARY KEY, + backup_type VARCHAR(20) DEFAULT 'full', -- full / incremental + filename VARCHAR(255) NOT NULL, + file_size_bytes BIGINT DEFAULT 0, + duration_seconds FLOAT DEFAULT 0, + status VARCHAR(20) DEFAULT 'pending', -- pending / success / failed + error_message TEXT, + host VARCHAR(100), + storage_path VARCHAR(500), + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + completed_at TIMESTAMP +); + +CREATE INDEX IF NOT EXISTS idx_backup_log_status ON backup_log(status); +CREATE INDEX IF NOT EXISTS idx_backup_log_created ON backup_log(created_at DESC); diff --git a/run_scheduler.py b/run_scheduler.py index a35782c..50a3964 100644 --- a/run_scheduler.py +++ b/run_scheduler.py @@ -46,6 +46,8 @@ def main(): run_competitor_price_feeder_task, run_icaim_analysis_task, run_weekly_strategy_task, + run_db_backup_task, + run_backup_monitor_task, ) logger.info("✅ 排程任務模組載入成功") except ImportError as e: @@ -83,6 +85,12 @@ def main(): schedule.every().monday.at("07:00").do(run_weekly_strategy_task) logger.info("📅 已設定:每週一 07:00 執行 Gemini 策略師週報任務") + schedule.every().day.at("02:00").do(run_db_backup_task) + logger.info("📅 已設定:每日 02:00 執行 PostgreSQL 資料庫備份") + + schedule.every(6).hours.do(run_backup_monitor_task) + logger.info("📅 已設定:每 6 小時執行備份健康監控(AI Agent 跟進)") + logger.info("=" * 60) logger.info("✅ 排程器已啟動,等待任務執行...") logger.info("=" * 60) diff --git a/scheduler.py b/scheduler.py index 96ff42b..c4826ce 100644 --- a/scheduler.py +++ b/scheduler.py @@ -1652,6 +1652,159 @@ def run_weekly_strategy_task(): _save_stats('weekly_strategy', {"status": "Failed", "error": str(e)}) +def run_db_backup_task(): + """ + 每日凌晨 02:00 執行 pg_dump 備份 momo_analytics, + 並清理超過 7 天的舊備份。完成後透過 Telegram 通知統帥。 + 失敗同樣發出告警,並沉澱到 ai_insights(供 RAG 查詢)。 + """ + logging.info("[Scheduler] [Backup] 🚀 啟動資料庫備份任務...") + try: + from services.db_backup_service import run_backup, cleanup_old_backups + from services.notification_manager import NotificationManager + + result = run_backup() + deleted_count = cleanup_old_backups() + now_str = datetime.now(TAIPEI_TZ).strftime('%Y-%m-%d %H:%M') + + notifier = NotificationManager() + + if result["success"]: + size_kb = result["file_size"] // 1024 + msg = ( + f"💾 資料庫備份完成 ({now_str})\n" + f"{'='*30}\n" + f"✅ 狀態:成功\n" + f"📁 檔案:{result['filename']}\n" + f"📦 大小:{size_kb} KB\n" + f"⏱ 耗時:{result['duration']:.1f} 秒\n" + f"🗑 清理舊備份:{deleted_count} 個" + ) + logging.info(f"[Scheduler] [Backup] ✅ 備份成功 | {result['filename']} ({size_kb}KB)") + _save_stats('db_backup', {"status": "Success", "filename": result["filename"], "size_kb": size_kb}) + + # 沉澱到 ai_insights(RAG 可查詢備份歷史) + try: + from services.openclaw_learning_service import store_insight + store_insight( + insight_type='backup_status', + content=f"資料庫備份成功:{result['filename']},大小 {size_kb}KB,耗時 {result['duration']:.1f}s", + period=datetime.now(TAIPEI_TZ).strftime('%Y-%m-%d'), + metadata={"status": "success", "size_kb": size_kb, "deleted_old": deleted_count}, + ai_model="scheduler", + ) + except Exception: + pass + else: + msg = ( + f"🚨 資料庫備份失敗 ({now_str})\n" + f"{'='*30}\n" + f"❌ 狀態:失敗\n" + f"🔍 原因:{result.get('error', '未知錯誤')}\n" + f"⚠️ 請立即檢查 momo-db 容器狀態!" + ) + logging.error(f"[Scheduler] [Backup] ❌ 備份失敗: {result.get('error')}") + _save_stats('db_backup', {"status": "Failed", "error": result.get("error")}) + + try: + from services.openclaw_learning_service import store_insight + store_insight( + insight_type='backup_status', + content=f"資料庫備份失敗:{result.get('error', '未知')}", + period=datetime.now(TAIPEI_TZ).strftime('%Y-%m-%d'), + metadata={"status": "failed", "error": result.get("error")}, + ai_model="scheduler", + ) + except Exception: + pass + + notifier._send_telegram_messages([msg]) + + except Exception as e: + logging.error(f"[Scheduler] [Backup] 🚨 任務異常 | Error: {e}") + _save_stats('db_backup', {"status": "Error", "error": str(e)}) + try: + from services.notification_manager import NotificationManager + NotificationManager()._send_telegram_messages([ + f"🚨 DB 備份排程異常\n錯誤:{e}" + ]) + except Exception: + pass + + +def run_backup_monitor_task(): + """ + 每 6 小時檢查最近一次備份是否在 25 小時內(允許一點偏差)。 + 若發現備份過期或從未備份,立即發出 Telegram 告警, + 並透過 store_insight 讓 NemoTron/OpenClaw 可感知備份健康狀態。 + """ + logging.info("[Scheduler] [BackupMonitor] 🔍 檢查備份健康狀態...") + try: + from services.db_backup_service import get_latest_backup_info + from services.notification_manager import NotificationManager + + info = get_latest_backup_info() + now = datetime.now(TAIPEI_TZ) + alert_needed = False + alert_reason = "" + + if info["status"] == "no_backup" or info["filename"] is None: + alert_needed = True + alert_reason = "從未執行過備份,backup_log 與備份目錄均為空" + elif info["status"] == "failed": + alert_needed = True + alert_reason = f"最近一次備份失敗:{info.get('error', '未知')}" + else: + created_at = info["created_at"] + if created_at is not None: + # 統一為 naive datetime 比較 + if hasattr(created_at, 'tzinfo') and created_at.tzinfo is not None: + created_at = created_at.replace(tzinfo=None) + now_naive = now.replace(tzinfo=None) + hours_ago = (now_naive - created_at).total_seconds() / 3600 + if hours_ago > 25: + alert_needed = True + alert_reason = f"最近備份距今 {hours_ago:.1f} 小時(超過 25h 閾值),可能備份任務未執行" + + if alert_needed: + now_str = now.strftime('%Y-%m-%d %H:%M') + msg = ( + f"⚠️ 資料庫備份異常告警 ({now_str})\n" + f"{'='*30}\n" + f"🔴 原因:{alert_reason}\n" + f"📋 最新備份:{info.get('filename', '無')}\n" + f"🕐 備份時間:{info.get('created_at', '無')}\n" + f"💡 請確認 momo-scheduler 備份排程是否正常執行!" + ) + logging.warning(f"[Scheduler] [BackupMonitor] ⚠️ 備份告警: {alert_reason}") + NotificationManager()._send_telegram_messages([msg]) + + try: + from services.openclaw_learning_service import store_insight + store_insight( + insight_type='backup_status', + content=f"備份監控告警:{alert_reason}", + period=now.strftime('%Y-%m-%d'), + metadata={"alert": True, "reason": alert_reason, "latest_file": info.get("filename")}, + ai_model="scheduler", + ) + except Exception: + pass + else: + created_at = info.get("created_at") + logging.info(f"[Scheduler] [BackupMonitor] ✅ 備份狀態正常 | 最新: {info.get('filename')} @ {created_at}") + + _save_stats('backup_monitor', { + "status": "Alert" if alert_needed else "OK", + "latest_file": info.get("filename"), + "alert_reason": alert_reason if alert_needed else None, + }) + + except Exception as e: + logging.error(f"[Scheduler] [BackupMonitor] 🚨 監控任務異常 | Error: {e}") + _save_stats('backup_monitor', {"status": "Error", "error": str(e)}) + + if __name__ == "__main__": # 此檔案現在由 app.py 導入並由其主執行緒管理排程。 # 若需獨立測試,可在此處臨時加入調用程式碼。 diff --git a/scripts/pg_backup.sh b/scripts/pg_backup.sh new file mode 100644 index 0000000..251500a --- /dev/null +++ b/scripts/pg_backup.sh @@ -0,0 +1,72 @@ +#!/bin/bash +# EwoooC PostgreSQL 備份腳本 (Host-Level) +# 執行環境:192.168.0.188 host,每日 02:00 cron 觸發 +# pg_dump 在 momo-db container 內執行 + +set -euo pipefail + +BACKUP_DIR="/home/ollama/momo_backups" +DB_CONTAINER="momo-db" +DB_USER="momo" +DB_NAME="momo_analytics" +DB_PASS="wooo_pg_2026" +KEEP_DAYS=7 +TIMESTAMP=$(date +%Y%m%d_%H%M%S) +FILENAME="momo_analytics_${TIMESTAMP}.sql.gz" +FILEPATH="${BACKUP_DIR}/${FILENAME}" +LOG_FILE="${BACKUP_DIR}/backup.log" + +TELEGRAM_TOKEN="8075645931:AAH-EGKMo8ZC4QJs-Nc1_0s92xHrGdQvdpg" +TELEGRAM_CHAT="5619078117" + +log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"; } +send_tg() { + curl -s -X POST "https://api.telegram.org/bot${TELEGRAM_TOKEN}/sendMessage" \ + -d "chat_id=${TELEGRAM_CHAT}&text=$1&parse_mode=HTML" > /dev/null 2>&1 || true +} + +mkdir -p "$BACKUP_DIR" +log "===== EwoooC DB Backup 開始 =====" + +START=$(date +%s) + +# 執行 pg_dump(在 momo-db container 內,透過 docker exec) +if PGPASSWORD="$DB_PASS" docker exec -e PGPASSWORD="$DB_PASS" \ + "$DB_CONTAINER" pg_dump -U "$DB_USER" -d "$DB_NAME" --no-password | \ + gzip > "$FILEPATH"; then + + END=$(date +%s) + DURATION=$((END - START)) + SIZE=$(du -h "$FILEPATH" | cut -f1) + SIZE_BYTES=$(stat -c%s "$FILEPATH" 2>/dev/null || stat -f%z "$FILEPATH" 2>/dev/null || echo 0) + + log "✅ 備份成功: $FILENAME ($SIZE, ${DURATION}s)" + + # 寫入 backup_log(PostgreSQL) + docker exec -e PGPASSWORD="$DB_PASS" "$DB_CONTAINER" psql -U "$DB_USER" -d "$DB_NAME" -c \ + "INSERT INTO backup_log (filename, file_size_bytes, duration_seconds, status, host, storage_path, completed_at) + VALUES ('$FILENAME', $SIZE_BYTES, $DURATION, 'success', '$(hostname)', '$FILEPATH', CURRENT_TIMESTAMP);" \ + > /dev/null 2>&1 || log "⚠️ backup_log 寫入失敗(不影響備份本體)" + + # 清理舊備份 + DELETED=$(find "$BACKUP_DIR" -name "momo_analytics_*.sql.gz" -mtime +${KEEP_DAYS} -print -delete | wc -l) + log "🗑 清理舊備份:${DELETED} 個" + + MSG="💾 EwoooC DB 備份完成%0A✅ 狀態:成功%0A📁 ${FILENAME}%0A📦 大小:${SIZE}%0A⏱ 耗時:${DURATION}秒%0A🗑 清理:${DELETED} 個舊備份" + send_tg "$MSG" +else + END=$(date +%s) + DURATION=$((END - START)) + log "❌ 備份失敗!" + + docker exec -e PGPASSWORD="$DB_PASS" "$DB_CONTAINER" psql -U "$DB_USER" -d "$DB_NAME" -c \ + "INSERT INTO backup_log (filename, file_size_bytes, duration_seconds, status, host, storage_path, error_message, completed_at) + VALUES ('$FILENAME', 0, $DURATION, 'failed', '$(hostname)', '$BACKUP_DIR', 'pg_dump 執行失敗', CURRENT_TIMESTAMP);" \ + > /dev/null 2>&1 || true + + MSG="🚨 EwoooC DB 備份失敗%0A❌ 時間:$(date '+%Y-%m-%d %H:%M')%0A⚠️ 請立即檢查 momo-db 容器!" + send_tg "$MSG" + exit 1 +fi + +log "===== Backup 完成 =====" diff --git a/services/db_backup_service.py b/services/db_backup_service.py new file mode 100644 index 0000000..84c9cdc --- /dev/null +++ b/services/db_backup_service.py @@ -0,0 +1,187 @@ +""" +DB Backup Service — EwoooC V10.3 +負責執行 pg_dump 備份、保留策略、以及備份狀態寫入 backup_log +""" +import os +import subprocess +import logging +import glob +from datetime import datetime, timedelta, timezone + +TAIPEI_TZ = timezone(timedelta(hours=8)) +logger = logging.getLogger(__name__) + +# 備份目錄:container 內掛載點 +BACKUP_DIR = os.environ.get("BACKUP_DIR", "/app/data/db_backups") +# pg_dump 目標:在 momo-db container 內執行(docker exec) +DB_CONTAINER = os.environ.get("DB_CONTAINER", "momo-db") +DB_USER = os.environ.get("POSTGRES_USER", "momo") +DB_NAME = os.environ.get("POSTGRES_DB", "momo_analytics") +# 保留天數 +RETENTION_DAYS = int(os.environ.get("BACKUP_RETENTION_DAYS", "7")) + + +def _ensure_backup_dir(): + os.makedirs(BACKUP_DIR, exist_ok=True) + + +def _log_backup(filename, file_size, duration, status, error=None, storage_path=None): + """寫入 backup_log 表,失敗不阻斷主流程""" + try: + from database.manager import DatabaseManager + db = DatabaseManager() + with db.get_session() as session: + from sqlalchemy import text + session.execute(text(""" + INSERT INTO backup_log + (filename, file_size_bytes, duration_seconds, status, error_message, + host, storage_path, completed_at) + VALUES + (:filename, :size, :dur, :status, :error, + :host, :path, CURRENT_TIMESTAMP) + """), { + "filename": filename, + "size": file_size, + "dur": duration, + "status": status, + "error": error, + "host": os.uname().nodename if hasattr(os, 'uname') else "unknown", + "path": storage_path or BACKUP_DIR, + }) + session.commit() + except Exception as e: + logger.warning(f"[Backup] backup_log 寫入失敗(不影響備份本體): {e}") + + +def run_backup() -> dict: + """ + 執行 pg_dump 備份。 + 因 scheduler 在 momo-scheduler container 內,pg_dump 直連 momo-db service。 + 回傳 dict: {success, filename, file_size, duration, error} + """ + _ensure_backup_dir() + now = datetime.now(TAIPEI_TZ) + filename = f"momo_analytics_{now.strftime('%Y%m%d_%H%M%S')}.sql.gz" + filepath = os.path.join(BACKUP_DIR, filename) + start = datetime.now() + + db_host = os.environ.get("POSTGRES_HOST", "momo-db") + db_port = os.environ.get("POSTGRES_PORT", "5432") + + # 若 pg_dump 不存在則嘗試安裝(容器重建後需重裝;Dockerfile 已加入 postgresql-client) + if not os.path.exists("/usr/bin/pg_dump"): + logger.info("[Backup] pg_dump 不存在,嘗試安裝 postgresql-client...") + subprocess.run( + ["apt-get", "install", "-y", "-qq", "postgresql-client"], + capture_output=True + ) + + cmd = [ + "sh", "-c", + f"PGPASSWORD={os.environ.get('POSTGRES_PASSWORD', 'wooo_pg_2026')} " + f"pg_dump -h {db_host} -p {db_port} -U {DB_USER} -d {DB_NAME} " + f"--no-password -Fp | gzip > {filepath}" + ] + + logger.info(f"[Backup] 開始備份 → {filepath}") + result = {"success": False, "filename": filename, "file_size": 0, "duration": 0, "error": None} + + try: + proc = subprocess.run(cmd, capture_output=True, text=True, timeout=300) + duration = (datetime.now() - start).total_seconds() + + if proc.returncode != 0: + error_msg = proc.stderr.strip() or "pg_dump 非零退出碼" + logger.error(f"[Backup] 備份失敗: {error_msg}") + result["error"] = error_msg + result["duration"] = duration + _log_backup(filename, 0, duration, "failed", error=error_msg) + else: + file_size = os.path.getsize(filepath) if os.path.exists(filepath) else 0 + logger.info(f"[Backup] 備份成功 | 大小={file_size//1024}KB | 耗時={duration:.1f}s") + result.update({"success": True, "file_size": file_size, "duration": duration}) + _log_backup(filename, file_size, duration, "success", storage_path=filepath) + + except subprocess.TimeoutExpired: + duration = (datetime.now() - start).total_seconds() + error_msg = "pg_dump 超時(300s)" + logger.error(f"[Backup] {error_msg}") + result["error"] = error_msg + result["duration"] = duration + _log_backup(filename, 0, duration, "failed", error=error_msg) + except Exception as e: + duration = (datetime.now() - start).total_seconds() + error_msg = str(e) + logger.error(f"[Backup] 備份異常: {e}") + result["error"] = error_msg + result["duration"] = duration + _log_backup(filename, 0, duration, "failed", error=error_msg) + + return result + + +def cleanup_old_backups() -> int: + """刪除超過保留期限的備份檔,回傳刪除數量""" + _ensure_backup_dir() + cutoff = datetime.now() - timedelta(days=RETENTION_DAYS) + deleted = 0 + for f in glob.glob(os.path.join(BACKUP_DIR, "momo_analytics_*.sql.gz")): + try: + mtime = datetime.fromtimestamp(os.path.getmtime(f)) + if mtime < cutoff: + os.remove(f) + deleted += 1 + logger.info(f"[Backup] 已刪除舊備份: {os.path.basename(f)}") + except Exception as e: + logger.warning(f"[Backup] 刪除舊備份失敗 {f}: {e}") + return deleted + + +def get_latest_backup_info() -> dict: + """ + 回傳最新備份的資訊(供監控用)。 + 優先從 backup_log 讀取,fallback 掃描檔案系統。 + """ + try: + from database.manager import DatabaseManager + db = DatabaseManager() + with db.get_session() as session: + from sqlalchemy import text + row = session.execute(text(""" + SELECT filename, file_size_bytes, duration_seconds, status, created_at, error_message + FROM backup_log + ORDER BY created_at DESC + LIMIT 1 + """)).fetchone() + if row: + return { + "filename": row[0], + "file_size": row[1], + "duration": row[2], + "status": row[3], + "created_at": row[4], + "error": row[5], + "source": "db", + } + except Exception as e: + logger.warning(f"[Backup] 無法從 DB 讀取最新備份資訊: {e}") + + # fallback: 掃描檔案 + _ensure_backup_dir() + files = sorted( + glob.glob(os.path.join(BACKUP_DIR, "momo_analytics_*.sql.gz")), + key=os.path.getmtime, reverse=True + ) + if files: + f = files[0] + mtime = datetime.fromtimestamp(os.path.getmtime(f)) + return { + "filename": os.path.basename(f), + "file_size": os.path.getsize(f), + "duration": None, + "status": "success", + "created_at": mtime, + "error": None, + "source": "filesystem", + } + return {"filename": None, "status": "no_backup", "created_at": None, "source": "none"} diff --git a/services/ollama_service.py b/services/ollama_service.py index ab3ee0d..2bbfa1f 100644 --- a/services/ollama_service.py +++ b/services/ollama_service.py @@ -505,29 +505,36 @@ class OllamaService: return self.generate(prompt, system_prompt=system_prompt, temperature=0.5, timeout=120) - def generate_embedding(self, text: str, model: str = "bge-m3:latest") -> List[float]: + def generate_embedding(self, text: str, model: str = "bge-m3:latest", + host: str = None) -> List[float]: """ [ADR-007, Step 3] 呼叫 Ollama API 將文字轉換為向量 Embedding + + 2026-04-19 更新(ADR-003 對齊): + embedding 預設走 Hermes 主機 `EMBEDDING_HOST`(env: EMBEDDING_HOST + → fallback http://192.168.0.111:11434,內網免認證), + 避免 self.host 若指向公開 ollama.wooo.work 時回 401。 + 可透過 host 參數 override。 """ + import os + target_host = host or os.getenv("EMBEDDING_HOST", "http://192.168.0.111:11434") try: - # V-Opt: 發送 embedding 請求 - payload = { - "model": model, - "prompt": text - } + payload = {"model": model, "prompt": text} response = requests.post( - f"{self.host}/api/embeddings", + f"{target_host}/api/embeddings", json=payload, - timeout=60 + timeout=60, ) if response.status_code == 200: data = response.json() return data.get("embedding", []) else: - logger.error(f"Ollama Embed Error HTTP {response.status_code}: {response.text}") + logger.error( + f"Ollama Embed Error HTTP {response.status_code} @ {target_host}: {response.text[:200]}" + ) return [] except Exception as e: - logger.error(f"Ollama Embed Exception: {e}") + logger.error(f"Ollama Embed Exception @ {target_host}: {e}") return [] # 建立全域服務實例