update
This commit is contained in:
433
routes/grammarRoutes.js
Normal file
433
routes/grammarRoutes.js
Normal file
@@ -0,0 +1,433 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const grammarController = require('../controllers/grammarController');
|
||||
const { authenticateToken } = require('../middleware/auth');
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* tags:
|
||||
* name: Grammar
|
||||
* description: Grammar rule management system for sentence generation
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/grammar:
|
||||
* post:
|
||||
* summary: Create a new grammar rule
|
||||
* tags: [Grammar]
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* required:
|
||||
* - grammar_code
|
||||
* - title
|
||||
* - structure
|
||||
* properties:
|
||||
* grammar_code:
|
||||
* type: string
|
||||
* example: "gram-001-present-cont"
|
||||
* title:
|
||||
* type: string
|
||||
* example: "Present Continuous"
|
||||
* translation:
|
||||
* type: string
|
||||
* example: "Thì hiện tại tiếp diễn"
|
||||
* structure:
|
||||
* type: object
|
||||
* required:
|
||||
* - formula
|
||||
* properties:
|
||||
* formula:
|
||||
* type: string
|
||||
* example: "S + am/is/are + V-ing + (a/an) + O + Adv"
|
||||
* pattern_logic:
|
||||
* type: array
|
||||
* items:
|
||||
* type: object
|
||||
* properties:
|
||||
* slot_id:
|
||||
* type: string
|
||||
* role:
|
||||
* type: string
|
||||
* semantic_filter:
|
||||
* type: array
|
||||
* items:
|
||||
* type: string
|
||||
* use_form:
|
||||
* type: string
|
||||
* dependency:
|
||||
* type: string
|
||||
* is_optional:
|
||||
* type: boolean
|
||||
* position:
|
||||
* type: string
|
||||
* instructions:
|
||||
* type: object
|
||||
* properties:
|
||||
* vi:
|
||||
* type: string
|
||||
* hint:
|
||||
* type: string
|
||||
* difficulty_score:
|
||||
* type: integer
|
||||
* minimum: 1
|
||||
* maximum: 10
|
||||
* category:
|
||||
* type: string
|
||||
* tags:
|
||||
* type: array
|
||||
* items:
|
||||
* type: string
|
||||
* mappings:
|
||||
* type: array
|
||||
* items:
|
||||
* type: object
|
||||
* properties:
|
||||
* book_id:
|
||||
* type: string
|
||||
* grade:
|
||||
* type: integer
|
||||
* unit:
|
||||
* type: integer
|
||||
* lesson:
|
||||
* type: integer
|
||||
* context_note:
|
||||
* type: string
|
||||
* media_stories:
|
||||
* type: array
|
||||
* items:
|
||||
* type: object
|
||||
* properties:
|
||||
* story_id:
|
||||
* type: string
|
||||
* title:
|
||||
* type: string
|
||||
* type:
|
||||
* type: string
|
||||
* enum: [story, video, animation, audio]
|
||||
* url:
|
||||
* type: string
|
||||
* thumbnail:
|
||||
* type: string
|
||||
* description:
|
||||
* type: string
|
||||
* duration_seconds:
|
||||
* type: integer
|
||||
* min_grade:
|
||||
* type: integer
|
||||
* example:
|
||||
* grammar_code: "gram-001-present-cont"
|
||||
* title: "Present Continuous"
|
||||
* translation: "Thì hiện tại tiếp diễn"
|
||||
* structure:
|
||||
* formula: "S + am/is/are + V-ing + (a/an) + O + Adv"
|
||||
* pattern_logic:
|
||||
* - slot_id: "S_01"
|
||||
* role: "is_subject"
|
||||
* semantic_filter: ["human", "animal"]
|
||||
* - slot_id: "BE_01"
|
||||
* role: "is_be"
|
||||
* dependency: "S_01"
|
||||
* - slot_id: "V_01"
|
||||
* role: "is_verb"
|
||||
* use_form: "v_ing"
|
||||
* semantic_filter: ["action"]
|
||||
* - slot_id: "ART_01"
|
||||
* role: "is_article"
|
||||
* dependency: "O_01"
|
||||
* is_optional: true
|
||||
* - slot_id: "O_01"
|
||||
* role: "is_object"
|
||||
* semantic_match: "V_01"
|
||||
* - slot_id: "ADV_01"
|
||||
* role: "is_adv"
|
||||
* is_optional: true
|
||||
* position: "end"
|
||||
* instructions:
|
||||
* vi: "Dùng để nói về hành động đang diễn ra."
|
||||
* hint: "Cấu trúc: Be + V-ing"
|
||||
* difficulty_score: 2
|
||||
* category: "Tenses"
|
||||
* tags: ["present", "continuous", "action"]
|
||||
* mappings:
|
||||
* - book_id: "global-success-2"
|
||||
* grade: 2
|
||||
* unit: 5
|
||||
* lesson: 1
|
||||
* media_stories:
|
||||
* - story_id: "st-01"
|
||||
* title: "The Greedy Cat"
|
||||
* type: "story"
|
||||
* url: "https://cdn.sena.tech/stories/the-greedy-cat-full.mp4"
|
||||
* thumbnail: "https://cdn.sena.tech/thumbs/greedy-cat.jpg"
|
||||
* description: "A story about a cat who loves eating everything."
|
||||
* duration_seconds: 180
|
||||
* min_grade: 2
|
||||
* responses:
|
||||
* 201:
|
||||
* description: Grammar rule created successfully
|
||||
* 400:
|
||||
* description: Invalid input
|
||||
* 500:
|
||||
* description: Server error
|
||||
*/
|
||||
router.post('/', authenticateToken, grammarController.createGrammar);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/grammar:
|
||||
* get:
|
||||
* summary: Get all grammar rules with pagination and filters
|
||||
* tags: [Grammar]
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* parameters:
|
||||
* - in: query
|
||||
* name: page
|
||||
* schema:
|
||||
* type: integer
|
||||
* default: 1
|
||||
* description: Page number
|
||||
* - in: query
|
||||
* name: limit
|
||||
* schema:
|
||||
* type: integer
|
||||
* default: 20
|
||||
* description: Items per page
|
||||
* - in: query
|
||||
* name: category
|
||||
* schema:
|
||||
* type: string
|
||||
* description: Filter by category (e.g., "Tenses", "Modal Verbs")
|
||||
* - in: query
|
||||
* name: grade
|
||||
* schema:
|
||||
* type: integer
|
||||
* description: Filter by grade level
|
||||
* - in: query
|
||||
* name: book_id
|
||||
* schema:
|
||||
* type: string
|
||||
* description: Filter by book ID (e.g., "global-success-1")
|
||||
* - in: query
|
||||
* name: difficulty_min
|
||||
* schema:
|
||||
* type: integer
|
||||
* description: Minimum difficulty score
|
||||
* - in: query
|
||||
* name: difficulty_max
|
||||
* schema:
|
||||
* type: integer
|
||||
* description: Maximum difficulty score
|
||||
* - in: query
|
||||
* name: search
|
||||
* schema:
|
||||
* type: string
|
||||
* description: Search in title, translation, or grammar_code
|
||||
* - in: query
|
||||
* name: include_media
|
||||
* schema:
|
||||
* type: string
|
||||
* enum: ['true', 'false']
|
||||
* default: 'false'
|
||||
* description: Include media stories in response
|
||||
* responses:
|
||||
* 200:
|
||||
* description: List of grammar rules
|
||||
* 500:
|
||||
* description: Server error
|
||||
*/
|
||||
router.get('/', authenticateToken, grammarController.getAllGrammars);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/grammar/curriculum:
|
||||
* get:
|
||||
* summary: Get grammar rules by curriculum mapping
|
||||
* tags: [Grammar]
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* parameters:
|
||||
* - in: query
|
||||
* name: book_id
|
||||
* required: false
|
||||
* schema:
|
||||
* type: string
|
||||
* description: Book ID (e.g., "global-success-1")
|
||||
* - in: query
|
||||
* name: grade
|
||||
* required: false
|
||||
* schema:
|
||||
* type: integer
|
||||
* description: Grade level
|
||||
* - in: query
|
||||
* name: unit
|
||||
* schema:
|
||||
* type: integer
|
||||
* description: Unit number
|
||||
* - in: query
|
||||
* name: lesson
|
||||
* schema:
|
||||
* type: integer
|
||||
* description: Lesson number
|
||||
* responses:
|
||||
* 200:
|
||||
* description: List of grammar rules for the specified curriculum
|
||||
* 400:
|
||||
* description: Invalid parameters
|
||||
* 500:
|
||||
* description: Server error
|
||||
*/
|
||||
router.get('/curriculum', authenticateToken, grammarController.getGrammarsByCurriculum);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/grammar/guide:
|
||||
* get:
|
||||
* summary: Get comprehensive guide for AI to create grammar rules
|
||||
* tags: [Grammar]
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Complete guide with rules, examples, and data structures
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* guide_version:
|
||||
* type: string
|
||||
* last_updated:
|
||||
* type: string
|
||||
* data_structure:
|
||||
* type: object
|
||||
* pattern_logic_roles:
|
||||
* type: object
|
||||
* semantic_filters:
|
||||
* type: object
|
||||
* form_keys_reference:
|
||||
* type: object
|
||||
* rules:
|
||||
* type: object
|
||||
* examples:
|
||||
* type: object
|
||||
* validation_checklist:
|
||||
* type: array
|
||||
* common_mistakes:
|
||||
* type: array
|
||||
* ai_tips:
|
||||
* type: object
|
||||
* 500:
|
||||
* description: Server error
|
||||
*/
|
||||
router.get('/guide', authenticateToken, grammarController.getGrammarGuide);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/grammar/stats:
|
||||
* get:
|
||||
* summary: Get grammar statistics
|
||||
* tags: [Grammar]
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Grammar statistics
|
||||
* 500:
|
||||
* description: Server error
|
||||
*/
|
||||
router.get('/stats', authenticateToken, grammarController.getGrammarStats);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/grammar/{id}:
|
||||
* get:
|
||||
* summary: Get grammar rule by ID or code
|
||||
* tags: [Grammar]
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* description: Grammar ID (numeric) or grammar_code (string)
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Grammar rule details
|
||||
* 404:
|
||||
* description: Grammar rule not found
|
||||
* 500:
|
||||
* description: Server error
|
||||
*/
|
||||
router.get('/:id', authenticateToken, grammarController.getGrammarById);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/grammar/{id}:
|
||||
* put:
|
||||
* summary: Update grammar rule
|
||||
* tags: [Grammar]
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* description: Grammar ID (numeric) or grammar_code (string)
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* example:
|
||||
* translation: "Thì hiện tại tiếp diễn (cập nhật)"
|
||||
* difficulty_score: 3
|
||||
* tags: ["present", "continuous", "action", "updated"]
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Grammar rule updated successfully
|
||||
* 404:
|
||||
* description: Grammar rule not found
|
||||
* 500:
|
||||
* description: Server error
|
||||
*/
|
||||
router.put('/:id', authenticateToken, grammarController.updateGrammar);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/grammar/{id}:
|
||||
* delete:
|
||||
* summary: Delete grammar rule (soft delete)
|
||||
* tags: [Grammar]
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* description: Grammar ID (numeric) or grammar_code (string)
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Grammar rule deleted successfully
|
||||
* 404:
|
||||
* description: Grammar rule not found
|
||||
* 500:
|
||||
* description: Server error
|
||||
*/
|
||||
router.delete('/:id', authenticateToken, grammarController.deleteGrammar);
|
||||
|
||||
module.exports = router;
|
||||
273
routes/learningContentRoutes.js
Normal file
273
routes/learningContentRoutes.js
Normal file
@@ -0,0 +1,273 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const learningContentController = require('../controllers/learningContentController');
|
||||
const { authenticateToken } = require('../middleware/auth');
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* tags:
|
||||
* name: Learning Content
|
||||
* description: Learning content management (Subject → Chapter → Lesson)
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/learning-content/guide:
|
||||
* get:
|
||||
* summary: Get comprehensive learning content guide for AI
|
||||
* tags: [Learning Content]
|
||||
* description: Returns complete guide for Subject → Chapter → Lesson hierarchy and content structure
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Learning content guide
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* success:
|
||||
* type: boolean
|
||||
* data:
|
||||
* type: object
|
||||
* properties:
|
||||
* guide_version:
|
||||
* type: string
|
||||
* hierarchy:
|
||||
* type: object
|
||||
* subject_structure:
|
||||
* type: object
|
||||
* chapter_structure:
|
||||
* type: object
|
||||
* lesson_structure:
|
||||
* type: object
|
||||
* lesson_content_types:
|
||||
* type: object
|
||||
*/
|
||||
router.get('/guide', learningContentController.getLearningContentGuide);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/learning-content/lessons:
|
||||
* post:
|
||||
* summary: Create new lesson
|
||||
* tags: [Learning Content]
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* required:
|
||||
* - chapter_id
|
||||
* - lesson_number
|
||||
* - lesson_title
|
||||
* properties:
|
||||
* chapter_id:
|
||||
* type: string
|
||||
* format: uuid
|
||||
* description: Parent chapter UUID
|
||||
* lesson_number:
|
||||
* type: integer
|
||||
* description: Sequential lesson number
|
||||
* lesson_title:
|
||||
* type: string
|
||||
* maxLength: 200
|
||||
* lesson_type:
|
||||
* type: string
|
||||
* enum: [json_content, url_content]
|
||||
* default: json_content
|
||||
* lesson_description:
|
||||
* type: string
|
||||
* lesson_content_type:
|
||||
* type: string
|
||||
* enum: [vocabulary, grammar, phonics, review, mixed]
|
||||
* content_json:
|
||||
* type: object
|
||||
* description: JSON content structure (see guide for details)
|
||||
* content_url:
|
||||
* type: string
|
||||
* content_type:
|
||||
* type: string
|
||||
* enum: [video, audio, pdf, image, interactive]
|
||||
* duration_minutes:
|
||||
* type: integer
|
||||
* is_published:
|
||||
* type: boolean
|
||||
* default: false
|
||||
* is_free:
|
||||
* type: boolean
|
||||
* default: false
|
||||
* display_order:
|
||||
* type: integer
|
||||
* default: 0
|
||||
* thumbnail_url:
|
||||
* type: string
|
||||
* responses:
|
||||
* 201:
|
||||
* description: Lesson created successfully
|
||||
* 400:
|
||||
* description: Validation error
|
||||
* 404:
|
||||
* description: Chapter not found
|
||||
*/
|
||||
router.post('/lessons', authenticateToken, learningContentController.createLesson);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/learning-content/lessons:
|
||||
* get:
|
||||
* summary: Get all lessons with pagination and filters
|
||||
* tags: [Learning Content]
|
||||
* parameters:
|
||||
* - in: query
|
||||
* name: page
|
||||
* schema:
|
||||
* type: integer
|
||||
* default: 1
|
||||
* - in: query
|
||||
* name: limit
|
||||
* schema:
|
||||
* type: integer
|
||||
* default: 20
|
||||
* - in: query
|
||||
* name: chapter_id
|
||||
* schema:
|
||||
* type: string
|
||||
* format: uuid
|
||||
* description: Filter by chapter
|
||||
* - in: query
|
||||
* name: lesson_content_type
|
||||
* schema:
|
||||
* type: string
|
||||
* enum: [vocabulary, grammar, phonics, review, mixed]
|
||||
* description: Filter by content type
|
||||
* - in: query
|
||||
* name: lesson_type
|
||||
* schema:
|
||||
* type: string
|
||||
* enum: [json_content, url_content]
|
||||
* - in: query
|
||||
* name: is_published
|
||||
* schema:
|
||||
* type: boolean
|
||||
* - in: query
|
||||
* name: is_free
|
||||
* schema:
|
||||
* type: boolean
|
||||
* - in: query
|
||||
* name: search
|
||||
* schema:
|
||||
* type: string
|
||||
* description: Search in title and description
|
||||
* responses:
|
||||
* 200:
|
||||
* description: List of lessons with pagination
|
||||
*/
|
||||
router.get('/lessons', learningContentController.getAllLessons);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/learning-content/lessons/{id}:
|
||||
* get:
|
||||
* summary: Get lesson by ID
|
||||
* tags: [Learning Content]
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* format: uuid
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Lesson details
|
||||
* 404:
|
||||
* description: Lesson not found
|
||||
*/
|
||||
router.get('/lessons/:id', learningContentController.getLessonById);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/learning-content/lessons/{id}:
|
||||
* put:
|
||||
* summary: Update lesson
|
||||
* tags: [Learning Content]
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* format: uuid
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* lesson_title:
|
||||
* type: string
|
||||
* lesson_description:
|
||||
* type: string
|
||||
* content_json:
|
||||
* type: object
|
||||
* is_published:
|
||||
* type: boolean
|
||||
* is_free:
|
||||
* type: boolean
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Lesson updated successfully
|
||||
* 404:
|
||||
* description: Lesson not found
|
||||
*/
|
||||
router.put('/lessons/:id', authenticateToken, learningContentController.updateLesson);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/learning-content/lessons/{id}:
|
||||
* delete:
|
||||
* summary: Delete lesson
|
||||
* tags: [Learning Content]
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* format: uuid
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Lesson deleted successfully
|
||||
* 404:
|
||||
* description: Lesson not found
|
||||
*/
|
||||
router.delete('/lessons/:id', authenticateToken, learningContentController.deleteLesson);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/learning-content/lessons/chapter/{chapter_id}:
|
||||
* get:
|
||||
* summary: Get all lessons in a chapter
|
||||
* tags: [Learning Content]
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: chapter_id
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* format: uuid
|
||||
* responses:
|
||||
* 200:
|
||||
* description: List of lessons in chapter
|
||||
*/
|
||||
router.get('/lessons/chapter/:chapter_id', learningContentController.getLessonsByChapter);
|
||||
|
||||
module.exports = router;
|
||||
333
routes/storyRoutes.js
Normal file
333
routes/storyRoutes.js
Normal file
@@ -0,0 +1,333 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const storyController = require('../controllers/storyController');
|
||||
const { authenticateToken } = require('../middleware/auth');
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* tags:
|
||||
* name: Stories
|
||||
* description: Story management system for interactive learning content
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/stories:
|
||||
* post:
|
||||
* summary: Create a new story
|
||||
* tags: [Stories]
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* required:
|
||||
* - name
|
||||
* properties:
|
||||
* name:
|
||||
* type: string
|
||||
* example: "The Greedy Cat"
|
||||
* logo:
|
||||
* type: string
|
||||
* example: "https://cdn.sena.tech/thumbs/greedy-cat.jpg"
|
||||
* vocabulary:
|
||||
* type: array
|
||||
* items:
|
||||
* type: string
|
||||
* example: ["cat", "eat", "apple", "happy", "greedy"]
|
||||
* context:
|
||||
* type: array
|
||||
* items:
|
||||
* type: object
|
||||
* properties:
|
||||
* image:
|
||||
* type: string
|
||||
* text:
|
||||
* type: string
|
||||
* audio:
|
||||
* type: string
|
||||
* order:
|
||||
* type: integer
|
||||
* example:
|
||||
* - image: "https://cdn.sena.tech/story/scene1.jpg"
|
||||
* text: "Once upon a time, there was a greedy cat."
|
||||
* audio: "https://cdn.sena.tech/audio/scene1.mp3"
|
||||
* order: 1
|
||||
* - image: "https://cdn.sena.tech/story/scene2.jpg"
|
||||
* text: "The cat loved eating apples every day."
|
||||
* audio: "https://cdn.sena.tech/audio/scene2.mp3"
|
||||
* order: 2
|
||||
* grade:
|
||||
* type: array
|
||||
* items:
|
||||
* type: string
|
||||
* example: ["Grade 1", "Grade 2"]
|
||||
* tag:
|
||||
* type: array
|
||||
* items:
|
||||
* type: string
|
||||
* example: ["animals", "food", "lesson", "health", "fiction"]
|
||||
* responses:
|
||||
* 201:
|
||||
* description: Story created successfully
|
||||
* 400:
|
||||
* description: Invalid input
|
||||
* 500:
|
||||
* description: Server error
|
||||
*/
|
||||
router.post('/', authenticateToken, storyController.createStory);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/stories:
|
||||
* get:
|
||||
* summary: Get all stories with pagination and filters
|
||||
* tags: [Stories]
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* parameters:
|
||||
* - in: query
|
||||
* name: page
|
||||
* schema:
|
||||
* type: integer
|
||||
* default: 1
|
||||
* description: Page number
|
||||
* - in: query
|
||||
* name: limit
|
||||
* schema:
|
||||
* type: integer
|
||||
* default: 20
|
||||
* description: Items per page
|
||||
* - in: query
|
||||
* name: search
|
||||
* schema:
|
||||
* type: string
|
||||
* description: Search in story name
|
||||
* - in: query
|
||||
* name: grade_filter
|
||||
* schema:
|
||||
* type: string
|
||||
* description: Filter by grade (e.g., "Grade 1")
|
||||
* - in: query
|
||||
* name: tag_filter
|
||||
* schema:
|
||||
* type: string
|
||||
* description: Filter by tag (e.g., "animals")
|
||||
* - in: query
|
||||
* name: sort_by
|
||||
* schema:
|
||||
* type: string
|
||||
* default: created_at
|
||||
* description: Sort by field
|
||||
* - in: query
|
||||
* name: sort_order
|
||||
* schema:
|
||||
* type: string
|
||||
* enum: [ASC, DESC]
|
||||
* default: DESC
|
||||
* description: Sort order
|
||||
* responses:
|
||||
* 200:
|
||||
* description: List of stories
|
||||
* 500:
|
||||
* description: Server error
|
||||
*/
|
||||
router.get('/', authenticateToken, storyController.getAllStories);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/stories/grade:
|
||||
* get:
|
||||
* summary: Get stories by grade level
|
||||
* tags: [Stories]
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* parameters:
|
||||
* - in: query
|
||||
* name: grade
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* description: Grade level (e.g., "Grade 1")
|
||||
* responses:
|
||||
* 200:
|
||||
* description: List of stories for the specified grade
|
||||
* 400:
|
||||
* description: Missing grade parameter
|
||||
* 500:
|
||||
* description: Server error
|
||||
*/
|
||||
router.get('/grade', authenticateToken, storyController.getStoriesByGrade);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/stories/tag:
|
||||
* get:
|
||||
* summary: Get stories by tag
|
||||
* tags: [Stories]
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* parameters:
|
||||
* - in: query
|
||||
* name: tag
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* description: Tag name (e.g., "animals")
|
||||
* responses:
|
||||
* 200:
|
||||
* description: List of stories with the specified tag
|
||||
* 400:
|
||||
* description: Missing tag parameter
|
||||
* 500:
|
||||
* description: Server error
|
||||
*/
|
||||
router.get('/tag', authenticateToken, storyController.getStoriesByTag);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/stories/guide:
|
||||
* get:
|
||||
* summary: Get comprehensive guide for AI to create stories
|
||||
* tags: [Stories]
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Complete guide with rules, examples, and data structures
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* guide_version:
|
||||
* type: string
|
||||
* last_updated:
|
||||
* type: string
|
||||
* data_structure:
|
||||
* type: object
|
||||
* context_structure:
|
||||
* type: object
|
||||
* vocabulary_guidelines:
|
||||
* type: object
|
||||
* grade_levels:
|
||||
* type: object
|
||||
* tag_categories:
|
||||
* type: object
|
||||
* examples:
|
||||
* type: object
|
||||
* validation_checklist:
|
||||
* type: array
|
||||
* common_mistakes:
|
||||
* type: array
|
||||
* ai_tips:
|
||||
* type: object
|
||||
* 500:
|
||||
* description: Server error
|
||||
*/
|
||||
router.get('/guide', authenticateToken, storyController.getStoryGuide);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/stories/stats:
|
||||
* get:
|
||||
* summary: Get story statistics
|
||||
* tags: [Stories]
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Story statistics
|
||||
* 500:
|
||||
* description: Server error
|
||||
*/
|
||||
router.get('/stats', authenticateToken, storyController.getStoryStats);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/stories/{id}:
|
||||
* get:
|
||||
* summary: Get story by ID
|
||||
* tags: [Stories]
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* description: Story ID (UUID)
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Story details
|
||||
* 404:
|
||||
* description: Story not found
|
||||
* 500:
|
||||
* description: Server error
|
||||
*/
|
||||
router.get('/:id', authenticateToken, storyController.getStoryById);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/stories/{id}:
|
||||
* put:
|
||||
* summary: Update story
|
||||
* tags: [Stories]
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* description: Story ID (UUID)
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* example:
|
||||
* name: "The Greedy Cat (Updated)"
|
||||
* tag: ["animals", "food", "lesson", "health", "fiction", "updated"]
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Story updated successfully
|
||||
* 404:
|
||||
* description: Story not found
|
||||
* 500:
|
||||
* description: Server error
|
||||
*/
|
||||
router.put('/:id', authenticateToken, storyController.updateStory);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/stories/{id}:
|
||||
* delete:
|
||||
* summary: Delete story
|
||||
* tags: [Stories]
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* description: Story ID (UUID)
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Story deleted successfully
|
||||
* 404:
|
||||
* description: Story not found
|
||||
* 500:
|
||||
* description: Server error
|
||||
*/
|
||||
router.delete('/:id', authenticateToken, storyController.deleteStory);
|
||||
|
||||
module.exports = router;
|
||||
337
routes/vocabRoutes.js
Normal file
337
routes/vocabRoutes.js
Normal file
@@ -0,0 +1,337 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const vocabController = require('../controllers/vocabController');
|
||||
const { authenticateToken } = require('../middleware/auth');
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* tags:
|
||||
* name: Vocabulary
|
||||
* description: Vocabulary management system for curriculum-based language learning
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/vocab:
|
||||
* post:
|
||||
* summary: Create a new vocabulary entry
|
||||
* tags: [Vocabulary]
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/VocabComplete'
|
||||
* example:
|
||||
* vocab_code: "vocab-001-eat"
|
||||
* base_word: "eat"
|
||||
* translation: "ăn"
|
||||
* attributes:
|
||||
* difficulty_score: 1
|
||||
* category: "Action Verbs"
|
||||
* images:
|
||||
* - "https://cdn.sena.tech/img/eat-main.png"
|
||||
* - "https://cdn.sena.tech/img/eat-context.jpg"
|
||||
* tags: ["daily-routine", "verb"]
|
||||
* mappings:
|
||||
* - book_id: "global-success-1"
|
||||
* grade: 1
|
||||
* unit: 2
|
||||
* lesson: 3
|
||||
* form_key: "v1"
|
||||
* - book_id: "global-success-2"
|
||||
* grade: 2
|
||||
* unit: 5
|
||||
* lesson: 1
|
||||
* form_key: "v_ing"
|
||||
* forms:
|
||||
* v1:
|
||||
* text: "eat"
|
||||
* phonetic: "/iːt/"
|
||||
* audio: "https://cdn.sena.tech/audio/eat_v1.mp3"
|
||||
* min_grade: 1
|
||||
* v_s_es:
|
||||
* text: "eats"
|
||||
* phonetic: "/iːts/"
|
||||
* audio: "https://cdn.sena.tech/audio/eats_s.mp3"
|
||||
* min_grade: 2
|
||||
* v_ing:
|
||||
* text: "eating"
|
||||
* phonetic: "/ˈiː.tɪŋ/"
|
||||
* audio: "https://cdn.sena.tech/audio/eating_ing.mp3"
|
||||
* min_grade: 2
|
||||
* v2:
|
||||
* text: "ate"
|
||||
* phonetic: "/et/"
|
||||
* audio: "https://cdn.sena.tech/audio/ate_v2.mp3"
|
||||
* min_grade: 3
|
||||
* relations:
|
||||
* synonyms: ["consume", "dine"]
|
||||
* antonyms: ["fast", "starve"]
|
||||
* syntax:
|
||||
* is_subject: false
|
||||
* is_verb: true
|
||||
* is_object: false
|
||||
* is_be: false
|
||||
* is_adj: false
|
||||
* verb_type: "transitive"
|
||||
* semantics:
|
||||
* can_be_subject_type: ["human", "animal"]
|
||||
* can_take_object_type: ["food", "plant"]
|
||||
* word_type: "action"
|
||||
* constraints:
|
||||
* requires_object: true
|
||||
* semantic_object_types: ["food", "plant"]
|
||||
* responses:
|
||||
* 201:
|
||||
* description: Vocabulary created successfully
|
||||
* 400:
|
||||
* description: Invalid input
|
||||
* 500:
|
||||
* description: Server error
|
||||
*/
|
||||
router.post('/', authenticateToken, vocabController.createVocab);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/vocab:
|
||||
* get:
|
||||
* summary: Get all vocabulary entries with pagination and filters
|
||||
* tags: [Vocabulary]
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* parameters:
|
||||
* - in: query
|
||||
* name: page
|
||||
* schema:
|
||||
* type: integer
|
||||
* default: 1
|
||||
* description: Page number
|
||||
* - in: query
|
||||
* name: limit
|
||||
* schema:
|
||||
* type: integer
|
||||
* default: 20
|
||||
* description: Items per page
|
||||
* - in: query
|
||||
* name: category
|
||||
* schema:
|
||||
* type: string
|
||||
* description: Filter by category (e.g., "Action Verbs")
|
||||
* - in: query
|
||||
* name: grade
|
||||
* schema:
|
||||
* type: integer
|
||||
* description: Filter by grade level
|
||||
* - in: query
|
||||
* name: book_id
|
||||
* schema:
|
||||
* type: string
|
||||
* description: Filter by book ID (e.g., "global-success-1")
|
||||
* - in: query
|
||||
* name: difficulty_min
|
||||
* schema:
|
||||
* type: integer
|
||||
* description: Minimum difficulty score
|
||||
* - in: query
|
||||
* name: difficulty_max
|
||||
* schema:
|
||||
* type: integer
|
||||
* description: Maximum difficulty score
|
||||
* - in: query
|
||||
* name: search
|
||||
* schema:
|
||||
* type: string
|
||||
* description: Search in base_word, translation, or vocab_code
|
||||
* - in: query
|
||||
* name: include_relations
|
||||
* schema:
|
||||
* type: string
|
||||
* enum: ['true', 'false']
|
||||
* default: 'false'
|
||||
* description: Include synonyms/antonyms in response
|
||||
* responses:
|
||||
* 200:
|
||||
* description: List of vocabularies
|
||||
* 500:
|
||||
* description: Server error
|
||||
*/
|
||||
router.get('/', authenticateToken, vocabController.getAllVocabs);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/vocab/curriculum:
|
||||
* get:
|
||||
* summary: Get vocabularies by curriculum mapping
|
||||
* tags: [Vocabulary]
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* parameters:
|
||||
* - in: query
|
||||
* name: book_id
|
||||
* required: false
|
||||
* schema:
|
||||
* type: string
|
||||
* description: Book ID (e.g., "global-success-1")
|
||||
* - in: query
|
||||
* name: grade
|
||||
* required: false
|
||||
* schema:
|
||||
* type: integer
|
||||
* description: Grade level
|
||||
* - in: query
|
||||
* name: unit
|
||||
* schema:
|
||||
* type: integer
|
||||
* description: Unit number
|
||||
* - in: query
|
||||
* name: lesson
|
||||
* schema:
|
||||
* type: integer
|
||||
* description: Lesson number
|
||||
* responses:
|
||||
* 200:
|
||||
* description: List of vocabularies for the specified curriculum
|
||||
* 400:
|
||||
* description: Invalid parameters
|
||||
* 500:
|
||||
* description: Server error
|
||||
*/
|
||||
router.get('/curriculum', authenticateToken, vocabController.getVocabsByCurriculum);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/vocab/guide:
|
||||
* get:
|
||||
* summary: Get comprehensive guide for AI to create vocabulary entries
|
||||
* tags: [Vocabulary]
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Complete guide with rules, examples, and data structures
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* guide_version:
|
||||
* type: string
|
||||
* last_updated:
|
||||
* type: string
|
||||
* data_structure:
|
||||
* type: object
|
||||
* rules:
|
||||
* type: object
|
||||
* examples:
|
||||
* type: object
|
||||
* 500:
|
||||
* description: Server error
|
||||
*/
|
||||
router.get('/guide', authenticateToken, vocabController.getVocabGuide);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/vocab/stats:
|
||||
* get:
|
||||
* summary: Get vocabulary statistics
|
||||
* tags: [Vocabulary]
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Vocabulary statistics
|
||||
* 500:
|
||||
* description: Server error
|
||||
*/
|
||||
router.get('/stats', authenticateToken, vocabController.getVocabStats);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/vocab/{id}:
|
||||
* get:
|
||||
* summary: Get vocabulary by ID or code
|
||||
* tags: [Vocabulary]
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* description: Vocabulary ID (numeric) or vocab_code (string)
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Vocabulary details
|
||||
* 404:
|
||||
* description: Vocabulary not found
|
||||
* 500:
|
||||
* description: Server error
|
||||
*/
|
||||
router.get('/:id', authenticateToken, vocabController.getVocabById);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/vocab/{id}:
|
||||
* put:
|
||||
* summary: Update vocabulary entry
|
||||
* tags: [Vocabulary]
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* description: Vocabulary ID (numeric) or vocab_code (string)
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/VocabComplete'
|
||||
* example:
|
||||
* translation: "ăn uống"
|
||||
* attributes:
|
||||
* difficulty_score: 2
|
||||
* tags: ["daily-routine", "verb", "food"]
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Vocabulary updated successfully
|
||||
* 404:
|
||||
* description: Vocabulary not found
|
||||
* 500:
|
||||
* description: Server error
|
||||
*/
|
||||
router.put('/:id', authenticateToken, vocabController.updateVocab);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/vocab/{id}:
|
||||
* delete:
|
||||
* summary: Delete vocabulary (soft delete)
|
||||
* tags: [Vocabulary]
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* description: Vocabulary ID (numeric) or vocab_code (string)
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Vocabulary deleted successfully
|
||||
* 404:
|
||||
* description: Vocabulary not found
|
||||
* 500:
|
||||
* description: Server error
|
||||
*/
|
||||
router.delete('/:id', authenticateToken, vocabController.deleteVocab);
|
||||
|
||||
module.exports = router;
|
||||
Reference in New Issue
Block a user