Files
Trivy/docs/ARCHITECTURE.md
consultoria-as 6248037b47 docs: añade documentación completa del proyecto
- README.md: descripción general, stack, instalación rápida
- docs/API.md: referencia completa de API REST y WebSocket
- docs/ARCHITECTURE.md: arquitectura del sistema con diagramas
- docs/INSTALLATION.md: guía detallada de instalación
- backend/.env.example: plantilla de configuración

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-26 23:50:34 +00:00

16 KiB

Arquitectura de Trivy

Visión General

Trivy utiliza una arquitectura de microservicios con comunicación en tiempo real mediante WebSockets.

┌─────────────────────────────────────────────────────────────────────┐
│                            CLIENTES                                  │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐            │
│  │ Browser  │  │ Browser  │  │ Browser  │  │ Browser  │            │
│  │ React App│  │ React App│  │ React App│  │ React App│            │
│  └────┬─────┘  └────┬─────┘  └────┬─────┘  └────┬─────┘            │
│       │             │             │             │                    │
│       └─────────────┴──────┬──────┴─────────────┘                    │
│                            │                                         │
│                     WebSocket/HTTP                                   │
└────────────────────────────┼─────────────────────────────────────────┘
                             │
┌────────────────────────────┼─────────────────────────────────────────┐
│                      BACKEND                                         │
│                            │                                         │
│  ┌─────────────────────────▼─────────────────────────────┐          │
│  │                    FastAPI + Socket.IO                 │          │
│  │  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐   │          │
│  │  │  REST API   │  │  WebSocket  │  │   ASGI      │   │          │
│  │  │  Endpoints  │  │   Events    │  │  Middleware │   │          │
│  │  └──────┬──────┘  └──────┬──────┘  └─────────────┘   │          │
│  └─────────┼────────────────┼────────────────────────────┘          │
│            │                │                                        │
│  ┌─────────▼────────────────▼────────────────────────────┐          │
│  │                    SERVICES                            │          │
│  │  ┌────────────┐  ┌────────────┐  ┌────────────┐       │          │
│  │  │   Room     │  │   Game     │  │    AI      │       │          │
│  │  │  Manager   │  │  Manager   │  │ Validator  │       │          │
│  │  └─────┬──────┘  └─────┬──────┘  └─────┬──────┘       │          │
│  └────────┼───────────────┼───────────────┼──────────────┘          │
│           │               │               │                          │
│     ┌─────▼─────┐   ┌─────▼─────┐   ┌─────▼─────┐                   │
│     │   Redis   │   │ PostgreSQL│   │  Claude   │                   │
│     │  (State)  │   │   (Data)  │   │   API     │                   │
│     └───────────┘   └───────────┘   └───────────┘                   │
└──────────────────────────────────────────────────────────────────────┘

Componentes

Frontend (React + TypeScript)

frontend/src/
├── components/          # Componentes reutilizables
│   ├── chat/           # EmojiReactions, ReactionOverlay, TeamChat
│   ├── game/           # Componentes del tablero
│   ├── lobby/          # Sala de espera
│   └── ui/             # SoundControl, botones, inputs
├── hooks/              # Custom hooks
│   ├── useSocket.ts    # Gestión de WebSocket
│   └── useSound.ts     # Gestión de audio
├── pages/              # Rutas principales
│   ├── Home.tsx        # Crear/unir sala
│   ├── Lobby.tsx       # Sala de espera
│   ├── Game.tsx        # Tablero de juego
│   ├── Results.tsx     # Resultados finales
│   └── admin/          # Panel de administración
├── services/
│   └── socket.ts       # Singleton de Socket.IO
├── stores/             # Estado global (Zustand)
│   ├── gameStore.ts    # Estado del juego
│   ├── themeStore.ts   # Tema visual
│   └── soundStore.ts   # Configuración de audio
├── themes/             # Definiciones de temas
└── types/              # Tipos TypeScript

Backend (FastAPI + Python)

