diff --git a/docs/superpowers/specs/2026-04-08-sprint5-api-changes.md b/docs/superpowers/specs/2026-04-08-sprint5-api-changes.md new file mode 100644 index 00000000..67dc015d --- /dev/null +++ b/docs/superpowers/specs/2026-04-08-sprint5-api-changes.md @@ -0,0 +1,102 @@ +# Sprint 5 — API 變更規格 + +> 後端 Dashboard API 的擴充欄位定義 + +--- + +## 現有 API (不動) + +``` +GET /api/v1/dashboard → DashboardResponse (聚合資料) +GET /api/v1/dashboard/stream → SSE (每30秒推送) +GET /api/v1/dashboard/hosts → 4主機概覽 +GET /api/v1/stats/disposition → Sprint 4 處置統計 +GET /api/v1/approvals/pending → 待核准列表 +``` + +--- + +## 擴充: DashboardResponse 新增欄位 + +```python +# apps/api/src/api/v1/dashboard.py + +class DashboardResponse(BaseModel): + # ─── 現有欄位 (不動) ─── + timestamp: datetime + environment: str + mock_mode: bool + overall_status: str + hosts: list[HostStatusResponse] + alerts_count: int + pending_approvals: int + + # ─── Sprint 5 新增 ─── + k8s_pods: list[PodStatusBrief] = [] + ai_diagnosing: list[str] = [] # OpenClaw 正在分析的服務名稱 + topology_metadata: TopologyMetadata | None = None + + +class PodStatusBrief(BaseModel): + """K8s Pod 簡要狀態 (不需要完整 Pod spec)""" + name: str # "awoooi-api-7b8f4c-x9k2p" + deployment: str # "awoooi-api" + namespace: str # "awoooi-prod" + status: str # "Running" | "Pending" | "CrashLoopBackOff" + restart_count: int = 0 + ready: bool = True + + +class TopologyMetadata(BaseModel): + """拓撲圖元資料""" + total_entities: int # 67 + healthy_count: int # 65 + warning_count: int # 1 + critical_count: int # 0 + ai_active: bool # OpenClaw 是否正在分析 +``` + +--- + +## 擴充: SSE 新事件類型 + +```python +# 現有事件: +# - HOST_UPDATE: 每30秒推送主機狀態 + +# Sprint 5 新增: +# - AI_DIAGNOSING: OpenClaw 開始/結束分析時推送 +{ + "type": "AI_DIAGNOSING", + "data": { + "service_name": "awoooi-worker", + "incident_id": "INC-20260407-5E0B84", + "action": "start", # "start" | "complete" | "failed" + "confidence": 0.91, # 分析完成時 + "playbook_matched": "restart_worker.yml" # 匹配到的 Playbook + }, + "timestamp": "2026-04-08T10:03:42+08:00" +} +``` + +--- + +## 資料來源 + +| 新欄位 | 來源 | 檔案 | +|--------|------|------| +| `k8s_pods` | `k3s_monitor_service.py` 已有的 Pod 查詢 | `services/k3s_monitor_service.py` | +| `ai_diagnosing` | Redis working memory 中 active incidents | `services/incident_service.py` | +| `topology_metadata` | 從 hosts 聚合計算 | `api/v1/dashboard.py` 內計算 | + +--- + +## 不需要的新 API + +| 之前提過的 | 是否需要 | 原因 | +|-----------|---------|------| +| `POST /topology/execute` | ❌ 不需要 | 現有 `/approvals/{id}/sign` 已涵蓋 | +| `GET /topology/service/{host}/{name}` | ❌ 不需要 | 現有 `/dashboard` 已有服務資料 | +| `GET /topology/events?host={ip}` | ❌ 不需要 | 現有 `/incidents` + SSE 已涵蓋 | + +**結論: 後端只需擴充 DashboardResponse (+3 欄位) + SSE 新事件 (+1 類型),不需要新 API 端點。** diff --git a/docs/superpowers/specs/2026-04-08-sprint5-component-extraction.md b/docs/superpowers/specs/2026-04-08-sprint5-component-extraction.md new file mode 100644 index 00000000..536fcf5a --- /dev/null +++ b/docs/superpowers/specs/2026-04-08-sprint5-component-extraction.md @@ -0,0 +1,130 @@ +# Sprint 5 — 元件抽取計畫 + +> 哪些頁面需要抽取為獨立元件,以便在 Tab 容器中複用 + +--- + +## 抽取原則 + +1. **只移動,不修改邏輯** — 確保功能零損失 +2. **保留原始 page.tsx 做 redirect** — 舊連結不失效 +3. **元件接收 props 而非自行 fetch** — 父層 (Tab 容器) 負責資料載入 +4. **i18n 不變** — 繼續使用原有的 `useTranslations()` namespace + +--- + +## 抽取清單 + +### 高優先 (AI 指令中心需要) + +| # | 原始頁面 | 抽取為 | 放置位置 | 行數 | 用於 | +|---|---------|--------|---------|------|------| +| 1 | `/alerts/page.tsx` | `AlertsPanel.tsx` | `components/panels/` | 183 | AI 中心 Tab 2 左側 | +| 2 | `/reports/page.tsx` | `DispositionPanel.tsx` | `components/panels/` | 317 | AI 中心 Tab 4 | + +### 中優先 (其他整合頁面需要) + +| # | 原始頁面 | 抽取為 | 放置位置 | 行數 | 用於 | +|---|---------|--------|---------|------|------| +| 3 | `/monitoring/page.tsx` | `MonitoringPanel.tsx` | `components/panels/` | 269 | 可觀測性 Tab 1 | +| 4 | `/apm/page.tsx` | `APMPanel.tsx` | `components/panels/` | 128 | 可觀測性 Tab 2 | +| 5 | `/errors/page.tsx` | `ErrorsPanel.tsx` | `components/panels/` | 164 | 可觀測性 Tab 3 | +| 6 | `/apps/page.tsx` | `AppsPanel.tsx` | `components/panels/` | 103 | 可觀測性 Tab 4 | +| 7 | `/services/page.tsx` | `ServicesPanel.tsx` | `components/panels/` | 120 | 可觀測性 Tab 5 | +| 8 | `/auto-repair/page.tsx` | `AutoRepairPanel.tsx` | `components/panels/` | 460 | 自動化 Tab 1 | +| 9 | `/neural-command/page.tsx` | `NeuralCommandPanel.tsx` | `components/panels/` | 209 | 自動化 Tab 2 | +| 10 | `/drift/page.tsx` | `DriftPanel.tsx` | `components/panels/` | 324 | 自動化 Tab 3 | +| 11 | `/deployments/page.tsx` | `DeploymentsPanel.tsx` | `components/panels/` | 113 | 營運 Tab 1 | +| 12 | `/tickets/page.tsx` | `TicketsPanel.tsx` | `components/panels/` | 120 | 營運 Tab 2 | +| 13 | `/cost/page.tsx` | `CostPanel.tsx` | `components/panels/` | 95 | 營運 Tab 3 | +| 14 | `/action-logs/page.tsx` | `ActionLogsPanel.tsx` | `components/panels/` | 551 | 營運 Tab 4 | +| 15 | `/billing/page.tsx` | `BillingPanel.tsx` | `components/panels/` | 113 | 營運 Tab 5 | +| 16 | `/security/page.tsx` | `SecurityPanel.tsx` | `components/panels/` | 137 | 安全合規 Tab 1 | +| 17 | `/compliance/page.tsx` | `CompliancePanel.tsx` | `components/panels/` | 124 | 安全合規 Tab 2 | + +--- + +## 抽取步驟 (以 AlertsPanel 為例) + +### Step 1: 建立 Panel 元件 + +```typescript +// apps/web/src/components/panels/AlertsPanel.tsx + +'use client' + +// 從原始 /alerts/page.tsx 移入完整內容 +// 唯一差異: 移除 AppLayout wrapper (由 Tab 容器提供) + +import { useTranslations } from 'next-intl' +// ... 原始 import 保留 ... + +// 移除 export default function AlertsPage({ params }) +// 改為: +export function AlertsPanel() { + const t = useTranslations('alerts') + + // ... 原始邏輯完全不動 ... + + return ( + // 移除 wrapper + // 只保留內部內容 +
+ {/* 原始告警列表內容 */} +
+ ) +} +``` + +### Step 2: 原始頁面改為 redirect + +```typescript +// apps/web/src/app/[locale]/alerts/page.tsx +import { redirect } from 'next/navigation' + +export default function AlertsPage() { + redirect('/?tab=alerts') +} +``` + +### Step 3: 在 Tab 容器中使用 + +```typescript +// apps/web/src/app/[locale]/page.tsx (AI 指令中心) +import { lazy, Suspense } from 'react' + +const AlertsPanel = lazy(() => import('@/components/panels/AlertsPanel').then(m => ({ default: m.AlertsPanel }))) + +// Tab 2 內容: +}> + + +``` + +--- + +## 新建目錄結構 + +``` +apps/web/src/components/panels/ +├── index.ts # 匯出所有 Panel +├── AlertsPanel.tsx # 從 /alerts 抽取 +├── DispositionPanel.tsx # 從 /reports 抽取 +├── MonitoringPanel.tsx # 從 /monitoring 抽取 +├── APMPanel.tsx # 從 /apm 抽取 +├── ErrorsPanel.tsx # 從 /errors 抽取 +├── AppsPanel.tsx # 從 /apps 抽取 +├── ServicesPanel.tsx # 從 /services 抽取 +├── AutoRepairPanel.tsx # 從 /auto-repair 抽取 +├── NeuralCommandPanel.tsx # 從 /neural-command 抽取 +├── DriftPanel.tsx # 從 /drift 抽取 +├── DeploymentsPanel.tsx # 從 /deployments 抽取 +├── TicketsPanel.tsx # 從 /tickets 抽取 +├── CostPanel.tsx # 從 /cost 抽取 +├── ActionLogsPanel.tsx # 從 /action-logs 抽取 +├── BillingPanel.tsx # 從 /billing 抽取 +├── SecurityPanel.tsx # 從 /security 抽取 +└── CompliancePanel.tsx # 從 /compliance 抽取 +``` + +**共 17 個 Panel 元件** diff --git a/docs/superpowers/specs/2026-04-08-sprint5-route-mapping.md b/docs/superpowers/specs/2026-04-08-sprint5-route-mapping.md new file mode 100644 index 00000000..83201f4d --- /dev/null +++ b/docs/superpowers/specs/2026-04-08-sprint5-route-mapping.md @@ -0,0 +1,73 @@ +# Sprint 5 — 頁面路由對照表 + +> 每個舊 URL 的精確去向,確保零連結失效 + +--- + +## 路由對照表 + +| # | 舊路由 | 新位置 | 實作方式 | Tab ID | +|---|--------|--------|---------|--------| +| 1 | `/` | `/` | **保留** (重構為 4-Tab) | — | +| 2 | `/alerts` | `/?tab=alerts` | redirect | `alerts` | +| 3 | `/authorizations` | `/?tab=alerts` | redirect | `alerts` | +| 4 | `/topology` | `/topology` | **保留** (升級為完整拓撲) | — | +| 5 | `/reports` | `/?tab=disposition` | redirect | `disposition` | +| 6 | `/monitoring` | `/observability?tab=monitoring` | redirect | `monitoring` | +| 7 | `/apm` | `/observability?tab=apm` | redirect | `apm` | +| 8 | `/errors` | `/observability?tab=errors` | redirect | `errors` | +| 9 | `/apps` | `/observability?tab=apps` | redirect | `apps` | +| 10 | `/services` | `/observability?tab=services` | redirect | `services` | +| 11 | `/auto-repair` | `/automation?tab=repair` | redirect | `repair` | +| 12 | `/neural-command` | `/automation?tab=neural` | redirect | `neural` | +| 13 | `/drift` | `/automation?tab=drift` | redirect | `drift` | +| 14 | `/deployments` | `/operations?tab=deployments` | redirect | `deployments` | +| 15 | `/tickets` | `/operations?tab=tickets` | redirect | `tickets` | +| 16 | `/cost` | `/operations?tab=cost` | redirect | `cost` | +| 17 | `/action-logs` | `/operations?tab=logs` | redirect | `logs` | +| 18 | `/billing` | `/operations?tab=billing` | redirect | `billing` | +| 19 | `/security` | `/security-compliance?tab=security` | redirect | `security` | +| 20 | `/compliance` | `/security-compliance?tab=compliance` | redirect | `compliance` | +| 21 | `/knowledge-base` | `/knowledge` | redirect | — | +| 22 | `/terminal` | `/terminal` | **保留** (底部固定項) | — | +| 23 | `/settings` | `/settings` | **保留** (加 Tab: users/notifications/help) | — | +| 24 | `/users` | `/settings?tab=users` | redirect | `users` | +| 25 | `/notifications` | `/settings?tab=notifications` | redirect | `notifications` | +| 26 | `/help` | `/settings?tab=help` | redirect | `help` | +| 27 | `/demo` | `/demo` | **保留** (環境變數保護) | — | + +## 保留的獨立路由 (不整合) + +| 路由 | 原因 | +|------|------| +| `/` | 首頁,重構為 4-Tab | +| `/topology` | 完整版拓撲圖 (全展開模式) | +| `/terminal` | 需要全螢幕,不適合 Tab | +| `/settings` | 底部固定項 | +| `/demo` | 開發用 | + +## Redirect 實作方式 + +```typescript +// 方案 A: next.config.js (推薦 — 伺服器端 301) +module.exports = { + async redirects() { + return [ + { source: '/alerts', destination: '/?tab=alerts', permanent: false }, + { source: '/authorizations', destination: '/?tab=alerts', permanent: false }, + { source: '/reports', destination: '/?tab=disposition', permanent: false }, + { source: '/monitoring', destination: '/observability?tab=monitoring', permanent: false }, + // ... 其他 20+ 條 + ] + } +} + +// 方案 B: 各頁面內 redirect (fallback) +// apps/web/src/app/[locale]/alerts/page.tsx +import { redirect } from 'next/navigation' +export default function AlertsPage() { + redirect('/?tab=alerts') +} +``` + +**建議**: 方案 A (next.config.js) 為主,效能最好。 diff --git a/docs/superpowers/specs/2026-04-08-sprint5-tab-spec.md b/docs/superpowers/specs/2026-04-08-sprint5-tab-spec.md new file mode 100644 index 00000000..21a487e7 --- /dev/null +++ b/docs/superpowers/specs/2026-04-08-sprint5-tab-spec.md @@ -0,0 +1,150 @@ +# Sprint 5 — Tab 結構規格書 + +> 每個新頁面的 Tab 配置、來源元件、資料源、互動行為 + +--- + +## 🏠 AI 指令中心 (`/`) + +| Tab ID | 名稱 | i18n Key | Icon | Badge | 內容來源 | +|--------|------|---------|------|-------|---------| +| `overview` | 戰情總覽 | `tabs.overview` | LayoutDashboard | — | 現有首頁 + **新拓撲圖** | +| `alerts` | 告警 & 授權 | `tabs.alerts` | Bell | `alertsCount` | `/alerts` + `/authorizations` | +| `stream` | 活動串流 | `tabs.stream` | Activity | — | SSE + ActionTimeline | +| `disposition` | 處置統計 | `tabs.disposition` | BarChart3 | — | `/reports` (Sprint 4) | + +### Tab 1: 戰情總覽 — 區塊配置 + +``` +┌─────────────────────────────────────────────────┐ +│ [MetricsStrip] 7 指標橫排 (現有,不動) │ +├───────────────────────────┬─────────────────────┤ +│ │ OpenClaw Panel │ +│ ServiceTopology │ (現有,不動) │ +│ (React Flow 收合模式) │ │ +│ 4 群組 + 依賴邊線 │ Toggle: 拓撲/主機 │ +│ │ [拓撲圖] [主機網格] │ +├───────────────────────────┤ │ +│ IncidentCard Feed │ │ +│ (現有,不動,最新 3 筆) │ │ +└───────────────────────────┴─────────────────────┘ +``` + +**元件複用**: +- `MetricsStrip`: 現有首頁 L446-525 → 原地保留 +- `IncidentCard`: 現有 `components/incident/incident-card.tsx` → 原地保留 +- `OpenClawPanel`: 現有 `components/ai/openclaw-panel.tsx` → 原地保留 +- `ServiceTopology`: **新建** `components/topology/ServiceTopology.tsx` +- `HostGrid`: 現有 `components/infra/host-grid.tsx` → Toggle 切換顯示 + +**Toggle 行為**: 「拓撲圖 / 主機網格」切換按鈕 +- 預設: 拓撲圖 +- 切換: 隱藏拓撲,顯示現有 HostGrid (4主機卡片) +- 記住選擇: localStorage + +### Tab 2: 告警 & 授權 — 區塊配置 + +``` +┌──────────────────────┬──────────────────────────┐ +│ AlertsPanel │ LiveApprovalPanel │ +│ (從 /alerts 抽取) │ (現有元件) │ +│ │ │ +│ · 嚴重度篩選 │ · 待批准列表 │ +│ · 告警列表 │ · SSH URI + 風險等級 │ +│ · 時間排序 │ · 批准/拒絕按鈕 │ +│ │ · 執行歷史 │ +└──────────────────────┴──────────────────────────┘ +``` + +**元件複用**: +- `AlertsPanel`: 從 `/alerts/page.tsx` (183行) 抽取核心內容 +- `LiveApprovalPanel`: 現有 `components/approval/live-approval-panel.tsx` (484行) + +### Tab 3: 活動串流 — 區塊配置 + +``` +┌─────────────────────────────────────────────────┐ +│ 篩選: [全部] [告警] [AI] [修復] [心跳] │ +├─────────────────────────────────────────────────┤ +│ 18:05 ● 心跳確認 mon/mon1 Ready │ +│ 18:04 ● OpenClaw 匹配 Playbook (91%) │ +│ 18:03 ● OpenClaw 啟動 RCA │ +│ 18:02 ● Prometheus 警報: Worker CPU 89% │ +│ 17:58 ● 自動修復完成 restart: api (12s) │ +│ ... │ +└─────────────────────────────────────────────────┘ +``` + +**資料源**: SSE `/api/v1/dashboard/stream` (現有) +**元件複用**: `ActionTimeline` (`components/timeline/action-timeline.tsx`, 245行) + +### Tab 4: 處置統計 — 區塊配置 + +直接嵌入 `/reports/page.tsx` 的完整內容: +- KPI 3 卡 (處置總數/自動化率/人工介入率) +- 四大計數卡 (自動/人工/手動/冷啟動) +- 堆疊分佈條 +- 按異常類型明細表 +- 事件摘要 + 解決率 + +**元件複用**: 從 `/reports/page.tsx` (317行) 抽取為 `DispositionPanel` + +--- + +## 📊 可觀測性 (`/observability`) + +| Tab ID | 名稱 | i18n Key | 內容來源 | 行數 | +|--------|------|---------|---------|------| +| `monitoring` | 服務監控 | `obs.monitoring` | `/monitoring/page.tsx` | 269 | +| `apm` | APM | `obs.apm` | `/apm/page.tsx` | 128 | +| `errors` | 錯誤追蹤 | `obs.errors` | `/errors/page.tsx` | 164 | +| `apps` | 應用 | `obs.apps` | `/apps/page.tsx` | 103 | +| `services` | 服務目錄 | `obs.services` | `/services/page.tsx` | 120 | + +--- + +## 🔧 自動化 (`/automation`) + +| Tab ID | 名稱 | i18n Key | 內容來源 | 行數 | +|--------|------|---------|---------|------| +| `repair` | 自動修復 | `auto.repair` | `/auto-repair/page.tsx` | 460 | +| `neural` | 神經指揮 | `auto.neural` | `/neural-command/page.tsx` + 4元件 | 1241 | +| `drift` | Drift 偵測 | `auto.drift` | `/drift/page.tsx` | 324 | + +--- + +## 📦 營運 (`/operations`) + +| Tab ID | 名稱 | i18n Key | 內容來源 | 行數 | +|--------|------|---------|---------|------| +| `deployments` | 部署管理 | `ops.deployments` | `/deployments/page.tsx` | 113 | +| `tickets` | 工單 | `ops.tickets` | `/tickets/page.tsx` | 120 | +| `cost` | 成本分析 | `ops.cost` | `/cost/page.tsx` | 95 | +| `logs` | 行動日誌 | `ops.logs` | `/action-logs/page.tsx` | 551 | +| `billing` | 計費 | `ops.billing` | `/billing/page.tsx` | 113 | + +--- + +## 🛡️ 安全合規 (`/security-compliance`) + +| Tab ID | 名稱 | i18n Key | 內容來源 | 行數 | +|--------|------|---------|---------|------| +| `security` | 安全掃描 | `sec.security` | `/security/page.tsx` | 137 | +| `compliance` | 合規報告 | `sec.compliance` | `/compliance/page.tsx` | 124 | + +--- + +## 📚 知識 (`/knowledge`) + +單頁,無 Tab。內容 = `/knowledge-base/page.tsx` (532行) + +--- + +## ⚙️ 設定 (`/settings`) + +| Tab ID | 名稱 | i18n Key | 內容來源 | 行數 | +|--------|------|---------|---------|------| +| `general` | 一般設定 | `settings.general` | `/settings/page.tsx` | 245 | +| `users` | 用戶管理 | `settings.users` | `/users/page.tsx` | 136 | +| `notifications` | 通知 | `settings.notifications` | `/notifications/page.tsx` | 97 | +| `help` | 幫助 | `settings.help` | `/help/page.tsx` | 57 |