Skip to content

Analise de Seguranca - Plataforma ProCases

Data: 2026-02-11 Versao: 2.0 Branch analisada: procases Classificacao: CONFIDENCIAL - Documento interno para desenvolvedores Autor: Equipe de Seguranca


AVISO: Este documento NAO deve ser publicado, vinculado ou referenciado em nenhuma documentacao publica do site. Trata-se de uma analise interna de seguranca destinada exclusivamente ao time de desenvolvimento.


Sumario Executivo

Esta analise de seguranca foi realizada com base no codigo-fonte real do projeto na branch procases. Foram identificados 10 problemas de seguranca (sendo 3 criticos), 2 dependencias mortas, 4+ blocos de codigo morto, 8+ itens documentados no CLAUDE.md que NAO existem no codigo, e 8 gargalos para producao e escala. Este documento detalha cada achado com referencias diretas aos arquivos, trechos de codigo, impacto real, e recomendacoes priorizadas.


Indice

  1. Infraestrutura Implementada
  2. Problemas de Seguranca Encontrados
  3. Dependencias Mortas
  4. Codigo Morto
  5. Itens no CLAUDE.md que NAO Existem no Codigo
  6. Gargalos para Producao e Escala
  7. Recomendacoes Prioritarias
  8. Checklist Pre-Producao
  9. Plano de Escalabilidade
  10. Apendices

1. Infraestrutura Implementada

Esta secao documenta todos os mecanismos de robustez, integridade e seguranca de dados que estao efetivamente implementados e funcionando no codigo-fonte.


1.1 Double-Entry Bookkeeping (Contabilidade de Partida Dupla)

Arquivo: src/application/services/transaction.service.ts

Toda operacao financeira da plataforma gera DOIS registros atomicos na tabela Transaction: um DEBIT e um CREDIT pareados via relatedTransactionId.

Metodos implementados:

  • createDoubleEntry(fromUserId, toUserId, amount, reason) - Transferencia entre usuarios
  • debit(userId, amount, reason) - Usuario → Sistema (abertura de caixa, upgrade, batalha)
  • credit(userId, amount, reason) - Sistema → Usuario (deposito, venda de item)
  • transfer(fromUserId, toUserId, amount, reason) - P2P

Garantias:

  • Invariante: SUM(CREDITS) - SUM(DEBITS) = 0 no sistema inteiro
  • Nenhum dinheiro e criado ou destruido - apenas transferido
  • Rastreabilidade completa: cada transacao aponta para sua contraparte
  • SYSTEM_USER_ID (BigInt(0)) representa a casa/plataforma

Source of Truth: O saldo real de qualquer usuario e SEMPRE calculado por TransactionRepository.getUserBalance(userId) via GROUP BY em todas as transacoes. O campo user.balanceCents e apenas um cache atualizado apos cada transacao.


1.2 Distributed Locks (Redlock)

Arquivo: src/infrastructure/locks/lock.service.ts

Locks distribuidos baseados em Redis previnem race conditions em todas as operacoes financeiras criticas. Usa a biblioteca redlock v5.0.0-beta.2.

Locks implementados:

LockTTLUso
withUserBalanceLock(userId)10sToda operacao que altera saldo
withTransactionLock()5sCriacao atomica de double-entry
withWithdrawalLock(inventoryItemId)30sPrevine saque duplicado do mesmo item
withLock(resource, callback, ttl)CustomizavelLock generico

Configuracao:

  • Retry: 10 tentativas com 200ms delay + 200ms jitter
  • Drift factor: 0.01
  • Auto-extension: threshold de 500ms

Padrao de uso no codigo:

LockService.withUserBalanceLock(userId) → Prisma.$transaction() → double-entry → commit → release

Cada operacao financeira adquire o lock do usuario ANTES de iniciar a transacao PostgreSQL, garantindo que apenas uma operacao financeira por usuario executa por vez.


1.3 BIGINT para Valores Monetarios (Zero Imprecisao)

100% consistente em todas as tabelas do schema Prisma. Nenhum valor monetario usa Decimal, Float ou String.

Campos implementados:

  • User: balanceCents, totalDepositedCents, totalWithdrawnCents, totalWageredCents
  • Transaction: amountCents
  • Deposit: amountCents
  • Withdrawal: amountCents
  • Item: valueCents (USD), valueCentsBrl (BRL), originalPriceCents
  • CaseOpening: pricePaidCents, itemValueCents, profitCents
  • Case: priceCents
  • Upgrade: valueInvestedCents, balanceUsedCents, targetValueCents
  • Battle: totalValueCents
  • BattlePlayer: totalProfitCents
  • BattleSettlement: valueCents
  • RafflePrize: valueCents, valueCentsBrl

Beneficios: Zero erro de ponto flutuante, operacoes atomicas no banco, comparacoes simples com inteiros, compativel com JavaScript BigInt.


1.4 Audit Log com Hash Chaining (Blockchain-like)

Arquivo: src/infrastructure/audit/audit.service.ts

Sistema de auditoria imutavel com encadeamento de hashes SHA-256. Cada registro aponta para o hash do registro anterior, formando uma cadeia verificavel. Qualquer alteracao em um registro intermediario quebra a cadeia.

Campos por registro:

  • id (Snowflake ID), userId, action, entityType, entityId
  • metadata (JSON), ipAddress, userAgent
  • previousHash → hash do registro anterior
  • currentHash → SHA256(previousHash:logData)

Metodos implementados:

  • log(userId, action, entityType, entityId, metadata, ipAddress, userAgent)
  • logUserAction(userId, ...) - Acoes de usuario
  • logSystemAction(action, ...) - Acoes do sistema
  • verifyChainIntegrity(startId?, endId?) - Verifica cadeia completa ou parcial

Endpoint de verificacao: GET /api/audit/verify-chain retorna { valid: boolean, corruptedLogId?: bigint, logsVerified: number }.

Indexes: userId, action, (entityType, entityId), createdAt, (action, createdAt DESC), (entityType, entityId, createdAt DESC).


1.5 Snowflake IDs (64-bit Distributed)

Arquivo: src/infrastructure/snowflake/snowflake.service.ts

IDs de 64 bits gerados localmente, sem dependencia de auto-increment do banco.

Estrutura do ID:

  • 41 bits: Timestamp (epoch customizado: 2024-01-01)
  • 10 bits: Machine ID (suporta 1024 maquinas)
  • 12 bits: Sequence (4096 IDs por milissegundo por maquina)

Protecoes:

  • Clock drift: recusa gerar ID se relogio voltar
  • Sequence overflow: espera proximo milissegundo se 4096 IDs esgotados
  • Machine ID configuravel via MACHINE_ID (env variable)
  • Batch generation: generateMany(count) para criacao em lote

Beneficios: Ordenaveis por tempo (sem ORDER BY extra), menores que UUID (8 bytes vs 16 bytes), indexacao mais eficiente, sem colisao entre instancias.


1.6 Prisma Transactions com Timeout Configurado

Arquivo: src/infrastructure/database/prisma.service.ts

Configuracao:

  • maxWait: 10 segundos (tempo maximo na fila de conexoes)
  • timeout: 30 segundos (tempo maximo de execucao da transacao)
  • Lifecycle: onModuleInit$connect, onModuleDestroy$disconnect

Padrao no codigo: Todas as operacoes financeiras usam prisma.$transaction() com suporte a externalTx?: PrismaTransactionClient para composicao de transacoes.


1.7 PostgreSQL: pg_trgm + GIN Indexes para Busca Fuzzy

Migration: 20260202120000_add_performance_indexes

A extensao pg_trgm esta habilitada no banco com um index GIN na tabela de itens:

sql
CREATE EXTENSION IF NOT EXISTS pg_trgm;
CREATE INDEX idx_items_name_trgm ON items USING gin (name gin_trgm_ops);

Permite busca fuzzy/parcial por nome de item com performance O(1) via index invertido. Nao requer Elasticsearch ou servico externo.


1.8 PostgreSQL: 90+ Indexes de Performance

A migration 20260202120000_add_performance_indexes cria 50+ indexes adicionais alem dos indexes declarados no Prisma schema, totalizando 90+ indexes.

Destaques:

TabelaIndexTipoUso
itemsidx_items_name_trgmGIN (pg_trgm)Busca fuzzy por nome
case_itemsidx_case_items_case_hash_rangeB-tree compostoProvably Fair roll lookup
case_itemsidx_case_items_rareB-tree parcial (WHERE is_rare)Sistema FLIP
transactionsidx_transactions_user_type_statusB-tree compostoCalculo de saldo
user_inventoryidx_inventory_availableB-tree parcial (WHERE AVAILABLE)Inventario do usuario
battlesidx_battles_public_waitingB-tree parcial (WHERE WAITING)Lobby de batalhas
case_openingsidx_case_openings_recentB-tree DESCLive drops
notificationsidx_notifications_unread_typeB-tree parcial (WHERE UNREAD)Notificacoes nao lidas
itemsidx_items_rarity_valueB-tree compostoSistema de upgrades
depositsidx_deposits_pendingB-tree parcial (WHERE PENDING)Depositos pendentes

Partial indexes (WHERE clause) reduzem o tamanho do index significativamente ao indexar apenas registros relevantes (ex: apenas items AVAILABLE, apenas batalhas WAITING, apenas notificacoes UNREAD).


1.9 Schema Prisma: 35 Enums + 17 Unique Constraints + 17 Cascades

Enums tipados (35): Previnem estados invalidos no banco. UserStatus, UserRole, TransactionType, TransactionStatus, PaymentMethod, DepositStatus, WithdrawalMethod, WithdrawalStatus, CaseStatus, RiskLevel, Rarity, WeaponCategory, ItemExterior, InventoryItemStatus, WaxpeerWithdrawalStatus, NotificationType, NotificationStatus, BattleMode, BattleType, BattleStatus, BattleSettlementType, SwapStatus, RaffleStatus, EventType, EventStatus, RewardType. Os modulos mais recentes adicionam CrashCycleStatus, CrashProfitMode, SkinsBackDepositStatus, CryptoDepositStatus, entre outros (total: 35).

