import { Injectable, UnauthorizedException, HttpException, HttpStatus } from '@nestjs/common'; import { JwtService } from '@nestjs/jwt'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import * as bcrypt from 'bcrypt'; import { Usuario } from '../usuario/entities/usuario.entity.js'; import { LoginDto } from './dto/login.dto.js'; interface LoginAttempt { count: number; blockedUntil: Date | null; } @Injectable() export class AuthService { private loginAttempts = new Map(); private readonly MAX_ATTEMPTS = 5; private readonly BLOCK_DURATION_MS = 60 * 60 * 1000; // 1 hora constructor( @InjectRepository(Usuario) private usuarioRepo: Repository, private jwtService: JwtService, ) {} async login(dto: LoginDto) { const attempts = this.loginAttempts.get(dto.usuario); if (attempts?.blockedUntil && attempts.blockedUntil > new Date()) { const remainingMs = attempts.blockedUntil.getTime() - Date.now(); const remainingMin = Math.ceil(remainingMs / 60000); throw new HttpException( `Cuenta bloqueada. Intenta de nuevo en ${remainingMin} minuto(s).`, HttpStatus.TOO_MANY_REQUESTS, ); } const usuario = await this.usuarioRepo.findOne({ where: { usuario: dto.usuario }, }); if (!usuario) { this.trackFailedAttempt(dto.usuario); throw new UnauthorizedException('Credenciales inválidas'); } const passwordValid = await bcrypt.compare(dto.password, usuario.password); if (!passwordValid) { this.trackFailedAttempt(dto.usuario); throw new UnauthorizedException('Credenciales inválidas'); } this.loginAttempts.delete(dto.usuario); const payload = { sub: usuario.idUsuario, usuario: usuario.usuario, rol: usuario.rol, }; return { token: this.jwtService.sign(payload), usuario: { idUsuario: usuario.idUsuario, nombre: usuario.nombre, usuario: usuario.usuario, rol: usuario.rol, }, }; } private trackFailedAttempt(username: string) { const current = this.loginAttempts.get(username); if (!current) { this.loginAttempts.set(username, { count: 1, blockedUntil: null, }); return; } const newCount = current.count + 1; if (newCount >= this.MAX_ATTEMPTS) { this.loginAttempts.set(username, { count: newCount, blockedUntil: new Date(Date.now() + this.BLOCK_DURATION_MS), }); } else { this.loginAttempts.set(username, { count: newCount, blockedUntil: null, }); } } }