486 lines
13 KiB
JavaScript
486 lines
13 KiB
JavaScript
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"]
|
|
* grade_number:
|
|
* type: integer
|
|
* example: 10101
|
|
* description: "Numeric code GG UU LL (e.g. 10101 = grade 1, unit 1, lesson 1)"
|
|
* type:
|
|
* type: string
|
|
* example: "story"
|
|
* 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('/', 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: type
|
|
* schema:
|
|
* type: string
|
|
* description: Filter by story type (exact match, e.g. "story", "poem")
|
|
* - in: query
|
|
* name: grade_start
|
|
* schema:
|
|
* type: integer
|
|
* description: "grade_number range start (GG UU LL). If equal to grade_end → exact match"
|
|
* - in: query
|
|
* name: grade_end
|
|
* schema:
|
|
* type: integer
|
|
* description: "grade_number range end. Returns stories where grade_number >= grade_start AND <= grade_end"
|
|
* - in: query
|
|
* name: grade_filter
|
|
* schema:
|
|
* type: string
|
|
* description: "Legacy: filter by grade JSON array value (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('/', storyController.getAllStories);
|
|
|
|
/**
|
|
* @swagger
|
|
* /api/stories/grade:
|
|
* get:
|
|
* summary: Get stories by grade level
|
|
* tags: [Stories]
|
|
* security:
|
|
* - bearerAuth: []
|
|
* parameters:
|
|
* - in: query
|
|
* name: grade
|
|
* schema:
|
|
* type: string
|
|
* description: "Legacy: grade level string (e.g. \"Grade 1\")"
|
|
* - in: query
|
|
* name: start
|
|
* schema:
|
|
* type: integer
|
|
* description: "grade_number range start (GG UU LL). If start == end → exact match"
|
|
* - in: query
|
|
* name: end
|
|
* schema:
|
|
* type: integer
|
|
* description: "grade_number range end. Returns stories where grade_number >= start AND <= end"
|
|
* responses:
|
|
* 200:
|
|
* description: List of stories for the specified grade
|
|
* 400:
|
|
* description: Missing grade parameter
|
|
* 500:
|
|
* description: Server error
|
|
*/
|
|
router.get('/grade', 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', storyController.getStoriesByTag);
|
|
|
|
/**
|
|
* @swagger
|
|
* /api/stories/type:
|
|
* get:
|
|
* summary: Get stories by type
|
|
* tags: [Stories]
|
|
* security:
|
|
* - bearerAuth: []
|
|
* parameters:
|
|
* - in: query
|
|
* name: type
|
|
* required: true
|
|
* schema:
|
|
* type: string
|
|
* description: Story type (e.g. "story", "poem", "dialogue")
|
|
* responses:
|
|
* 200:
|
|
* description: List of stories with the specified type
|
|
* 400:
|
|
* description: Missing type parameter
|
|
* 500:
|
|
* description: Server error
|
|
*/
|
|
router.get('/type', storyController.getStoriesByType);
|
|
|
|
/**
|
|
* @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', 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', 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', 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', 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', storyController.deleteStory);
|
|
|
|
// ============ Nested Lesson Routes ============
|
|
/**
|
|
* @swagger
|
|
* /api/stories/{storyId}/lessons:
|
|
* get:
|
|
* tags: [Stories]
|
|
* summary: Lấy danh sách lessons sử dụng story này
|
|
* parameters:
|
|
* - in: path
|
|
* name: storyId
|
|
* required: true
|
|
* schema:
|
|
* type: string
|
|
* format: uuid
|
|
* - in: query
|
|
* name: page
|
|
* schema:
|
|
* type: integer
|
|
* default: 1
|
|
* - in: query
|
|
* name: limit
|
|
* schema:
|
|
* type: integer
|
|
* default: 20
|
|
* responses:
|
|
* 200:
|
|
* description: Danh sách lessons sử dụng story
|
|
*/
|
|
router.get('/:storyId/lessons', storyController.getLessonsByStory);
|
|
|
|
/**
|
|
* @swagger
|
|
* /api/stories/{storyId}/lessons:
|
|
* post:
|
|
* tags: [Stories]
|
|
* summary: Thêm lesson vào story (alternative way)
|
|
* security:
|
|
* - bearerAuth: []
|
|
* parameters:
|
|
* - in: path
|
|
* name: storyId
|
|
* required: true
|
|
* schema:
|
|
* type: string
|
|
* format: uuid
|
|
* requestBody:
|
|
* required: true
|
|
* content:
|
|
* application/json:
|
|
* schema:
|
|
* type: object
|
|
* required:
|
|
* - lesson_id
|
|
* properties:
|
|
* lesson_id:
|
|
* type: string
|
|
* format: uuid
|
|
* display_order:
|
|
* type: integer
|
|
* default: 0
|
|
* is_required:
|
|
* type: boolean
|
|
* default: true
|
|
* responses:
|
|
* 201:
|
|
* description: Lesson đã được thêm vào story
|
|
*/
|
|
router.post('/:storyId/lessons', storyController.addLessonToStory);
|
|
|
|
/**
|
|
* @swagger
|
|
* /api/stories/{storyId}/lessons/{lessonId}:
|
|
* delete:
|
|
* tags: [Stories]
|
|
* summary: Xóa lesson khỏi story
|
|
* security:
|
|
* - bearerAuth: []
|
|
* parameters:
|
|
* - in: path
|
|
* name: storyId
|
|
* required: true
|
|
* schema:
|
|
* type: string
|
|
* format: uuid
|
|
* - in: path
|
|
* name: lessonId
|
|
* required: true
|
|
* schema:
|
|
* type: string
|
|
* format: uuid
|
|
* responses:
|
|
* 200:
|
|
* description: Lesson đã được xóa khỏi story
|
|
*/
|
|
router.delete('/:storyId/lessons/:lessonId', storyController.removeLessonFromStory);
|
|
|
|
module.exports = router;
|