# AWOOOI API 開發 SOP > **版本**: v1.0 > **建立日期**: 2026-03-20 > **負責人**: CTO > **狀態**: Phase 0 草稿 --- ## 概述 此文件定義 AWOOOI 所有 API 端點的開發標準流程,確保 Contract-First 原則與 CI 強制檢查能夠有效執行。 --- ## API 開發流程 ### 1. 設計階段 (Design) ``` ┌─────────────────────────────────────────────────────────────┐ │ Step 1: OpenAPI 定義 │ ├─────────────────────────────────────────────────────────────┤ │ 位置: docs/api/openapi.yaml │ │ 工具: Stoplight Studio / VS Code OpenAPI Editor │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ Step 2: 端點文檔 (Markdown) │ ├─────────────────────────────────────────────────────────────┤ │ 位置: docs/api/endpoints/{module}/{endpoint}.md │ │ 內容: 用途說明、請求範例、回應範例、錯誤碼 │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ Step 3: PR Review + Approval │ ├─────────────────────────────────────────────────────────────┤ │ 審核者: CTO / CISO (安全相關 API) │ │ 檢查項: 命名規範、版本策略、錯誤處理、安全考量 │ └─────────────────────────────────────────────────────────────┘ ``` ### 2. 實作階段 (Implementation) ```python # apps/api/app/routers/{module}.py from fastapi import APIRouter, Depends, HTTPException, status from app.schemas.{module} import {RequestModel}, {ResponseModel} from app.services.{module} import {ServiceClass} from app.core.deps import get_current_user, rate_limit from app.core.cache import cache_response router = APIRouter(prefix="/v1/{module}", tags=["{module}"]) @router.get( "/{endpoint}", response_model={ResponseModel}, summary="端點摘要", description="詳細說明", responses={ 200: {"description": "成功"}, 400: {"description": "請求格式錯誤"}, 401: {"description": "未授權"}, 403: {"description": "權限不足"}, 404: {"description": "資源不存在"}, 429: {"description": "請求過於頻繁"}, 500: {"description": "伺服器錯誤"}, } ) @cache_response(ttl=60) # 快取策略 @rate_limit(requests=100, window=60) # 限流策略 async def endpoint_name( request: {RequestModel}, current_user: User = Depends(get_current_user), service: {ServiceClass} = Depends() ): """ 端點實作邏輯 """ pass ``` ### 3. 測試階段 (Testing) ```python # apps/api/tests/test_{module}.py import pytest from httpx import AsyncClient from app.main import app class Test{Module}API: """ 測試命名規範: test_{method}_{endpoint}_{scenario} """ @pytest.mark.asyncio async def test_get_endpoint_success(self, client: AsyncClient, auth_headers: dict): response = await client.get("/v1/{module}/{endpoint}", headers=auth_headers) assert response.status_code == 200 assert "data" in response.json() @pytest.mark.asyncio async def test_get_endpoint_unauthorized(self, client: AsyncClient): response = await client.get("/v1/{module}/{endpoint}") assert response.status_code == 401 @pytest.mark.asyncio async def test_get_endpoint_not_found(self, client: AsyncClient, auth_headers: dict): response = await client.get("/v1/{module}/nonexistent", headers=auth_headers) assert response.status_code == 404 ``` --- ## API 版本策略 ### URL 版本控制 ``` https://api.awoooi.wooo.work/v1/... # 目前版本 https://api.awoooi.wooo.work/v2/... # 未來版本 (重大變更時) ``` ### 版本升級規則 | 變更類型 | 版本影響 | 範例 | |---------|---------|------| | **新增端點** | 不變 | 新增 `GET /v1/metrics/custom` | | **新增可選欄位** | 不變 | Response 新增 `metadata` 欄位 | | **移除/重命名欄位** | 升版 | `user_id` → `userId` | | **變更回應結構** | 升版 | 陣列 → 分頁物件 | | **變更認證方式** | 升版 | Bearer → OAuth2 | ### 棄用流程 1. **通知** (v1.x): 在回應 Header 加入 `Deprecation: true` + `Sunset: 2026-06-01` 2. **文檔標記**: OpenAPI 加入 `deprecated: true` 3. **過渡期**: 至少 3 個月 4. **移除**: 完全移除舊端點 --- ## 命名規範 ### URL 路徑 ``` # ✅ 正確 GET /v1/hosts # 列表 GET /v1/hosts/{id} # 單一資源 POST /v1/hosts # 建立 PUT /v1/hosts/{id} # 完整更新 PATCH /v1/hosts/{id} # 部分更新 DELETE /v1/hosts/{id} # 刪除 # ✅ 子資源 GET /v1/hosts/{id}/metrics # 主機的指標 POST /v1/hosts/{id}/actions/scan # 動作 (非 CRUD) # ❌ 錯誤 GET /v1/getHosts # 動詞命名 GET /v1/host_list # 底線 + 複數不一致 POST /v1/hosts/create # 冗餘動詞 ``` ### 查詢參數 ``` # 分頁 ?page=1&limit=20 # 排序 ?sort=created_at&order=desc # 篩選 ?status=active&host_id=h-001 # 搜尋 ?q=keyword # 時間範圍 ?start_time=2026-03-01T00:00:00Z&end_time=2026-03-20T23:59:59Z ``` ### 請求/回應欄位 ```json // ✅ 正確: camelCase (JSON 標準) { "hostId": "h-001", "hostName": "web-server-01", "createdAt": "2026-03-20T10:00:00Z", "isActive": true } // ❌ 錯誤: snake_case (Python 內部使用) { "host_id": "h-001", "host_name": "web-server-01" } ``` --- ## 回應格式 ### 成功回應 ```json // 單一資源 { "data": { "id": "h-001", "name": "web-server-01", "status": "healthy" }, "meta": { "requestId": "req-abc123", "timestamp": "2026-03-20T10:00:00Z" } } // 列表 (分頁) { "data": [ {"id": "h-001", "name": "web-server-01"}, {"id": "h-002", "name": "web-server-02"} ], "pagination": { "page": 1, "limit": 20, "total": 45, "totalPages": 3 }, "meta": { "requestId": "req-abc123", "timestamp": "2026-03-20T10:00:00Z" } } ``` ### 錯誤回應 ```json { "error": { "code": "VALIDATION_ERROR", "message": "請求參數驗證失敗", "details": [ { "field": "email", "message": "必須為有效的電子郵件格式" } ] }, "meta": { "requestId": "req-abc123", "timestamp": "2026-03-20T10:00:00Z" } } ``` ### 錯誤碼對照表 | HTTP Status | Error Code | 說明 | |-------------|-----------|------| | 400 | `VALIDATION_ERROR` | 請求參數驗證失敗 | | 400 | `INVALID_FORMAT` | 請求格式錯誤 | | 401 | `UNAUTHORIZED` | 未提供認證資訊 | | 401 | `TOKEN_EXPIRED` | Token 已過期 | | 403 | `FORBIDDEN` | 權限不足 | | 404 | `NOT_FOUND` | 資源不存在 | | 409 | `CONFLICT` | 資源衝突 | | 422 | `UNPROCESSABLE_ENTITY` | 語意錯誤 | | 429 | `RATE_LIMITED` | 請求過於頻繁 | | 500 | `INTERNAL_ERROR` | 伺服器內部錯誤 | | 503 | `SERVICE_UNAVAILABLE` | 服務暫時不可用 | --- ## 安全規範 ### 認證 ```http Authorization: Bearer ``` ### 必要 Headers ```http Content-Type: application/json Accept: application/json X-Request-ID: # 用於追蹤 X-Source: awoooi # 識別來源 (Kali Scanner 需要) ``` ### 敏感資料處理 ```python # ❌ 禁止: 回應中包含密碼、Token { "user": { "password": "hashed_value", # 禁止 "apiKey": "sk-xxx" # 禁止 } } # ✅ 正確: 脫敏處理 { "user": { "hasPassword": true, "apiKeyPrefix": "sk-***xxx" } } ``` ### 日誌脫敏 ```python # 自動脫敏欄位 SENSITIVE_FIELDS = [ "password", "token", "api_key", "secret", "authorization", "cookie", "credit_card" ] # 日誌輸出 # ✅ {"user": "admin", "password": "[REDACTED]"} # ❌ {"user": "admin", "password": "actual_password"} ``` --- ## 快取策略 ### TTL 分層 | 資料類型 | TTL | 說明 | |---------|-----|------| | 靜態配置 | 1 小時 | 系統設定、選單 | | 聚合數據 | 5 分鐘 | Dashboard 統計 | | 即時數據 | 30 秒 | 主機狀態、指標 | | 無快取 | 0 | 審計日誌、敏感操作 | ### 快取 Key 規範 ``` awoooi:{version}:{module}:{resource}:{id}:{params_hash} # 範例 awoooi:v1:hosts:list:abc123 awoooi:v1:hosts:detail:h-001 awoooi:v1:metrics:dashboard:user-001:7d ``` --- ## CI 檢查項目 ### 自動化檢查 | 檢查項 | 工具 | 失敗處理 | |-------|------|---------| | OpenAPI Schema 驗證 | spectral | 阻擋合併 | | OpenAPI ↔ 程式碼一致性 | openapi-diff | 阻擋合併 | | 端點文檔存在性 | 自訂腳本 | 阻擋合併 | | 測試覆蓋率 > 80% | pytest-cov | 阻擋合併 | | 安全掃描 | bandit | 警告 | ### PR Checklist ```markdown ## API 變更 Checklist - [ ] OpenAPI 規格已更新 (`docs/api/openapi.yaml`) - [ ] 端點文檔已更新 (`docs/api/endpoints/...`) - [ ] 單元測試已撰寫 (覆蓋率 > 80%) - [ ] 錯誤處理已實作 - [ ] 快取策略已定義 - [ ] 限流規則已設定 - [ ] 日誌已加入 (不含敏感資料) - [ ] CISO 已審核 (安全相關 API) ``` --- ## 文檔工具 ### 內部開發 - **Swagger UI**: `http://localhost:8000/docs` - 用途: 開發階段測試、除錯 ### 對外文檔 - **Scalar**: `https://api.awoooi.wooo.work/scalar` - 風格: Nothing.tech 純白主題 - 用途: 外部開發者文檔 --- ## 變更記錄 | 日期 | 版本 | 變更 | 作者 | |------|------|------|------| | 2026-03-20 | v1.0 | 初版建立 | CTO | --- *此文件由 CTO 維護,API 開發者必須遵守此 SOP。*