workspace-v1/shared/libs/rate-limiting/_reference/rate-limiter.service.reference.ts
rckrdmrd 66161b1566 feat: Workspace-v1 complete migration with NEXUS v3.4
Sistema NEXUS v3.4 migrado con:

Estructura principal:
- core/orchestration: Sistema SIMCO + CAPVED (27 directivas, 28 perfiles)
- core/catalog: Catalogo de funcionalidades reutilizables
- shared/knowledge-base: Base de conocimiento compartida
- devtools/scripts: Herramientas de desarrollo
- control-plane/registries: Control de servicios y CI/CD
- orchestration/: Configuracion de orquestacion de agentes

Proyectos incluidos (11):
- gamilit (submodule -> GitHub)
- trading-platform (OrbiquanTIA)
- erp-suite con 5 verticales:
  - erp-core, construccion, vidrio-templado
  - mecanicas-diesel, retail, clinicas
- betting-analytics
- inmobiliaria-analytics
- platform_marketing_content
- pos-micro, erp-basico

Configuracion:
- .gitignore completo para Node.js/Python/Docker
- gamilit como submodule (git@github.com:rckrdmrd/gamilit-workspace.git)
- Sistema de puertos estandarizado (3005-3199)

Generated with NEXUS v3.4 Migration System
EPIC-010: Configuracion Git y Repositorios
2026-01-04 03:37:42 -06:00

187 lines
4.4 KiB
TypeScript

/**
* RATE LIMITER SERVICE - REFERENCE IMPLEMENTATION
*
* @description Servicio de rate limiting para proteger endpoints.
* Implementación in-memory simple con soporte para diferentes estrategias.
*
* @usage
* ```typescript
* // En middleware o guard
* const limiter = getRateLimiter({ windowMs: 60000, max: 100 });
* if (limiter.isRateLimited(req.ip)) {
* throw new TooManyRequestsException();
* }
* ```
*
* @origin gamilit/apps/backend/src/shared/services/rate-limiter.service.ts
*/
/**
* Configuración del rate limiter
*/
export interface RateLimitConfig {
/** Ventana de tiempo en milisegundos */
windowMs: number;
/** Máximo de requests por ventana */
max: number;
/** Mensaje de error personalizado */
message?: string;
/** Función para generar key (default: IP) */
keyGenerator?: (req: any) => string;
}
/**
* Estado interno de un cliente
*/
interface ClientState {
count: number;
resetTime: number;
}
/**
* Rate Limiter Factory
*
* @param config - Configuración del limiter
* @returns Instancia del rate limiter
*/
export function getRateLimiter(config: RateLimitConfig): RateLimiter {
const clients = new Map<string, ClientState>();
// Limpieza periódica de entradas expiradas
const cleanup = setInterval(() => {
const now = Date.now();
for (const [key, state] of clients.entries()) {
if (state.resetTime <= now) {
clients.delete(key);
}
}
}, config.windowMs);
return {
/**
* Verificar si un cliente está rate limited
*/
check(key: string): RateLimitResult {
const now = Date.now();
const state = clients.get(key);
// Cliente nuevo o ventana expirada
if (!state || state.resetTime <= now) {
clients.set(key, {
count: 1,
resetTime: now + config.windowMs,
});
return {
limited: false,
remaining: config.max - 1,
resetTime: now + config.windowMs,
};
}
// Incrementar contador
state.count++;
// Verificar límite
if (state.count > config.max) {
return {
limited: true,
remaining: 0,
resetTime: state.resetTime,
retryAfter: Math.ceil((state.resetTime - now) / 1000),
};
}
return {
limited: false,
remaining: config.max - state.count,
resetTime: state.resetTime,
};
},
/**
* Resetear contador de un cliente
*/
reset(key: string): void {
clients.delete(key);
},
/**
* Limpiar todos los contadores
*/
clear(): void {
clients.clear();
},
/**
* Destruir el limiter (detener cleanup)
*/
destroy(): void {
clearInterval(cleanup);
clients.clear();
},
};
}
/**
* Interfaz del rate limiter
*/
export interface RateLimiter {
check(key: string): RateLimitResult;
reset(key: string): void;
clear(): void;
destroy(): void;
}
/**
* Resultado de verificación
*/
export interface RateLimitResult {
limited: boolean;
remaining: number;
resetTime: number;
retryAfter?: number;
}
/**
* Middleware para Express/NestJS
*/
export function rateLimitMiddleware(config: RateLimitConfig) {
const limiter = getRateLimiter(config);
const keyGenerator = config.keyGenerator || ((req) => req.ip || 'unknown');
return (req: any, res: any, next: () => void) => {
const key = keyGenerator(req);
const result = limiter.check(key);
// Agregar headers informativos
res.setHeader('X-RateLimit-Limit', config.max);
res.setHeader('X-RateLimit-Remaining', result.remaining);
res.setHeader('X-RateLimit-Reset', result.resetTime);
if (result.limited) {
res.setHeader('Retry-After', result.retryAfter);
res.status(429).json({
statusCode: 429,
message: config.message || 'Too many requests',
retryAfter: result.retryAfter,
});
return;
}
next();
};
}
/**
* Error de rate limit
*/
export class TooManyRequestsError extends Error {
public readonly statusCode = 429;
public readonly retryAfter: number;
constructor(retryAfter: number, message?: string) {
super(message || 'Too many requests');
this.retryAfter = retryAfter;
}
}