青禾教學後端 API 契約
本契約對應隨包提供的 Node.js 後端,實際伺服器與持久JSON檔案,不是展示頁模擬。僅使用虛構資料;教學版不等於正式營運系統或安全認證。
啟動與來源
Node.js 22以上,無第三方套件。前端與後端優先由同一個 http://127.0.0.1:8780 提供;學生教材預覽 http://127.0.0.1:8774 為明確核准開發來源。前端 config.js 僅有公開 API base,不放密碼。使用其他來源需在後端 ALLOWED_ORIGINS 明確設定,不能用 no-cors 或萬用來源代替。
GET接受核准來源或無Origin的工具請求;POST/PATCH需要核准Origin及Content-Type: application/json;詢問建立另外使用Idempotency-Key。前端使用credentials: include。錯誤為 {error:{code,message,fields?}},不含堆疊或秘密。非JSON/伺服器斷線須另外處理。
公開功能
| 方法與路徑 | 成功資料 | 失敗與限制 |
|---|---|---|
| GET /api/health | 200,ok與teachingMode | 只顯示健康狀態,不列秘密或資料位置 |
| GET /api/services | 200,{services:[{id,name}]} | 空陣列代表無服務;503代表讀取失敗,兩者不同 |
| POST /api/inquiries | 201,{case:{id,status,createdAt}} | 422欄位錯誤;503尚未保存;不回傳聯絡資料或內部備註 |
每次邏輯詢問以crypto.randomUUID()產生Idempotency-Key header;同頁相同五欄內容重試沿用此key,修改內容才換新key。後端持久記錄key與內容摘要,同key同內容只回同一id、不重複新增;不同內容重用key回409。回應中斷可能有底層重複請求,不能只靠前端pending防重複。requestKey與摘要不放公開或管理回應。重新開頁會建立新key,結果不明時仍先受控核對,不盲目重送。
詢問body只接受name、email、serviceId、subject、message:trim後必填;姓名最多80字、Email最多254字且符合教學格式、主旨120字、需求500字。服務id必須存在。成功編號為伺服器產生的QH-UUID,不得由前端編號或用畫面陣列假存。案件初始status=pending。資料持久存於DATA_DIR/cases.json,重啟仍在。
管理員身份
| 方法與路徑 | 成功 | 未授權/錯誤 |
|---|---|---|
| POST /api/auth/login | username、password;200 {user:{username}}並設HttpOnly session cookie | 401錯誤帳密;過多嘗試429 |
| GET /api/auth/me | 200 {user:{username}} | 401 |
| POST /api/auth/logout | 200 {ok:true},後端撤銷session並清cookie | 401 |
管理員由後端ADMIN_USER(預設teacher)與自行設定的ADMIN_PASSWORD建立。密碼不得寫進前端、localStorage、Console或AI對話。Cookie為HttpOnly、SameSite=Strict、Path=/api,HTTPS PUBLIC_ORIGIN時加Secure;session 30分鐘,後端重啟會失效,案件資料不因此消失。
案件管理
| 方法與路徑 | 成功 | 邊界 |
|---|---|---|
| GET /api/admin/cases | 200 {cases:[{id,subject,serviceId,status,createdAt}]} | 未登入401;空列表與未授權不同 |
| GET /api/admin/cases/:id | 200 {case:{五欄,id,status,internalNote,createdAt,updatedAt}} | 未登入401、找不到404 |
| PATCH /api/admin/cases/:id | JSON只含status及/或internalNote;200 {case:更新後完整資料} | 401、404、422、測試失敗503 |
狀態映射:pending=待處理、in_progress=處理中、completed=已完成;三者間可切換。使用穩定id,不使用列表或篩選後索引。備註最多2000字;不用HTML執行、不存localStorage。每次成功更新後重讀同id,列表與明細同步;失敗保留當前輸入並明示尚未保存。
隔離教學測試模式
只有TEACHING_MODE=1且管理員已登入才能操作;正式交付關閉此模式。
- POST /api/admin/testing/mode,body可含services=normal/empty/error、inquiry=normal/error/disconnect/slow、cases=normal/empty、update=normal/error。只影響回應,不刪既有資料;結束全部回normal。
- inquiry=slow會在保存後延後9秒回應,驗證8秒前端逾時、防連點與結果不明;不代表案件沒有保存。
- inquiry=disconnect會在真正保存後中斷回應,用來驗證結果不明時不自動重送;先以受控工具核對同主旨與id,再決定後續。
- POST /api/admin/testing/expire,令目前教學session全部失效。下一次受保護請求應401,前端清私密內容並提示重新登入,不無限重試。
- 本機node backend/manage.mjs inspect --id 案件id,受控核對保存與重啟;不要把真實個資貼給AI。
- 本機node backend/manage.mjs rename-service --id web --name 新名稱,重載前端應讀到新名稱。
- 本機node backend/manage.mjs seed,僅加入QH-DEMO-001/002虛構固定案,既有資料保留。
備份還原、公開部署需依另附指南;本契約不承諾平台永久免費、不自動操作學生帳號,也不表示已部署。
官方API核對:本機Node.js v22.22.0,HTTP、Crypto、File system,2026-10-20。