import { Injectable, ConflictException, UnauthorizedException, NotFoundException, } from '@nestjs/common'; import { JwtService } from '@nestjs/jwt'; import * as bcrypt from 'bcrypt'; import { PrismaService } from '../../prisma/prisma.service'; import { RegisterDto, LoginDto } from './dto'; import { UserRole } from '../../generated/prisma/client.js'; @Injectable() export class AuthService { constructor( private prisma: PrismaService, private jwtService: JwtService, ) {} async register(dto: RegisterDto) { // Check if username or email already exists const existingUser = await this.prisma.user.findFirst({ where: { OR: [ { username: dto.username.toLowerCase() }, { email: dto.email.toLowerCase() }, ], }, }); if (existingUser) { if (existingUser.username.toLowerCase() === dto.username.toLowerCase()) { throw new ConflictException('Username already taken'); } throw new ConflictException('Email already registered'); } // Hash password const saltRounds = 10; const passwordHash = await bcrypt.hash(dto.password, saltRounds); // Create user const user = await this.prisma.user.create({ data: { username: dto.username.toLowerCase(), email: dto.email.toLowerCase(), passwordHash, role: UserRole.PLAYER, }, select: { id: true, username: true, email: true, role: true, avatarUrl: true, createdAt: true, }, }); // Generate JWT const token = this.generateToken(user.id, user.username, user.role); return { user, token, }; } async login(dto: LoginDto) { // Find user by username or email const user = await this.prisma.user.findFirst({ where: { OR: [ { username: dto.identifier.toLowerCase() }, { email: dto.identifier.toLowerCase() }, ], }, }); if (!user) { throw new UnauthorizedException('Invalid credentials'); } // Verify password const isPasswordValid = await bcrypt.compare(dto.password, user.passwordHash); if (!isPasswordValid) { throw new UnauthorizedException('Invalid credentials'); } // Generate JWT const token = this.generateToken(user.id, user.username, user.role); return { user: { id: user.id, username: user.username, email: user.email, role: user.role, avatarUrl: user.avatarUrl, }, token, }; } async getProfile(userId: string) { const user = await this.prisma.user.findUnique({ where: { id: userId }, select: { id: true, username: true, email: true, role: true, avatarUrl: true, createdAt: true, updatedAt: true, }, }); if (!user) { throw new NotFoundException('User not found'); } return user; } async updateProfile(userId: string, data: { avatarUrl?: string }) { const user = await this.prisma.user.update({ where: { id: userId }, data, select: { id: true, username: true, email: true, role: true, avatarUrl: true, }, }); return user; } async searchUsers(query: string, currentUserId: string) { const users = await this.prisma.user.findMany({ where: { AND: [ { id: { not: currentUserId } }, { OR: [ { username: { contains: query, mode: 'insensitive' } }, { email: { contains: query, mode: 'insensitive' } }, ], }, ], }, select: { id: true, username: true, email: true, avatarUrl: true, }, take: 10, }); return users; } private generateToken(userId: string, username: string, role: UserRole): string { const payload = { sub: userId, username, role, }; return this.jwtService.sign(payload); } }