backend/app/
├── api/                # Endpoints REST
│   ├── admin.py        # CRUD de preguntas/categorías
│   ├── auth.py         # Autenticación admin
│   └── game.py         # Datos públicos del juego
├── models/             # Modelos SQLAlchemy
│   ├── question.py     # Preguntas
│   ├── category.py     # Categorías
│   ├── admin.py        # Administradores
│   └── game_session.py # Sesiones de juego
├── schemas/            # Schemas Pydantic
├── services/           # Lógica de negocio
│   ├── room_manager.py    # Gestión de salas (Redis)
│   ├── game_manager.py    # Lógica del juego
│   ├── question_service.py # Carga de preguntas
│   └── ai_validator.py    # Validación con Claude
├── sockets/            # Eventos WebSocket
│   └── game_events.py  # Todos los eventos del juego
├── config.py           # Configuración
└── main.py             # Punto de entrada ASGI

Flujo de Datos

1. Creación de Sala

Cliente                    Backend                     Redis
   │                          │                          │
   │──── create_room ────────>│                          │
   │                          │                          │
   │                          │──── SETEX room:CODE ────>│
   │                          │                          │
   │                          │<─── OK ─────────────────│
   │                          │                          │
   │<─── room_created ────────│                          │
   │                          │                          │

2. Unirse a Sala

Cliente                    Backend                     Redis
   │                          │                          │
   │──── join_room ──────────>│                          │
   │                          │                          │
   │                          │──── GET room:CODE ──────>│
   │                          │<─── room_data ──────────│
   │                          │                          │
   │                          │──── SETEX (updated) ────>│
   │                          │                          │
   │<─── player_joined ───────│                          │
   │                          │                          │
   │                          │──── emit to room ───────>│ (otros clientes)

3. Inicio del Juego

Cliente                    Backend                     PostgreSQL
   │                          │                          │
   │──── start_game ─────────>│                          │
   │                          │                          │
   │                          │──── SELECT questions ───>│
   │                          │     (5 random cats)      │
   │                          │<─── questions ──────────│
   │                          │                          │
   │                          │──── SELECT 1 per diff ──>│
   │                          │<─── board data ─────────│
   │                          │                          │
   │<─── game_started ────────│                          │
   │     (with board)         │                          │

4. Responder Pregunta

Cliente                    Backend                     Claude API
   │                          │                          │
   │──── submit_answer ──────>│                          │
   │                          │                          │
   │                          │──── validate_answer ────>│
   │                          │     (question, answer)   │
   │                          │                          │
   │                          │<─── {valid, reason} ────│
   │                          │                          │
   │<─── answer_result ───────│                          │
   │                          │                          │

Estado del Juego

Redis (Estado en Tiempo Real)

{
  "room:ABC123": {
    "code": "ABC123",
    "status": "playing",
    "host": "Player1",
    "teams": {
      "A": [{"name": "Player1", "team": "A", "socket_id": "..."}],
      "B": [{"name": "Player2", "team": "B", "socket_id": "..."}]
    },
    "current_team": "A",
    "current_player_index": {"A": 0, "B": 0},
    "current_question": null,
    "can_steal": false,
    "scores": {"A": 500, "B": 300},
    "board": {
      "1": [/* preguntas categoría 1 */],
      "3": [/* preguntas categoría 3 */]
    }
  },
  "player:socket_id": {
    "room": "ABC123",
    "name": "Player1",
    "team": "A"
  }
}

PostgreSQL (Datos Persistentes)

-- Categorías
categories (id, name, icon, color)

-- Preguntas (pool de 200)
questions (
  id, category_id, question_text, correct_answer,
  alt_answers[], difficulty, points, time_seconds,
  date_active, status, fun_fact
)

-- Sesiones de juego (historial)
game_sessions (
  id, room_code, status,
  team_a_score, team_b_score,
  questions_used[], created_at, finished_at
)

-- Eventos de juego (analytics)
game_events (
  id, session_id, event_type, player_name,
  question_id, data, created_at
)

-- Administradores
admins (id, username, password_hash)

Validación de Respuestas con IA

