> ## Documentation Index
> Fetch the complete documentation index at: https://vantsgg.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> Documentación interna de VANTS, la plataforma de esports, escrita en español. Cubre siete repositorios de la organización de GitHub VANTSNET y dos de la cuenta feispla: el prototipo VANTSNET, que se llama igual que la organización, y vantsoport.
> Empieza por /mapa-del-codigo para saber qué repositorio y qué carpeta tocar, y consulta /glosario para los términos propios como pt_, mm_, RR, VANTS-BUS o VANTCALL.
> Cuando un componente existe en varios repositorios, estas páginas describen la versión de Vantsgames salvo que digan otra cosa. El esquema de la base de datos se versiona en VantsBasededatos.
> La web y la API pública están en https://vants.vants.workers.dev. Rutas y respuestas: /referencia/api-publica. Tablas y estados: /referencia/esquema-base-de-datos.
> No inventes rutas, tablas, comandos ni variables de entorno: si algo no aparece aquí, revisa el código del repositorio correspondiente.

# VANTSNET

> Prototipo de red de estadísticas y plataforma competitiva en Next.js y Prisma: Valorant, Steam, torneos, equipos, Discord, FACEIT, puesta en marcha y CI.

Prototipo de una red de estadísticas de videojuegos y plataforma competitiva: perfiles de jugadores, historial de partidas y scoreboards, con Valorant como primer juego, más torneos, equipos, inscripciones y resultados. Está en la cuenta de GitHub feispla ([feispla/VANTSNET](https://github.com/feispla/VANTSNET)), no en la organización VANTSNET aunque se llame igual, y no comparte código ni servicios con el resto de repositorios: no usa Auth0. Cada push a una rama se despliega en Prisma Cloud con el workflow `prisma-deploy.yml`: `main` va a producción y las demás ramas crean un preview con su propia base de datos.

Stack: Next.js 16 (App Router), React 19, TypeScript, Tailwind 4, componentes de shadcn/ui, Zustand, Prisma ORM 8 con PostgreSQL (el contrato vive en `src/prisma/contract.prisma` y la conexión en `prisma.config.ts`), zod para validar las entradas de la API y Vitest para los tests. La versión del paquete es la 0.2.0.

> [!NOTE]
>
> Riot reserva los datos de partidas de Valorant (VAL-MATCH-V1) para claves de producción. Con una clave de desarrollo la app verifica cuentas con `account-v1` y explica el bloqueo; sin clave solo funciona el modo demo.

## Qué incluye

* **Modo demo**: busca `Demo#VANTS` sin ninguna clave. Son 8 partidas simuladas de forma determinista, con estadísticas agregadas (winrate, K/D, ACS, ADR y porcentaje de headshots) y scoreboard por partida.
* **Valorant con la API de Riot**: resuelve Riot IDs reales (`Nombre#TAG`) y, con clave de producción, guarda y agrega las últimas 8 partidas del jugador.
* **Steam**: `GET /api/steam/resolve` devuelve un perfil de Steam y sus 10 juegos con más horas.
* **Plataforma competitiva**: torneos con estados (borrador, inscripción, en juego, completado o cancelado), equipos con plantilla, inscripciones sin duplicados, partidas con resultados validados y un registro de auditoría de cada cambio. Las lecturas son públicas y los cambios exigen el token de administración.
* **Discord y FACEIT**: vinculación de cuentas de Discord con OAuth2 y consulta de jugadores de FACEIT. Sin credenciales responden `503` con el código `NOT_CONFIGURED` en lugar de fallar.
* **Estado del servicio**: `GET /api/health` y la pestaña «Estado» indican si la base de datos responde y qué integraciones están configuradas, sin mostrar ninguna clave.

## Interfaz

Todo está en una sola página (`/`) que cambia de vista sin recargar:

* **Buscador**: un Riot ID y una región. El botón «API key» permite pegar una clave de Riot propia (`RGAPI-...`). Se guarda en el navegador y se envía al backend en la cabecera `x-riot-key`, que tiene prioridad sobre `RIOT_API_KEY`.
* **Perfil y partida**: estadísticas agregadas, resumen por agente, últimas partidas y scoreboard.
* **Competitivo** (botón con el trofeo en la cabecera): pestañas Torneos, Equipos y Estado, con datos de la base de datos. El detalle de un torneo muestra sus inscripciones y partidas.

Con «Soy admin» se introduce el token de administración, que solo se recuerda en esa pestaña del navegador. Con él aparecen las acciones de gestión: crear torneos (empiezan en borrador) y equipos, cambiar el estado de un torneo, inscribir equipos, aprobar o rechazar inscripciones, programar partidas y reportar resultados.

Discord y FACEIT todavía no tienen botones en la interfaz: se usan por API. Al volver de Discord, la página muestra el resultado de la vinculación (`?auth=linked` o `?auth=error`) y limpia la URL.

## Estructura

```text theme={null}
src/app/page.tsx                interfaz: buscador, perfil, partida y vista Competitivo en una sola página
src/app/api/valorant/           GET profile y match/{matchId}
src/app/api/steam/resolve/      GET /api/steam/resolve
src/app/api/tournaments/        torneos, inscripciones y partidas
src/app/api/teams/              equipos y miembros
src/app/api/admin/audit/        GET /api/admin/audit
src/app/api/auth/discord/       start, callback, status y link de la vinculación con Discord
src/app/api/faceit/player/      GET /api/faceit/player
src/app/api/health/             GET /api/health
src/components/vantsnet/        vistas: search-view, profile-view, match-view, hub-view, tournament-detail y agent-avatar
src/components/ui/              componentes de shadcn/ui
src/lib/store.ts                estado global de la interfaz (Zustand)
src/lib/valorant/riot.ts        cliente de la API de Riot, caché en base de datos y agregación
src/lib/valorant/demo-data.ts   datos del modo demo
src/lib/valorant/types.ts       tipos de las respuestas
src/lib/steam/steam.ts          cliente de la Steam Web API
src/lib/discord.ts              OAuth2 de Discord: state firmado, intercambio del código y datos del usuario
src/lib/faceit.ts               cliente de la FACEIT Data API v4
src/lib/bus/                    cliente de VANTS-BUS (vants-bus.ts) y publicación de eventos (publish.ts)
src/lib/admin.ts                comprobación del token de administración
src/lib/audit.ts                escritura del registro de auditoría
src/lib/validators.ts           esquemas zod de las entradas de la API
src/lib/db.ts                   punto único de acceso a la base de datos que usan las rutas
src/prisma/                     contrato de Prisma ORM 8 (`contract.prisma`), cliente (`db.ts`) y artefactos emitidos
migrations/                     migraciones versionadas del esquema
module.mjs y service.mjs        despliegue de la aplicación en Prisma Cloud
tests/                          tests unitarios (Vitest): unit.test.ts y bus.test.ts
docs/                           arquitectura, base de datos, entorno, integraciones, despliegue, tests y CI
.github/                        workflows y configuración de Dependabot
scripts/                        regenera el diagrama de arquitectura (HTML y Playwright)
```

## Puesta en marcha

```bash theme={null}
bun install            # o npm install
cp .env.example .env   # opcional: añade las claves que necesites
bun run db:update      # aplica el contrato a la base de datos
bun run dev            # o npm run dev; abre http://localhost:3000
```

Busca `Demo#VANTS` para probar la app sin claves. Para validar un cambio antes de subirlo:

```bash theme={null}
bun run lint        # ESLint
bun run typecheck   # tsc --noEmit
bun run test        # Vitest
bun run build       # build de producción
```

### Variables de entorno

La referencia completa está en `docs/ENVIRONMENT.md`.

| Variable | Obligatoria | Uso |
| - | - | - |
| `DATABASE_URL` | Sí | Base de datos PostgreSQL: Prisma Postgres, Neon o cualquier PostgreSQL accesible. En Prisma Cloud la plataforma la inyecta en runtime y no hace falta definirla |
| `RIOT_API_KEY` | No | Clave de Riot. Sin ella solo funciona el modo demo |
| `STEAM_API_KEY` | No | Clave de la Steam Web API. Habilita `/api/steam/resolve` |
| `ADMIN_TOKEN` | Para gestionar torneos y equipos | Valor que se envía en la cabecera `X-Admin-Token`. Sin él, las rutas de administración responden `503` |
| `DISCORD_CLIENT_ID`, `DISCORD_CLIENT_SECRET` y `DISCORD_OAUTH_STATE_SECRET` | Para Discord | Aplicación OAuth2 de Discord y secreto con el que se firma el parámetro `state` |
| `DISCORD_REDIRECT_URI` | No | URL de retorno fija. Si falta, se usa `<origen de la petición>/api/auth/discord/callback` |
| `FACEIT_API_KEY` | Para FACEIT | Clave de servidor de la FACEIT Data API v4 |
| `VANTS_BUS_URL` y `VANTS_BUS_SECRET` | Para el bus | URL pública del bus y secreto HMAC del proyecto `vantsnet`. Sin ellas, la publicación queda apagada |

En `.env.example` todas las claves están vacías. Si no tienes una, déjala así: la API responde con el aviso de que falta la clave en lugar de llamar al servicio externo.

`.env`, la carpeta `db/` y los archivos `*.db` están en `.gitignore`.

### Scripts

| Script | Qué ejecuta |
| - | - |
| `dev` | `next dev -p 3000` y guarda la salida en `dev.log` |
| `build` | `next build` con salida `standalone` y copia `.next/static` y `public/` a `.next/standalone/` |
| `start` | `.next/standalone/server.js` con Bun en modo producción y guarda la salida en `server.log` |
| `lint` | `eslint .` |
| `typecheck` | `tsc --noEmit` |
| `test` | `vitest run` |
| `test:watch` | `vitest` en modo observación |
| `db:emit` | `prisma contract emit` |
| `db:update` | `prisma db update` |
| `db:sign` | `prisma db sign` |
| `db:plan` | `prisma migration plan` |
| `db:migrate` | `prisma db migrate --advance-ref db` |

## Endpoints

Todos responden en JSON. Los errores tienen la forma `{ "error": "<mensaje en español>", "code": "<código>" }`; algunos solo traen `error`.

### Estadísticas de Valorant y Steam

Son `GET` y públicos.

| Ruta | Parámetros | Devuelve |
| - | - | - |
| `/api/valorant/profile` | `riotId` (obligatorio, `Nombre#TAG`) y `region`: `europe` (por defecto), `americas` o `asia` | Jugador, estadísticas agregadas, resumen por agente y últimas partidas |
| `/api/valorant/match/{matchId}` | `region` (opcional, `europe` por defecto) | Scoreboard: mapa, modo, fecha, duración y equipos con K/D/A, ACS, ADR y porcentaje de headshots de cada jugador |
| `/api/steam/resolve` | `query`: SteamID64, URL de perfil (`/profiles/<id>` o `/id/<vanity>`) o nombre de la vanity URL | SteamID64, perfil, número de juegos y los 10 juegos con más horas |

```bash theme={null}
curl "http://localhost:3000/api/valorant/profile?riotId=Demo%23VANTS&region=europe"
```

El `#` del Riot ID va codificado como `%23`.

* `Demo#VANTS`, sin distinguir mayúsculas, siempre devuelve datos demo, haya o no clave.
* Los IDs de partida `demo-match-<n>` devuelven el scoreboard demo.
* Con clave, una partida que ya está guardada se sirve desde la base de datos sin volver a llamar a Riot.
* Si los detalles de juego de un perfil de Steam son privados, la respuesta trae el perfil con la lista de juegos vacía.

Un `riotId`, una `region` o una `query` ausentes o con formato inválido devuelven `400` solo con `error`, y un fallo inesperado devuelve `500`. El resto de errores:

| Código | Cuándo | HTTP |
| - | - | - |
| `NO_KEY` | No hay clave de Riot o de Steam | `400` en Valorant, `502` en Steam |
| `INVALID_KEY` | Riot o Steam rechazan la clave | `403` |
| `FORBIDDEN_PRODUCT` | La clave de Riot es válida pero no da acceso a los endpoints de Valorant | `403` en perfil, `502` en partida |
| `NOT_FOUND` | Jugador, partida o perfil de Steam no encontrado, o entrada de Steam no reconocida | `404` |
| `RATE_LIMIT` | Límite de peticiones alcanzado | `429` |
| `NO_DATA` | El jugador no tiene partidas recientes | `502` |
| `NETWORK` | Fallo de conexión o estado inesperado de la API externa | `502` |

Con `FORBIDDEN_PRODUCT`, la respuesta del perfil incluye `verifiedAccount` con la cuenta que `account-v1` ya verificó.

### Plataforma competitiva

Las rutas marcadas como admin exigen la cabecera `X-Admin-Token` con el valor de `ADMIN_TOKEN`, y cada cambio queda en el registro de auditoría.

| Método y ruta | Acceso | Qué hace |
| - | - | - |
| `GET /api/tournaments` | Público | Lista hasta 100 torneos, del más reciente al más antiguo, con el número de equipos inscritos y de partidas. Filtro opcional `?status=` |
| `POST /api/tournaments` | Admin | Crea un torneo. Empieza en `draft` y el slug se genera a partir del nombre |
| `GET /api/tournaments/{id}` | Público | Detalle con las inscripciones (equipo y plantilla) y las partidas ordenadas por ronda |
| `PATCH /api/tournaments/{id}` | Admin | Cambia el nombre, el estado, la descripción, las reglas o las fechas |
| `POST /api/tournaments/{id}/registrations` | Admin | Inscribe un equipo: `teamId` y `notes` opcional. La inscripción empieza en `pending` |
| `PATCH /api/tournaments/{id}/registrations` | Admin | Aprueba o rechaza una inscripción: `registrationId` y `action` (`approve` o `reject`) |
| `POST /api/tournaments/{id}/matches` | Admin | Programa una partida: `round` (de 1 a 16), `slot` (por ejemplo `QF1`), `homeTeamId`, `awayTeamId` y `playedAt`, todos opcionales |
| `PATCH /api/tournaments/{id}/matches` | Admin | Reporta un resultado: `matchId` con `homeScore`, `awayScore` o `status` |
| `GET /api/teams` | Público | Lista hasta 100 equipos con su plantilla y su número de inscripciones. Filtro opcional `?game=` |
| `POST /api/teams` | Admin | Crea un equipo con hasta 12 miembros opcionales |
| `POST /api/teams/{teamId}/members` | Admin | Añade un miembro: `nickname` y `role` |
| `DELETE /api/teams/{teamId}/members/{memberId}` | Admin | Quita un miembro |
| `GET /api/admin/audit` | Admin | Últimos registros de auditoría: 50 por defecto y hasta 200 con `?take=` |

```bash theme={null}
curl -X POST http://localhost:3000/api/tournaments \
  -H "Content-Type: application/json" \
  -H "X-Admin-Token: $ADMIN_TOKEN" \
  -d '{"name": "Copa VANTS", "maxTeams": 8}'
```

Valores admitidos:

| Campo | Valores |
| - | - |
| Estado del torneo | `draft`, `registration`, `running`, `completed` y `cancelled` |
| Formato del torneo | `single_elimination` (por defecto), `double_elimination` y `round_robin` |
| Rol de un miembro | `player` (por defecto), `captain`, `coach` y `substitute` |
| Estado de una inscripción | `pending`, `approved` y `rejected` |
| Estado de una partida | `scheduled`, `live`, `completed` y `forfeit` |

El juego por defecto es `valorant` y un torneo admite de 2 a 128 equipos (8 por defecto).

Reglas que aplica la API:

* Solo se inscriben equipos en torneos en estado `registration` y antes de `registrationDeadline`. Si no, responde `409`.
* Un torneo completo responde `409`, y un equipo ya inscrito, `409` con `DUPLICATE_REGISTRATION`.
* Para completar una partida hay que enviar `homeScore` y `awayScore`, y se guarda la hora en `playedAt`.
* Una partida `completed` o `forfeit` ya no se puede modificar (`409`). Para corregir un resultado se crea otra partida.
* El nombre de un equipo es único por juego (`409` con `DUPLICATE_TEAM`) y el apodo, único dentro del equipo (`409` con `DUPLICATE_MEMBER`).
* Si el slug de un torneo ya existe, se le añade un sufijo: `-2`, `-3`...

Sin `ADMIN_TOKEN` en el servidor, las rutas admin responden `503` con `ADMIN_NOT_CONFIGURED`; con un token ausente o incorrecto, `401` con `ADMIN_INVALID`. Una entrada inválida devuelve `400` con el primer error de validación y el campo afectado, y un torneo, equipo, inscripción o partida inexistentes devuelven `404`.

### Discord y FACEIT

| Método y ruta | Qué hace |
| - | - |
| `GET /api/auth/discord/start` | Redirige a la autorización de Discord con el scope `identify` |
| `GET /api/auth/discord/callback` | Recibe la respuesta de Discord, vincula la cuenta y vuelve a `/?auth=linked` o a `/?auth=error&reason=<motivo>` |
| `GET /api/auth/discord/status` | Indica si Discord está configurado: `configured` y `redirectUriReady` |
| `DELETE /api/auth/discord/link` | Desvincula una cuenta de Discord a partir de su `providerId`. Exige `X-Admin-Token` |
| `GET /api/faceit/player?nickname=` | Jugador de FACEIT, con su nivel y ELO por juego, y sus 5 últimas partidas |

* La vinculación con Discord guarda la cuenta en `ProviderIdentity` y crea un usuario interno si no existía. Repetirla actualiza los datos en lugar de duplicarlos.
* El parámetro `state` del flujo va firmado y caduca a los 10 minutos.
* Al desvincular una cuenta, también se borra el usuario interno si se queda sin otras cuentas vinculadas ni equipos.
* Si falla la consulta del historial de FACEIT, la respuesta trae el perfil con la lista de partidas vacía.

Errores de FACEIT: `400` con `BAD_INPUT` si el nickname tiene menos de 2 caracteres, `503` con `NOT_CONFIGURED` sin clave, `404` con `NOT_FOUND`, `429` con `RATE_LIMIT`, y `502` con `INVALID_KEY` o `NETWORK`.

### Publicación en VANTS-BUS

Las mutaciones de la plataforma competitiva publican sus eventos al bus del ecosistema con identidad `vantsnet`, mediante el cliente de `src/lib/bus/` y el mismo contrato de firma que los demás proyectos. La tabla muestra qué endpoint publica qué evento:

| Endpoint | Evento publicado |
| - | - |
| `POST /api/tournaments` (al crear el torneo) | `tournament.inscriptions_opened` |
| `PATCH /api/tournaments/{id}` (pasa el torneo a `registration`) | `tournament.inscriptions_opened` |
| `POST /api/tournaments/{id}/registrations` | `inscription.created` |
| `POST /api/tournaments/{id}/matches` (con `playedAt`) | `match.scheduled` |
| `PATCH /api/tournaments/{id}/matches` (cierra como `completed`) | `match.finished` |

El envío no bloquea la respuesta y un fallo del bus solo deja una línea en el log: la mutación ya está guardada. Si faltan `VANTS_BUS_URL` o `VANTS_BUS_SECRET`, la publicación queda apagada y la API funciona igual. Los consumidores son flask-agents y VANTS HUB, y `vants-consumer-playvants` copia los eventos a `vants_feed`.

### Estado del servicio

`GET /api/health` responde `200` con `"status": "healthy"` si la base de datos responde, y `503` con `"status": "degraded"` si no. `checks.integrations` indica con `true` o `false` si Riot, Steam, Discord y FACEIT están configurados, y `checks.admin`, si lo está `ADMIN_TOKEN`. Nunca devuelve las claves.

## Cómo se obtienen los datos de Riot

**1. Resolver la cuenta**

`account-v1` convierte `Nombre#TAG` en un `puuid` y guarda o actualiza al jugador en `Player`.

**2. Pedir el historial**

Se piden a VAL-MATCH-V1 los IDs de las últimas 8 partidas del jugador (`MATCH_COUNT`).

**3. Guardar cada partida una sola vez**

Las partidas que no están en `Match` se descargan y se guardan con las estadísticas de cada jugador en `MatchPlayerStat`, y cada jugador visto se registra en `Player`. Los nombres de mapas y agentes salen de VAL-CONTENT-V1 (`locale=es-ES`), que se guarda en memoria 24 horas.

**4. Agregar desde la base de datos**

`buildProfile` calcula winrate, K/D, ACS, ADR, porcentaje de headshots y el resumen por agente a partir de lo guardado.

Las llamadas a Riot y a Steam se hacen desde el servidor, sin caché HTTP y con un timeout de 12 segundos; las de FACEIT, con un timeout de 10 segundos. Riot recibe la clave en la cabecera `X-Riot-Token`.

## Base de datos

`src/prisma/contract.prisma` define once modelos:

| Modelo | Clave única | Contenido |
| - | - | - |
| `Player` | `puuid` | Riot ID (`gameName` y `tagLine`), región, juego y fecha de la última consulta (`lastFetchedAt`) |
| `Match` | `externalId`, el ID de partida de Riot | Mapa, modo, inicio, duración, rondas, equipos (`teamsJson`) y la respuesta original de Riot (`raw`) |
| `MatchPlayerStat` | `id`, con índices en `matchId` y `puuid` | Estadísticas de un jugador en una partida: agente, K/D/A, puntuación, daño, impactos por zona y si ganó |
| `User` | `id` | Usuario interno con su nombre visible, sus cuentas vinculadas y sus equipos |
| `ProviderIdentity` | `provider` y `providerId` | Cuenta externa vinculada (Discord, FACEIT, Riot o Steam): nombre, avatar y fecha de la última sincronización |
| `Team` | `name` y `game` | Equipo: nombre, tag, juego, región, logo y descripción |
| `TeamMember` | `teamId` y `nickname` | Miembro de un equipo con su rol, enlazado opcionalmente a un `User` |
| `Tournament` | `slug` | Torneo: juego, formato, estado, máximo de equipos, descripción, reglas, fecha de inicio y cierre de inscripción |
| `TournamentRegistration` | `tournamentId` y `teamId` | Inscripción de un equipo en un torneo, con su estado y notas |
| `TournamentMatch` | `id`, con índice en `tournamentId` y `round` | Partida de torneo: ronda, slot, equipos local y visitante, marcador, estado y fecha |
| `AuditLog` | `id`, con índices en `entity` y `entityId` y en `createdAt` | Quién hizo el cambio (`actor`), qué acción, sobre qué entidad y el detalle en JSON |

Los estados, formatos y roles se guardan como texto y los valida zod en la frontera de la API. Con `onDelete: Cascade`, borrar una partida de Riot borra sus estadísticas; borrar un torneo, sus inscripciones y partidas; borrar un equipo, sus miembros e inscripciones, y borrar un usuario, sus cuentas vinculadas.

> [!NOTE]
>
> El contrato no declara bloques `generator` ni `datasource`: la conexión vive en `prisma.config.ts` y la base de datos es PostgreSQL (Prisma Postgres, Neon o cualquier PostgreSQL). Los artefactos `contract.json` y `contract.d.ts` se generan con `db:emit` y se commitean. El esquema SQL de `docs/ARQUITECTURA.md`, con la tabla `leaderboards`, es el diseño planificado y no el que usa el prototipo.

## Calidad y CI

| Workflow | Cuándo se ejecuta | Qué hace |
| - | - | - |
| `ci.yml` | Push y pull request a `main` | Con Bun y el lockfile congelado, ejecuta `lint`, `typecheck`, `test` y `build` |
| `codeql.yml` | Push y pull request a `main`, y cada lunes a las 05:30 UTC | Análisis de CodeQL con las consultas `security-extended` para JavaScript y TypeScript |
| `sonarcloud.yml` | Push a `main` y pull requests | Quality Gate de SonarCloud. Se omite mientras no exista el secreto `SONAR_TOKEN` |
| `prisma-deploy.yml` | En cada push a una rama y a mano | Despliega en Prisma Cloud: `main` va a producción y las demás ramas crean un preview con su propia base de datos |

Dependabot revisa cada semana las dependencias de npm y las GitHub Actions, con un máximo de 5 pull requests abiertas de cada tipo.

`.github/workflows/` contiene solo los cuatro workflows de la tabla anterior.

`tests/unit.test.ts` tiene 14 tests: el `state` firmado de Discord, los errores de FACEIT, los validadores de la API, las reglas de resultados de partidas y la generación de slugs. No hay tests de extremo a extremo automatizados; la verificación manual está en `docs/TESTING.md`.

## Integraciones

Estado según `docs/INTEGRATIONS.md`:

| Integración | Estado |
| - | - |
| Riot Games (Valorant) | Implementada y verificada |
| Steam Web API | Implementada y verificada |
| Discord OAuth2 | Implementada, pendiente de credenciales |
| FACEIT Data API v4 | Implementada, pendiente de credenciales |
| VANTS-BUS | Implementada, pendiente del secreto del proyecto `vantsnet` en el bus |
| Prisma Cloud | Activa: despliegue de la aplicación en cada push |
| Cloudflare | Documentada como alternativa (VPS con Cloudflare delante o Workers) |

## Documentos del repositorio

| Archivo | Contenido |
| - | - |
| `docs/ARQUITECTURA.md` | Arquitectura objetivo en cuatro capas (fuentes de datos, ingesta y colecta, núcleo backend y presentación) más operación, con diagrama Mermaid y esquema SQL |
| `docs/ROADMAP.md` | Plan de construcción en 5 fases con criterios de aceptación |
| `docs/DATABASE.md` | Base de datos PostgreSQL con Prisma ORM 8: contrato, comandos y migraciones |
| `docs/ENVIRONMENT.md` | Referencia de variables de entorno |
| `docs/INTEGRATIONS.md` | Configuración de Riot, Steam, Discord, FACEIT, VANTS-BUS y Cloudflare |
| `docs/DEPLOYMENT.md` | Despliegue en Prisma Cloud (opción activa) y alternativas en VPS con Cloudflare delante o Cloudflare Workers, checklist de producción y rollback |
| `docs/TESTING.md` | Qué cubren los tests y la verificación manual |
| `docs/CI-SETUP.md` | Instalación de CI, CodeQL y SonarCloud |
| `docs/SECURITY.md` | Modelo de seguridad del prototipo |
| `docs/KNOWN_ISSUES.md` | Incidencias conocidas y trabajo pendiente |
| `docs/PROJECT_AUDIT.md` y `docs/FINAL_COMPLETION_REPORT.md` | Auditoría e informe final de la fase de trabajo del 25/10/2026 |
| `docs/SOLICITUD-CLAVE-PRODUCCION.md` | Expediente para pedir a Riot la clave de producción de Valorant |

## Hoja de ruta

Estado de cada fase según el README:

| Fase | Entregable | Estado |
| - | - | - |
| 1 | Colector de Valorant con la API de Riot y esquema inicial | Prototipo funcional |
| 2 | API REST propia, caché y colas | Prototipo: API routes y caché en base de datos |
| 3 | Web de estadísticas: perfil, historial y partidas | Prototipo funcional |
| 4 | Multijuego con temas e ingesta de servidores | Planificado, con el cliente de Steam ya integrado |
| 5 | App Flutter, bot de Discord, API pública y Premium | Planificado |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.