import { Get, Body, Post, Route, Tags, SuccessResponse, Response, Security, Put, Path, Delete, Request, Example, FormField, UploadedFile } from 'tsoa'; import { ValidationError } from '../middleware/errorHandler'; import { CoursesInstructorService } from '../services/CoursesInstructor.service'; import { createCourses, createCourseResponse, GetMyCourseResponse, ListMyCourseResponse, addinstructorCourse, addinstructorCourseResponse, removeinstructorCourse, removeinstructorCourseResponse, setprimaryCourseInstructor, setprimaryCourseInstructorResponse, UpdateMyCourse, UpdateMyCourseResponse, DeleteMyCourseResponse, submitCourseResponse, listinstructorCourseResponse, GetCourseApprovalsResponse } from '../types/CoursesInstructor.types'; import { CreateCourseValidator } from "../validators/CoursesInstructor.validator"; import jwt from 'jsonwebtoken'; import { config } from '../config'; @Route('api/instructors/courses') @Tags('CoursesInstructor') export class CoursesInstructorController { /** * ดึงรายการคอร์สทั้งหมดของผู้สอน * Get all courses where the authenticated user is an instructor */ @Get('') @Security('jwt', ['instructor']) @SuccessResponse('200', 'Courses retrieved successfully') @Response('401', 'Invalid or expired token') @Response('404', 'Courses not found') public async listMyCourses(@Request() request: any): Promise { const token = request.headers.authorization?.replace('Bearer ', ''); if (!token) { throw new ValidationError('No token provided'); } return await CoursesInstructorService.listMyCourses(token); } /** * ดึงข้อมูลคอร์สเฉพาะของผู้สอน (พร้อมบทเรียนและเนื้อหา) * Get detailed course information including chapters, lessons, attachments, and quizzes * @param courseId - รหัสคอร์ส / Course ID */ @Get('{courseId}') @Security('jwt', ['instructor']) @SuccessResponse('200', 'Course retrieved successfully') @Response('401', 'Invalid or expired token') @Response('404', 'Course not found') public async getMyCourse(@Request() request: any, @Path() courseId: number): Promise { const token = request.headers.authorization?.replace('Bearer ', ''); if (!token) { throw new ValidationError('No token provided'); } return await CoursesInstructorService.getmyCourse({ token, course_id: courseId }); } /** * แก้ไขข้อมูลคอร์ส * Update course information (only for course instructors) * @param courseId - รหัสคอร์ส / Course ID */ @Put('{courseId}') @Security('jwt', ['instructor']) @SuccessResponse('200', 'Course updated successfully') @Response('401', 'Invalid or expired token') @Response('404', 'Course not found') public async updateCourse(@Request() request: any, @Path() courseId: number, @Body() body: UpdateMyCourse): Promise { const token = request.headers.authorization?.replace('Bearer ', ''); if (!token) { throw new ValidationError('No token provided'); } return await CoursesInstructorService.updateCourse(token, courseId, body.data); } /** * สร้างคอร์สใหม่ (พร้อมอัปโหลดรูป thumbnail) * Create a new course with optional thumbnail upload (status will be DRAFT by default) * @param data - JSON string containing course data * @param thumbnail - Optional thumbnail image file */ @Post('') @Security('jwt', ['instructor']) @SuccessResponse('201', 'Course created successfully') @Response('400', 'Validation error') @Response('500', 'Internal server error') public async createCourse( @Request() request: any, @FormField() data: string, @UploadedFile() thumbnail?: Express.Multer.File ): Promise { const token = request.headers.authorization?.replace('Bearer ', ''); if (!token) throw new ValidationError('No token provided'); const decoded = jwt.verify(token, config.jwt.secret) as { id: number }; const parsed = JSON.parse(data); const { error, value } = CreateCourseValidator.validate(parsed); if (error) throw new ValidationError(error.details[0].message); // Validate thumbnail file type if provided if (thumbnail && !thumbnail.mimetype?.startsWith('image/')) throw new ValidationError('Only image files are allowed for thumbnail'); return await CoursesInstructorService.createCourse(value, decoded.id, thumbnail); } /** * อัปโหลดรูป thumbnail ของคอร์ส * Upload course thumbnail image * @param courseId - รหัสคอร์ส / Course ID * @param file - ไฟล์รูปภาพ / Image file */ @Post('{courseId}/thumbnail') @Security('jwt', ['instructor']) @SuccessResponse('200', 'Thumbnail uploaded successfully') @Response('401', 'Invalid or expired token') @Response('400', 'Validation error') public async uploadThumbnail( @Request() request: any, @Path() courseId: number, @UploadedFile() file: Express.Multer.File ): Promise<{ code: number; message: string; data: { course_id: number; thumbnail_url: string } }> { const token = request.headers.authorization?.replace('Bearer ', ''); if (!token) throw new ValidationError('No token provided'); if (!file.mimetype?.startsWith('image/')) throw new ValidationError('Only image files are allowed'); return await CoursesInstructorService.uploadThumbnail(token, courseId, file); } /** * ลบคอร์ส (เฉพาะผู้สอนหลักเท่านั้น) * Delete a course (only primary instructor can delete) * @param courseId - รหัสคอร์ส / Course ID */ @Delete('{courseId}') @Security('jwt', ['instructor']) @SuccessResponse('200', 'Course deleted successfully') @Response('401', 'Invalid or expired token') @Response('404', 'Course not found') public async deleteCourse(@Request() request: any, @Path() courseId: number): Promise { const token = request.headers.authorization?.replace('Bearer ', ''); if (!token) throw new ValidationError('No token provided') return await CoursesInstructorService.deleteCourse(token, courseId); } /** * ส่งคอร์สเพื่อขออนุมัติจากแอดมิน * Submit course for admin review and approval * @param courseId - รหัสคอร์ส / Course ID */ @Post('send-review/{courseId}') @Security('jwt', ['instructor']) @SuccessResponse('200', 'Course submitted successfully') @Response('401', 'Invalid or expired token') @Response('404', 'Course not found') public async submitCourse(@Request() request: any, @Path() courseId: number): Promise { const token = request.headers.authorization?.replace('Bearer ', ''); if (!token) throw new ValidationError('No token provided') return await CoursesInstructorService.sendCourseForReview({ token, course_id: courseId }); } /** * ดึงประวัติการส่งอนุมัติคอร์สทั้งหมด * Get all course approval history * @param courseId - รหัสคอร์ส / Course ID */ @Get('{courseId}/approvals') @Security('jwt', ['instructor']) @SuccessResponse('200', 'Course approvals retrieved successfully') @Response('401', 'Invalid or expired token') @Response('403', 'You are not an instructor of this course') @Response('404', 'Course not found') public async getCourseApprovals(@Request() request: any, @Path() courseId: number): Promise { const token = request.headers.authorization?.replace('Bearer ', ''); if (!token) throw new ValidationError('No token provided') return await CoursesInstructorService.getCourseApprovals(token, courseId); } /** * ดึงรายชื่อผู้สอนทั้งหมดในคอร์ส * Get list of all instructors in a specific course * @param courseId - รหัสคอร์ส / Course ID */ @Get('listinstructor/{courseId}') @Security('jwt', ['instructor']) @SuccessResponse('200', 'Instructors retrieved successfully') @Response('401', 'Invalid or expired token') @Response('404', 'Instructors not found') public async listInstructorCourses(@Request() request: any, @Path() courseId: number): Promise { const token = request.headers.authorization?.replace('Bearer ', ''); if (!token) throw new ValidationError('No token provided') return await CoursesInstructorService.listInstructorsOfCourse({ token, course_id: courseId }); } /** * เพิ่มผู้สอนเข้าในคอร์ส * Add a new instructor to the course * @param courseId - รหัสคอร์ส / Course ID * @param userId - รหัสผู้ใช้ที่ต้องการเพิ่มเป็นผู้สอน / User ID to add as instructor */ @Post('add-instructor/{courseId}/{userId}') @Security('jwt', ['instructor']) @SuccessResponse('200', 'Instructor added successfully') @Response('401', 'Invalid or expired token') @Response('404', 'Instructor not found') public async addInstructor(@Request() request: any, @Path() courseId: number, @Path() userId: number): Promise { const token = request.headers.authorization?.replace('Bearer ', ''); if (!token) throw new ValidationError('No token provided') return await CoursesInstructorService.addInstructorToCourse({ token, course_id: courseId, user_id: userId }); } /** * ลบผู้สอนออกจากคอร์ส * Remove an instructor from the course * @param courseId - รหัสคอร์ส / Course ID * @param userId - รหัสผู้ใช้ที่ต้องการลบออกจากผู้สอน / User ID to remove from instructors */ @Delete('remove-instructor/{courseId}/{userId}') @Security('jwt', ['instructor']) @SuccessResponse('200', 'Instructor removed successfully') @Response('401', 'Invalid or expired token') @Response('404', 'Instructor not found') public async removeInstructor(@Request() request: any, @Path() courseId: number, @Path() userId: number): Promise { const token = request.headers.authorization?.replace('Bearer ', ''); if (!token) throw new ValidationError('No token provided') return await CoursesInstructorService.removeInstructorFromCourse({ token, course_id: courseId, user_id: userId }); } /** * กำหนดผู้สอนหลักของคอร์ส * Set a user as the primary instructor of the course * @param courseId - รหัสคอร์ส / Course ID * @param userId - รหัสผู้ใช้ที่ต้องการตั้งเป็นผู้สอนหลัก / User ID to set as primary instructor */ @Put('set-primary-instructor/{courseId}/{userId}') @Security('jwt', ['instructor']) @SuccessResponse('200', 'Primary instructor set successfully') @Response('401', 'Invalid or expired token') @Response('404', 'Primary instructor not found') public async setPrimaryInstructor(@Request() request: any, @Path() courseId: number, @Path() userId: number): Promise { const token = request.headers.authorization?.replace('Bearer ', ''); if (!token) throw new ValidationError('No token provided') return await CoursesInstructorService.setPrimaryInstructor({ token, course_id: courseId, user_id: userId }); } }