# 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) ```json { "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) ```sql -- 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: ```python # 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 ```python # 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 ```typescript // 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