本卡屬 FR-182(母卡 FR-182 母案(同清單上一張)),卡 L3,批 2。只改 license_center repo。依賴:L1。

這張卡做什麼(白話)

後台頁是給員工用的,這張卡做給機器打的三支 API:①線上開通 POST /api/activation/agent/activate——以後主系統會代 Agent 來打(Agent 永遠不直連 LC),帶序號+機器碼,LC 簽出綁機的照回去;沒有 token,靠序號本身+既有的來源 IP 失敗鎖定(跟主系統的線上開通共用同一張鎖定表,同一個 IP 不管猜主系統序號還是 Agent 序號合計算)。②內部簽發 POST /api/internal/agent/issue——主系統 root 後台以後可以帶 API token 直接請 LC 簽 Agent 照(跟既有 /api/internal/issue 同一道 token 白名單)。③內部查詢 GET /api/internal/agent/licenses?customer_code=——主系統查某客戶有幾張 Agent 照、各自狀態。既有四支機器 API 的合約是凍結的(主系統已對接,守衛測試擋著),本卡全部新路徑、一字不碰既有四支;token 驗證那段從 internal_api.py(已 492 行超標)抽到共用檔,兩邊用同一支。回應形狀、錯誤字串、驗證順序比照既有,寫進合約基線文件與合約測試。

reuse/extend/new

new:填 L1 建的空殼——web/views/agent_activation_api.py POST /api/activation/agent/activate(無 token;body activation_code+machine_fingerprint strip 後必填;鎖定走 modules/auth/service.check_activation_lockout/record_activation_failure 同一張 activation_attempts 表、同 _lockout_key;驗證順序比照既有:格式 → 鎖定 → 查無 → 已使用 → 序號逾期 → 授權過期 → passphrase;成功 {"license": {…}, "license_id": "alic-…"},失敗 {"error": "…"} 字串比照既有六句);web/views/internal_agent_api.py POST /api/internal/agent/issue(X-API-Token,body customer_code/order_no/issued_to/parent_license_id?/license_type?/days?/count?(預設 1,上限 50)/notify_days_before?/grace_days?;回 {"batch_id", "licenses": [{"license_id", "activation_code", "license": {…}}]})與 GET /api/internal/agent/licenses?customer_code=(回現行照清單含狀態、到期、機器碼前 12 碼、開通狀態);token 驗證與 _authenticate/_require_valid_token 從 internal_api.py 抽到 web/internal_auth.py 兩邊共用(internal_api 已 492 行,不往裡加)。extend:web/api_schemas.py 加三個 schema(只驗格式)、@bp.doc 進 OpenAPI;tests/test_api_contract.py 加 Agent 三支的情境表(成功/401/400 缺欄/404 查無/409 已用/400 序號逾期/429 鎖定/503);tests/test_csrf.py 加「請求真的走進 view」斷言;docs/api-contract-baseline.md 加三節

在哪裡

repo:~/Projects/Billows/Audit-Manager/license_center
改既有:src/license_center/web/views/agent_activation_api.py(填空殼)
改既有:src/license_center/web/views/internal_agent_api.py(填空殼)
改既有:src/license_center/web/views/internal_api.py(抽 token 驗證到 internal_auth.py,改 import)
改既有:src/license_center/web/api_schemas.py
改既有:tests/test_api_contract.py
改既有:tests/test_csrf.py
改既有:docs/api-contract-baseline.md
新開:src/license_center/web/internal_auth.py
新開:tests/test_agent_activation_api.py

怎麼做

驗收(首腦照這個驗)

pytest tests/test_api_contract.py 既有四支情境零變動、新三支全綠;tests/test_csrf.py 新斷言:POST 不帶 csrf token 仍走進 view(回 400 是參數錯不是 CSRF);手打:curl -XPOST :5062/api/activation/agent/activate -d '{"activation_code":"<L1 簽的>","machine_fingerprint":"<64 hex>"}' 回 200 帶信封、agent-verify 過;同序號再打 409;錯序號連打 6 次第 6 次 429,且主系統那支 /api/activation/activate 同 IP 也被鎖(共用表,預期行為,寫進合約文件);curl -H 'X-API-Token: <dev token>' -XPOST /api/internal/agent/issue -d '{…,"count":2}' 回 2 張;無 token 401 先於 body 錯;/api/docs 看得到三支

必讀