const express = require('express'); const router = express.Router(); const vocabController = require('../controllers/vocabController'); const { authenticateToken } = require('../middleware/auth'); /** * ============================================ * POST /api/vocabs * ============================================ * Tạo một vocab entry mới * * INPUT: * { * text: String (required) - từ thực tế (wash, washes, washing, ate, eaten...) * ipa: String - phiên âm IPA (ví dụ: /wɒʃ/) * base_word: String (required) - từ gốc để nhóm lại (wash, eat...) * form_key: JSON - loại biến thể (V1, V2, V3, V_ing, Noun_Form...), mặc định 'base' * vi: String - nghĩa tiếng Việt * category: String - category của từ (Action Verbs, Nouns, etc.) * topic: String - chủ đề (Food, Travel, Education, etc.) * image_small: JSON Array - mảng URLs của hình ảnh nhỏ * image_square: JSON Array - mảng URLs của hình ảnh vuông * image_normal: JSON Array - mảng URLs của hình ảnh bình thường * audio: JSON Array - mảng URLs của audio files * example_sentences: JSON - các câu ví dụ * tags: JSON Array - các tags để phân loại * syntax: JSON - vai trò cú pháp * semantics: JSON - ràng buộc ngữ nghĩa * constraints: JSON - ràng buộc ngữ pháp * } * * OUTPUT: * { * success: Boolean, * message: String, * data: Vocab object đã tạo (bao gồm vocab_id, created_at, updated_at) * } **/ router.post('/', vocabController.createVocab); /** * ============================================ * POST /api/vocabs/bulk * ============================================ * Tạo nhiều vocab entries cùng lúc * * INPUT: * { * vocabs: Array of Vocab objects - mỗi object phải có text và base_word * [ * { * text: String (required), * base_word: String (required), * ipa: String, * vi: String, * ... * }, * ... * ] * } * * OUTPUT: * { * success: Boolean, * message: String, * data: Array of created Vocab objects, * count: Number - số lượng vocab đã tạo * } **/ router.post('/bulk', vocabController.bulkCreateVocabs); /** * ============================================ * POST /api/vocabs/search * ============================================ * Tìm kiếm vocab nâng cao với nhiều filter * * INPUT: * { * topic: String (optional) - chủ đề (exact match) * category: String (optional) - loại từ (exact match) * base_word: String (optional) - từ gốc (partial match với LIKE) * form_key: JSON (optional) - loại biến thể (V1, V2, V3, V_ing, Noun_Form, etc.) * text: String (optional) - từ thực tế (partial match với LIKE) * vi: String (optional) - nghĩa tiếng Việt (partial match với LIKE) * * v_type: Boolean (optional) - tìm các biến thể khác của cùng một base_word * base_word_filter: String (optional) - base_word cụ thể (dùng khi v_type=true) * * shuffle_pos: Object (optional) - tìm từ thay thế dựa trên syntax * { * is_subject: Boolean, * is_verb: Boolean, * is_object: Boolean, * is_be: Boolean, * is_adj: Boolean, * is_adv: Boolean, * is_article: Boolean * } * * page: Number - trang hiện tại (mặc định: 1) * limit: Number - số items mỗi trang (mặc định: 100) * } * * OUTPUT: * { * success: Boolean, * message: String, * data: Array of Vocab objects, * pagination: { * total: Number, * page: Number, * limit: Number, * totalPages: Number * } * } **/ router.post('/search', vocabController.searchVocabs); /** * ============================================ * GET /api/vocabs * ============================================ * Lấy danh sách tất cả vocab với phân trang và filter * * INPUT (Query Parameters): * { * page: Number - trang hiện tại (mặc định: 1) * limit: Number - số items mỗi trang (mặc định: 20) * category: String - lọc theo category * topic: String - lọc theo topic * base_word: String - lọc theo base_word chính xác * text: String - lọc theo text chính xác * search: String - tìm kiếm trong text, base_word và vi * is_active: Boolean - lọc theo trạng thái active (mặc định: true) * } * * OUTPUT: * { * success: Boolean, * message: String, * data: Array of Vocab objects, * pagination: { * total: Number, * page: Number, * limit: Number, * totalPages: Number * } * } * **/ router.get('/', vocabController.getAllVocabs); /** * ============================================ * GET /api/vocabs/stats/overview * ============================================ * Lấy thống kê tổng quan về vocab * * INPUT: Không có * * OUTPUT: * { * success: Boolean, * message: String, * data: { * total: { * active: Number, * inactive: Number, * all: Number * }, * unique_base_words: Number, * by_category: Array [{category: String, count: Number}], * by_topic: Array [{topic: String, count: Number}] * } * } **/ router.get('/stats/overview', vocabController.getVocabStats); /** * ============================================ * GET /api/vocabs/meta/categories * ============================================ * Lấy danh sách tất cả categories * * INPUT: Không có * * OUTPUT: * { * success: Boolean, * message: String, * data: Array of String - danh sách categories, * count: Number - số lượng categories * } **/ router.get('/meta/categories', vocabController.getAllCategories); /** * ============================================ * GET /api/vocabs/meta/topics * ============================================ * Lấy danh sách tất cả topics * * INPUT: Không có * * OUTPUT: * { * success: Boolean, * message: String, * data: Array of String - danh sách topics, * count: Number - số lượng topics * } **/ router.get('/meta/topics', vocabController.getAllTopics); /** * ============================================ * GET /api/vocabs/missing/ipa * ============================================ * Lấy tất cả các vocab chưa có IPA * * INPUT (Query Parameters): * { * page: Number - trang hiện tại (mặc định: 1), * limit: Number - số items mỗi trang (mặc định: 50) * } * * OUTPUT: * { * success: Boolean, * message: String, * data: Array of Vocab objects - các vocab chưa có IPA, * pagination: { * total: Number, * page: Number, * limit: Number, * totalPages: Number * } * } **/ router.get('/missing/ipa', vocabController.getVocabsWithoutIpa); /** * ============================================ * GET /api/vocabs/missing/images * ============================================ * Lấy tất cả các vocab chưa đủ hình ảnh * * INPUT (Query Parameters): * { * page: Number - trang hiện tại (mặc định: 1), * limit: Number - số items mỗi trang (mặc định: 50) * } * * OUTPUT: * { * success: Boolean, * message: String, * data: Array of Vocab objects - các vocab chưa đủ hình ảnh, * pagination: { * total: Number, * page: Number, * limit: Number, * totalPages: Number * } * } **/ router.get('/missing/images', vocabController.getVocabsWithoutImages); /** * ============================================ * GET /api/vocabs/:id * ============================================ * Lấy chi tiết một vocab theo ID * * INPUT (URL Parameter): * { * id: UUID - vocab_id của vocab cần lấy * } * * OUTPUT: * { * success: Boolean, * message: String, * data: Vocab object với đầy đủ thông tin * } **/ router.get('/:id', vocabController.getVocabById); /** * ============================================ * PUT /api/vocabs/:id * ============================================ * Cập nhật thông tin vocab * * INPUT (URL Parameter + Body): * { * id: UUID - vocab_id cần update * Body: Object - các trường cần update (có thể update một hoặc nhiều trường) * { * text: String, * ipa: String, * base_word: String, * form_key: JSON, * vi: String, * category: String, * topic: String, * image_small: JSON Array, * image_square: JSON Array, * image_normal: JSON Array, * audio: JSON Array, * example_sentences: JSON, * tags: JSON Array, * syntax: JSON, * semantics: JSON, * constraints: JSON, * is_active: Boolean * } * } * * OUTPUT: * { * success: Boolean, * message: String, * data: Updated Vocab object * } * **/ router.put('/:id', vocabController.updateVocab); /** * ============================================ * DELETE /api/vocabs/:id * ============================================ * Xóa mềm vocab (set is_active = false) * * INPUT (URL Parameter): * { * id: UUID - vocab_id cần xóa * } * * OUTPUT: * { * success: Boolean, * message: String * } **/ router.delete('/:id', vocabController.deleteVocab); module.exports = router;