import { Body, Delete, Path, Post, Put, Request, Response, Route, Security, SuccessResponse, Tags, UploadedFile } from 'tsoa'; import { ValidationError } from '../middleware/errorHandler'; import { ChaptersLessonService } from '../services/ChaptersLesson.service'; import { UploadedFileInfo, CreateLessonResponse, UpdateLessonResponse, VideoOperationResponse, AttachmentOperationResponse, DeleteAttachmentResponse, YouTubeVideoResponse, SetYouTubeVideoBody, } from '../types/ChaptersLesson.typs'; const chaptersLessonService = new ChaptersLessonService(); @Route('api/instructors/courses/{courseId}/chapters/{chapterId}/lessons') @Tags('Lessons - File Upload') export class LessonsController { /** * อัพโหลดวิดีโอใหม่ให้บทเรียน (ครั้งแรก) * Upload video to lesson (first time) * * @param courseId Course ID * @param chapterId Chapter ID * @param lessonId Lesson ID * @param video ไฟล์วิดีโอ (required) */ @Post('{lessonId}/video') @Security('jwt', ['instructor']) @SuccessResponse('200', 'Video uploaded successfully') @Response('400', 'Validation error - Video already exists') @Response('401', 'Unauthorized') @Response('403', 'Forbidden') @Response('404', 'Lesson not found') public async uploadVideo( @Request() request: any, @Path() courseId: number, @Path() chapterId: number, @Path() lessonId: number, @UploadedFile() video: 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'); } const videoInfo: UploadedFileInfo = { originalname: video.originalname, mimetype: video.mimetype, size: video.size, buffer: video.buffer, }; return await chaptersLessonService.uploadVideo({ token, course_id: courseId, lesson_id: lessonId, video: videoInfo, }); } /** * อัพเดต (เปลี่ยน) วิดีโอของบทเรียน * Update (replace) video in lesson * * @param courseId Course ID * @param chapterId Chapter ID * @param lessonId Lesson ID * @param video ไฟล์วิดีโอใหม่ (required) */ @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 updateVideo( @Request() request: any, @Path() courseId: number, @Path() chapterId: number, @Path() lessonId: number, @UploadedFile() video: 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'); } const videoInfo: UploadedFileInfo = { originalname: video.originalname, mimetype: video.mimetype, size: video.size, buffer: video.buffer, }; return await chaptersLessonService.updateVideo({ token, course_id: courseId, lesson_id: lessonId, video: videoInfo, }); } /** * อัพโหลดไฟล์แนบทีละไฟล์ * Upload a single attachment to lesson * * @param courseId Course ID * @param chapterId Chapter ID * @param lessonId Lesson ID * @param attachment ไฟล์แนบ (required) */ @Post('{lessonId}/attachments') @Security('jwt', ['instructor']) @SuccessResponse('200', 'Attachment uploaded successfully') @Response('400', 'Validation error') @Response('401', 'Unauthorized') @Response('403', 'Forbidden') @Response('404', 'Lesson not found') public async uploadAttachment( @Request() request: any, @Path() courseId: number, @Path() chapterId: number, @Path() lessonId: number, @UploadedFile() attachment: Express.Multer.File ): Promise { const token = request.headers.authorization?.replace('Bearer ', ''); if (!token) throw new ValidationError('No token provided'); if (!attachment) { throw new ValidationError('Attachment file is required'); } const attachmentInfo: UploadedFileInfo = { originalname: attachment.originalname, mimetype: attachment.mimetype, size: attachment.size, buffer: attachment.buffer, }; return await chaptersLessonService.uploadAttachment({ token, course_id: courseId, lesson_id: lessonId, attachment: attachmentInfo, }); } /** * ลบไฟล์แนบทีละไฟล์ * Delete a single attachment from lesson * * @param courseId Course ID * @param chapterId Chapter ID * @param lessonId Lesson ID * @param attachmentId Attachment ID */ @Delete('{lessonId}/attachments/{attachmentId}') @Security('jwt', ['instructor']) @SuccessResponse('200', 'Attachment deleted successfully') @Response('400', 'Validation error') @Response('401', 'Unauthorized') @Response('403', 'Forbidden') @Response('404', 'Attachment not found') public async deleteAttachment( @Request() request: any, @Path() courseId: number, @Path() chapterId: number, @Path() lessonId: number, @Path() attachmentId: number ): Promise { const token = request.headers.authorization?.replace('Bearer ', ''); if (!token) throw new ValidationError('No token provided'); return await chaptersLessonService.deleteAttachment({ token, course_id: courseId, lesson_id: lessonId, attachment_id: attachmentId, }); } /** * ตั้งค่าวิดีโอ YouTube ให้บทเรียน (แทนที่วิดีโอเดิมถ้ามี) * Set YouTube video for a lesson (replaces existing video if any) * * @param courseId Course ID * @param chapterId Chapter ID * @param lessonId Lesson ID * @param body YouTube video info */ @Post('{lessonId}/youtube-video') @Security('jwt', ['instructor']) @SuccessResponse('200', 'YouTube video set successfully') @Response('400', 'Validation error') @Response('401', 'Unauthorized') @Response('403', 'Forbidden') @Response('404', 'Lesson not found') public async setYouTubeVideo( @Request() request: any, @Path() courseId: number, @Path() chapterId: number, @Path() lessonId: number, @Body() body: SetYouTubeVideoBody ): Promise { const token = request.headers.authorization?.replace('Bearer ', ''); if (!token) throw new ValidationError('No token provided'); if (!body.youtube_video_id) { throw new ValidationError('YouTube video ID is required'); } if (!body.video_title) { throw new ValidationError('Video title is required'); } return await chaptersLessonService.setYouTubeVideo({ token, course_id: courseId, lesson_id: lessonId, youtube_video_id: body.youtube_video_id, video_title: body.video_title, }); } }