import { Injectable, CanActivate, ExecutionContext, ForbiddenException } from '@nestjs/common'; import { PrismaService } from './prisma.service.js'; @Injectable() export class TourRoleGuard implements CanActivate { constructor(private prisma: PrismaService) {} async canActivate(context: ExecutionContext): Promise { const request = context.switchToHttp().getRequest(); const user = request.user; // Giả sử đã qua AuthGuard (Passport/JWT) // UUID không cần parseInt const tourId = request.params.id || request.params.tourId; const path = request.url; if (!user || !tourId) { throw new ForbiddenException("Thông tin xác thực hoặc mã Tour không hợp lệ."); } /** * TỐI ƯU: Chỉ truy vấn Database 1 lần duy nhất để lấy thông tin thành viên. * Chúng ta lưu kết quả vào request object để các interceptor hoặc controller * sau này có thể dùng lại mà không cần query lại. */ const participation = await this.prisma.tourParticipant.findUnique({ where: { tourId_userId: { tourId: tourId, userId: user.id, }, }, }); if (!participation) { throw new ForbiddenException("Bạn không phải là thành viên của tour này."); } // Gắn thông tin vào request để sử dụng ở tầng Controller request.tourParticipation = participation; const role = participation.role; const isPlanPath = path.includes('/plans'); const isExpensePath = path.includes('/expenses'); // Theo định nghĩa mới: MEMBER_NO_FINANCE và VIEWER_ONLY bị hạn chế if ( (role === 'MEMBER_NO_FINANCE' || role === 'VIEWER_ONLY') && (isPlanPath || isExpensePath) ) { throw new ForbiddenException("Bạn không có quyền xem kế hoạch và chi phí của tour này."); } return true; } }