feat: introduce Joi validation schemas and integrate them across various controllers for categories, lessons, courses, chapters, announcements, and admin course approvals.
This commit is contained in:
parent
c5aa195b13
commit
b56f604890
14 changed files with 553 additions and 28 deletions
|
|
@ -1,6 +1,7 @@
|
|||
import { Body, Get, Path, Post, Request, Response, Route, Security, SuccessResponse, Tags } from 'tsoa';
|
||||
import { ValidationError } from '../middleware/errorHandler';
|
||||
import { AdminCourseApprovalService } from '../services/AdminCourseApproval.service';
|
||||
import { ApproveCourseValidator, RejectCourseValidator } from '../validators/AdminCourseApproval.validator';
|
||||
import {
|
||||
ListPendingCoursesResponse,
|
||||
GetCourseDetailForAdminResponse,
|
||||
|
|
@ -65,6 +66,13 @@ export class AdminCourseApprovalController {
|
|||
): Promise<ApproveCourseResponse> {
|
||||
const token = request.headers.authorization?.replace('Bearer ', '');
|
||||
if (!token) throw new ValidationError('No token provided');
|
||||
|
||||
// Validate body if provided
|
||||
if (body) {
|
||||
const { error } = ApproveCourseValidator.validate(body);
|
||||
if (error) throw new ValidationError(error.details[0].message);
|
||||
}
|
||||
|
||||
return await AdminCourseApprovalService.approveCourse(token, courseId, body?.comment);
|
||||
}
|
||||
|
||||
|
|
@ -87,6 +95,11 @@ export class AdminCourseApprovalController {
|
|||
): Promise<RejectCourseResponse> {
|
||||
const token = request.headers.authorization?.replace('Bearer ', '');
|
||||
if (!token) throw new ValidationError('No token provided');
|
||||
|
||||
// Validate body
|
||||
const { error } = RejectCourseValidator.validate(body);
|
||||
if (error) throw new ValidationError(error.details[0].message);
|
||||
|
||||
return await AdminCourseApprovalService.rejectCourse(token, courseId, body.comment);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { Get, Body, Post, Route, Tags, SuccessResponse, Response, Delete, Contro
|
|||
import { ValidationError } from '../middleware/errorHandler';
|
||||
import { CategoryService } from '../services/categories.service';
|
||||
import { createCategory, createCategoryResponse, deleteCategoryResponse, updateCategory, updateCategoryResponse, ListCategoriesResponse } from '../types/categories.type';
|
||||
import { CreateCategoryValidator, UpdateCategoryValidator } from '../validators/categories.validator';
|
||||
|
||||
@Route('api/categories')
|
||||
@Tags('Categories')
|
||||
|
|
@ -27,6 +28,11 @@ export class CategoriesAdminController {
|
|||
@Response('401', 'Invalid or expired token')
|
||||
public async createCategory(@Request() request: any, @Body() body: createCategory): Promise<createCategoryResponse> {
|
||||
const token = request.headers.authorization?.replace('Bearer ', '') || '';
|
||||
|
||||
// Validate body
|
||||
const { error } = CreateCategoryValidator.validate(body);
|
||||
if (error) throw new ValidationError(error.details[0].message);
|
||||
|
||||
return await this.categoryService.createCategory(token, body);
|
||||
}
|
||||
|
||||
|
|
@ -36,6 +42,11 @@ export class CategoriesAdminController {
|
|||
@Response('401', 'Invalid or expired token')
|
||||
public async updateCategory(@Request() request: any, @Body() body: updateCategory): Promise<updateCategoryResponse> {
|
||||
const token = request.headers.authorization?.replace('Bearer ', '') || '';
|
||||
|
||||
// Validate body
|
||||
const { error } = UpdateCategoryValidator.validate(body);
|
||||
if (error) throw new ValidationError(error.details[0].message);
|
||||
|
||||
return await this.categoryService.updateCategory(token, body.id, body);
|
||||
}
|
||||
|
||||
|
|
@ -45,6 +56,6 @@ export class CategoriesAdminController {
|
|||
@Response('401', 'Invalid or expired token')
|
||||
public async deleteCategory(@Request() request: any, @Path() id: number): Promise<deleteCategoryResponse> {
|
||||
const token = request.headers.authorization?.replace('Bearer ', '') || '';
|
||||
return await this.categoryService.deleteCategory(token,id);
|
||||
return await this.categoryService.deleteCategory(token, id);
|
||||
}
|
||||
}
|
||||
|
|
@ -27,6 +27,18 @@ import {
|
|||
UpdateQuizResponse,
|
||||
UpdateQuizBody,
|
||||
} from '../types/ChaptersLesson.typs';
|
||||
import {
|
||||
CreateChapterValidator,
|
||||
UpdateChapterValidator,
|
||||
ReorderChapterValidator,
|
||||
CreateLessonValidator,
|
||||
UpdateLessonValidator,
|
||||
ReorderLessonsValidator,
|
||||
AddQuestionValidator,
|
||||
UpdateQuestionValidator,
|
||||
ReorderQuestionValidator,
|
||||
UpdateQuizValidator
|
||||
} from '../validators/ChaptersLesson.validator';
|
||||
|
||||
const chaptersLessonService = new ChaptersLessonService();
|
||||
|
||||
|
|
@ -55,6 +67,10 @@ export class ChaptersLessonInstructorController {
|
|||
): Promise<CreateChapterResponse> {
|
||||
const token = request.headers.authorization?.replace('Bearer ', '');
|
||||
if (!token) throw new ValidationError('No token provided');
|
||||
|
||||
const { error } = CreateChapterValidator.validate(body);
|
||||
if (error) throw new ValidationError(error.details[0].message);
|
||||
|
||||
return await chaptersLessonService.createChapter({
|
||||
token,
|
||||
course_id: courseId,
|
||||
|
|
@ -82,6 +98,10 @@ export class ChaptersLessonInstructorController {
|
|||
): Promise<UpdateChapterResponse> {
|
||||
const token = request.headers.authorization?.replace('Bearer ', '');
|
||||
if (!token) throw new ValidationError('No token provided');
|
||||
|
||||
const { error } = UpdateChapterValidator.validate(body);
|
||||
if (error) throw new ValidationError(error.details[0].message);
|
||||
|
||||
return await chaptersLessonService.updateChapter({
|
||||
token,
|
||||
course_id: courseId,
|
||||
|
|
@ -125,6 +145,10 @@ export class ChaptersLessonInstructorController {
|
|||
): Promise<ReorderChapterResponse> {
|
||||
const token = request.headers.authorization?.replace('Bearer ', '');
|
||||
if (!token) throw new ValidationError('No token provided');
|
||||
|
||||
const { error } = ReorderChapterValidator.validate(body);
|
||||
if (error) throw new ValidationError(error.details[0].message);
|
||||
|
||||
return await chaptersLessonService.reorderChapter({
|
||||
token,
|
||||
course_id: courseId,
|
||||
|
|
@ -170,6 +194,10 @@ export class ChaptersLessonInstructorController {
|
|||
): Promise<CreateLessonResponse> {
|
||||
const token = request.headers.authorization?.replace('Bearer ', '');
|
||||
if (!token) throw new ValidationError('No token provided');
|
||||
|
||||
const { error } = CreateLessonValidator.validate(body);
|
||||
if (error) throw new ValidationError(error.details[0].message);
|
||||
|
||||
return await chaptersLessonService.createLesson({
|
||||
token,
|
||||
course_id: courseId,
|
||||
|
|
@ -197,6 +225,10 @@ export class ChaptersLessonInstructorController {
|
|||
): Promise<UpdateLessonResponse> {
|
||||
const token = request.headers.authorization?.replace('Bearer ', '');
|
||||
if (!token) throw new ValidationError('No token provided');
|
||||
|
||||
const { error } = UpdateLessonValidator.validate(body);
|
||||
if (error) throw new ValidationError(error.details[0].message);
|
||||
|
||||
return await chaptersLessonService.updateLesson({
|
||||
token,
|
||||
course_id: courseId,
|
||||
|
|
@ -246,6 +278,10 @@ export class ChaptersLessonInstructorController {
|
|||
): Promise<ReorderLessonsResponse> {
|
||||
const token = request.headers.authorization?.replace('Bearer ', '');
|
||||
if (!token) throw new ValidationError('No token provided');
|
||||
|
||||
const { error } = ReorderLessonsValidator.validate(body);
|
||||
if (error) throw new ValidationError(error.details[0].message);
|
||||
|
||||
return await chaptersLessonService.reorderLessons({
|
||||
token,
|
||||
course_id: courseId,
|
||||
|
|
@ -275,6 +311,10 @@ export class ChaptersLessonInstructorController {
|
|||
): Promise<AddQuestionResponse> {
|
||||
const token = request.headers.authorization?.replace('Bearer ', '');
|
||||
if (!token) throw new ValidationError('No token provided');
|
||||
|
||||
const { error } = AddQuestionValidator.validate(body);
|
||||
if (error) throw new ValidationError(error.details[0].message);
|
||||
|
||||
return await chaptersLessonService.addQuestion({
|
||||
token,
|
||||
course_id: courseId,
|
||||
|
|
@ -300,6 +340,10 @@ export class ChaptersLessonInstructorController {
|
|||
): Promise<UpdateQuestionResponse> {
|
||||
const token = request.headers.authorization?.replace('Bearer ', '');
|
||||
if (!token) throw new ValidationError('No token provided');
|
||||
|
||||
const { error } = UpdateQuestionValidator.validate(body);
|
||||
if (error) throw new ValidationError(error.details[0].message);
|
||||
|
||||
return await chaptersLessonService.updateQuestion({
|
||||
token,
|
||||
course_id: courseId,
|
||||
|
|
@ -322,6 +366,10 @@ export class ChaptersLessonInstructorController {
|
|||
): Promise<ReorderQuestionResponse> {
|
||||
const token = request.headers.authorization?.replace('Bearer ', '');
|
||||
if (!token) throw new ValidationError('No token provided');
|
||||
|
||||
const { error } = ReorderQuestionValidator.validate(body);
|
||||
if (error) throw new ValidationError(error.details[0].message);
|
||||
|
||||
return await chaptersLessonService.reorderQuestion({
|
||||
token,
|
||||
course_id: courseId,
|
||||
|
|
@ -371,6 +419,10 @@ export class ChaptersLessonInstructorController {
|
|||
): Promise<UpdateQuizResponse> {
|
||||
const token = request.headers.authorization?.replace('Bearer ', '');
|
||||
if (!token) throw new ValidationError('No token provided');
|
||||
|
||||
const { error } = UpdateQuizValidator.validate(body);
|
||||
if (error) throw new ValidationError(error.details[0].message);
|
||||
|
||||
return await chaptersLessonService.updateQuiz({
|
||||
token,
|
||||
course_id: courseId,
|
||||
|
|
|
|||
|
|
@ -2,30 +2,28 @@ import { Get, Body, Post, Route, Tags, SuccessResponse, Response, Security, Put,
|
|||
import { ValidationError } from '../middleware/errorHandler';
|
||||
import { CoursesInstructorService } from '../services/CoursesInstructor.service';
|
||||
import {
|
||||
createCourses,
|
||||
createCourseResponse,
|
||||
GetMyCourseResponse,
|
||||
ListMyCoursesInput,
|
||||
ListMyCourseResponse,
|
||||
addinstructorCourseResponse,
|
||||
removeinstructorCourseResponse,
|
||||
setprimaryCourseInstructorResponse,
|
||||
GetMyCourseResponse,
|
||||
UpdateMyCourse,
|
||||
UpdateMyCourseResponse,
|
||||
DeleteMyCourseResponse,
|
||||
submitCourseResponse,
|
||||
listinstructorCourseResponse,
|
||||
GetCourseApprovalsResponse,
|
||||
SearchInstructorResponse,
|
||||
addinstructorCourseResponse,
|
||||
removeinstructorCourseResponse,
|
||||
setprimaryCourseInstructorResponse,
|
||||
GetEnrolledStudentsResponse,
|
||||
GetEnrolledStudentDetailResponse,
|
||||
GetQuizScoresResponse,
|
||||
GetQuizAttemptDetailResponse,
|
||||
GetEnrolledStudentDetailResponse,
|
||||
GetCourseApprovalsResponse,
|
||||
SearchInstructorResponse,
|
||||
GetCourseApprovalHistoryResponse,
|
||||
setCourseDraftResponse,
|
||||
CloneCourseResponse,
|
||||
} from '../types/CoursesInstructor.types';
|
||||
import { CreateCourseValidator } from "../validators/CoursesInstructor.validator";
|
||||
import { CreateCourseValidator, UpdateCourseValidator, CloneCourseValidator } from "../validators/CoursesInstructor.validator";
|
||||
|
||||
import jwt from 'jsonwebtoken';
|
||||
import { config } from '../config';
|
||||
|
|
@ -104,9 +102,11 @@ export class CoursesInstructorController {
|
|||
@Response('404', 'Course not found')
|
||||
public async updateCourse(@Request() request: any, @Path() courseId: number, @Body() body: UpdateMyCourse): Promise<UpdateMyCourseResponse> {
|
||||
const token = request.headers.authorization?.replace('Bearer ', '');
|
||||
if (!token) {
|
||||
throw new ValidationError('No token provided');
|
||||
}
|
||||
if (!token) throw new ValidationError('No token provided');
|
||||
|
||||
const { error } = UpdateCourseValidator.validate(body.data);
|
||||
if (error) throw new ValidationError(error.details[0].message);
|
||||
|
||||
return await CoursesInstructorService.updateCourse(token, courseId, body.data);
|
||||
}
|
||||
|
||||
|
|
@ -199,13 +199,16 @@ export class CoursesInstructorController {
|
|||
const token = request.headers.authorization?.replace('Bearer ', '');
|
||||
if (!token) throw new ValidationError('No token provided');
|
||||
|
||||
return await CoursesInstructorService.cloneCourse({
|
||||
const { error } = CloneCourseValidator.validate(body);
|
||||
if (error) throw new ValidationError(error.details[0].message);
|
||||
|
||||
const result = await CoursesInstructorService.cloneCourse({
|
||||
token,
|
||||
course_id: courseId,
|
||||
title: body.title
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* ส่งคอร์สเพื่อขออนุมัติจากแอดมิน
|
||||
* Submit course for admin review and approval
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import {
|
|||
GetQuizAttemptsResponse,
|
||||
} from '../types/CoursesStudent.types';
|
||||
import { EnrollmentStatus } from '@prisma/client';
|
||||
import { SaveVideoProgressValidator, SubmitQuizValidator } from '../validators/CoursesStudent.validator';
|
||||
|
||||
@Route('api/students')
|
||||
@Tags('CoursesStudent')
|
||||
|
|
@ -149,9 +150,11 @@ export class CoursesStudentController {
|
|||
@Body() body: SaveVideoProgressBody
|
||||
): Promise<SaveVideoProgressResponse> {
|
||||
const token = request.headers.authorization?.replace('Bearer ', '');
|
||||
if (!token) {
|
||||
throw new ValidationError('No token provided');
|
||||
}
|
||||
if (!token) throw new ValidationError('No token provided');
|
||||
|
||||
const { error } = SaveVideoProgressValidator.validate(body);
|
||||
if (error) throw new ValidationError(error.details[0].message);
|
||||
|
||||
return await this.service.saveVideoProgress({
|
||||
token,
|
||||
lesson_id: lessonId,
|
||||
|
|
@ -225,9 +228,11 @@ export class CoursesStudentController {
|
|||
@Body() body: SubmitQuizBody
|
||||
): Promise<SubmitQuizResponse> {
|
||||
const token = request.headers.authorization?.replace('Bearer ', '');
|
||||
if (!token) {
|
||||
throw new ValidationError('No token provided');
|
||||
}
|
||||
if (!token) throw new ValidationError('No token provided');
|
||||
|
||||
const { error } = SubmitQuizValidator.validate(body);
|
||||
if (error) throw new ValidationError(error.details[0].message);
|
||||
|
||||
return await this.service.submitQuiz({
|
||||
token,
|
||||
course_id: courseId,
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import {
|
|||
YouTubeVideoResponse,
|
||||
SetYouTubeVideoBody,
|
||||
} from '../types/ChaptersLesson.typs';
|
||||
import { SetYouTubeVideoValidator } from '../validators/Lessons.validator';
|
||||
|
||||
const chaptersLessonService = new ChaptersLessonService();
|
||||
|
||||
|
|
@ -213,12 +214,8 @@ export class LessonsController {
|
|||
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');
|
||||
}
|
||||
const { error } = SetYouTubeVideoValidator.validate(body);
|
||||
if (error) throw new ValidationError(error.details[0].message);
|
||||
|
||||
return await chaptersLessonService.setYouTubeVideo({
|
||||
token,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { Body, Delete, Get, Path, Post, Put, Query, Request, Response, Route, Security, SuccessResponse, Tags, UploadedFile, UploadedFiles, FormField } from 'tsoa';
|
||||
import { ValidationError } from '../middleware/errorHandler';
|
||||
import { AnnouncementsService } from '../services/announcements.service';
|
||||
import { CreateAnnouncementValidator, UpdateAnnouncementValidator } from '../validators/announcements.validator';
|
||||
import {
|
||||
ListAnnouncementResponse,
|
||||
CreateAnnouncementResponse,
|
||||
|
|
@ -68,6 +69,10 @@ export class AnnouncementsController {
|
|||
// Parse JSON data field
|
||||
const parsed = JSON.parse(data) as CreateAnnouncementBody;
|
||||
|
||||
// Validate parsed data
|
||||
const { error } = CreateAnnouncementValidator.validate(parsed);
|
||||
if (error) throw new ValidationError(error.details[0].message);
|
||||
|
||||
return await announcementsService.createAnnouncement({
|
||||
token,
|
||||
course_id: courseId,
|
||||
|
|
@ -100,6 +105,11 @@ export class AnnouncementsController {
|
|||
): Promise<UpdateAnnouncementResponse> {
|
||||
const token = request.headers.authorization?.replace('Bearer ', '');
|
||||
if (!token) throw new ValidationError('No token provided');
|
||||
|
||||
// Validate body
|
||||
const { error } = UpdateAnnouncementValidator.validate(body);
|
||||
if (error) throw new ValidationError(error.details[0].message);
|
||||
|
||||
return await announcementsService.updateAnnouncement({
|
||||
token,
|
||||
course_id: courseId,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue