# DramaLing API 規格文檔 ## 1. API 概述 ### 1.1 基本資訊 - **Base URL**: - 開發: `http://localhost:3000/api` - 生產: `https://api.dramaling.com` - **版本**: v1 - **協議**: HTTPS - **格式**: JSON - **認證**: Bearer Token (JWT) ### 1.2 通用規範 #### 請求標頭 ```http Content-Type: application/json Authorization: Bearer Accept-Language: zh-TW X-Request-ID: ``` #### 響應格式 ```typescript interface SuccessResponse { success: true; data: T; meta?: { page?: number; limit?: number; total?: number; hasMore?: boolean; }; } interface ErrorResponse { success: false; error: { code: string; message: string; details?: any; }; timestamp: string; } ``` #### HTTP 狀態碼 - `200 OK` - 請求成功 - `201 Created` - 資源創建成功 - `204 No Content` - 刪除成功 - `400 Bad Request` - 請求參數錯誤 - `401 Unauthorized` - 未認證 - `403 Forbidden` - 無權限 - `404 Not Found` - 資源不存在 - `409 Conflict` - 資源衝突 - `422 Unprocessable Entity` - 驗證失敗 - `429 Too Many Requests` - 請求過多 - `500 Internal Server Error` - 伺服器錯誤 ## 2. 認證 API ### 2.1 註冊 **POST** `/api/auth/register` #### 請求 ```json { "email": "user@example.com", "password": "SecurePass123!", "username": "johndoe" } ``` #### 響應 (201) ```json { "success": true, "data": { "user": { "id": "uuid", "email": "user@example.com", "username": "johndoe", "emailVerified": false, "createdAt": "2024-03-15T10:00:00Z" }, "tokens": { "accessToken": "eyJ...", "refreshToken": "eyJ...", "expiresIn": 900 } } } ``` #### 錯誤響應 (409) ```json { "success": false, "error": { "code": "EMAIL_EXISTS", "message": "Email already registered" } } ``` ### 2.2 登入 **POST** `/api/auth/login` #### 請求 ```json { "email": "user@example.com", "password": "SecurePass123!", "rememberMe": true } ``` #### 響應 (200) ```json { "success": true, "data": { "user": { "id": "uuid", "email": "user@example.com", "username": "johndoe", "avatarUrl": "https://...", "lastLoginAt": "2024-03-15T10:00:00Z" }, "tokens": { "accessToken": "eyJ...", "refreshToken": "eyJ...", "expiresIn": 2592000 } } } ``` ### 2.3 Google OAuth **POST** `/api/auth/google` #### 請求 ```json { "idToken": "google_id_token" } ``` #### 響應 (200) ```json { "success": true, "data": { "user": { "id": "uuid", "email": "user@gmail.com", "username": "user", "provider": "google", "avatarUrl": "https://..." }, "tokens": { "accessToken": "eyJ...", "refreshToken": "eyJ..." }, "isNewUser": false } } ``` ### 2.4 重新整理 Token **POST** `/api/auth/refresh` #### 請求 ```json { "refreshToken": "eyJ..." } ``` #### 響應 (200) ```json { "success": true, "data": { "accessToken": "eyJ...", "refreshToken": "eyJ...", "expiresIn": 900 } } ``` ### 2.5 登出 **POST** `/api/auth/logout` #### 請求 ```json { "refreshToken": "eyJ..." } ``` #### 響應 (200) ```json { "success": true, "data": { "message": "Logged out successfully" } } ``` ### 2.6 忘記密碼 **POST** `/api/auth/forgot-password` #### 請求 ```json { "email": "user@example.com" } ``` #### 響應 (200) ```json { "success": true, "data": { "message": "Password reset email sent" } } ``` ### 2.7 重設密碼 **POST** `/api/auth/reset-password` #### 請求 ```json { "token": "reset_token", "newPassword": "NewSecurePass123!" } ``` #### 響應 (200) ```json { "success": true, "data": { "message": "Password reset successfully" } } ``` ## 3. 用戶 API ### 3.1 取得個人資料 **GET** `/api/users/me` #### 響應 (200) ```json { "success": true, "data": { "id": "uuid", "email": "user@example.com", "username": "johndoe", "avatarUrl": "https://...", "emailVerified": true, "createdAt": "2024-03-01T00:00:00Z", "stats": { "totalFlashcards": 150, "totalDecks": 5, "studyStreak": 7, "level": 3, "experience": 1250 }, "preferences": { "dailyGoal": 20, "reminderTime": "09:00", "reminderEnabled": true, "theme": "light", "language": "zh-TW" } } } ``` ### 3.2 更新個人資料 **PATCH** `/api/users/me` #### 請求 ```json { "username": "newusername", "avatarUrl": "https://..." } ``` #### 響應 (200) ```json { "success": true, "data": { "id": "uuid", "username": "newusername", "avatarUrl": "https://...", "updatedAt": "2024-03-15T10:00:00Z" } } ``` ### 3.3 更新偏好設定 **PUT** `/api/users/me/preferences` #### 請求 ```json { "dailyGoal": 30, "reminderTime": "20:00", "reminderEnabled": true, "theme": "dark", "language": "zh-TW", "soundEnabled": true, "autoPlayAudio": false } ``` ## 4. 卡組 API ### 4.1 取得卡組列表 **GET** `/api/decks` #### 查詢參數 - `page` (number): 頁數,預設 1 - `limit` (number): 每頁數量,預設 20 - `sort` (string): 排序方式 `created_at` | `updated_at` | `name` - `order` (string): 排序順序 `asc` | `desc` #### 響應 (200) ```json { "success": true, "data": [ { "id": "uuid", "name": "Business English", "description": "Common business vocabulary", "coverImage": "https://...", "flashcardCount": 45, "isPublic": false, "tags": ["business", "professional"], "createdAt": "2024-03-01T00:00:00Z", "updatedAt": "2024-03-15T00:00:00Z" } ], "meta": { "page": 1, "limit": 20, "total": 5, "hasMore": false } } ``` ### 4.2 取得單一卡組 **GET** `/api/decks/{deckId}` #### 響應 (200) ```json { "success": true, "data": { "id": "uuid", "name": "Business English", "description": "Common business vocabulary", "coverImage": "https://...", "flashcardCount": 45, "isPublic": false, "tags": ["business", "professional"], "flashcards": [ { "id": "uuid", "word": "negotiate", "translation": "協商", "difficulty": "intermediate" } ], "createdAt": "2024-03-01T00:00:00Z" } } ``` ### 4.3 創建卡組 **POST** `/api/decks` #### 請求 ```json { "name": "TOEFL Vocabulary", "description": "Essential TOEFL words", "coverImage": "https://...", "isPublic": false, "tags": ["toefl", "exam"] } ``` #### 響應 (201) ```json { "success": true, "data": { "id": "uuid", "name": "TOEFL Vocabulary", "description": "Essential TOEFL words", "flashcardCount": 0, "createdAt": "2024-03-15T10:00:00Z" } } ``` ### 4.4 更新卡組 **PATCH** `/api/decks/{deckId}` #### 請求 ```json { "name": "Updated Name", "description": "Updated description" } ``` ### 4.5 刪除卡組 **DELETE** `/api/decks/{deckId}` #### 響應 (204) 無內容 ## 5. 詞卡 API ### 5.1 取得詞卡列表 **GET** `/api/flashcards` #### 查詢參數 - `deckId` (string): 卡組 ID - `search` (string): 搜尋關鍵字 - `tags` (string[]): 標籤篩選 - `difficulty` (string): 難度篩選 - `status` (string): 學習狀態 `new` | `learning` | `review` | `mastered` - `page` (number): 頁數 - `limit` (number): 每頁數量 #### 響應 (200) ```json { "success": true, "data": [ { "id": "uuid", "deckId": "uuid", "word": "negotiate", "translation": "協商", "definition": "To discuss something with someone in order to reach an agreement", "partOfSpeech": "verb", "pronunciation": "/nɪˈɡoʊʃieɪt/", "exampleSentence": "We need to negotiate a better deal.", "exampleTranslation": "我們需要協商一個更好的交易。", "difficulty": "intermediate", "tags": ["business", "communication"], "learningStatus": { "status": "learning", "nextReviewDate": "2024-03-16T00:00:00Z", "accuracy": 75 } } ], "meta": { "page": 1, "limit": 20, "total": 150, "hasMore": true } } ``` ### 5.2 取得單一詞卡 **GET** `/api/flashcards/{flashcardId}` ### 5.3 創建詞卡 **POST** `/api/flashcards` #### 請求 ```json { "deckId": "uuid", "word": "negotiate", "translation": "協商", "definition": "To discuss something with someone in order to reach an agreement", "partOfSpeech": "verb", "pronunciation": "/nɪˈɡoʊʃieɪt/", "exampleSentence": "We need to negotiate a better deal.", "exampleTranslation": "我們需要協商一個更好的交易。", "difficulty": "intermediate", "memoryTip": "Think of 'go' + 'she' + 'ate' = negotiate", "tags": ["business", "communication"] } ``` ### 5.4 更新詞卡 **PATCH** `/api/flashcards/{flashcardId}` ### 5.5 刪除詞卡 **DELETE** `/api/flashcards/{flashcardId}` ### 5.6 批量操作 **POST** `/api/flashcards/bulk` #### 請求 ```json { "flashcardIds": ["uuid1", "uuid2", "uuid3"], "operation": "move", "targetDeckId": "uuid" } ``` ## 6. AI 生成 API ### 6.1 生成詞卡 **POST** `/api/ai/generate` #### 請求 ```json { "text": "The quick brown fox jumps over the lazy dog...", "theme": "daily_conversation", "count": 10, "difficulty": "intermediate", "includeExamples": true, "targetDeckId": "uuid" } ``` #### 響應 (200) ```json { "success": true, "data": { "requestId": "uuid", "status": "processing", "estimatedTime": 5000, "flashcards": [ { "word": "negotiate", "translation": "協商", "definition": "To discuss...", "pronunciation": "/nɪˈɡoʊʃieɪt/", "example": "We need to negotiate...", "difficulty": "intermediate" } ], "metadata": { "tokensUsed": 1500, "processingTime": 3200, "model": "gemini-pro" } } } ``` ### 6.2 取得生成狀態 **GET** `/api/ai/generate/{requestId}` #### 響應 (200) ```json { "success": true, "data": { "requestId": "uuid", "status": "completed", "flashcards": [...], "completedAt": "2024-03-15T10:00:05Z" } } ``` ### 6.3 取得用戶配額 **GET** `/api/ai/quota` #### 響應 (200) ```json { "success": true, "data": { "daily": { "used": 25, "limit": 50, "resetsAt": "2024-03-16T00:00:00Z" }, "monthly": { "used": 500, "limit": 1500 }, "isPremium": false } } ``` ## 7. 學習 API ### 7.1 開始學習會話 **POST** `/api/learning/sessions` #### 請求 ```json { "deckId": "uuid", "mode": "flashcard", "cardLimit": 20 } ``` #### 響應 (200) ```json { "success": true, "data": { "sessionId": "uuid", "cards": [...], "totalCards": 20, "newCards": 5, "reviewCards": 15 } } ``` ### 7.2 提交學習結果 **POST** `/api/learning/reviews` #### 請求 ```json { "sessionId": "uuid", "flashcardId": "uuid", "rating": 4, "timeSpent": 15, "isCorrect": true } ``` #### 響應 (200) ```json { "success": true, "data": { "nextReviewDate": "2024-03-18T00:00:00Z", "interval": 3, "easeFactor": 2.5, "repetitions": 2 } } ``` ### 7.3 取得待複習詞卡 **GET** `/api/learning/due` #### 查詢參數 - `deckId` (string): 特定卡組 - `limit` (number): 數量限制 #### 響應 (200) ```json { "success": true, "data": { "dueCards": [...], "newCards": [...], "totalDue": 25, "totalNew": 10 } } ``` ### 7.4 結束學習會話 **POST** `/api/learning/sessions/{sessionId}/complete` #### 響應 (200) ```json { "success": true, "data": { "summary": { "cardsStudied": 20, "correctAnswers": 18, "accuracy": 90, "timeSpent": 600, "experience": 100, "streakDays": 8 } } } ``` ## 8. 統計 API ### 8.1 取得學習統計 **GET** `/api/stats` #### 查詢參數 - `period` (string): `daily` | `weekly` | `monthly` | `yearly` - `startDate` (string): YYYY-MM-DD - `endDate` (string): YYYY-MM-DD #### 響應 (200) ```json { "success": true, "data": { "overview": { "totalCards": 500, "masteredCards": 200, "learningCards": 250, "newCards": 50, "studyStreak": 15, "totalStudyTime": 12000 }, "daily": [ { "date": "2024-03-15", "cardsStudied": 30, "newCards": 5, "reviewCards": 25, "accuracy": 85, "studyTime": 45 } ], "heatmap": { "2024-03-15": 3, "2024-03-14": 2, "2024-03-13": 4 } } } ``` ### 8.2 取得成就列表 **GET** `/api/stats/achievements` #### 響應 (200) ```json { "success": true, "data": { "unlocked": [ { "id": "first_card", "name": "First Step", "description": "Create your first flashcard", "icon": "🎯", "unlockedAt": "2024-03-01T00:00:00Z", "points": 10 } ], "inProgress": [ { "id": "week_streak", "name": "Week Warrior", "description": "Study for 7 days in a row", "progress": 85, "target": 7, "current": 6 } ], "totalPoints": 250 } } ``` ## 9. 錯誤處理 ### 9.1 錯誤碼列表 | 錯誤碼 | HTTP 狀態 | 描述 | |--------|-----------|------| | `UNAUTHORIZED` | 401 | 未認證 | | `FORBIDDEN` | 403 | 無權限 | | `NOT_FOUND` | 404 | 資源不存在 | | `VALIDATION_ERROR` | 422 | 驗證失敗 | | `EMAIL_EXISTS` | 409 | Email 已存在 | | `USERNAME_EXISTS` | 409 | 用戶名已存在 | | `INVALID_CREDENTIALS` | 401 | 認證資訊錯誤 | | `TOKEN_EXPIRED` | 401 | Token 過期 | | `RATE_LIMIT_EXCEEDED` | 429 | 請求過多 | | `QUOTA_EXCEEDED` | 402 | 配額超限 | | `AI_SERVICE_ERROR` | 503 | AI 服務錯誤 | | `DATABASE_ERROR` | 500 | 資料庫錯誤 | ### 9.2 錯誤響應範例 ```json { "success": false, "error": { "code": "VALIDATION_ERROR", "message": "Validation failed", "details": { "email": "Invalid email format", "password": "Password must be at least 8 characters" } }, "timestamp": "2024-03-15T10:00:00Z" } ``` ## 10. Rate Limiting ### 10.1 限制規則 - 一般 API: 100 requests/minute - AI 生成: 10 requests/minute - 認證相關: 5 requests/minute ### 10.2 響應標頭 ```http X-RateLimit-Limit: 100 X-RateLimit-Remaining: 95 X-RateLimit-Reset: 1710496800 ``` ### 10.3 超限響應 (429) ```json { "success": false, "error": { "code": "RATE_LIMIT_EXCEEDED", "message": "Too many requests", "retryAfter": 60 } } ``` ## 11. Webhook ### 11.1 事件類型 - `user.created` - 新用戶註冊 - `flashcard.generated` - AI 生成完成 - `achievement.unlocked` - 成就解鎖 - `subscription.updated` - 訂閱更新 ### 11.2 Webhook 格式 ```json { "id": "evt_uuid", "type": "flashcard.generated", "created": "2024-03-15T10:00:00Z", "data": { "requestId": "uuid", "userId": "uuid", "flashcardCount": 10 } } ``` ### 11.3 簽名驗證 ```javascript const signature = req.headers['x-webhook-signature']; const payload = JSON.stringify(req.body); const expected = crypto .createHmac('sha256', WEBHOOK_SECRET) .update(payload) .digest('hex'); if (signature !== expected) { throw new Error('Invalid signature'); } ```