import { Body, FormField, Path, Post, Put, Request, Response, Route, Security, SuccessResponse, Tags, UploadedFile, UploadedFiles } from 'tsoa'; import { ValidationError } from '../middleware/errorHandler'; import { ChaptersLessonService } from '../services/ChaptersLesson.service'; import { MultiLanguageText } from '../types/index'; import { UploadedFileInfo, CreateLessonInput, CreateLessonResponse, UpdateLessonResponse } from '../types/ChaptersLesson.typs'; const chaptersLessonService = new ChaptersLessonService(); @Route('api/instructors/courses/{courseId}/chapters/{chapterId}/lessons') @Tags('Lessons - File Upload') export class LessonsController { /** * สร้างบทเรียนใหม่พร้อมไฟล์แนบ * Create a new lesson with optional video and attachments * * @param courseId Course ID * @param chapterId Chapter ID * @param title ชื่อบทเรียน (JSON string: { th: "", en: "" }) * @param type ประเภทบทเรียน (VIDEO | QUIZ) * @param content เนื้อหาบทเรียน (JSON string: { th: "", en: "" }) * @param sort_order ลำดับการแสดงผล * @param video ไฟล์วิดีโอ (สำหรับ type=VIDEO เท่านั้น) * @param attachments ไฟล์แนบ (PDFs, เอกสาร, รูปภาพ) */ @Post('upload') @Security('jwt', ['instructor']) @SuccessResponse('201', 'Lesson created successfully') @Response('400', 'Validation error') @Response('401', 'Unauthorized') @Response('403', 'Forbidden') public async createLessonWithFiles( @Request() request: any, @Path() courseId: number, @Path() chapterId: number, @FormField() title: string, @FormField() type: 'VIDEO' | 'QUIZ', @FormField() content?: string, @FormField() sort_order?: string, @UploadedFile() video?: Express.Multer.File, @UploadedFiles() attachments?: Express.Multer.File[] ): Promise { const token = request.headers.authorization?.replace('Bearer ', ''); if (!token) throw new ValidationError('No token provided'); // Parse JSON fields const parsedTitle: MultiLanguageText = JSON.parse(title); const parsedContent = content ? JSON.parse(content) : undefined; const sortOrder = sort_order ? parseInt(sort_order, 10) : undefined; if (!parsedTitle.th || !parsedTitle.en) { throw new ValidationError('Title must have both Thai (th) and English (en) values'); } // Transform files to UploadedFileInfo let videoInfo: UploadedFileInfo | undefined; let attachmentsInfo: UploadedFileInfo[] | undefined; if (video) { videoInfo = { originalname: video.originalname, mimetype: video.mimetype, size: video.size, buffer: video.buffer, }; } if (attachments && attachments.length > 0) { attachmentsInfo = attachments.map(file => ({ originalname: file.originalname, mimetype: file.mimetype, size: file.size, buffer: file.buffer, })); } const input: CreateLessonInput = { token, course_id: courseId, chapter_id: chapterId, title: parsedTitle, content: parsedContent, type, sort_order: sortOrder, video: videoInfo, attachments: attachmentsInfo, }; return await chaptersLessonService.createLesson(input); } /** * เพิ่มวิดีโอและไฟล์แนบให้บทเรียน VIDEO ที่มีอยู่แล้ว * Add video and attachments to an existing VIDEO type lesson * * @param courseId Course ID * @param chapterId Chapter ID * @param lessonId Lesson ID * @param video ไฟล์วิดีโอ (required) * @param attachments ไฟล์แนบ */ @Post('{lessonId}/video') @Security('jwt', ['instructor']) @SuccessResponse('200', 'Video added successfully') @Response('400', 'Validation error') @Response('401', 'Unauthorized') @Response('403', 'Forbidden') @Response('404', 'Lesson not found') public async addVideoToLesson( @Request() request: any, @Path() courseId: number, @Path() chapterId: number, @Path() lessonId: number, @UploadedFile() video: Express.Multer.File, @UploadedFiles() attachments?: Express.Multer.File[] ): Promise { const token = request.headers.authorization?.replace('Bearer ', ''); if (!token) throw new ValidationError('No token provided'); if (!video) { throw new ValidationError('Video file is required'); } // Transform files to UploadedFileInfo const videoInfo: UploadedFileInfo = { originalname: video.originalname, mimetype: video.mimetype, size: video.size, buffer: video.buffer, }; let attachmentsInfo: UploadedFileInfo[] | undefined; if (attachments && attachments.length > 0) { attachmentsInfo = attachments.map(file => ({ originalname: file.originalname, mimetype: file.mimetype, size: file.size, buffer: file.buffer, })); } return await chaptersLessonService.addVideoLesson({ token, course_id: courseId, lesson_id: lessonId, video: videoInfo, attachments: attachmentsInfo, }); } /** * อัปเดตวิดีโอและไฟล์แนบของบทเรียน VIDEO * Update video and attachments of an existing VIDEO type lesson * * @param courseId Course ID * @param chapterId Chapter ID * @param lessonId Lesson ID * @param video ไฟล์วิดีโอใหม่ * @param attachments ไฟล์แนบใหม่ */ @Put('{lessonId}/video') @Security('jwt', ['instructor']) @SuccessResponse('200', 'Video updated successfully') @Response('400', 'Validation error') @Response('401', 'Unauthorized') @Response('403', 'Forbidden') @Response('404', 'Lesson not found') public async updateVideoLesson( @Request() request: any, @Path() courseId: number, @Path() chapterId: number, @Path() lessonId: number, @UploadedFile() video?: Express.Multer.File, @UploadedFiles() attachments?: Express.Multer.File[] ): Promise { const token = request.headers.authorization?.replace('Bearer ', ''); if (!token) throw new ValidationError('No token provided'); // Transform files to UploadedFileInfo let videoInfo: UploadedFileInfo | undefined; let attachmentsInfo: UploadedFileInfo[] | undefined; if (video) { videoInfo = { originalname: video.originalname, mimetype: video.mimetype, size: video.size, buffer: video.buffer, }; } if (attachments && attachments.length > 0) { attachmentsInfo = attachments.map(file => ({ originalname: file.originalname, mimetype: file.mimetype, size: file.size, buffer: file.buffer, })); } return await chaptersLessonService.updateVideoLesson({ token, course_id: courseId, lesson_id: lessonId, video: videoInfo, attachments: attachmentsInfo, }); } }