API 規格¶
API 概述¶
Base URL¶
Production: https://api.solidfocus.com/v1
Staging: https://api-staging.solidfocus.com/v1
Development: http://localhost:8000/v1
注意:Base URL 為預想值,需與 SolidFocus RD 團隊確認。
架構設計 (v3.0)¶
離線優先設計 (Offline-First Design):
- APP 是運動數據的真實來源 (Source of Truth)
- Server 扮演歸檔與同步協調者角色 (「數據歸檔中心」)
- APP 在本地計算所有指標 (卡路里、距離、XP)
- 使用批次上傳策略進行 session 同步
- Store-and-Forward 模式:APP 先寫入本地 SQLite,上線後再同步
核心原則:
- 離線優先 (Offline First):APP 是第一個資料儲存點 (Source of Truth)
- 異質數據標準化:使用 PostgreSQL JSONB 儲存異質裝置數據 (bike, rower, treadmill)
- 儲存後轉發 (Store-and-Forward):APP 總是先寫入本地 DB,之後批次同步
以下項目尚未定義 - 需要討論:
- Content-Type 格式 (假設為 JSON)
- 日期/時間格式 (假設為 ISO 8601 UTC)
- Request/Response body 結構
- 錯誤回應格式
- HTTP status codes
- Authentication header 格式
狀態:這些項目應與 SolidFocus RD 團隊討論並達成共識。
身份驗證與授權¶
預設的身份驗證方式¶
- JWT (JSON Web Token) 為基礎的身份驗證
- Bearer token scheme (預設)
- Access token + Refresh token 模式 (預設)
狀態:需與 SolidFocus RD 團隊確認:
- Token 格式與 claims
- Token 過期時間
- Authorization header 格式
- Token 撤銷機制
身份驗證 APIs¶
目前定義了以下身份驗證端點與描述。尚未提供 request/response 格式。
| Method | Endpoint | 客戶描述 |
|---|---|---|
| POST | /auth/register |
帳號註冊 (僅限 Email/Password) |
| POST | /auth/login |
帳號登入 (回傳 JWT) |
| POST | /auth/password/request-reset |
忘記密碼 (寄送驗證碼/信件) |
| POST | /auth/password/reset |
重設密碼 (帶入驗證碼) |
| POST | /auth/sso/{provider} |
SSO 登入 (provider = apple/google/facebook) |
| POST | /auth/logout |
登出 (撤銷 Refresh Token) |
| POST | /auth/refresh |
刷新 Access Token |
POST /auth/sso/{provider} 補充說明 (2025-01-20 更新):
首次 SSO 登入行為(詳見 03.3 Flow 2):
- Server 檢查 Apple ID/Google ID 是否已註冊
- 若不存在:自動建立新帳號(
gdpr_consent_given = FALSE) - 建立裝置綁定(
bound_device_id = device_id) - 產生 JWT tokens
Response 必須包含的欄位:
user_profile.gdpr_consent_given:是否已同意 GDPR consentuser_profile.gdpr_consent_version:Consent 版本-
user_profile.gdpr_consent_date:Consent 時間戳記 -
is_new_user:是否為首次 SSO 登入(Server 剛建立新帳號)true:首次 SSO 登入,Server 剛建立新帳號false:既有使用者,帳號已存在
is_new_user 欄位用途:
App 端需要此欄位來判斷使用者不同意 Consent 時是否要刪除帳號:
is_new_user = true+ 不同意 → 刪除剛建立的帳號(避免孤兒帳號)is_new_user = false+ 不同意 → 保留帳號,僅清空 Local 資料(既有使用者可能改變心意)
GDPR Consent 流程:
- App 端在登入後檢查
gdpr_consent_given(Flow 2 步驟 6) - 若為
FALSE:顯示阻擋式 Consent 對話框 - 使用者同意後:呼叫
POST /users/me/gdpr/consent更新 consent 狀態 - Server 建立 UserConsent 稽核記錄
- 使用者不同意時:根據
is_new_user決定是否刪除帳號(詳見 03.3 Flow 2)
使用者 APIs¶
目前定義了以下使用者端點與描述。尚未提供 request/response 格式。
| Method | Endpoint | 客戶描述 |
|---|---|---|
| GET | /users/me |
取得個人資料 (含 UserStat 與 GDPR consent 狀態) |
| PATCH | /users/me |
更新身高、體重、暱稱、性別 |
| PATCH | /users/me/stats |
同步累計數據 (強制以 APP 端的累計里程/XP/等級覆蓋伺服器) |
| POST | /users/me/avatar |
上傳大頭貼 |
| GET | /users/me/gdpr/consent |
檢查使用者的 GDPR consent 狀態與版本 |
| POST | /users/me/gdpr/consent |
記錄使用者的 GDPR consent (建立 UserConsent 記錄) |
| POST | /users/me/gdpr/delete-account |
請求刪除帳號 (硬刪除所有雲端資料) |
課程 APIs¶
目前定義了以下課程端點:
| Method | Endpoint | 客戶描述 |
|---|---|---|
| GET | /trainings |
取得課程列表 (支援設備篩選、分頁) |
| GET | /trainings/{id} |
取得單一課程詳細設定 |
- 支援使用
device_type參數篩選 - 回傳課程 metadata,包含影片 URLs
- 使用 JSONB 儲存彈性的
settings與detail欄位 (參見資料庫 schema)
運動紀錄 APIs¶
目前定義了以下 session 端點:
| Method | Endpoint | 客戶描述 |
|---|---|---|
| POST | /sessions/batch_upload |
批次同步 離線生成的運動紀錄 |
| POST | /sessions |
單筆運動紀錄上傳 |
| GET | /sessions |
取得歷史運動列表 |
| GET | /sessions/{id} |
取得運動詳情 (含曲線圖數據) |
| DELETE | /sessions/{id} |
刪除運動紀錄 |
規格架構設計:
冪等性設計 (Idempotency Design):
- APP 產生
client_session_uuid(UUIDv4) - Server 在建立前檢查
client_session_uuid是否已存在 - 重複上傳回傳成功 (冪等行為)
批次上傳流程 (來自客戶 sequence diagram):
APP → POST /sessions/batch_upload
Header: X-Idempotency-Key (規格中提及)
Server 檢查 client_session_uuid
├─ 已存在 → 200 OK (忽略寫入)
└─ 新紀錄 → 201 Created → 更新 UserStats
離線優先模式 (Offline-First Pattern):
- APP 在本地儲存 sessions,狀態為
Status: PENDING - 網路可用時批次上傳
- 上傳成功後標記為
Status: SYNCED
資料庫 Schema (WorkoutSession model):
- 使用 PostgreSQL JSONB 儲存裝置無關的 metrics 資料
- 欄位:
client_session_id,device_type,source_type,metrics_summary,time_series_data - 完整 schema 請參見 資料模型
Meta APIs¶
目前定義了以下 meta 端點:
| Method | Endpoint | 客戶描述 |
|---|---|---|
| GET | /meta/schemas |
取得各器材 (Rower/Bike) 的數據欄位定義 (供 VUE 後台動態渲染) |
| GET | /meta/app_version |
檢查 APP 是否需要強制更新 |
| GET | /meta/gdpr/consent-text |
取得目前的 GDPR consent 文字與版本 |
用途:
/meta/schemas:提供裝置特定的欄位定義,供後台管理介面動態渲染使用/meta/app_version:檢查版本,判斷是否需要強制更新 APP/meta/gdpr/consent-text:提供最新的 consent 文字與版本號,供 App 顯示 GDPR 同意對話框使用
GDPR 合規說明¶
App 將在歐盟上架,因此必須符合 GDPR (通用資料保護規則) 合規要求。
核心要求¶
- 首次使用明確同意:使用阻擋式 consent 對話框,使用者必須明確同意才能使用 App
- 帳號刪除功能:提供硬刪除所有雲端資料的功能
- 刪除稽核日誌:保留刪除行為的稽核記錄,用於合規證明
API 整合方式¶
SSO 登入 API 回應:
POST /auth/sso/{provider}回應包含:gdpr_consent_given(boolean)gdpr_consent_version(string)gdpr_consent_date(timestamp)is_new_user(boolean) - 是否為首次 SSO 登入
使用者資料 API 回應:
GET /users/me回應包含相同的 GDPR consent 欄位- App 根據
gdpr_consent_given決定是否顯示阻擋式 consent 對話框
核心流程¶
1. Consent 同意流程:
使用者登入
↓
檢查 gdpr_consent_given
↓
如果為 false → 顯示阻擋式 consent 對話框
↓
使用者點擊「同意」
↓
呼叫 POST /users/me/gdpr/consent
↓
更新 User.gdpr_consent_given = true
↓
建立 UserConsent 稽核記錄
↓
允許使用 App
2. 帳號刪除流程:
使用者進入設定頁面
↓
點擊「刪除帳號」
↓
輸入 "DELETE" 確認
↓
呼叫 POST /users/me/gdpr/delete-account
↓
Backend 立即執行刪除(同步):
1. 建立 DeletionRequest 記錄 (status: pending)
2. Atomic Transaction:
- 刪除所有 WorkoutSession 記錄
- 刪除 UserStats 記錄
- 刪除所有 SocialAccount 記錄
- 刪除所有 UserConsent 記錄
- 刪除 User 記錄
- 更新 DeletionRequest (status: completed, completed_at)
- 將 tokens 加入黑名單
3. Transaction 提交
↓
回傳 200 OK
↓
使用者立即登出 (token 失效)
錯誤處理:
- Transaction 失敗 → 回滾
- 更新 DeletionRequest (status: failed)
- 回傳 500 錯誤
資料模型¶
GDPR 相關的資料模型包含:
- User Model 新增欄位:
gdpr_consent_given、gdpr_consent_version、gdpr_consent_date - UserConsent Model:記錄每次 consent 的稽核日誌
- DeletionRequest Model:記錄帳號刪除請求的稽核日誌
完整 schema 請參見 資料模型。
內容總結¶
目前已定義:
- Endpoint 路徑與 HTTP methods
- 高階描述 (中文)
- 資料庫 schema (SQLModel classes 與欄位型別)
- 架構概念 (Offline-First, Store-and-Forward, JSONB 使用)
- Sequence diagram 顯示同步流程
尚未定義:
- Request body 結構
- Response body 結構
- HTTP status codes (200, 201, 400, 403, etc.)
- 錯誤回應格式
- 錯誤代碼 (例如:
validation_error,duplicate_session) - Header 需求 (除了提及
X-Idempotency-Key) - 分頁細節
- Query parameter 格式