Unique constraints (17): Garantem unicidade de dados criticos. User.steamId, User.referralCode, CaseCategory.name, CaseCategory.slug, Case.slug, Item.marketHashName, CaseItem.(caseId,itemId), Transaction.relatedTransactionId, Deposit.externalId, WaxpeerWithdrawal.waxpeerId, WaxpeerWithdrawal.projectId, Battle.joinCode, BattlePlayer.(battleId,userId), RaffleTicket.(raffleId,ticketNumber), EventLevel.(eventId,level), EventProgress.(eventId,userId), EventReward.(eventLevelId,userId).

Cascade deletes (17): Integridade referencial automatica. Ao deletar uma entidade pai, entidades filhas sao removidas automaticamente (Session→User, CaseItem→Case, BattlePlayer→Battle, etc).


1.10 Redis: Sessions com Binding de IP/UA

Arquivo: src/infrastructure/auth/session.service.ts

Sessions armazenadas em Redis com binding de IP e User-Agent para deteccao de session hijacking.

Dados por sessao: userId, steamId, ip, userAgent, deviceId, createdAt, lastActivity.

Protecoes:

  • Revogacao instantanea (deletar do Redis)
  • Deteccao de hijacking: IP ou UA diferente = sessao invalida
  • Multiplos dispositivos: cada sessao e independente
  • Logout de todos os dispositivos: deleta todas as sessions do usuario
  • TTL automatico: 7 dias de inatividade

1.11 2FA (TOTP) para Operacoes Sensiveis

Arquivo: src/infrastructure/auth/two-factor.service.ts

Autenticacao de dois fatores baseada em TOTP (Time-based One-Time Password) usando speakeasy. Compativel com Google Authenticator, Authy e similares.

Onde e exigido:

  • Saques acima do threshold configuravel (WITHDRAWAL_2FA_THRESHOLD_CENTS)

Funcionalidades:

  • Geracao de secret com QR code
  • Verificacao de token com window de 1 (aceita codigo anterior/posterior)
  • Ativacao/desativacao pelo usuario

1.12 Seguranca HTTP: Helmet + CORS + CSRF

Arquivo: src/main.ts

Helmet (headers de seguranca):

  • Content-Security-Policy (CSP)
  • HTTP Strict Transport Security (HSTS)
  • X-XSS-Protection
  • X-Content-Type-Options: nosniff
  • Referrer-Policy

CORS (whitelist estrita):

  • Apenas dominios autorizados (procases.example, admin.procases.example, localhost em dev)
  • credentials: true para cookies
  • Metodos restritos: GET, POST, PUT, PATCH, DELETE

CSRF Protection: @fastify/csrf-protection com signed cookies.


1.13 Rate Limiting 3 Camadas (NestJS Throttler)

Arquivo: src/app.module.ts

CamadaJanelaLimite
short1 segundo10 requisicoes
medium10 segundos50 requisicoes
long1 minuto100 requisicoes

Aplicado globalmente via APP_GUARD. Endpoints financeiros tem limites adicionais via @Throttle({ default: { limit: 5, ttl: 60000 } }).


1.14 31 Repositories com Clean Architecture

Pattern de repositorios com interfaces no dominio e implementacoes na infraestrutura.

Repositories implementados (principais): Transaction, User, Deposit, Withdrawal, Case, CaseCategory, CaseOpening, Item, UserInventory, Upgrade, Battle, BattleSettlement, Swap, Notification, Raffle, Event, EventShoot, WaxpeerWithdrawal. Os modulos mais recentes adicionam SkinsBackDeposit, CryptoDeposit, WithdrawalAutoApprovalConfig, UserWithdrawalLimits, alem dos repositorios de Mines e Crash, totalizando 31 implementacoes (30 interfaces em domain/repositories/).

Todos seguem o padrao: IXxxRepository (interface em domain/repositories/) → XxxRepository (implementacao em infrastructure/database/repositories/).


1.15 Validacao de Entrada Global

ValidationPipe global com:

  • whitelist: true - Remove campos nao declarados no DTO
  • forbidNonWhitelisted: true - Erro se campo extra for enviado
  • transform: true - Conversao automatica de tipos

SanitizePipe customizado: Remove tags HTML de inputs string (prevencao XSS).

DTOs com class-validator: Todos os endpoints usam DTOs tipados com decorators @IsNotEmpty, @IsString, @Length, @IsInt, @Min, @Max, etc.


1.16 Provably Fair (HMAC-SHA256)

Arquivo: src/application/services/provably-fair.service.ts

Sistema criptografico de verificacao independente de resultados.

Algoritmo:

  1. Backend gera serverSeed (256 bits) e envia o commitment HMAC-SHA256(serverSeed) (HMAC com o serverSeed como chave, mensagem vazia) ao usuario
  2. Usuario define clientSeed + nonce auto-incrementado
  3. Roll = HMAC-SHA256(serverSeed, clientSeed:nonce)substring(0,13)parseInt(hex,16)% 10000000 + 1
  4. Range: 1 a 10.000.000
  5. Overflow retry: se parseInt > Number.MAX_SAFE_INTEGER, usa nonce + 1000000

Verificavel: Apos o jogo, serverSeed e revelado. Qualquer pessoa pode recalcular HMAC-SHA256(serverSeed) e confirmar que bate com o commitment mostrado antes.


Resumo da Infraestrutura

ComponenteStatusNota
Double-Entry BookkeepingImplementadoTodas as operacoes financeiras
Distributed Locks (Redlock)ImplementadoSaldo, transacoes, saques
BIGINT para dinheiroImplementado100% consistente, zero imprecisao
Audit Log Hash ChainingImplementadoImutavel, verificavel
Snowflake IDsImplementado64-bit, clock drift protection
Prisma TransactionsImplementado10s maxWait, 30s timeout
pg_trgm + GIN IndexImplementadoBusca fuzzy sem Elasticsearch
90+ IndexesImplementadoPartial indexes, compostos
35 Enums + 17 UniquesImplementadoPrevencao de estados invalidos
Sessions Redis + IP/UAImplementadoDeteccao de hijacking
2FA TOTPImplementadoSaques acima do threshold
Helmet + CORS + CSRFImplementadoHeaders, whitelist, signed cookies
Rate Limiting 3 camadasImplementadoshort/medium/long
31 RepositoriesImplementadoClean Architecture
Validacao + SanitizacaoImplementadoValidationPipe + SanitizePipe
Provably FairImplementadoHMAC-SHA256, range 1-10M

2. Problemas de Seguranca Encontrados

2.1 [CRITICO] POST /raffle/:id/draw - So tem AuthGuard, qualquer usuario autenticado pode sortear

Arquivo: src/presentation/controllers/raffle.controller.ts (linhas 154-160)

Codigo atual:

typescript
@Post(':id/draw')
@UseGuards(AuthGuard)
@ApiOperation({ summary: 'Sortear vencedor (Admin)' })
@ApiResponse({ status: 200, description: 'Sorteio realizado' })
async drawRaffle(@Param('id') raffleId: string, @Body() dto: DrawRaffleDto) {
  const result = await this.drawRaffleUseCase.execute(BigInt(raffleId), dto.clientSeed);
  return this.formatRaffle(result);
}

Problema: O endpoint usa apenas @UseGuards(AuthGuard). Qualquer usuario autenticado (mesmo um usuario comum recem-cadastrado) pode chamar este endpoint e sortear um raffle. O proprio comment da ApiOperation diz "Admin", mas o guard AdminGuard NAO esta aplicado.

Impacto: Um usuario mal-intencionado pode sortear qualquer raffle ativo a qualquer momento, potencialmente antes que todos os tickets sejam vendidos ou em momento estrategico que o beneficie. Isso compromete a integridade do sistema de sorteios inteiro.

Comparacao com AdminController: O AdminController (src/presentation/controllers/admin.controller.ts, linha 106) aplica AMBOS os guards no nivel do controller:

typescript
@UseGuards(AuthGuard, AdminGuard)
export class AdminController {

Correcao necessaria: Adicionar AdminGuard ao endpoint:

typescript
@Post(':id/draw')
@UseGuards(AuthGuard, AdminGuard)
@ApiOperation({ summary: 'Sortear vencedor (Admin)' })

Severidade: CRITICA - Permite que qualquer usuario execute acao administrativa.


2.2 [CRITICO] Endpoints de Audit sem AdminGuard - Qualquer usuario acessa logs de auditoria

Arquivo: src/presentation/controllers/audit.controller.ts

Codigo atual:

typescript
@ApiTags('Audit')
@Controller('audit')
@UseGuards(AuthGuard)
export class AuditController {
  // ...

  @Get('verify-chain')
  @ApiOperation({ summary: 'Verify audit chain integrity (admin only)' })
  async verifyChain(...) { ... }

  @Get('entity/:type/:id')
  @ApiOperation({ summary: 'Get entity audit logs (admin only)' })
  async getEntityLogs(...) { ... }
}

Problema: O controller inteiro usa apenas @UseGuards(AuthGuard). Existem DOIS endpoints que os proprios comments da ApiOperation indicam como "admin only", mas que NAO possuem AdminGuard:

  1. GET /audit/verify-chain - Verifica integridade da chain de auditoria. Qualquer usuario autenticado pode executar esta operacao, que pode ser computacionalmente cara (itera sobre todos os logs de auditoria) e revelar informacoes sensiveis sobre a integridade do sistema.

  2. GET /audit/entity/:type/:id - Retorna logs de auditoria por entidade. Qualquer usuario autenticado pode ver logs de QUALQUER entidade do sistema, incluindo logs de transacoes financeiras de OUTROS usuarios, operacoes administrativas, mudancas de status de contas, etc.

Impacto: Vazamento massivo de informacoes. Um usuario pode:

  • Ver todas as transacoes financeiras de outros usuarios
  • Ver operacoes administrativas (bans, ajustes de saldo, etc.)
  • Ver metadata sensivel armazenada nos logs de auditoria
  • Consumir recursos do servidor com verificacoes de chain pesadas
  • Identificar padroes internos de operacao da plataforma

Nota: O endpoint GET /audit/my-logs esta corretamente protegido pois filtra pelo userId do usuario autenticado. Os outros dois NAO filtram.

Correcao necessaria:

typescript
@Controller('audit')
@UseGuards(AuthGuard, AdminGuard)
export class AuditController {

Ou, se GET /audit/my-logs deve continuar acessivel para usuarios comuns:

typescript
@Controller('audit')
@UseGuards(AuthGuard)
export class AuditController {
  @Get('my-logs')
  async getMyLogs(...) { ... }  // OK - filtra pelo userId do usuario