El sistema usa Claude para validación flexible de respuestas:

# ai_validator.py
async def validate_answer(
    question: str,
    correct_answer: str,
    alt_answers: list,
    player_answer: str
) -> dict:
    prompt = f"""
    Pregunta: {question}
    Respuesta correcta: {correct_answer}
    Respuestas alternativas: {alt_answers}
    Respuesta del jugador: {player_answer}

    ¿La respuesta del jugador es correcta?
    Considera sinónimos, abreviaciones, errores menores de ortografía.

    Responde en JSON: {{"valid": bool, "reason": "explicación"}}
    """

    response = await anthropic.messages.create(
        model="claude-3-haiku-20240307",
        messages=[{"role": "user", "content": prompt}]
    )

    return json.loads(response.content[0].text)

Selección de Preguntas

Cada partida selecciona aleatoriamente:

  1. 5 categorías de las 8 disponibles
  2. 1 pregunta por dificultad de las 5 opciones disponibles
# question_service.py
async def get_board_for_game(db, target_date):
    # 1. Obtener todas las preguntas del día
    full_board = await get_daily_questions(db, target_date)

    # 2. Seleccionar 5 categorías aleatorias
    selected_cats = random.sample(list(full_board.keys()), 5)

    # 3. Para cada categoría, seleccionar 1 pregunta por dificultad
    game_board = {}
    for cat_id in selected_cats:
        questions_by_diff = group_by_difficulty(full_board[cat_id])
        selected = []
        for diff in range(1, 6):
            if diff in questions_by_diff:
                selected.append(random.choice(questions_by_diff[diff]))
        game_board[cat_id] = selected

    return game_board

Temas y Sonidos

Estructura de Temas

// themes/drrr.ts
export const drrrTheme: ThemeConfig = {
  name: 'drrr',
  displayName: 'DRRR Style',
  colors: {
    primary: '#FFD93D',
    secondary: '#6BCB77',
    accent: '#FF6B6B',
    bg: '#1a1a2e',
    text: '#EAEAEA',
    textMuted: '#888888'
  },
  fonts: {
    heading: '"Press Start 2P", monospace',
    body: 'Inter, sans-serif'
  }
}

Estructura de Sonidos

public/sounds/
├── drrr/
│   ├── correct.mp3
│   ├── incorrect.mp3
│   ├── select.mp3
│   └── ...
├── retro/
├── minimal/
├── rgb/
└── anime/

Cada tema tiene sus propios archivos de audio, con fallback a sonidos generados por Web Audio API si no existen.

Escalabilidad

Horizontal Scaling

                    ┌─────────────┐
                    │   NGINX     │
                    │   (LB)      │
                    └──────┬──────┘
                           │
         ┌─────────────────┼─────────────────┐
         │                 │                 │
   ┌─────▼─────┐    ┌─────▼─────┐    ┌─────▼─────┐
   │  Backend  │    │  Backend  │    │  Backend  │
   │  Node 1   │    │  Node 2   │    │  Node 3   │
   └─────┬─────┘    └─────┬─────┘    └─────┬─────┘
         │                │                │
         └────────────────┼────────────────┘
                          │
              ┌───────────┴───────────┐
              │                       │
        ┌─────▼─────┐          ┌─────▼─────┐
        │   Redis   │          │ PostgreSQL│
        │  Cluster  │          │  Primary  │
        └───────────┘          └───────────┘

Consideraciones

  1. Socket.IO con Redis Adapter - Para sincronizar eventos entre múltiples instancias
  2. Sticky Sessions - Para mantener conexiones WebSocket
  3. PostgreSQL Read Replicas - Para queries de lectura
  4. Redis Cluster - Para alta disponibilidad del estado

Seguridad

  1. CORS - Configurado para dominios permitidos
  2. Rate Limiting - En endpoints REST
  3. Input Validation - Pydantic schemas
  4. SQL Injection - Prevenido por SQLAlchemy ORM
  5. XSS - React escapa contenido por defecto
  6. WebSocket Auth - Validación de sala/jugador en cada evento