- 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>
7.7 KiB
API Reference - Trivy
Base URL
http://localhost:8000/api
Autenticación
El panel de administración usa autenticación básica HTTP.
# Ejemplo con curl
curl -u admin:admin123 http://localhost:8000/api/admin/questions
Endpoints REST
Game
GET /game/categories
Obtiene todas las categorías disponibles.
Response:
[
{
"id": 1,
"name": "Nintendo",
"icon": "🍄",
"color": "#E60012"
},
{
"id": 2,
"name": "Xbox",
"icon": "🎮",
"color": "#107C10"
}
]
GET /game/today-questions
Obtiene las preguntas activas para hoy (sin respuestas).
Response:
{
"1": [
{
"id": 1,
"category_id": 1,
"question_text": "¿En qué año se lanzó la NES?",
"difficulty": 1,
"points": 100,
"time_seconds": 15
}
]
}
Admin - Preguntas
GET /admin/questions
Lista todas las preguntas.
Query Parameters:
status(optional):pending,approved,usedcategory_id(optional): ID de categoría
Response:
[
{
"id": 1,
"category_id": 1,
"question_text": "¿En qué año se lanzó la NES?",
"correct_answer": "1983",
"alt_answers": ["1984"],
"difficulty": 1,
"points": 100,
"time_seconds": 15,
"date_active": "2024-01-26",
"status": "approved",
"fun_fact": "La NES salvó la industria del videojuego",
"created_at": "2024-01-25T10:00:00Z"
}
]
POST /admin/questions
Crea una nueva pregunta.
Request Body:
{
"category_id": 1,
"question_text": "¿Cuál es el nombre del fontanero de Nintendo?",
"correct_answer": "Mario",
"alt_answers": ["Super Mario"],
"difficulty": 1,
"points": 100,
"time_seconds": 15,
"date_active": "2024-01-26",
"status": "approved",
"fun_fact": "Mario apareció por primera vez en Donkey Kong"
}
Response: 201 Created
PUT /admin/questions/{id}
Actualiza una pregunta existente.
DELETE /admin/questions/{id}
Elimina una pregunta.
Admin - Categorías
GET /admin/categories
Lista todas las categorías.
POST /admin/categories
Crea una nueva categoría.
Request Body:
{
"name": "Ciencia",
"icon": "🔬",
"color": "#00BCD4"
}
Admin - Salas
GET /admin/rooms/active
Obtiene las salas activas en Redis.
Response:
[
{
"code": "ABC123",
"status": "playing",
"host": "Player1",
"teams": {
"A": [{"name": "Player1", "team": "A"}],
"B": [{"name": "Player2", "team": "B"}]
},
"scores": {"A": 500, "B": 300},
"current_team": "A"
}
]
WebSocket Events
Conexión
import { io } from 'socket.io-client';
const socket = io('http://localhost:8000', {
transports: ['websocket', 'polling']
});
Eventos Cliente → Servidor
create_room
Crea una nueva sala de juego.
socket.emit('create_room', {
player_name: 'MiNombre'
});
join_room
Unirse a una sala existente.
socket.emit('join_room', {
room_code: 'ABC123',
player_name: 'MiNombre',
team: 'A' // o 'B'
});
change_team
Cambiar de equipo en el lobby.
socket.emit('change_team', {
team: 'B'
});
start_game
Iniciar el juego (solo host).
socket.emit('start_game', {});
select_question
Seleccionar una pregunta del tablero.
socket.emit('select_question', {
question_id: 1,
category_id: 1
});
submit_answer
Enviar respuesta a la pregunta actual.
socket.emit('submit_answer', {
answer: 'Mi respuesta',
question: { /* objeto pregunta */ },
is_steal: false
});
steal_decision
Decidir si intentar robar.
socket.emit('steal_decision', {
attempt: true, // o false para pasar
question_id: 1,
answer: 'Mi respuesta' // solo si attempt=true
});
chat_message
Enviar mensaje al chat general.
socket.emit('chat_message', {
message: 'Hola a todos!'
});
team_message
Enviar mensaje al chat de equipo.
socket.emit('team_message', {
room_code: 'ABC123',
team: 'A',
player_name: 'MiNombre',
message: 'Mensaje privado del equipo'
});
send_reaction
Enviar reacción emoji.
socket.emit('send_reaction', {
emoji: '🎉',
room_code: 'ABC123',
player_name: 'MiNombre'
});
Eventos Servidor → Cliente
room_created
Confirmación de sala creada.
socket.on('room_created', (data) => {
console.log(data.room.code); // 'ABC123'
});
Payload:
{
"room": {
"code": "ABC123",
"status": "waiting",
"host": "Player1",
"teams": {"A": [...], "B": []},
"scores": {"A": 0, "B": 0}
}
}
player_joined
Un jugador se unió a la sala.
socket.on('player_joined', (data) => {
console.log('Nuevo jugador:', data.room.teams);
});
player_left
Un jugador abandonó la sala.
socket.on('player_left', (data) => {
console.log('Jugador salió');
});
game_started
El juego ha comenzado.
socket.on('game_started', (data) => {
// data.room.board contiene el tablero con las preguntas
console.log('Categorías:', Object.keys(data.room.board));
});
Payload:
{
"room": {
"status": "playing",
"board": {
"1": [
{
"id": 1,
"question_text": "...",
"difficulty": 1,
"points": 100,
"answered": false
}
]
},
"current_team": "A",
"current_player_index": {"A": 0, "B": 0}
}
}
question_selected
Se seleccionó una pregunta.
socket.on('question_selected', (data) => {
console.log('Pregunta:', data.question_id);
});
answer_result
Resultado de una respuesta.
socket.on('answer_result', (data) => {
if (data.valid) {
console.log('¡Correcto! +', data.points_earned);
} else {
console.log('Incorrecto:', data.reason);
}
});
Payload:
{
"valid": true,
"reason": "Respuesta correcta",
"points_earned": 100,
"was_steal": false,
"room": { /* estado actualizado */ }
}
steal_prompt
Oportunidad de robo disponible.
socket.on('steal_prompt', (data) => {
console.log('¡Puedes robar!');
});
game_finished
El juego ha terminado.
socket.on('game_finished', (data) => {
console.log('Ganador:', data.winner);
console.log('Puntuación final:', data.room.scores);
});
new_reaction
Nueva reacción de un jugador.
socket.on('new_reaction', (data) => {
console.log(data.player_name, 'reaccionó con', data.emoji);
});
team_message
Mensaje de chat de equipo.
socket.on('team_message', (data) => {
console.log(`[Equipo] ${data.player_name}: ${data.message}`);
});
error
Error del servidor.
socket.on('error', (data) => {
console.error('Error:', data.message);
});
Códigos de Error
| Código | Mensaje | Descripción |
|---|---|---|
room_not_found |
Room not found | La sala no existe |
room_full |
Room is full | La sala está llena |
not_host |
Only the host can start | Solo el host puede iniciar |
need_players |
Both teams need players | Ambos equipos necesitan jugadores |
not_your_turn |
Not your turn | No es tu turno |
question_answered |
Question already answered | Pregunta ya contestada |
Rate Limiting
- API REST: 100 requests/minuto por IP
- WebSocket: Sin límite específico (controlado por lógica de juego)
Timeouts
- Preguntas: 15-35 segundos según dificultad
- Robo: 50% del tiempo original
- Sala inactiva: 3 horas (configurable)