diff --git a/.github/workflows/cd.yaml b/.github/workflows/cd.yaml index b13407f0e..265182dac 100644 --- a/.github/workflows/cd.yaml +++ b/.github/workflows/cd.yaml @@ -24,6 +24,14 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 + with: + # 修復: Harbor 是 HTTP,需要設定 insecure registry + driver-opts: | + network=host + buildkitd-config-inline: | + [registry."192.168.0.110:5000"] + http = true + insecure = true - name: Login to WOOO Harbor uses: docker/login-action@v3 @@ -53,19 +61,20 @@ jobs: run: | echo "::notice::Image pushed: ${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}-${{ matrix.app }}:${{ steps.tag.outputs.tag }}" - # ==================== Deploy to UAT ==================== - deploy-uat: - name: Deploy to UAT + # ==================== Deploy to Production ==================== + # Memory 鐵律: 禁止 UAT,只有 Dev + Prod + deploy-prod: + name: Deploy to Production runs-on: self-hosted needs: build-images - environment: uat + environment: production steps: - uses: actions/checkout@v4 - name: Setup Kubeconfig run: | mkdir -p ~/.kube - echo "${{ secrets.KUBE_CONFIG_UAT }}" | base64 -d > ~/.kube/config + echo "${{ secrets.KUBE_CONFIG_PROD }}" | base64 -d > ~/.kube/config chmod 600 ~/.kube/config - name: Generate image tag @@ -77,7 +86,7 @@ jobs: - name: Deploy with Kustomize run: | - cd k8s/overlays/uat + cd k8s/overlays/prod kustomize edit set image \ awoooi-web=${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}-web:${{ steps.tag.outputs.tag }} \ awoooi-api=${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}-api:${{ steps.tag.outputs.tag }} @@ -85,10 +94,26 @@ jobs: - name: Wait for rollout run: | - kubectl rollout status deployment/awoooi-web -n awoooi-uat --timeout=300s - kubectl rollout status deployment/awoooi-api -n awoooi-uat --timeout=300s + kubectl rollout status deployment/awoooi-web -n awoooi --timeout=300s + kubectl rollout status deployment/awoooi-api -n awoooi --timeout=300s - name: Health check run: | sleep 10 - curl -f https://api-uat.awoooi.wooo.work/v1/health || exit 1 + curl -f https://api.awoooi.wooo.work/api/v1/health || exit 1 + + - name: Notify Telegram on Success + if: success() + run: | + curl -s -X POST "https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMessage" \ + -d chat_id="${{ secrets.TELEGRAM_CHAT_ID }}" \ + -d text="✅ *AWOOOI 部署成功*%0A%0ACommit: \`${{ github.sha }}\`%0ABranch: \`${{ github.ref_name }}\`%0AWorkflow: [查看](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})" \ + -d parse_mode="Markdown" + + - name: Notify Telegram on Failure + if: failure() + run: | + curl -s -X POST "https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMessage" \ + -d chat_id="${{ secrets.TELEGRAM_CHAT_ID }}" \ + -d text="❌ *AWOOOI 部署失敗*%0A%0ACommit: \`${{ github.sha }}\`%0ABranch: \`${{ github.ref_name }}\`%0AWorkflow: [查看](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})" \ + -d parse_mode="Markdown" diff --git a/.github/workflows/deploy-prod.yml b/.github/workflows/deploy-prod.yml index 6799dc6f2..5f670e90a 100644 --- a/.github/workflows/deploy-prod.yml +++ b/.github/workflows/deploy-prod.yml @@ -303,9 +303,10 @@ jobs: fi # 發送 Telegram 訊息 (JSON payload with HTML + Inline Keyboard) - curl -s -X POST "https://api.telegram.org/bot8569720657:AAHdvKf_P2ms-QKFTyqTLtLiqEggz8cpjMk/sendMessage" \ + # 鐵律: Token 必須用 Secrets,禁止硬編碼 + curl -s -X POST "https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMessage" \ -H "Content-Type: application/json" \ - -d "{\"chat_id\":\"5619078117\",\"text\":\"${MESSAGE}\",\"parse_mode\":\"HTML\",\"reply_markup\":${KEYBOARD}}" + -d "{\"chat_id\":\"${{ secrets.TELEGRAM_CHAT_ID }}\",\"text\":\"${MESSAGE}\",\"parse_mode\":\"HTML\",\"reply_markup\":${KEYBOARD}}" - name: Send OpenClaw Webhook if: always() diff --git a/docs/guidelines/ARCHITECTURE.md b/docs/guidelines/ARCHITECTURE.md new file mode 100644 index 000000000..ad4a20e4f --- /dev/null +++ b/docs/guidelines/ARCHITECTURE.md @@ -0,0 +1,137 @@ +# AWOOOI 架構指引 + +> 架構相關的核心原則與規範 + +## 快速索引 + +| 主題 | 核心原則 | 詳細章節 | +|------|---------|---------| +| OpenClaw | 產品核心,只能增強不能移除 | [→ OpenClaw](#openclaw-核心架構) | +| 模組化 | Interface → Memory → Brain → Skill | [→ leWOOOgo](#lewooogo-模組化) | +| API 整合 | Props Mapping 五步驟檢查 | [→ API](#api-整合) | +| 防禦性 | 先質疑後實作 | [→ 防禦性工程](#防禦性工程) | + +--- + +## OpenClaw 核心架構 + +**Memory 來源:** `feedback_architecture_openclaw_core.md` + +### 原則 + +``` +✅ OpenClaw 是 AWOOOI 產品核心 +✅ 只能增強,不能移除 +✅ 決策鏈必須可視化 (ThinkingTerminal) +✅ 雙軌決策: LLM + Expert System Fallback +``` + +### 決策流程 + +``` +Signal → IncidentEngine → DecisionManager → Telegram/UI + ↓ + LLM 提案 + Expert System 備援 +``` + +### 命名規範 + +- ✅ 正確: OpenClaw, openclaw +- ❌ 禁止: ClawBot, clawbot (舊稱) + +--- + +## leWOOOgo 模組化 + +**Memory 來源:** `feedback_modular_architecture.md`, `feedback_modular_core_spirit.md` + +### 四層架構 + +``` +┌─────────────────────────────────────────┐ +│ Skill Layer (技能層) │ +│ - 特定領域知識 │ +│ - 可組合、可替換 │ +├─────────────────────────────────────────┤ +│ Brain Layer (決策層) │ +│ - LLM + Expert System │ +│ - 決策邏輯 │ +├─────────────────────────────────────────┤ +│ Memory Layer (記憶層) │ +│ - Working: Redis (短期) │ +│ - Episodic: PostgreSQL (長期) │ +├─────────────────────────────────────────┤ +│ Interface Layer (介面層) │ +│ - Telegram Gateway │ +│ - REST API │ +└─────────────────────────────────────────┘ +``` + +### 核心精神 + +1. **Interface 先行** - 先定義清晰的介面契約 +2. **Memory 分離** - Working Memory ≠ Episodic Memory +3. **Brain 獨立** - 決策邏輯不綁定特定 LLM +4. **Skill 可組合** - 技能可插拔、可替換 + +--- + +## API 整合 + +**Memory 來源:** `feedback_api_response_verification.md` + +### Props Mapping 五步驟檢查 + +新增 API 欄位時,必須確認完整鏈路: + +``` +1. API Response 有該欄位 ✓ +2. Mapper 函數有轉換該欄位 ✓ ← 最常遺漏! +3. TypeScript 型別有定義該欄位 ✓ +4. 組件 Props 有接收該欄位 ✓ +5. 組件有使用該欄位 ✓ +``` + +### 快速診斷 + +```bash +# 確認 API 有回傳 +curl -s API_URL | jq '.[0].decision' + +# 確認 Mapper 有轉換 +grep -n "decision" apps/web/src/app/*/page.tsx +``` + +### 2026-03-23 教訓 + +Y/n 按鈕灰色無法點擊,因為 `mapToDualState()` 遺漏傳遞 `decision` 欄位。 + +--- + +## 防禦性工程 + +**Memory 來源:** `feedback_defensive_engineering.md` + +### 原則 + +``` +1. 先質疑,後實作 +2. 看到可疑代碼,先問為什麼存在 +3. 不確定的變更,先問統帥 +4. 任何刪除操作,三思後行 +``` + +### 常見陷阱 + +- 「這段代碼看起來沒用」→ 先 grep 確認沒人用 +- 「這個 TODO 很久了」→ 先確認是否已完成 +- 「這個邏輯很奇怪」→ 可能是特殊 case 處理 + +--- + +## 相關 ADR + +- [ADR-003: leWOOOgo 模組架構](adr/ADR-003-lewooogo-module-architecture.md) +- [ADR-006: AI Fallback 策略](adr/ADR-006-ai-fallback-strategy.md) +- [ADR-008: Python 模組化](adr/ADR-008-python-modular-packages.md) +- [ADR-009: OpenClaw Agent Teams](adr/ADR-009-openclaw-agent-teams.md) diff --git a/docs/guidelines/FRONTEND.md b/docs/guidelines/FRONTEND.md new file mode 100644 index 000000000..861e3371f --- /dev/null +++ b/docs/guidelines/FRONTEND.md @@ -0,0 +1,159 @@ +# AWOOOI 前端指引 + +> 前端開發的核心原則與規範 + +## 快速索引 + +| 主題 | 核心原則 | 詳細章節 | +|------|---------|---------| +| i18n | 100% next-intl,零硬編碼 | [→ i18n](#i18n-雙語) | +| 視覺 | Nothing.tech 風格 | [→ 視覺規範](#視覺規範) | +| 狀態 | Zustand (禁止 Redux) | [→ 狀態管理](#狀態管理) | +| 數據 | 真實 API,禁止假數據 | [→ 數據規範](#數據規範) | + +--- + +## i18n 雙語 + +**Memory 來源:** `feedback_i18n_zero_hardcode.md`, `feedback_naming_i18n.md` + +### 鐵律 + +```tsx +// ❌ 禁止 - 任何硬編碼文字 + +Loading... +
System Status
+ +// ✅ 正確 - 100% next-intl + +{t('common.loading')} +{t('dashboard.systemStatus')}
+``` + +### 支援語言 + +- `zh-TW` - 繁體中文 (預設) +- `en` - English + +### 翻譯檔位置 + +``` +apps/web/src/messages/ +├── zh-TW.json +└── en.json +``` + +### 新增翻譯流程 + +1. 在 `zh-TW.json` 新增 key +2. 在 `en.json` 新增對應翻譯 +3. 在組件使用 `t('your.key')` + +--- + +## 視覺規範 + +**Memory 來源:** `feedback_naming_i18n.md` + +### Nothing.tech 風格 + +``` +- 白玻璃毛玻璃效果 (awoooi-glass) +- 點陣紋理背景 +- DataPincer 數據鉗容器 +- 黑白紅極簡配色 +``` + +### 顏色系統 + +```css +/* 主色 */ +--nothing-black: #000000 +--nothing-white: #ffffff +--claw-red: #ff0000 + +/* 狀態色 */ +--status-healthy: #00ff00 +--status-warning: #ffaa00 +--status-critical: #ff0000 +``` + +### 組件規範 + +```tsx +// DataPincerPanel - 標準容器 +