app-paqueteria-nest/src/modules/auth/auth.service.ts
2026-09-18 20:22:24 -04:00

97 lines
2.6 KiB
TypeScript

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<string, LoginAttempt>();
private readonly MAX_ATTEMPTS = 5;
private readonly BLOCK_DURATION_MS = 60 * 60 * 1000; // 1 hora
constructor(
@InjectRepository(Usuario)
private usuarioRepo: Repository<Usuario>,
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,
});
}
}
}