refactor: atualizações e remoção de módulos não utilizados

This commit is contained in:
joelson brito
2025-11-10 09:39:44 -03:00
parent ed68b7e865
commit b8630adf92
121 changed files with 3507 additions and 3531 deletions

View File

@@ -50,7 +50,7 @@ describe('RateLimitingGuard - Tests that expose problems', () => {
describe('canActivate', () => {
/**
* NOTA: Estes testes identificam problemas no método canActivate.
*
*
* PROBLEMAS IDENTIFICADOS:
* 1. Não valida se IP extraído é válido
* 2. Não valida se rate limiting service retorna dados válidos
@@ -196,7 +196,7 @@ describe('RateLimitingGuard - Tests that expose problems', () => {
mockGetRequest.mockReturnValue(request);
mockRateLimitingService.isAllowed.mockRejectedValue(
new Error('Erro de conexão com Redis')
new Error('Erro de conexão com Redis'),
);
try {
@@ -225,7 +225,7 @@ describe('RateLimitingGuard - Tests that expose problems', () => {
mockGetRequest.mockReturnValue(request);
mockRateLimitingService.isAllowed.mockResolvedValue(false);
mockRateLimitingService.getAttemptInfo.mockRejectedValue(
new Error('Erro ao buscar informações')
new Error('Erro ao buscar informações'),
);
try {
@@ -336,7 +336,9 @@ describe('RateLimitingGuard - Tests that expose problems', () => {
const result = await guard.canActivate(mockExecutionContext);
expect(result).toBe(true);
expect(mockRateLimitingService.isAllowed).toHaveBeenCalledWith('192.168.1.1');
expect(mockRateLimitingService.isAllowed).toHaveBeenCalledWith(
'192.168.1.1',
);
});
it('should handle concurrent requests with same IP', async () => {
@@ -363,7 +365,7 @@ describe('RateLimitingGuard - Tests that expose problems', () => {
const results = await Promise.all(promises);
results.forEach(result => {
results.forEach((result) => {
expect(result).toBe(true);
});
});
@@ -394,7 +396,9 @@ describe('RateLimitingGuard - Tests that expose problems', () => {
fail('Deveria ter lançado exceção');
} catch (error) {
expect(error).toBeInstanceOf(HttpException);
expect((error as HttpException).getStatus()).toBe(HttpStatus.TOO_MANY_REQUESTS);
expect((error as HttpException).getStatus()).toBe(
HttpStatus.TOO_MANY_REQUESTS,
);
}
});
@@ -419,7 +423,9 @@ describe('RateLimitingGuard - Tests that expose problems', () => {
fail('Deveria ter lançado exceção');
} catch (error) {
const response = (error as HttpException).getResponse() as any;
expect(response.error).toBe('Muitas tentativas de login. Tente novamente em alguns minutos.');
expect(response.error).toBe(
'Muitas tentativas de login. Tente novamente em alguns minutos.',
);
expect(response.success).toBe(false);
}
});
@@ -512,7 +518,9 @@ describe('RateLimitingGuard - Tests that expose problems', () => {
const result = await guard.canActivate(mockExecutionContext);
expect(result).toBe(true);
expect(mockRateLimitingService.isAllowed).toHaveBeenCalledWith('2001:0db8:85a3:0000:0000:8a2e:0370:7334');
expect(mockRateLimitingService.isAllowed).toHaveBeenCalledWith(
'2001:0db8:85a3:0000:0000:8a2e:0370:7334',
);
});
it('should reject invalid IPv6 format', async () => {
@@ -556,7 +564,9 @@ describe('RateLimitingGuard - Tests that expose problems', () => {
await guard.canActivate(mockExecutionContext);
expect(mockRateLimitingService.isAllowed).toHaveBeenCalledWith('192.168.1.1');
expect(mockRateLimitingService.isAllowed).toHaveBeenCalledWith(
'192.168.1.1',
);
});
it('should fallback to connection.remoteAddress when x-forwarded-for is missing', async () => {
@@ -572,7 +582,9 @@ describe('RateLimitingGuard - Tests that expose problems', () => {
await guard.canActivate(mockExecutionContext);
expect(mockRateLimitingService.isAllowed).toHaveBeenCalledWith('10.0.0.1');
expect(mockRateLimitingService.isAllowed).toHaveBeenCalledWith(
'10.0.0.1',
);
});
it('should use default IP when all sources are missing', async () => {
@@ -603,4 +615,3 @@ describe('RateLimitingGuard - Tests that expose problems', () => {
});
});
});

View File

@@ -1,4 +1,10 @@
import { Injectable, CanActivate, ExecutionContext, HttpException, HttpStatus } from '@nestjs/common';
import {
Injectable,
CanActivate,
ExecutionContext,
HttpException,
HttpStatus,
} from '@nestjs/common';
import { RateLimitingService } from '../services/rate-limiting.service';
@Injectable()
@@ -19,7 +25,8 @@ export class RateLimitingGuard implements CanActivate {
try {
isAllowed = await this.rateLimitingService.isAllowed(ip);
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
const errorMessage =
error instanceof Error ? error.message : String(error);
throw new HttpException(
{
success: false,
@@ -30,13 +37,14 @@ export class RateLimitingGuard implements CanActivate {
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
if (!isAllowed) {
let attemptInfo;
try {
attemptInfo = await this.rateLimitingService.getAttemptInfo(ip);
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
const errorMessage =
error instanceof Error ? error.message : String(error);
throw new HttpException(
{
success: false,
@@ -49,11 +57,12 @@ export class RateLimitingGuard implements CanActivate {
}
this.validateAttemptInfo(attemptInfo);
throw new HttpException(
{
success: false,
error: 'Muitas tentativas de login. Tente novamente em alguns minutos.',
error:
'Muitas tentativas de login. Tente novamente em alguns minutos.',
data: null,
details: {
attempts: attemptInfo.attempts,
@@ -73,13 +82,16 @@ export class RateLimitingGuard implements CanActivate {
* @returns Endereço IP do cliente ou '127.0.0.1' se não encontrado
*/
private getClientIp(request: any): string {
const forwardedFor = request.headers['x-forwarded-for']?.split(',')[0]?.trim();
const forwardedFor = request.headers['x-forwarded-for']
?.split(',')[0]
?.trim();
const realIp = request.headers['x-real-ip']?.trim();
const connectionIp = request.connection?.remoteAddress;
const socketIp = request.socket?.remoteAddress;
const requestIp = request.ip;
const rawIp = forwardedFor || realIp || connectionIp || socketIp || requestIp;
const rawIp =
forwardedFor || realIp || connectionIp || socketIp || requestIp;
if (rawIp === null || rawIp === undefined) {
return '';
@@ -90,7 +102,7 @@ export class RateLimitingGuard implements CanActivate {
}
const trimmedIp = rawIp.trim();
if (trimmedIp === '') {
return '';
}
@@ -144,7 +156,11 @@ export class RateLimitingGuard implements CanActivate {
return;
}
if (!ipv4Regex.test(ip) && !ipv6Regex.test(ip) && !ipv6CompressedRegex.test(ip)) {
if (
!ipv4Regex.test(ip) &&
!ipv6Regex.test(ip) &&
!ipv6CompressedRegex.test(ip)
) {
if (!this.isValidIpv4(ip) && !this.isValidIpv6(ip)) {
throw new HttpException(
{
@@ -166,7 +182,7 @@ export class RateLimitingGuard implements CanActivate {
const parts = ip.split('.');
if (parts.length !== 4) return false;
return parts.every(part => {
return parts.every((part) => {
const num = parseInt(part, 10);
return !isNaN(num) && num >= 0 && num <= 255;
});
@@ -180,17 +196,17 @@ export class RateLimitingGuard implements CanActivate {
if (ip.includes('::')) {
const parts = ip.split('::');
if (parts.length > 2) return false;
const leftParts = parts[0] ? parts[0].split(':') : [];
const rightParts = parts[1] ? parts[1].split(':') : [];
return (leftParts.length + rightParts.length) <= 8;
return leftParts.length + rightParts.length <= 8;
}
const parts = ip.split(':');
if (parts.length !== 8) return false;
return parts.every(part => {
return parts.every((part) => {
if (!part) return false;
return /^[0-9a-fA-F]{1,4}$/.test(part);
});
@@ -223,8 +239,11 @@ export class RateLimitingGuard implements CanActivate {
);
}
if (attemptInfo.remainingTime !== undefined &&
(typeof attemptInfo.remainingTime !== 'number' || attemptInfo.remainingTime < 0)) {
if (
attemptInfo.remainingTime !== undefined &&
(typeof attemptInfo.remainingTime !== 'number' ||
attemptInfo.remainingTime < 0)
) {
throw new HttpException(
{
success: false,