- 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>
16 KiB
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:
- 5 categorías de las 8 disponibles
- 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
- Socket.IO con Redis Adapter - Para sincronizar eventos entre múltiples instancias
- Sticky Sessions - Para mantener conexiones WebSocket
- PostgreSQL Read Replicas - Para queries de lectura
- Redis Cluster - Para alta disponibilidad del estado
Seguridad
- CORS - Configurado para dominios permitidos
- Rate Limiting - En endpoints REST
- Input Validation - Pydantic schemas
- SQL Injection - Prevenido por SQLAlchemy ORM
- XSS - React escapa contenido por defecto
- WebSocket Auth - Validación de sala/jugador en cada evento