From 48e8f56e221ff6e14dc54469d523144d6c61d734 Mon Sep 17 00:00:00 2001 From: JakkrapartXD Date: Tue, 3 Feb 2026 10:38:59 +0700 Subject: [PATCH] feat: update instructor search to exclude self and existing course instructors, add email_verified_at to user responses Update searchInstructors endpoint to accept courseId parameter and filter out the requesting instructor and instructors already assigned to the course. Add email_verified_at field to UserResponse type and include it in auth and user service responses. --- .../controllers/CoursesInstructorController.ts | 15 ++++++++++----- .../src/services/CoursesInstructor.service.ts | 16 +++++++++++++--- Backend/src/services/auth.service.ts | 2 ++ Backend/src/services/user.service.ts | 2 ++ Backend/src/types/CoursesInstructor.types.ts | 1 + Backend/src/types/user.types.ts | 1 + 6 files changed, 29 insertions(+), 8 deletions(-) diff --git a/Backend/src/controllers/CoursesInstructorController.ts b/Backend/src/controllers/CoursesInstructorController.ts index 034971aa..2b0a7b37 100644 --- a/Backend/src/controllers/CoursesInstructorController.ts +++ b/Backend/src/controllers/CoursesInstructorController.ts @@ -48,18 +48,23 @@ export class CoursesInstructorController { } /** - * ค้นหาผู้สอนทั้งหมดในระบบ - * Search all instructors in database + * ค้นหาผู้สอนทั้งหมดในระบบ (ไม่รวมตัวเองและคนที่อยู่ในคอร์สแล้ว) + * Search all instructors in database (excluding self and existing course instructors) + * @param courseId - รหัสคอร์ส / Course ID * @param query - คำค้นหา (email หรือ username) / Search query (email or username) */ - @Get('search-instructors') + @Get('{courseId}/search-instructors') @Security('jwt', ['instructor']) @SuccessResponse('200', 'Instructors found') @Response('401', 'Invalid or expired token') - public async searchInstructors(@Request() request: any, @Query() query: string): Promise { + public async searchInstructors( + @Request() request: any, + @Path() courseId: number, + @Query() query: string + ): Promise { const token = request.headers.authorization?.replace('Bearer ', ''); if (!token) throw new ValidationError('No token provided'); - return await CoursesInstructorService.searchInstructors({ token, query }); + return await CoursesInstructorService.searchInstructors({ token, query, course_id: courseId }); } /** diff --git a/Backend/src/services/CoursesInstructor.service.ts b/Backend/src/services/CoursesInstructor.service.ts index 48f92ade..62ea5463 100644 --- a/Backend/src/services/CoursesInstructor.service.ts +++ b/Backend/src/services/CoursesInstructor.service.ts @@ -356,16 +356,26 @@ export class CoursesInstructorService { static async searchInstructors(input: SearchInstructorInput): Promise { try { - jwt.verify(input.token, config.jwt.secret) as { id: number }; + const decoded = jwt.verify(input.token, config.jwt.secret) as { id: number }; - // Search all instructors by email or username + // Get existing instructors in the course + const existingInstructors = await prisma.courseInstructor.findMany({ + where: { course_id: input.course_id }, + select: { user_id: true }, + }); + const existingInstructorIds = existingInstructors.map(i => i.user_id); + + // Search all instructors by email or username, excluding self and existing course instructors const users = await prisma.user.findMany({ where: { OR: [ { email: { contains: input.query, mode: 'insensitive' } }, { username: { contains: input.query, mode: 'insensitive' } }, ], - role: { code: 'INSTRUCTOR' } + role: { code: 'INSTRUCTOR' }, + id: { + notIn: [decoded.id, ...existingInstructorIds], + }, }, include: { profile: true diff --git a/Backend/src/services/auth.service.ts b/Backend/src/services/auth.service.ts index 1860b601..8973497d 100644 --- a/Backend/src/services/auth.service.ts +++ b/Backend/src/services/auth.service.ts @@ -398,6 +398,7 @@ export class AuthService { id: user.id, username: user.username, email: user.email, + email_verified_at: user.email_verified_at, updated_at: user.updated_at, created_at: user.created_at, role: { @@ -423,6 +424,7 @@ export class AuthService { id: user.id, username: user.username, email: user.email, + email_verified_at: user.email_verified_at, updated_at: user.updated_at, created_at: user.created_at, role: { diff --git a/Backend/src/services/user.service.ts b/Backend/src/services/user.service.ts index 6141b471..d4294e29 100644 --- a/Backend/src/services/user.service.ts +++ b/Backend/src/services/user.service.ts @@ -48,6 +48,7 @@ export class UserService { id: user.id, username: user.username, email: user.email, + email_verified_at: user.email_verified_at, updated_at: user.updated_at, created_at: user.created_at, role: { @@ -285,6 +286,7 @@ export class UserService { id: user.id, username: user.username, email: user.email, + email_verified_at: user.email_verified_at, updated_at: user.updated_at, created_at: user.created_at, role: { diff --git a/Backend/src/types/CoursesInstructor.types.ts b/Backend/src/types/CoursesInstructor.types.ts index 8fbd3446..74de0794 100644 --- a/Backend/src/types/CoursesInstructor.types.ts +++ b/Backend/src/types/CoursesInstructor.types.ts @@ -97,6 +97,7 @@ export interface addinstructorCourse { export interface SearchInstructorInput { token: string; query: string; + course_id: number; } export interface SearchInstructorResult { diff --git a/Backend/src/types/user.types.ts b/Backend/src/types/user.types.ts index 39c99765..413cb3f2 100644 --- a/Backend/src/types/user.types.ts +++ b/Backend/src/types/user.types.ts @@ -5,6 +5,7 @@ export interface UserResponse { id: number; username: string; email: string; + email_verified_at: Date | null; updated_at: Date | null; created_at: Date | null; role: {