  @Get('verify-chain')
  @UseGuards(AdminGuard)  // Adicionar
  async verifyChain(...) { ... }

  @Get('entity/:type/:id')
  @UseGuards(AdminGuard)  // Adicionar
  async getEntityLogs(...) { ... }
}

Severidade: CRITICA - Vazamento de dados sensiveis de todos os usuarios.


2.3 [ALTO] POST /upgrade/:id/verify - Sem guard explicito, depende do guard global

Arquivo: src/presentation/controllers/upgrade.controller.ts (linhas 156-167)

Codigo atual:

typescript
@Post(':id/verify')
@ApiOperation({ summary: 'Verify upgrade provably fair' })
@ApiResponse({ status: 200, description: 'Verification result' })
async verifyUpgrade(@Param('id') id: string): Promise<VerifyUpgradeResponseDto> {
  const result = await this.verifyUpgradeUseCase.execute(BigInt(id));
  // ...
}

Problema: Este endpoint NAO tem nenhum @UseGuards() no metodo NEM o controller inteiro usa guard no nivel de classe. O controller UpgradeController NAO tem @UseGuards() no nivel do controller. O endpoint depende exclusivamente do guard global (se existir).

Analise do controller:

  • @Public() @Get('targets') - Publico explicitamente. OK.
  • @UseGuards(AuthGuard) @Post('calculate') - Guard explicito. OK.
  • @UseGuards(AuthGuard) @Post('perform') - Guard explicito. OK.
  • @UseGuards(AuthGuard) @Get('history') - Guard explicito. OK.
  • @Post(':id/verify') - SEM guard, SEM @Public(). Ambiguo.

Impacto: Se houver um guard global APP_GUARD, o endpoint pode estar protegido por autenticacao. Mas sem @Public() explicito, a intencao nao esta clara. Se nao houver guard global, qualquer pessoa anonima pode verificar upgrades de qualquer usuario (o que pode nao ser um problema funcional, mas expoe informacoes como serverSeed, clientSeed, nonce e roll de QUALQUER upgrade ja feito).

Risco de exposicao de dados: O response inclui serverSeed revelado, que poderia ser usado para analise de padroes se combinado com dados de outros endpoints.

Correcao necessaria: Se a intencao e ser publico (para verificacao provably fair por qualquer pessoa):

typescript
@Public()
@Post(':id/verify')

Se a intencao e ser restrito ao dono do upgrade:

typescript
@UseGuards(AuthGuard)
@Post(':id/verify')
async verifyUpgrade(@CurrentUser('id') userId: bigint, @Param('id') id: string) {
  // Verificar que o upgrade pertence ao userId
}

Severidade: ALTA - Ambiguidade de autorizacao em endpoint que expoe dados criptograficos.


2.4 [ALTO] GET /event/active - Sem guard nenhum, usa @CurrentUser opcional sem OptionalAuthGuard

Arquivo: src/presentation/controllers/event.controller.ts (linhas 22-26)

Codigo atual:

typescript
@ApiTags('Event')
@Controller('event')
export class EventController {
  // ...

  @Get('active')
  @ApiOperation({ summary: 'Get all active events' })
  async getActiveEvents(@CurrentUser('id') userId?: bigint) {
    return this.getActiveEventsUseCase.execute(userId);
  }

Problema: O controller EventController NAO tem guard no nivel de classe. O endpoint GET /event/active NAO tem nenhum guard, NAO tem @Public(), e tenta usar @CurrentUser('id') como parametro opcional.

Questao tecnica: O decorator @CurrentUser depende de request.user estar populado, o que so acontece quando AuthGuard ou OptionalAuthGuard executa. Sem nenhum guard, request.user sera undefined, e userId sera sempre undefined.

Comparacao com raffle.controller.ts: O RaffleController resolve este problema corretamente no endpoint GET /raffle/:id:

typescript
@UseGuards(OptionalAuthGuard)
@Public()
@Get(':id')
async getRaffleDetails(@Param('id') raffleId: string, @CurrentUser('id') userId?: bigint) {

Ele usa @UseGuards(OptionalAuthGuard) + @Public() para permitir acesso anonimo mas ainda assim popular request.user se houver sessao valida.

Impacto: O userId nunca sera populado no endpoint de eventos ativos. Se o use case getActiveEventsUseCase.execute(userId) personaliza a resposta baseada no usuario (ex: mostrando progresso individual), essa personalizacao nunca funcionara. O endpoint sempre retornara a versao "anonima" dos eventos.

Correcao necessaria:

typescript
@UseGuards(OptionalAuthGuard)
@Public()
@Get('active')
@ApiOperation({ summary: 'Get all active events' })
async getActiveEvents(@CurrentUser('id') userId?: bigint) {
  return this.getActiveEventsUseCase.execute(userId);
}

Severidade: ALTA - Funcionalidade quebrada silenciosamente + ambiguidade de autorizacao.


2.5 [ALTO] Decorator @CurrentUser Duplicado - Dois arquivos identicos com imports divergentes

Arquivos:

  • src/presentation/decorators/user.decorator.ts
  • src/presentation/decorators/current-user.decorator.ts

Conteudo de user.decorator.ts:

typescript
import { createParamDecorator, ExecutionContext } from '@nestjs/common';

export const CurrentUser = createParamDecorator(
  (data: string | undefined, ctx: ExecutionContext) => {
    const request = ctx.switchToHttp().getRequest();
    const user = request.user;
    return data ? user?.[data] : user;
  },
);

Conteudo de current-user.decorator.ts:

typescript
import { createParamDecorator, ExecutionContext } from '@nestjs/common';

export const CurrentUser = createParamDecorator(
  (data: string | undefined, ctx: ExecutionContext) => {
    const request = ctx.switchToHttp().getRequest();
    const user = request.user;
    return data ? user?.[data] : user;
  },
);

export const CurrentSession = createParamDecorator((data: unknown, ctx: ExecutionContext) => {
  const request = ctx.switchToHttp().getRequest();
  return request.sessionId;
});

Controllers que importam de user.decorator.ts (arquivo ERRADO/duplicado):

  • raffle.controller.ts
  • swap.controller.ts
  • event.controller.ts

Controllers que importam de current-user.decorator.ts (arquivo principal):

  • admin.controller.ts
  • audit.controller.ts
  • auth.controller.ts
  • battle.controller.ts
  • case-opening.controller.ts
  • event-shoot.controller.ts
  • inventory.controller.ts
  • notification.controller.ts
  • payment.controller.ts
  • upgrade.controller.ts
  • user.controller.ts

Impacto: Atualmente ambos os decorators sao funcionalmente identicos (exceto que current-user.decorator.ts tambem exporta CurrentSession). O risco real e divergencia futura: se alguem alterar a logica do CurrentUser em um arquivo, o outro permanecera com a versao antiga. Isso pode causar bugs sutis e dificeis de diagnosticar onde alguns controllers obtem o usuario de um jeito e outros de outro.

Risco adicional: O arquivo user.decorator.ts NAO exporta CurrentSession. Se um dos controllers que importa de user.decorator.ts precisar do CurrentSession, o desenvolvedor pode criar OUTRA versao duplicada.

Correcao necessaria:

  1. Deletar src/presentation/decorators/user.decorator.ts
  2. Atualizar os imports em raffle.controller.ts, swap.controller.ts e event.controller.ts para importar de current-user.decorator.ts

Severidade: ALTA - Risco de divergencia futura com impacto de seguranca.


2.6 [MEDIO] Referral System Inerte - Codigo existe mas nao faz nada

Arquivos afetados:

  • src/application/use-cases/auth/steam-callback.use-case.ts - Gera referralCode
  • src/infrastructure/database/repositories/user.repository.ts - Salva referredById
  • src/application/use-cases/user/get-user-profile.use-case.ts - Retorna referralCode
  • src/application/dto/user.dto.ts - DTO com campo referralCode

Problema: O sistema de referral esta parcialmente implementado:

  1. Na criacao do usuario (Steam callback), um referralCode unico e gerado
  2. Quando um novo usuario se cadastra usando o codigo de outro, o referredById e salvo no banco de dados
  3. O referralCode e retornado no perfil do usuario

Porem:

  • NAO existe nenhum endpoint de referral (nenhum controller)
  • NAO existe logica de recompensa (bonus para quem indicou, bonus para quem foi indicado)
  • NAO existe contagem de referrals
  • NAO existe nenhum use case de referral ativo

Impacto: Codigo morto que pode confundir desenvolvedores futuros. O usuario ve o referralCode no perfil, pode ate compartilhar o link, mas nada acontece quando alguem usa o codigo. Isso e uma experiencia frustrante para o usuario e pode gerar reclamacoes.

Correcao necessaria: Ou implementar o sistema completo de referrals (com bonus, contagem, dashboard) ou remover o codigo morto para evitar confusao.

Severidade: MEDIA - Funcionalidade parcial que pode confundir.


2.7 [ALTO] Items LOCKED Sem Cleanup - Items ficam presos permanentemente

Arquivo: src/application/services/waxpeer-withdrawal-tracker.service.ts

Cenario de falha:

  1. Usuario solicita withdrawal de um item de inventario
  2. O item e marcado como LOCKED no banco
  3. A chamada para Waxpeer (buyItemByNameToSend) falha ANTES de obter um waxpeerId (ex: timeout, item indisponivel, erro de rede)
  4. O item permanece com status LOCKED no inventario
  5. O tracker so monitora withdrawals que TEM waxpeerId:
typescript
const waxpeerIds = pendingWithdrawals
  .filter((w) => w.waxpeerId)
  .map((w) => w.waxpeerId as string);
  1. O item fica permanentemente inacessivel para o usuario

Impacto: O usuario perde acesso ao item. Nao pode vender, trocar, sacar novamente ou usar em upgrade. O unico jeito de resolver e intervencao manual de um admin.

Nenhum cron job de cleanup existe: Verificado via busca por @Cron no projeto inteiro. O unico cron existente e o checkPendingWithdrawals do tracker, que NAO trata items LOCKED sem waxpeerId.

Correcao necessaria: Implementar cron job que a cada 15 minutos:

  1. Busca items com status LOCKED ha mais de 1 hora
  2. Verifica se existe WaxpeerWithdrawal associado com waxpeerId
  3. Se nao existe, reverte o item para AVAILABLE
  4. Notifica o usuario via WebSocket
  5. Loga a acao no AuditLog

Severidade: ALTA - Perda de acesso a itens do usuario.


2.8 [ALTO] Swaps Expirados Sem Cleanup - Items ficam IN_SWAP permanentemente

Arquivos:

  • src/domain/repositories/swap.repository.interface.ts (linha 34)
  • src/infrastructure/database/repositories/swap.repository.ts (linha 197)

Codigo existente no repository:

typescript
// Interface
deleteExpiredSwaps(): Promise<number>;

// Implementacao
async deleteExpiredSwaps(): Promise<number> {
  const result = await this.prisma.swap.updateMany({
    where: {
      status: SwapStatus.PENDING,
      expiresAt: { lt: new Date() },
    },
    // ...
  });
}

Problema: O metodo deleteExpiredSwaps() existe e esta implementado, MAS nao e chamado por NENHUM cron job, scheduler, ou qualquer outro mecanismo automatico.

Busca no codigo: Nao ha nenhum @Cron chamando deleteExpiredSwaps. O unico @Cron no projeto inteiro e o do withdrawal tracker.

Impacto: Quando um usuario cria um swap e o outro usuario nao aceita dentro de 24 horas, os items do ofertante ficam com status IN_SWAP indefinidamente. O usuario nao pode:

  • Vender os items
  • Usar em upgrade
  • Usar em outro swap
  • Sacar os items

Correcao necessaria: Implementar cron job que executa a cada hora:

typescript
@Cron(CronExpression.EVERY_HOUR)
async cleanupExpiredSwaps() {
  const expired = await this.swapRepository.deleteExpiredSwaps();
  if (expired > 0) {
    this.logger.log(`[SwapCleanup] Reverted ${expired} expired swaps`);
    // Reverter items para AVAILABLE
    // Notificar usuarios via WebSocket
  }
}

Severidade: ALTA - Items ficam permanentemente inacessiveis.


2.9 [MEDIO] Deposit Bonus 10% Hardcoded em TODO Deposito

Arquivo: src/application/services/deposit.service.ts (linhas 108-120)

Codigo atual:

typescript
const bonusPercentage = 10;
const bonusAmount = (deposit.amountCents * BigInt(bonusPercentage)) / BigInt(100);
const totalCredit = deposit.amountCents + bonusAmount;

await this.transactionService.credit(
  deposit.userId,
  totalCredit,
  `Deposit confirmed #${depositId} (+${bonusPercentage}% bonus)`,
  {
    depositId: depositId.toString(),
    method: deposit.method,
    bonusPercentage,
    bonusAmountCents: bonusAmount.toString(),
  },
);

Problema: O bonus de 10% e aplicado em TODOS os depositos de TODOS os usuarios, sem nenhuma verificacao de:

  • Se e o primeiro deposito do usuario (isFirstDeposit)
  • Se o usuario ja recebeu bonus antes
  • Limite maximo de bonus por usuario
  • Limite de depositos com bonus por periodo

Impacto financeiro: Um usuario pode fazer 100 depositos de R$100 e receber R$1.000 em bonus. Isso afeta diretamente a margem da casa e pode ser explorado sistematicamente.

Cenario de abuso: Um usuario faz depositos repetidos de valores altos, recebe 10% de bonus a cada vez, joga com risco minimo (ex: upgrades de 99% de chance), e saca o lucro. O bonus de 10% garante um edge positivo para o usuario.

Correcao necessaria:

  1. Verificar se e o primeiro deposito: isFirstDeposit
  2. Ou limitar bonus por periodo (ex: 1 bonus por mes)
  3. Ou usar tabela de bonus configuravel pelo admin
  4. Ou tornar o bonus condicional via configuracao (pode desativar)

Severidade: MEDIA - Impacto financeiro direto na casa.


2.10 [MEDIO] 2FA Secret Salvo no DB ANTES da Confirmacao

Arquivo: src/application/use-cases/payment/generate-2fa-secret.use-case.ts

Codigo atual:

typescript
async execute(userId: bigint) {
  const user = await this.userRepository.findById(userId);
  if (!user) throw new NotFoundException('User not found');

  const { secret, otpauthUrl } = this.twoFactorService.generateSecret(user.username);

  // Secret salvo ANTES do usuario confirmar com um token
  await this.userRepository.update(userId, {
    twoFactorSecret: secret,
  });

  const qrCode = await this.twoFactorService.generateQRCode(otpauthUrl);
  return { secret, qrCode, otpauthUrl };
}

Problema: O twoFactorSecret e salvo no banco de dados imediatamente quando o usuario chama POST /2fa/setup, ANTES de confirmar que configurou o app autenticador corretamente (via POST /2fa/enable).

Cenarios de problema:

  1. Usuario chama /2fa/setup multiplas vezes sem confirmar. Cada vez um novo secret e gerado e salvo, sobrescrevendo o anterior.
  2. Se o usuario configurou o app com um secret anterior e depois chamou /setup novamente, o secret do app ficara fora de sincronia com o banco.
  3. O campo twoFactorSecret fica preenchido mas twoFactorEnabled = false, criando um estado inconsistente.

Risco adicional no verify2FA:

typescript
@Post('2fa/verify')
async verify2FA(@CurrentUser() user: User, @Body() dto: Enable2FADto) {
  const isValid = user.twoFactorSecret
    ? await this.generate2FASecretUseCase['twoFactorService'].verifyToken(
        user.twoFactorSecret,
        dto.token,
      )
    : false;
  return { valid: isValid };
}

O endpoint verify2FA acessa this.generate2FASecretUseCase['twoFactorService'] usando bracket notation para acessar uma propriedade PRIVADA. Isso e um anti-pattern que quebra encapsulamento e pode falhar com minification ou obfuscation.

Correcao necessaria:

  1. NAO salvar o secret no DB durante o setup. Salvar em cache Redis com TTL de 10 minutos.
  2. Apenas salvar no DB apos confirmacao via POST /2fa/enable.
  3. Injetar TwoFactorService diretamente no controller ao inves de acessar via bracket notation.

Severidade: MEDIA - Inconsistencia de estado + anti-pattern de encapsulamento.


2.11 [MEDIO] Balance Source of Truth Inconsistente no Perfil do Usuario

Arquivo: src/application/use-cases/user/get-user-profile.use-case.ts

Codigo atual:

typescript
async execute(userId: bigint): Promise<UserProfileOutput> {
  const user = await this.userRepository.findById(userId);
  if (!user) throw new NotFoundException('User not found');

  return {
    // ...
    balanceCents: user.balanceCents.toString(),
    balance: this.moneyService.toReais(user.balanceCents).toFixed(2),
    // ...
  };
}

Problema: O GET /user/profile retorna user.balanceCents, que e o CACHE do saldo, NAO a source of truth. Conforme documentado no CLAUDE.md:

O saldo do usuario SEMPRE vem das transacoes (TransactionRepository), NUNCA da coluna balanceCents da tabela User.

Porem o endpoint de perfil viola essa regra, usando user.balanceCents diretamente.

Impacto: Se por qualquer motivo o cache user.balanceCents ficar desincronizado com o saldo real das transacoes, o usuario vera um saldo diferente no perfil vs. no header (que atualiza via WebSocket com o saldo real).

Cenario de divergencia: Race condition durante transacao atomica que falha APOS atualizar user.balanceCents mas ANTES de comitar. O Prisma deveria fazer rollback, mas erros parciais podem ocorrer em cenarios de falha de conexao com o banco.

Correcao necessaria: Usar TransactionService.getUserBalance() no use case:

typescript
const realBalance = await this.transactionService.getUserBalance(userId);
return {
  balanceCents: realBalance.toString(),
  balance: this.moneyService.toReais(realBalance).toFixed(2),
};

Severidade: MEDIA - Inconsistencia visual de saldo.


2.12 [MEDIO] WebSocket Waxpeer - Max 10 Reconnect sem Recovery

Arquivo: src/infrastructure/external-apis/waxpeer-websocket.service.ts (linhas 65-66, 172-187)

Codigo atual:

typescript
private reconnectAttempts = 0;
private readonly maxReconnectAttempts = 10;
private readonly reconnectDelay = 5000;

private scheduleReconnect() {
  if (this.reconnectAttempts >= this.maxReconnectAttempts) {
    this.logger.error('[WaxpeerWS] Max reconnection attempts reached');
    return;  // Para DEFINITIVAMENTE. Sem recovery.
  }

  this.reconnectAttempts++;
  const delay = this.reconnectDelay * this.reconnectAttempts;
  // ...
}

Problema: Apos 10 tentativas de reconnect (com backoff linear: 5s, 10s, 15s, ..., 50s = total ~4.5 minutos), o WebSocket do Waxpeer PARA completamente. Nao ha:

  • Mecanismo de recovery apos cooldown
  • Alerta para equipe operacional
  • Health check periodico que tente reconectar
  • Reset do contador apos periodo de inatividade

Impacto: O polling via @Cron(EVERY_30_SECONDS) continua como fallback, mas:

  • Updates de status de withdrawal tem ate 30s de delay
  • Se o Waxpeer WebSocket ficou fora por 5 minutos, todos os updates em tempo real param permanentemente ate restart manual da aplicacao
  • Nenhum alerta e gerado para o time operacional

Correcao necessaria:

  1. Implementar recovery com cooldown (ex: a cada 30 minutos, reseta o contador e tenta novamente)
  2. Emitir alerta para equipe quando max tentativas for atingido
  3. Implementar health check periodico via @Cron que verifica se o WebSocket esta conectado e tenta reconectar se necessario
  4. Considerar reducao do polling interval para 10s quando WebSocket desconectado

Severidade: MEDIA - Degradacao silenciosa do tracking em tempo real.


2.13 [MEDIO] Tracker Trunca em 100 IDs por Ciclo

Arquivo: src/application/services/waxpeer-withdrawal-tracker.service.ts (linha 43) Arquivo: src/infrastructure/database/repositories/waxpeer-withdrawal.repository.ts (linha 94)

Codigo atual:

typescript
// Tracker
const pendingWithdrawals = await this.waxpeerWithdrawalRepository.findPending(100);

// Repository
async findPending(limit: number = 100): Promise<WaxpeerWithdrawal[]> {
  return this.prisma.waxpeerWithdrawal.findMany({
    where: {
      status: { in: ['PENDING', 'WAITING_SELLER', 'TRADE_SENT'] },
    },
    take: limit,
    // ...
  });
}

Problema: O tracker busca no maximo 100 withdrawals pendentes por ciclo de 30 segundos. Se houver mais de 100, os excedentes ficam sem monitoramento ate que os primeiros 100 sejam processados.

Analise de escala:

PendentesCiclos necessariosTempo total para cobrir todos
50130s
100130s
200260s (1 minuto)
5005150s (2.5 minutos)
100010300s (5 minutos)

Impacto: Em periodos de pico, usuarios com withdrawals mais recentes podem esperar varios minutos para o status ser atualizado. Nao ha priorizacao por idade ou valor.

Correcao necessaria:

  1. Aumentar batch size para 500 (Waxpeer suporta consulta de ate 100 IDs por chamada, mas o tracker pode fazer multiplas chamadas por ciclo)
  2. Ou implementar paginacao com cursor: se retornou 100, busca mais 100
  3. Priorizar por createdAt ASC para processar os mais antigos primeiro

Severidade: MEDIA - Delay no tracking em periodos de pico.


3. Dependencias Mortas

3.1 bullmq v5.4.0 - Instalado, ZERO uso no codigo

Arquivo: package.json (linha 57)

json
"bullmq": "^5.4.0",

Verificacao: Busca por bullmq, BullModule, @InjectQueue, @Processor em todo o projeto retornou ZERO resultados em arquivos .ts.

Impacto:

  • ~1.2MB de dependencia instalada sem uso
  • Potencial confusao para desenvolvedores que podem pensar que filas ja estao implementadas
  • Vulnerabilidades em dependencias nao utilizadas aumentam a superficie de ataque

Recomendacao: Remover do package.json imediatamente. Quando for necessario implementar filas (ex: para battle execution), reinstalar e configurar corretamente.


3.2 kafkajs v2.2.4 - Instalado, ZERO uso no codigo

Arquivo: package.json (linha 63)

json
"kafkajs": "^2.2.4",

Verificacao: Busca por kafkajs em arquivos .ts retornou ZERO resultados. O CLAUDE.md menciona "Pub/Sub: Redis Pub/Sub (nao Kafka)" como decisao arquitetural explicita.

Impacto: Similar ao bullmq - dependencia morta que aumenta peso e superficie de ataque sem nenhum beneficio.

Nota: O CLAUDE.md lista uma estrutura de pastas com src/infrastructure/messaging/ contendo kafka.module.ts, kafka.service.ts, etc. Esses arquivos NAO existem no codigo real. Sao vestigios de um plano que nunca foi implementado.

Recomendacao: Remover do package.json.


4. Codigo Morto

4.1 emitWithdrawalProcessing() - Definido mas nunca chamado

Arquivo: src/infrastructure/websocket/websocket.gateway.ts (linha 488)

typescript
emitWithdrawalProcessing(
  userId: bigint,
  data: {
    inventoryItemId: string;
    waxpeerId?: string;
    sendUntil?: number;
    // ...
  },
) {
  // ...
}

Verificacao: O metodo esta definido com assinatura completa e implementacao, mas NAO e chamado por nenhum outro arquivo no projeto. O tracker pula diretamente de PENDING para WAITING_SELLER ou TRADE_SENT, sem emitir o evento intermediario "processing".

Impacto: Pode dar a falsa impressao de que existe um estado "processing" no fluxo de withdrawal, quando na verdade nao ha.

Recomendacao: Remover o metodo. Se necessario no futuro, reimplementar com os estados corretos.


4.2 checkTradeStatusByProjectId() - Implementado mas nunca usado

Arquivo: src/infrastructure/external-apis/waxpeer.service.ts (linha 360)

typescript
async checkTradeStatusByProjectId(projectIds: string[]): Promise<WaxpeerCheckTradesResult> {
  try {
    if (projectIds.length === 0) {
      return { success: true, trades: [] };
    }
    // ...
  }
}

Verificacao: O tracker usa checkTradeStatus() com waxpeerIds, nunca checkTradeStatusByProjectId().

Impacto: Similar ao anterior - confusao sobre qual metodo e o correto para verificar status.

Recomendacao: Remover ou marcar com @deprecated.


4.3 Arquivo user.decorator.ts - Duplicata desnecessaria

Arquivo: src/presentation/decorators/user.decorator.ts

Conforme detalhado na secao 2.5, este arquivo e uma copia identica do current-user.decorator.ts (sem o CurrentSession).

Recomendacao: Deletar o arquivo e atualizar imports.


4.4 Referral System - Codigo parcial sem funcionalidade

Conforme detalhado na secao 2.6, o sistema de referrals gera codigos e salva relacoes, mas nao oferece nenhum beneficio ou funcionalidade ao usuario.

Recomendacao: Implementar completamente ou remover.


5. Itens no CLAUDE.md que NAO Existem no Codigo

A seguinte tabela lista itens documentados no CLAUDE.md como parte da arquitetura do projeto, mas que NAO existem em nenhum arquivo .ts do codigo:

Item no CLAUDE.mdCategoriaExiste?Observacao
ip-blacklist.service.tsSegurancaNAONenhum arquivo com esse nome ou funcionalidade similar
rate-limiter.service.tsSegurancaNAONenhum service de rate limiting customizado
rate-limit.middleware.tsSegurancaNAONenhum middleware de rate limiting
security-scan-detector.middleware.tsSegurancaNAONenhuma deteccao de scan
turnstile.service.ts (Cloudflare CAPTCHA)SegurancaNAONenhuma integracao com Cloudflare Turnstile
circuit-breaker.tsResilienciaNAONenhum circuit breaker pattern
Leaderboard/rankingFuncionalidadeNAONenhum endpoint ou use case de leaderboard
Risk scoring/fraud detectionSegurancaNAONenhum scoring de risco implementado
Promo code systemFuncionalidadeNAONenhum sistema de codigos promocionais
kafka.module.ts, kafka.service.ts, etc.InfraestruturaNAOKafka removido em favor de Redis Pub/Sub
brute-force.service.tsSegurancaNAO VERIFICADONao encontrado na busca
prometheus/, grafana/ configs ativasMonitoramentoPARCIALConfigs Docker existem mas integracao no codigo e minima

Impacto: O CLAUDE.md descreve uma arquitetura significativamente mais robusta do que o que esta realmente implementado. Isso pode:

  1. Dar uma falsa sensacao de seguranca
  2. Confundir novos desenvolvedores que esperam encontrar essas implementacoes
  3. Causar problemas em auditorias de seguranca se o documento for apresentado como reflexo do estado real

Recomendacao: Atualizar o CLAUDE.md para refletir o estado REAL do codigo. Mover itens nao implementados para uma secao "Planejado / TODO" separada.


6. Gargalos para Producao e Escala

6.1 Single Cron Job para TODOS os Withdrawals

Problema: O projeto inteiro possui apenas UM cron job ativo:

typescript
// waxpeer-withdrawal-tracker.service.ts
@Cron(CronExpression.EVERY_30_SECONDS)
async checkPendingWithdrawals() { ... }

Este unico cron e responsavel por monitorar TODOS os withdrawals pendentes. Com batch de 100 e ciclo de 30s, a capacidade maxima e ~200 withdrawals/minuto.

Nenhum cron para:

  • Cleanup de items LOCKED
  • Cleanup de swaps expirados
  • Cleanup de sessions expiradas (depende de Redis TTL apenas)
  • Cleanup de deposits expirados/abandonados
  • Sincronizacao de saldo cache vs. saldo real
  • Health check de servicos externos

Impacto: Todo cleanup depende de acoes manuais ou Redis TTL. Dados inconsistentes acumulam indefinidamente no PostgreSQL.


6.2 Battle Execution Bloqueante - Roda inline no join

Arquivo: src/application/use-cases/battle/execute-battle.use-case.ts

Problema: A execucao da batalha e chamada diretamente no JoinBattleUseCase quando o ultimo jogador entra. O metodo execute() e assincrono mas bloqueia a thread do event loop por TODA a duracao da batalha:

typescript
// join-battle.use-case.ts (simplificado)
if (allSlotsFilled) {
  await this.executeBattleUseCase.execute(battleId);
}

O execute() contem:

  • await this.delay(3000) - Delay inicial de 3 segundos
  • Loop por cada round (ate 10 rounds):
    • Para cada jogador, calcula roll, verifica flip
    • Se FLIP: delay adicional + segunda roleta
    • Emite WebSocket events
    • await this.delay(...) entre rounds
  • Settlement de items
  • Distribuicao justa

Tempo estimado:

  • 10 rounds sem FLIP: ~85 segundos (3s inicio + 10 * ~8s + settlement)
  • 10 rounds com FLIPs: ~120+ segundos

Impacto: Durante esses ~85-120 segundos:

  • A request HTTP do join fica pendente (pode causar timeout)
  • A thread do Node.js esta ocupada com delays e calculos
  • Outros requests podem ficar lentos
  • Se o servidor restartar, a batalha e perdida

Correcao necessaria: Mover a execucao de batalhas para um worker BullMQ (a dependencia ja esta instalada, so nao esta configurada). O join deve apenas enfileirar a execucao e retornar imediatamente.


6.3 Redis Single Instance - Single Point of Failure

Problema: Todas as operacoes criticas dependem de uma UNICA instancia Redis:

  • Sessions de autenticacao
  • Distributed locks (Redlock)
  • Cache de dados (Waxpeer snapshot, exchange rates)
  • Pub/Sub para WebSocket (Redis Adapter)
  • Battle state (estado intermediario de batalhas)

Se o Redis reiniciar:

  • TODOS os usuarios sao deslogados instantaneamente
  • Todas as batalhas em andamento sao perdidas
  • Locks sao liberados prematuramente (race conditions)
  • WebSocket desconecta todos os clientes

Nota sobre Redlock: O Redlock esta configurado com UM unico client Redis:

typescript
this.redlock = new Redlock([client], { ... });

O algoritmo Redlock foi projetado para funcionar com multiplas instancias independentes (minimo 3 para tolerancia a falhas). Com uma unica instancia, o Redlock nao oferece nenhuma vantagem sobre um simples SET NX com TTL.

Recomendacao: Redis Sentinel com 3 nodes (1 master + 2 replicas).


6.4 PostgreSQL sem Particionamento

Problema: Tabelas de alto volume crescem indefinidamente:

  • Transaction: 2 registros por operacao financeira. Com 1000 usuarios ativos fazendo 10 operacoes/dia = 20.000 registros/dia = 600.000/mes
  • CaseOpening: 1 registro por abertura. Alto volume em picos.
  • BattleRound: Multiplos registros por batalha (jogadores * rounds)
  • AuditLog: 1+ registro por operacao auditada

Impacto no calculo de saldo: O TransactionRepository.getUserBalance() executa um GROUP BY em TODAS as transacoes do usuario:

typescript
const debitTransactions = await tx.transaction.groupBy({
  by: ['type'],
  where: { userId: debitUserId, status: 'COMPLETED' },
  _sum: { amountCents: true },
});

Com 100.000 transacoes por usuario (usuario ativo por 1 ano), este GROUP BY pode levar varios segundos, e isso executa DENTRO de uma transacao atomica com lock distribuido.

Recomendacao:

  1. Particionamento por data (mensal) para Transaction e CaseOpening
  2. Read replicas para queries de leitura pesada
  3. Ou: atualizacao incremental do cache de saldo (decrement/increment) ao inves de recalculo completo

6.5 BullMQ Instalado mas NAO Configurado

Problema: bullmq v5.4.0 esta no package.json mas:

  • Nenhum BullModule.forRoot() ou BullModule.registerQueue() no AppModule
  • Nenhum @Processor() declarado
  • Nenhum @InjectQueue() usado
  • Nenhuma queue configurada
  • Nenhum worker rodando

Batalhas DEVERIAM usar BullMQ ao inves de execucao sincrona inline (veja 6.2). Case openings poderiam beneficiar de processamento em fila em alta carga.

Recomendacao: Configurar BullMQ com:

  • Queue battle-execution para batalhas
  • Dead Letter Queue para jobs falhados
  • Dashboard de monitoramento (Bull Board)

6.6 ZERO Cleanup Jobs

Problema: Nenhum job de cleanup esta implementado para:

RecursoProblema sem cleanupImpacto
Sessions RedisTTL do Redis resolveBaixo
Deposits expirados/abandonadosFicam PENDING no DB indefinidamenteBaixo
Swaps expiradosItems presos em IN_SWAPALTO
Items LOCKED sem withdrawalItems inacessiveisALTO
Battle states no RedisOcupam memoriaBaixo
Items orfaos no inventarioEstado inconsistenteMedio
AuditLog antigosTabela cresce infinitamenteMedio
Transactions antigasTabela cresce infinitamenteMedio

Recomendacao: Implementar CleanupService com multiplos @Cron jobs para cada tipo de recurso.


6.7 Live Drops - Sem Cache Intermediario

Arquivo: src/presentation/controllers/live-drops.controller.ts

Problema: Cada request para GET /live-drops/recent executa queries no banco de dados. Considerando que o endpoint e publico e pode ser chamado por TODOS os visitantes do site, a carga pode ser significativa.

Recomendacao:

  1. Cache Redis com TTL de 10 segundos para live drops
  2. Ou materializar em Redis Sorted Set com TTL de 24h
  3. Cada use case (OpenCase, Upgrade, Swap) insere na lista de live drops apos completar

6.8 Waxpeer Snapshot Cache sem Circuit Breaker

Problema: O WaxpeerSnapshotService serve items publicos de um cache. Se a API do Waxpeer ficar indisponivel:

  • O cache eventualmente expira
  • Sem circuit breaker, o sistema continua tentando chamar a API
  • Cada tentativa gera timeout + retry
  • Acumula threads bloqueadas esperando timeout

Nao ha nenhum circuit-breaker.ts implementado no codigo (apesar de estar listado no CLAUDE.md).

Recomendacao: Implementar circuit breaker pattern para API do Waxpeer:

  • Estado CLOSED: funciona normalmente
  • Estado OPEN: retorna cache antigo sem tentar chamar API
  • Estado HALF-OPEN: tenta uma chamada periodicamente para verificar recovery

7. Recomendacoes Prioritarias

7.1 Criticas - Fazer ANTES de ir para producao

#AcaoArquivo(s)EsforcoDescricao
1Adicionar AdminGuard ao POST /raffle/:id/drawraffle.controller.ts1 linhaQualquer usuario pode sortear raffle
2Adicionar AdminGuard aos endpoints de auditaudit.controller.ts1-3 linhasQualquer usuario acessa logs de auditoria de qualquer entidade
3Implementar cron de cleanup para items LOCKED >1h sem withdrawalNovo service~50 linhasItems ficam permanentemente inacessiveis
4Implementar cron de cleanup para swaps expiradosNovo cron chamando deleteExpiredSwaps() existente~30 linhasItems ficam em IN_SWAP permanentemente
5Remover dependencias mortas (bullmq, kafkajs)package.json2 linhasDependencias sem uso, superficie de ataque
6Mover battle execution para BullMQ workerVarios~200 linhasBloqueia thread por ate 2 minutos
7Verificar logica do bonus 10% de depositodeposit.service.ts~20 linhasBonus em TODOS os depositos, exploravel

7.2 Importantes - Fazer logo apos lancamento

#AcaoArquivo(s)EsforcoDescricao
8Unificar decorator @CurrentUser (deletar user.decorator.ts)4 arquivos~10 linhasDuplicata com risco de divergencia
9Remover dead code (emitWithdrawalProcessing, checkTradeStatusByProjectId)2 arquivos~20 linhasCodigo confuso e nao utilizado
10Implementar referral system ou remover codigoVarios200+ linhasFuncionalidade parcial inutil
11Corrigir GET /event/active com OptionalAuthGuardevent.controller.ts2 linhasuserId nunca populado
12Explicitar guard no POST /upgrade/:id/verifyupgrade.controller.ts1 linhaAmbiguidade de autorizacao
13Adicionar health check para Waxpeer WebSocketwaxpeer-websocket.service.ts~40 linhasDegradacao silenciosa
14Aumentar batch de withdrawals para 500tracker.service.ts1 linhaDelay em periodos de pico
15Redis Sentinel para HAInfra + configAltoSPOF critico
16Nao salvar 2FA secret antes da confirmacaogenerate-2fa-secret.use-case.ts~30 linhasEstado inconsistente
17Usar TransactionService.getUserBalance() no perfilget-user-profile.use-case.ts~5 linhasSource of truth inconsistente
18Corrigir acesso bracket notation em verify2FApayment.controller.ts~10 linhasAnti-pattern de encapsulamento

7.3 Desejaveis - Melhorias continuas

#AcaoArquivo(s)EsforcoDescricao
19Cache de live drops em Redis (TTL 10s)Novo service~50 linhasReduz queries ao banco
20Particionamento de tabelas grandesMigrationsAltoPerformance a longo prazo
21Circuit breaker para APIs externasNovo service~100 linhasResiliencia
22Implementar IP blacklist, rate limiting, TurnstileVariosAltoPrometidos no CLAUDE.md
23Dead letter queue no BullMQConfig~30 linhasJobs falhados nao sao perdidos
24Metricas de latencia por endpointMiddleware~50 linhasVisibilidade de performance
25Read replicas PostgreSQLInfraAltoDistribui carga de leitura
26Atualizar CLAUDE.md para refletir estado realDocMedioElimina falsa sensacao de seguranca
27Substituir console.log por Logger em TransactionServicetransaction.service.ts~20 linhasDados financeiros em logs
28Atualizacao incremental de saldo cachetransaction.service.ts~20 linhasReduz tempo de lock
29WebSocket compressionConfig~5 linhasEconomia de bandwidth
30Recovery automatico para Waxpeer WebSocketwaxpeer-websocket.service.ts~30 linhasAuto-healing

8. Checklist Pre-Producao

8.1 Seguranca - Guards e Autorizacao

  • [ ] POST /raffle/:id/draw tem AdminGuard
  • [ ] GET /audit/verify-chain tem AdminGuard
  • [ ] GET /audit/entity/:type/:id tem AdminGuard
  • [ ] POST /upgrade/:id/verify tem guard explicito (@Public ou AuthGuard)
  • [ ] GET /event/active tem OptionalAuthGuard
  • [ ] Todos os endpoints com @CurrentUser tem guard que popula request.user
  • [ ] Nenhum endpoint admin acessivel sem AdminGuard
  • [ ] Audit trail para todas as operacoes admin (update-user-balance, ban, etc.)
  • [ ] Verificar que @Public() esta apenas em endpoints intencionalmente publicos
  • [ ] Rate limiting implementado em endpoints financeiros (deposito, saque, upgrade)

8.2 Seguranca - Dados e Criptografia

  • [ ] 2FA secret NAO salvo antes da confirmacao
  • [ ] Webhook de pagamento valida assinatura HMAC do provedor
  • [ ] Webhook de pagamento valida IP de origem (whitelist)
  • [ ] Session cookies com httpOnly, secure, sameSite=strict
  • [ ] Helmet configurado (X-Frame-Options, CSP, HSTS, etc.)
  • [ ] Variaveis de ambiente sem segredos hardcoded
  • [ ] .env no .gitignore
  • [ ] Nenhum console.log com dados financeiros em nivel de producao
  • [ ] Provably fair seeds com entropia adequada (256 bits)
  • [ ] Nonce incremental sem possibilidade de reutilizacao

8.3 Integridade Financeira

  • [ ] Source of truth do saldo e SEMPRE TransactionRepository
  • [ ] Double-entry bookkeeping em TODAS as operacoes financeiras
  • [ ] Distributed locks em TODAS as operacoes de saldo
  • [ ] Transacoes atomicas (Prisma $transaction) em operacoes criticas
  • [ ] Bonus de deposito tem logica de negocio correta (primeiro deposito? limite?)
  • [ ] Saldo cache atualizado APOS cada transacao
  • [ ] BigInt em centavos para TODOS os valores monetarios
  • [ ] Nenhuma operacao de ponto flutuante em calculos financeiros

8.4 Cleanup e Manutencao

  • [ ] Cron job para items LOCKED >1h sem withdrawal
  • [ ] Cron job para swaps expirados (>24h)
  • [ ] Cron job ou mecanismo para deposits abandonados
  • [ ] Dead code removido (emitWithdrawalProcessing, checkTradeStatusByProjectId)
  • [ ] Dependencias mortas removidas (bullmq, kafkajs)
  • [ ] Decorator @CurrentUser unificado (deletar user.decorator.ts)
  • [ ] CLAUDE.md atualizado para refletir estado real

8.5 Infraestrutura

  • [ ] Redis com alta disponibilidade (Sentinel ou Cluster, minimo 3 nodes)
  • [ ] PostgreSQL com backups automaticos e PITR testado
  • [ ] PostgreSQL com read replicas para queries pesadas
  • [ ] SSL/TLS em todas as conexoes (API, WebSocket, Redis, PostgreSQL)
  • [ ] Firewall restritivo (apenas portas necessarias)
  • [ ] CDN para assets estaticos
  • [ ] DNS com failover
  • [ ] PgBouncer ou pool equivalente para PostgreSQL

8.6 Monitoramento e Alertas

  • [ ] Health check endpoints ativos (/health verifica Redis, PostgreSQL, Waxpeer)
  • [ ] Prometheus coletando metricas
  • [ ] Grafana com dashboards de CPU, memoria, disco, rede
  • [ ] Dashboard de metricas de negocio (aberturas/dia, revenue, GGR)
  • [ ] Alerta: Waxpeer WebSocket desconectado >5 minutos
  • [ ] Alerta: Taxa de erro HTTP >5% em janela de 5 minutos
  • [ ] Alerta: Latencia P99 >2s em endpoints financeiros
  • [ ] Alerta: Saldo negativo detectado em qualquer usuario
  • [ ] Alerta: Lock timeout em operacao financeira
  • [ ] Alerta: Redis down ou failover

8.7 Resiliencia

  • [ ] Circuit breaker para API Waxpeer
  • [ ] Circuit breaker para API HorsePay
  • [ ] Recovery automatico para Waxpeer WebSocket
  • [ ] Fallback para cache expirado quando API externa indisponivel
  • [ ] BullMQ configurado com Dead Letter Queue
  • [ ] Batalhas executando em worker separado (nao inline)
  • [ ] Rollback procedure documentado e testado

8.8 Testes

  • [ ] Testes unitarios: cobertura >80% em services e use cases
  • [ ] Testes de integracao: fluxos criticos (deposito -> abertura -> saque)
  • [ ] Testes de carga: resultados para 100, 500, 1000 usuarios simultaneos
  • [ ] Testes de penetracao: resultado limpo (sem vulnerabilidades criticas/altas)
  • [ ] Testes de chaos: Redis restart, PostgreSQL failover
  • [ ] Testes de recuperacao: backup restore testado com sucesso

8.9 Operacoes

  • [ ] Logs centralizados (ELK Stack ou equivalente)
  • [ ] Rotacao de logs configurada
  • [ ] Logs sem dados sensiveis em nivel info/warn
  • [ ] Rollback procedure documentado e testado
  • [ ] Runbook para incidentes comuns
  • [ ] Plano de disaster recovery documentado
  • [ ] Procedimento de escalonamento definido

8.10 Conformidade

  • [ ] Termos de uso publicados
  • [ ] Politica de privacidade publicada
  • [ ] Pagina de verificacao provably fair funcional
  • [ ] KYC definido para saques acima de threshold
  • [ ] Anti-fraude com regras basicas (multiplos deposits, velocidade de jogo)

9. Plano de Escalabilidade

9.1 Fase 1: Ate 1.000 usuarios simultaneos

Infraestrutura recomendada:

  • 1 servidor API (4+ CPU cores, 8GB+ RAM)
  • 1 instancia PostgreSQL com backups automaticos
  • 1 instancia Redis com persistencia RDB + AOF
  • Nginx como reverse proxy com SSL termination
  • PM2 com 4 workers (cluster mode)

Configuracao:

ComponenteConfiguracao
Node.jsPM2 cluster mode, 4 workers
PostgreSQLmax_connections: 100, shared_buffers: 2GB
Redismaxmemory: 2GB, maxmemory-policy: allkeys-lru
Nginxworker_connections: 1024
WebSocketSocket.io com 1 instancia (sem Redis Adapter necessario)

Estimativas de carga:

  • ~50 requests/segundo
  • ~500 conexoes WebSocket simultaneas
  • ~10.000 transacoes/dia
  • ~5GB de dados no PostgreSQL/mes

O que implementar ANTES desta fase:

  1. Todos os itens "Criticos" da secao 7.1
  2. Monitoramento basico (Prometheus + Grafana)
  3. Alertas via Slack para erros criticos
  4. Health check endpoints
  5. Backup automatico do PostgreSQL (diario)

Riscos aceitaveis nesta fase:

  • Redis como SPOF (restart manual em ~5 minutos)
  • PostgreSQL sem replicacao (restore de backup em ~30 minutos)
  • Battle execution sincrona (se BullMQ nao implementado)
  • Sem circuit breaker (APIs externas podem causar lentidao)

Metricas de transicao para Fase 2:

  • CPU medio do servidor >60% por mais de 7 dias
  • Latencia P99 >500ms em endpoints financeiros
  • Mais de 800 conexoes WebSocket simultaneas
  • Redis memory usage >70%
  • Taxa de erro HTTP >1%

9.2 Fase 2: 1.000 a 10.000 usuarios simultaneos

Infraestrutura adicional:

  • 2-3 instancias de API (load balanced)
  • Redis Sentinel (1 master + 2 replicas)
  • PostgreSQL replicacao (1 primary + 2 read replicas)
  • PgBouncer para connection pooling
  • CDN para assets estaticos (Cloudflare)
  • Servidor separado para workers BullMQ

Configuracao:

ComponenteConfiguracao
API2-3 instancias, cada uma com PM2 cluster mode
PostgreSQLmax_connections: 300 (via PgBouncer), particionamento mensal
Redis Sentinel3 nodes, maxmemory: 8GB por node
BullMQ4-8 workers concorrentes em servidor separado
WebSocketSocket.io com Redis Adapter para multicast entre instancias
Load BalancerNginx/HAProxy com health checks, sticky sessions para WebSocket

O que implementar ANTES desta fase:

  1. Redis Sentinel (elimina SPOF)
  2. PostgreSQL read replicas + PgBouncer
  3. BullMQ para batalhas (elimina bloqueio de thread)
  4. Socket.io Redis Adapter (multicast entre instancias)
  5. Circuit breaker para APIs externas (Waxpeer, HorsePay)
  6. Cache de live drops em Redis (TTL 10s)
  7. Atualizacao incremental de saldo cache
  8. Particionamento de Transaction e CaseOpening por mes
  9. Rate limiting implementado (IP, usuario, acao)
  10. ELK Stack para centralizacao de logs

Estimativas de carga:

  • ~500 requests/segundo
  • ~5.000 conexoes WebSocket simultaneas
  • ~100.000 transacoes/dia
  • ~50GB de dados no PostgreSQL/mes

Riscos nesta fase:

  • Replication lag do PostgreSQL pode afetar leituras imediatas apos escrita
  • Consistencia eventual entre instancias de API
  • Failover automatico pode causar 2-5 segundos de indisponibilidade

Mitigacoes:

  • Queries criticas (saldo, transacao) SEMPRE no primary
  • Read replicas apenas para historico, relatorios, live drops
  • Health checks com intervalo de 5 segundos

Metricas de transicao para Fase 3:

  • CPU medio >70% nas instancias de API
  • Latencia P99 >200ms em endpoints financeiros
  • Mais de 8.000 conexoes WebSocket simultaneas
  • Redis memory usage >60% em qualquer node
  • Fila BullMQ com >50 jobs pendentes regularmente

9.3 Fase 3: 10.000+ usuarios simultaneos

Arquitetura de microservicos:

                     CDN (Cloudflare)
                          |
                    Load Balancer
                     (HAProxy)
                          |
           +--------------+--------------+
           |              |              |
        API Pod 1      API Pod 2      API Pod N
        (NestJS)       (NestJS)       (NestJS)
           |              |              |
           +--------------+--------------+
                          |
                   Redis Cluster
                   (6+ nodes)
                          |
              +-----------+-----------+
              |           |           |
          PgBouncer   PgBouncer   PgBouncer
              |           |           |
          Primary     Replica 1   Replica 2
        (PostgreSQL) (PostgreSQL) (PostgreSQL)

Infraestrutura:

  • Kubernetes ou Docker Swarm para orquestracao
  • 4-8 pods de API com auto-scaling
  • Redis Cluster (6+ nodes: 3 masters + 3 replicas)
  • PostgreSQL com particionamento + sharding por userId (se necessario)
  • Microservicos separados para funcionalidades isolaveis
  • Message queue dedicada (BullMQ com Redis Cluster)

Microservicos sugeridos:

ServicoResponsabilidadePodsComunicacao
API GatewayRoteamento, auth, rate limiting4-8HTTP
Game EngineAberturas, upgrades2-4BullMQ
Battle ServiceBatalhas (execucao assincrona)2-4BullMQ
Waxpeer ServiceIntegracao, tracking, swaps1-2BullMQ + WebSocket
Payment ServiceDepositos, saques, webhooks1-2HTTP + Webhook
Notification ServiceWebSocket, push2-4Redis Pub/Sub
Admin ServiceBackoffice, relatorios1HTTP
Worker ServiceBullMQ jobs genericos4-8BullMQ

Otimizacoes necessarias nesta fase:

  1. CQRS (Command Query Responsibility Segregation)
  2. Event Sourcing para audit trail completo
  3. Materialized views para queries pesadas
  4. gRPC para comunicacao entre microservicos
  5. Horizontal sharding do PostgreSQL por userId
  6. Redis Cluster com 32GB+ de memoria total
  7. Auto-scaling horizontal baseado em metricas customizadas
  8. Connection pooling com pool_mode=transaction (PgBouncer)
  9. Compressao WebSocket habilitada
  10. Read replicas dedicadas para relatorios e backoffice

Estimativas de carga:

  • ~5.000 requests/segundo
  • ~50.000 conexoes WebSocket simultaneas
  • ~1.000.000 transacoes/dia
  • ~500GB de dados no PostgreSQL/mes

Riscos nesta fase:

  • Complexidade operacional significativamente maior
  • Consistencia eventual entre microservicos
  • Necessidade de equipe DevOps dedicada (minimo 2 pessoas)
  • Custo de infraestrutura 10-20x maior que Fase 1
  • Debugging distribuido requer tooling especializado (Jaeger, Zipkin)

Decisoes arquiteturais:

  • Se o sistema atingir 50.000+ usuarios simultaneos, considerar migracao para Kafka ao inves de Redis Pub/Sub para garantir ordering e durabilidade de mensagens
  • Considerar separacao do banco de dados por dominio (financial DB, game DB, user DB)
  • Implementar API Gateway dedicado (Kong, Envoy) ao inves de NestJS como gateway

9.4 Tabela Resumo de Transicao entre Fases

MetricaFase 1Transicao 1->2Fase 2Transicao 2->3Fase 3
Usuarios simultaneos<1.000800+ por 7 dias1.000-10.0008.000+ por 7 dias10.000+
Requests/segundo<50>4050-500>400500+
WebSocket conexoes<500>400500-5.000>4.0005.000+
CPU medio<60%>60%<70%>70%--
Latencia P99 (financeiro)<500ms>500ms<200ms>200ms<100ms
Redis memory<70%>70%<60%>60%--
Instancias API1--2-3--4-8
RedisSingle-> SentinelSentinel-> ClusterCluster
PostgreSQLSingle-> Replicas1P+2R-> ShardSharded
Workers BullMQ0 (inline)-> 22-4-> 88-16
Downtime por falha~30 min--~5 min--<30 seg
Custo mensal estimado$50-200--$500-2.000--$2.000-10.000

10. Apendices

Apendice A: Mapa de Arquivos Criticos com Problemas Identificados

ArquivoProblemaSecaoSeveridade
src/presentation/controllers/raffle.controller.tsSem AdminGuard no draw2.1CRITICO
src/presentation/controllers/audit.controller.tsSem AdminGuard em verify-chain e entity2.2CRITICO
src/presentation/controllers/upgrade.controller.tsSem guard em verify2.3ALTO
src/presentation/controllers/event.controller.tsSem OptionalAuthGuard em active2.4ALTO
src/presentation/decorators/user.decorator.tsDuplicata2.5ALTO
src/application/services/deposit.service.tsBonus 10% em todos depositos2.9MEDIO
src/application/use-cases/payment/generate-2fa-secret.use-case.tsSecret salvo antes de confirmar2.10MEDIO
src/application/use-cases/user/get-user-profile.use-case.tsUsa cache ao inves de source of truth2.11MEDIO
src/infrastructure/external-apis/waxpeer-websocket.service.tsMax reconnect sem recovery2.12MEDIO
src/application/services/waxpeer-withdrawal-tracker.service.tsBatch limit 100, items LOCKED2.7, 2.13ALTO
src/infrastructure/websocket/websocket.gateway.tsemitWithdrawalProcessing morto4.1BAIXO
src/infrastructure/external-apis/waxpeer.service.tscheckTradeStatusByProjectId morto4.2BAIXO
src/application/services/transaction.service.tsconsole.log com dados financeiros6.4MEDIO
src/application/use-cases/battle/execute-battle.use-case.tsExecucao bloqueante inline6.2ALTO
src/presentation/controllers/payment.controller.tsBracket notation para acessar propriedade privada2.10MEDIO

Apendice B: Endpoints Sem Guard Adequado (Resumo)

EndpointGuard AtualGuard NecessarioRisco
POST /raffle/:id/drawAuthGuardAuthGuard + AdminGuardCRITICO
GET /audit/verify-chainAuthGuardAuthGuard + AdminGuardCRITICO
GET /audit/entity/:type/:idAuthGuardAuthGuard + AdminGuardCRITICO
POST /upgrade/:id/verifyNenhum explicito@Public() ou AuthGuardALTO
GET /event/activeNenhumOptionalAuthGuard + @Public()ALTO

Apendice C: Superficie de Ataque - Endpoints Publicos

Endpoints marcados com @Public() que NAO requerem autenticacao:

Autenticacao:

  • GET /auth/test - Endpoint de teste
  • GET /auth/steam/login - Inicia fluxo OAuth
  • GET /auth/steam/callback - Callback OAuth

Catalogo:

  • GET /cases - Lista caixas
  • GET /cases/categories - Categorias
  • GET /cases/:id - Detalhes da caixa
  • GET /cases/:id/items - Items da caixa
  • GET /cases/:id/probabilities - Probabilidades

Live Drops:

  • GET /live-drops/recent - Drops recentes
  • GET /live-drops/top - Top drops

Battles:

  • GET /battles/list - Lista de batalhas

Upgrade:

  • GET /upgrade/targets - Items alvo para upgrade

Swap:

  • GET /swap/targets - Items alvo para swap

Raffle:

  • GET /raffle/active - Sorteios ativos
  • GET /raffle/completed - Sorteios finalizados
  • GET /raffle/:id - Detalhes do sorteio (OptionalAuthGuard)

Provably Fair:

  • POST /provably-fair/generate-seeds - Gera seeds
  • POST /provably-fair/verify-seed - Verifica seed
  • POST /provably-fair/calculate-result - Calcula resultado
  • POST /provably-fair/verify-result - Verifica resultado

Items:

  • GET /api/items/* - Todos os endpoints de items publicos

Pagamentos:

  • POST /payment/deposit/webhook/:provider - Webhook de deposito

Infraestrutura:

  • GET /health - Health check
  • GET /docs - API reference (Scalar)

Aberturas:

  • GET /cases/openings/recent - Aberturas recentes

NOTA: Verificar se GET /auth/test deveria ser publico em producao. Endpoints de teste geralmente devem ser removidos ou desabilitados em producao.


Apendice D: Fluxo de Dados Financeiro Critico

Usuario solicita operacao financeira
         |
         v
  [AuthGuard] - Valida sessao, IP, User-Agent
         |
         v
  [Controller] - Recebe e valida input (DTOs com class-validator)
         |
         v
  [Use Case] - Logica de negocio, validacoes de regra
         |
         v
  [LockService.withUserBalanceLock(userId)] - Adquire lock Redlock (TTL 10s)
         |
         v
  [PrismaService.$transaction()] - Inicia transacao atomica PostgreSQL
         |
         v
  [TransactionRepository.getUserBalance()] - GROUP BY em todas as transacoes (SOURCE OF TRUTH)
         |
         v
  [Verifica saldo >= amountCents] - Rejeita se insuficiente
         |
         v
  [TransactionService.createDoubleEntry()] - Cria DEBIT + CREDIT atomicamente
         |
         v
  [User.balanceCents update] - Atualiza CACHE de saldo no User
         |
         v
  [COMMIT] - Transacao PostgreSQL commitada
         |
         v
  [Lock RELEASE] - Redlock liberado
         |
         v
  [WebSocketGateway.emitBalanceUpdate(userId, balanceCents: bigint)]
         |
         v
  [Frontend recebe balanceCents (string de inteiro)] -> parseInt -> exibe com toLocaleString('pt-BR')

Pontos de falha e protecoes:

  • Se o lock falha: operacao nao inicia (retry ate 10x com jitter)
  • Se a transacao falha: rollback automatico do PostgreSQL
  • Se o WebSocket falha: saldo correto no banco, frontend atualiza no proximo GET /user/profile
  • Se o Redis cai: lock nao pode ser adquirido, operacao rejeitada (seguro, nao causa inconsistencia)

Apendice E: Glossario

TermoDefinicao
AuthGuardGuard que valida sessao, IP e User-Agent do usuario
AdminGuardGuard que verifica role ADMIN ou SUPER_ADMIN
OptionalAuthGuardGuard que popula request.user se houver sessao, mas permite acesso anonimo
@Public()Decorator que marca endpoint como publico (bypass do AuthGuard global)
@CurrentUserDecorator que extrai usuario da request (depende de guard que popule request.user)
Source of TruthFonte unica de verdade para um dado. Para saldo: TransactionRepository
Cache de saldoCampo user.balanceCents - atualizado apos cada transacao, NAO e source of truth
Double-entryToda transacao cria DEBIT + CREDIT. Soma de todas = 0
SYSTEM_USER_IDBigInt(0) - usuario virtual que representa a casa/plataforma
RedlockAlgoritmo de lock distribuido usando Redis
BullMQBiblioteca de filas usando Redis (instalada mas nao configurada)
WaxpeerAPI externa de marketplace de skins CS2
HorsePayProvedor de pagamento PIX
Circuit BreakerPattern de resiliencia que para chamadas a servicos com falha (nao implementado)
Dead Letter QueueFila para jobs que falharam apos todas as tentativas (nao implementado)
SPOFSingle Point of Failure - componente cuja falha derruba o sistema inteiro
LOCKEDStatus de item de inventario reservado para withdrawal em andamento
IN_SWAPStatus de item de inventario reservado para swap pendente
Provably FairSistema criptografico de verificacao independente de resultados
FLIPMecanica de segunda roleta com items raros
SettlementDistribuicao justa de items entre vencedores de batalha

Documento gerado em 2026-02-11 por analise automatizada do codigo-fonte.Deve ser revisado e atualizado a cada release importante, correcao de seguranca, ou mudanca arquitetural significativa.Proxima revisao recomendada: antes do lancamento em producao.

Documentação Técnica - ProCases