Refactor code structure for improved readability and maintainability
This commit is contained in:
@@ -1,15 +1,222 @@
|
||||
# Comparativa de APIs — cobertura, límites, coste y calidad
|
||||
# APIs del Sistema — Guía completa
|
||||
|
||||
**Introducción**
|
||||
Comparar APIs públicas y comerciales que aportan metadatos (covers, screenshots, géneros, desarrolladores), y datos de precio/ofertas. Las decisiones de integración deben priorizar cobertura, coste (preferencia: gratuito), calidad y facilidad de uso.
|
||||
|
||||
**Nota:** límites y condiciones pueden cambiar — verificar TOS antes de integración.
|
||||
Este documento integra toda la información sobre APIs del sistema: obtención de claves, prioridades, estrategias, comparación y configuración.
|
||||
|
||||
---
|
||||
|
||||
## Resumen por API
|
||||
## Tabla de Contenidos
|
||||
|
||||
### IGDB (Internet Games Database)
|
||||
1. [APIs priorizadas (MVP)](#apis-priorizadas-mvp)
|
||||
2. [Obtención de claves](#obtención-de-claves)
|
||||
3. [Guía de integración](#guía-de-integración)
|
||||
4. [Comparación detallada](#comparación-detallada)
|
||||
5. [Estrategias técnicas](#estrategias-técnicas)
|
||||
6. [Configuración y despliegue](#configuración-y-despliegue)
|
||||
|
||||
---
|
||||
|
||||
## APIs priorizadas (MVP)
|
||||
|
||||
### Prioridad Alta
|
||||
|
||||
1. **IGDB (Internet Game Database)** - Calidad superior, amplia cobertura
|
||||
2. **RAWG (Rawg.io)** - Buena cobertura, datos de tiendas
|
||||
|
||||
### Prioridad Media
|
||||
|
||||
3. **TheGamesDB** - Artwork comunitario
|
||||
4. **ScreenScraper** - Media específica para ROMs
|
||||
|
||||
### Prioridad Baja (para futuras versiones)
|
||||
|
||||
5. **PriceCharting** - Precios físicos
|
||||
6. **IsThereAnyDeal** - Ofertas digitales
|
||||
7. **MobyGames** - Datos históricos detallados
|
||||
8. **eBay** - Datos de mercado
|
||||
|
||||
---
|
||||
|
||||
## Obtención de claves
|
||||
|
||||
### IGDB (Internet Game Database)
|
||||
|
||||
IGDB usa **OAuth 2.0 via Twitch**. Steps:
|
||||
|
||||
1. Go to [Twitch Developer Console](https://dev.twitch.tv/console/apps)
|
||||
2. Sign in with your Twitch account (create one if needed)
|
||||
3. Click "Create Application"
|
||||
- Name: "Quasar" (or your app name)
|
||||
- Category: Select relevant category
|
||||
- Accept terms, click Create
|
||||
4. You'll see:
|
||||
- **Client ID** — Copy this
|
||||
- Click "New Secret" to generate **Client Secret** — Copy this
|
||||
5. Go to Settings → OAuth Redirect URLs
|
||||
- Add: `http://localhost:3000/oauth/callback` (development)
|
||||
- For production: `https://yourdomain.com/oauth/callback`
|
||||
6. In your `.env` file:
|
||||
```
|
||||
IGDB_CLIENT_ID=your_client_id
|
||||
IGDB_CLIENT_SECRET=your_client_secret
|
||||
```
|
||||
7. Start Quasar, it will use IGDB automatically
|
||||
|
||||
**Rate Limit:** 4 requests/second
|
||||
|
||||
### RAWG (Rawg.io)
|
||||
|
||||
RAWG has a simpler **API Key** approach:
|
||||
|
||||
1. Go to [RAWG Settings](https://rawg.io/settings/account)
|
||||
2. Sign up if needed, then login
|
||||
3. Find "API Key" section
|
||||
4. Click "Create new key" (if needed) or copy existing key
|
||||
5. In your `.env` file:
|
||||
```
|
||||
RAWG_API_KEY=your_api_key_here
|
||||
```
|
||||
6. Start Quasar
|
||||
|
||||
**Rate Limit:** 20 requests/second (free tier)
|
||||
|
||||
**Note:** RAWG requires attribution in UI (include "Powered by RAWG" somewhere visible)
|
||||
|
||||
### TheGamesDB (thegamesdb.net)
|
||||
|
||||
TheGamesDB uses a simple **API Key**:
|
||||
|
||||
1. Go to [TheGamesDB API](https://thegamesdb.net/api)
|
||||
2. Find "API Key" section (free registration required)
|
||||
3. Register or login
|
||||
4. Copy your API key
|
||||
5. In your `.env` file:
|
||||
```
|
||||
THEGAMESDB_API_KEY=your_api_key_here
|
||||
```
|
||||
6. Start Quasar
|
||||
|
||||
**Rate Limit:** 1 request/second (free tier)
|
||||
|
||||
### ScreenScraper
|
||||
|
||||
ScreenScraper requiere cuenta y modelo de donación:
|
||||
|
||||
1. Go to [ScreenScraper](https://www.screenscraper.fr/)
|
||||
2. Create account
|
||||
3. Niveles de donación ofrecen límites distintos (ej.: 50.000 scrapes/día en nivel Bronze)
|
||||
4. En tu `.env` file:
|
||||
```
|
||||
SCREENSCRAPER_USERNAME=your_username
|
||||
SCREENSCRAPER_PASSWORD=your_password
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Guía de integración
|
||||
|
||||
### IGDB
|
||||
|
||||
- **Obtener credenciales**: registrar una app en Twitch Developer Console para obtener `CLIENT_ID` y `CLIENT_SECRET`. Obtener token con grant type `client_credentials` (POST a `https://id.twitch.tv/oauth2/token`).
|
||||
|
||||
- **Endpoints principales**: `POST https://api.igdb.com/v4/games` (consulta flexible via body con sintaxis IGDB), `POST https://api.igdb.com/v4/covers`, `POST https://api.igdb.com/v4/platforms`.
|
||||
|
||||
- **Ejemplo (buscar)**:
|
||||
|
||||
```bash
|
||||
# Obtener token
|
||||
curl -X POST 'https://id.twitch.tv/oauth2/token?client_id=$IGDB_CLIENT_ID&client_secret=$IGDB_CLIENT_SECRET&grant_type=client_credentials'
|
||||
|
||||
# Buscar juegos
|
||||
curl -X POST 'https://api.igdb.com/v4/games' \
|
||||
-H "Client-ID: $IGDB_CLIENT_ID" \
|
||||
-H "Authorization: Bearer $IGDB_TOKEN" \
|
||||
-H 'Accept: application/json' \
|
||||
--data 'fields id,name,first_release_date,platforms.name,genres.name,cover.url; search "zelda"; limit 5;'
|
||||
```
|
||||
|
||||
- **Respuesta (esquemática)**:
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": 12345,
|
||||
"name": "Ejemplo",
|
||||
"first_release_date": 1459468800,
|
||||
"platforms": [{ "name": "Nintendo Switch" }],
|
||||
"cover": { "url": "//images.igdb.com/...jpg" }
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
- **Límites y manejo**: la API puede devolver `429` o cabeceras de límite; implementar retries exponenciales (ej. 3 intentos) y respetar `Retry-After`. Implementar circuit breaker si la API falla repetidamente.
|
||||
- **Atribución**: mostrar origen de datos (ej. "Datos: IGDB") según términos del servicio.
|
||||
|
||||
### RAWG
|
||||
|
||||
- **Obtener credenciales**: registrarse en RAWG para obtener `RAWG_API_KEY` (https://rawg.io/apidocs).
|
||||
- **Endpoints principales**: `GET https://api.rawg.io/api/games?key=API_KEY&search=...`, `GET https://api.rawg.io/api/games/{id}`.
|
||||
- **Ejemplo**:
|
||||
|
||||
```bash
|
||||
curl 'https://api.rawg.io/api/games?key=$RAWG_API_KEY&search=zelda&page_size=5'
|
||||
```
|
||||
|
||||
- **Respuesta (esquemática)**:
|
||||
|
||||
```json
|
||||
{
|
||||
"count": 100,
|
||||
"results": [
|
||||
{ "id": 3498, "name": "GTA V", "released": "2013-09-17", "background_image": "https://..." }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
- **Límites y manejo**: RAWG suele tener límites por clave/plan; cachear y fallback a otros proveedores si falla.
|
||||
- **Atribución**: revisar condiciones y mostrar HTTP o texto de fuente si es requerido por el proveedor.
|
||||
|
||||
### TheGamesDB
|
||||
|
||||
- **Obtener credenciales**: crear cuenta y generar API Key en https://thegamesdb.net.
|
||||
- **Endpoints**: búsqueda por nombre y detalles (`/v1/Games/ByGameName?name=...`, `/v1/Games/ByGameID?id=...`).
|
||||
- **Ejemplo**:
|
||||
|
||||
```bash
|
||||
curl -H 'Authorization: Bearer $THEGAMESDB_KEY' 'https://api.thegamesdb.net/v1/Games/ByGameName?name=zelda'
|
||||
```
|
||||
|
||||
### Estrategia de fallback y normalización
|
||||
|
||||
- **Orden de prioridad**: IGDB → RAWG → TheGamesDB (configurable).
|
||||
- **Normalización (mapping)**:
|
||||
- `title` ← `name`
|
||||
- `platform` ← `platforms[].name`
|
||||
- `release_date` ← `first_release_date` / `released` → convertir a ISO 8601
|
||||
- `genres` ← `genres[].name`
|
||||
- `cover_url` ← `cover.url` / `background_image`
|
||||
- `external_ids` ← `{ igdb: id, rawg: id, thegamesdb: id }`
|
||||
|
||||
- **Fallback**: si IGDB no tiene portada, intentar RAWG; si falla, usar TheGamesDB. Registrar la fuente usada.
|
||||
|
||||
### Caché y almacenamiento de artwork
|
||||
|
||||
- **Caché metadata**: LRU en memoria o Redis con TTL (por ejemplo 24h) para evitar sobrecargar APIs.
|
||||
- **Almacenamiento de imágenes**: descargar y optimizar con `sharp` (crear versiones: thumb, medium), almacenar en `storage/artwork/{gameId}/cover.jpg` o S3.
|
||||
- **Servicio proxy**: servir imágenes desde backend para no exponer keys ni URLs externas.
|
||||
|
||||
### Manejo de errores y resiliencia
|
||||
|
||||
- Implementar **retries** exponenciales con jitter (3 intentos).
|
||||
- Implementar **circuit breaker** para desconectar llamadas a un proveedor fuera de servicio por N minutos.
|
||||
- Limitar concurrencia por proveedor (p. ej. 5 llamadas simultáneas) y usar colas para trabajos masivos (enriquecimiento masivo).
|
||||
|
||||
---
|
||||
|
||||
## Comparación detallada
|
||||
|
||||
### Resumen por API
|
||||
|
||||
#### IGDB (Internet Games Database)
|
||||
|
||||
- **Resumen:** Base de datos muy completa (propiedad de Twitch/Amazon) con endpoints para juegos, covers, screenshots, plataformas, ratings, compañías y más.
|
||||
- **Autenticación / Requisitos:** OAuth vía Twitch (Client ID + Client Secret → token) — requiere cuenta Twitch y 2FA para registrar apps.
|
||||
@@ -21,9 +228,7 @@ Comparar APIs públicas y comerciales que aportan metadatos (covers, screenshots
|
||||
- **Costes / modelo:** Gratuito para uso no comercial; acuerdos comerciales para partners (atribución en caso de partnership).
|
||||
- **Enlace:** https://api-docs.igdb.com/
|
||||
|
||||
---
|
||||
|
||||
### RAWG
|
||||
#### RAWG
|
||||
|
||||
- **Resumen:** Gran base de datos (medio millón de juegos), buena para metadata general y enlaces a tiendas.
|
||||
- **Autenticación / Requisitos:** API key en query string (`key=YOUR_API_KEY`).
|
||||
@@ -35,9 +240,7 @@ Comparar APIs públicas y comerciales que aportan metadatos (covers, screenshots
|
||||
- **Costes / modelo:** Free tier para proyectos personales; planes comerciales (pago mensual) para uso en productos con gran tráfico.
|
||||
- **Enlace:** https://rawg.io/apidocs
|
||||
|
||||
---
|
||||
|
||||
### TheGamesDB
|
||||
#### TheGamesDB
|
||||
|
||||
- **Resumen:** Base de datos comunitaria para juegos y artwork, con API pública v2.
|
||||
- **Autenticación / Requisitos:** Registro y uso de API key (ver docs); repositorio público del proyecto (GPLv3 para el código del servidor).
|
||||
@@ -48,9 +251,7 @@ Comparar APIs públicas y comerciales que aportan metadatos (covers, screenshots
|
||||
- **Cláusula clave:** No documentado públicamente — verificar con el equipo de TheGamesDB antes de uso comercial/redistribución.
|
||||
- **Enlace:** https://api.thegamesdb.net/
|
||||
|
||||
---
|
||||
|
||||
### ScreenScraper
|
||||
#### ScreenScraper
|
||||
|
||||
- **Resumen:** Servicio francés orientado a frontends, con enorme cantidad de media y opciones de scraping.
|
||||
- **Autenticación / Requisitos:** Cuenta en ScreenScraper; modelo de soporte/donación que habilita límites mayores.
|
||||
@@ -62,9 +263,7 @@ Comparar APIs públicas y comerciales que aportan metadatos (covers, screenshots
|
||||
- **Costes / modelo:** Donación / suscripción para aumentar cuotas y velocidad.
|
||||
- **Enlace:** https://www.screenscraper.fr/
|
||||
|
||||
---
|
||||
|
||||
### MobyGames
|
||||
#### MobyGames
|
||||
|
||||
- **Resumen:** Base histórica con screenshots, covers, reviews y credits; muy usada por investigación y metadata profunda.
|
||||
- **Autenticación / Requisitos:** API y/o MobyPlus; la API requiere registro y suscripción.
|
||||
@@ -76,9 +275,7 @@ Comparar APIs públicas y comerciales que aportan metadatos (covers, screenshots
|
||||
- **Costes / modelo:** Acceso vía suscripción / MobyPro; contactar para condiciones comerciales.
|
||||
- **Enlace:** https://www.mobygames.com/api/subscribe/
|
||||
|
||||
---
|
||||
|
||||
### PriceCharting
|
||||
#### PriceCharting
|
||||
|
||||
- **Resumen:** Fuente especializada en historial de precios para juegos físicos y coleccionables.
|
||||
- **Autenticación / Requisitos:** API documentada en el sitio; el acceso completo requiere suscripción / token pagado.
|
||||
@@ -90,9 +287,7 @@ Comparar APIs públicas y comerciales que aportan metadatos (covers, screenshots
|
||||
- **Costes / modelo:** Servicio comercial (licencias / API keys pagadas).
|
||||
- **Enlace:** https://www.pricecharting.com/api-documentation
|
||||
|
||||
---
|
||||
|
||||
### IsThereAnyDeal (Itad)
|
||||
#### IsThereAnyDeal (Itad)
|
||||
|
||||
- **Resumen:** Agregador de ofertas con histórico y mapeo de keys/tiendas; útil para tracking de ofertas digitales.
|
||||
- **Autenticación / Requisitos:** API Key (docs en https://docs.isthereanydeal.com/).
|
||||
@@ -104,9 +299,7 @@ Comparar APIs públicas y comerciales que aportan metadatos (covers, screenshots
|
||||
- **Costes / modelo:** Free tier; acuerdos comerciales para uso intensivo.
|
||||
- **Enlace:** https://docs.isthereanydeal.com/
|
||||
|
||||
---
|
||||
|
||||
### eBay
|
||||
#### eBay
|
||||
|
||||
- **Resumen:** Fuente de datos de mercado (listings, precios vendidos) para estimar valor real de mercado.
|
||||
- **Autenticación / Requisitos:** Registro en eBay Developers Program; claves y OAuth para endpoints de venta/completed items.
|
||||
@@ -118,9 +311,7 @@ Comparar APIs públicas y comerciales que aportan metadatos (covers, screenshots
|
||||
- **Costes / modelo:** Free para desarrolladores con límites; uso intensivo o comerciales pueden requerir acuerdos o certificaciones.
|
||||
- **Enlace:** https://developer.ebay.com/
|
||||
|
||||
---
|
||||
|
||||
## Tabla resumida
|
||||
### Tabla resumida
|
||||
|
||||
| API | Data types | Auth | Free / Paid | Fecha verificación | Licencia / Nota legal | Notes |
|
||||
| -------------- | ------------------------------------------------------- | -------------------------------- | ------------------------------------------ | ------------------ | ------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- |
|
||||
@@ -133,9 +324,7 @@ Comparar APIs públicas y comerciales que aportan metadatos (covers, screenshots
|
||||
| MobyGames | screenshots, credits, covers | Subscribe / API key | Paid / subscription | 2026-02-07 | Paid/Subscribe: https://www.mobygames.com/api/subscribe/ | Access via subscription; non-commercial rate limits documented |
|
||||
| eBay | listings, sold data | eBay Dev keys / OAuth | Free (with limits) | 2026-02-07 | TOS: https://developer.ebay.com/ | Terms restrict distribution; API License Agreement |
|
||||
|
||||
---
|
||||
|
||||
## Conclusión y recomendación para MVP
|
||||
### Conclusión y recomendación para MVP
|
||||
|
||||
Recomiendo un **set inicial de APIs (priorizado)**: **IGDB, RAWG, TheGamesDB, ScreenScraper, PriceCharting, IsThereAnyDeal.**
|
||||
|
||||
@@ -144,13 +333,171 @@ Recomiendo un **set inicial de APIs (priorizado)**: **IGDB, RAWG, TheGamesDB, Sc
|
||||
|
||||
---
|
||||
|
||||
## Vacíos y verificación pendiente
|
||||
## Estrategias técnicas
|
||||
|
||||
- **APIs que requieren suscripción / acuerdos comerciales:** PriceCharting (API premium, requiere suscripción), MobyGames (MobyPro/API requiere suscripción), EmuMovies (servicio comercial con TOS y cuentas), y en casos especiales eBay (certificaciones / acuerdos adicionales para ciertos permisos).
|
||||
- **PriceCharting:** la documentación de la API existe pero el acceso completo está sujeto a registro/pago; no se publicó límite público durante la verificación.
|
||||
- **MobyGames:** API y límites requieren suscripción/registro; hay que contactar para condiciones comerciales.
|
||||
- **eBay:** múltiples APIs y límites por endpoint; requiere revisar caso de uso específico y cumplimiento del API License Agreement.
|
||||
- **Notas:** Algunas APIs (ScreenScraper) usan modelos por donación/premium para aumentar cuotas; en APIs sin límites públicos, contactar al proveedor para confirmar condiciones.
|
||||
### Variables de entorno (ejemplos)
|
||||
|
||||
```
|
||||
IGDB_CLIENT_ID=...
|
||||
IGDB_CLIENT_SECRET=...
|
||||
RAWG_API_KEY=...
|
||||
THEGAMESDB_API_KEY=...
|
||||
SCREENSCRAPER_USERNAME=...
|
||||
SCREENSCRAPER_PASSWORD=...
|
||||
EXTERNAL_API_CONCURRENCY=5
|
||||
```
|
||||
|
||||
> Nota: **Nunca** exponer estas claves en el cliente; siempre pasar por el backend.
|
||||
|
||||
### Normalización de datos
|
||||
|
||||
```typescript
|
||||
interface NormalizedGame {
|
||||
title: string;
|
||||
platform: string;
|
||||
release_date: string; // ISO 8601
|
||||
genres: string[];
|
||||
cover_url: string;
|
||||
external_ids: {
|
||||
igdb?: string;
|
||||
rawg?: string;
|
||||
thegamesdb?: string;
|
||||
};
|
||||
source: 'igdb' | 'rawg' | 'thegamesdb' | 'screenscraper';
|
||||
}
|
||||
```
|
||||
|
||||
### Ejemplo de implementación
|
||||
|
||||
```typescript
|
||||
class MetadataService {
|
||||
private apis = [
|
||||
new IGDBService(),
|
||||
new RAWGService(),
|
||||
new TheGamesDBService(),
|
||||
new ScreenScraperService(),
|
||||
];
|
||||
|
||||
async searchGame(title: string): Promise<NormalizedGame> {
|
||||
for (const api of this.apis) {
|
||||
try {
|
||||
const result = await api.search(title);
|
||||
if (result) {
|
||||
return this.normalize(result, api.getSource());
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(`${api.getSource()} failed:`, error);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
throw new Error('All APIs failed');
|
||||
}
|
||||
|
||||
private normalize(data: any, source: string): NormalizedGame {
|
||||
return {
|
||||
title: data.name || data.title,
|
||||
platform: data.platforms?.[0]?.name || '',
|
||||
release_date: this.normalizeDate(data.first_release_date || data.released),
|
||||
genres: data.genres?.map((g: any) => g.name) || [],
|
||||
cover_url: data.cover?.url || data.background_image || '',
|
||||
external_ids: {
|
||||
igdb: data.id,
|
||||
rawg: data.id,
|
||||
thegamesdb: data.id,
|
||||
},
|
||||
source: source as any,
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Configuración y despliegue
|
||||
|
||||
### Testing Without Real Keys
|
||||
|
||||
Para desarrollo/testing:
|
||||
|
||||
- Dejar API keys como `your_*_here` en `.env.local`
|
||||
- Quasar will gracefully degrade and show limited metadata
|
||||
- Frontend will still work with manual game entry
|
||||
|
||||
### Production Deployment
|
||||
|
||||
Para producción:
|
||||
|
||||
1. Generar nuevas claves en cada servicio (no reutilizar claves de desarrollo)
|
||||
2. Almacenar claves en **Gitea Secrets** (para pipelines CI/CD automatizados)
|
||||
3. O usar variables de entorno en tu proveedor de hosting
|
||||
4. Rotar claves cada 3 meses
|
||||
5. Monitorear límites de rate en los dashboards de los servicios
|
||||
|
||||
### Gitea Actions CI/CD Setup
|
||||
|
||||
Para habilitar pruebas automatizadas con API keys en Gitea Actions:
|
||||
|
||||
#### 1. Store Secrets in Gitea
|
||||
|
||||
Navigate to your repository settings:
|
||||
|
||||
```
|
||||
https://your-gitea-instance/your-org/quasar/settings/secrets/actions
|
||||
```
|
||||
|
||||
Add these secrets:
|
||||
|
||||
- `IGDB_CLIENT_ID` (from Twitch Developer Console)
|
||||
- `IGDB_CLIENT_SECRET` (from Twitch Developer Console)
|
||||
- `RAWG_API_KEY` (from RAWG settings)
|
||||
- `THEGAMESDB_API_KEY` (from TheGamesDB API)
|
||||
- `SCREENSCRAPER_USERNAME` (from ScreenScraper)
|
||||
- `SCREENSCRAPER_PASSWORD` (from ScreenScraper)
|
||||
|
||||
#### 2. Workflow Configuration
|
||||
|
||||
The `.gitea/workflows/ci.yml` workflow automatically:
|
||||
|
||||
- ✅ Installs dependencies
|
||||
- ✅ Runs linting checks
|
||||
- ✅ Executes backend tests (Vitest)
|
||||
- ✅ Executes frontend tests (Vitest)
|
||||
- ✅ Starts backend + frontend servers
|
||||
- ✅ Runs E2E tests (Playwright) with real metadata APIs
|
||||
- ✅ Uploads test reports on failure
|
||||
|
||||
#### 3. Testing Flow
|
||||
|
||||
1. **Push** code to `main` or `develop`
|
||||
2. **Gitea Actions** picks up the `.gitea/workflows/ci.yml`
|
||||
3. **Secrets are injected** as environment variables
|
||||
4. **E2E tests** fetch real metadata from APIs (using injected secrets)
|
||||
5. **Build fails** if any test fails (prevents broken code)
|
||||
|
||||
#### 4. Local Development
|
||||
|
||||
For local testing, use `.env.local`:
|
||||
|
||||
```bash
|
||||
IGDB_CLIENT_ID=your_local_id
|
||||
IGDB_CLIENT_SECRET=your_local_secret
|
||||
RAWG_API_KEY=your_local_key
|
||||
THEGAMESDB_API_KEY=your_local_key
|
||||
SCREENSCRAPER_USERNAME=your_username
|
||||
SCREENSCRAPER_PASSWORD=your_password
|
||||
```
|
||||
|
||||
**Note:** CI/CD uses Gitea Secrets (not `.env` files), so never commit real credentials.
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
**"IGDB_CLIENT_ID not found"** → Check `.env` file exists and has correct format
|
||||
|
||||
**"429 Too Many Requests"** → Rate limit exceeded, wait and retry
|
||||
|
||||
**"Invalid API Key"** → Copy key exactly (no spaces), verify it's active on service website
|
||||
|
||||
**"ScreenScraper authentication failed"** → Check donation level and account status
|
||||
|
||||
---
|
||||
|
||||
1195
docs/02-tecnico/frontend.md
Normal file
1195
docs/02-tecnico/frontend.md
Normal file
File diff suppressed because it is too large
Load Diff
546
docs/04-operaciones/deployment.md
Normal file
546
docs/04-operaciones/deployment.md
Normal file
@@ -0,0 +1,546 @@
|
||||
# Guía de Despliegue y Operaciones 🚀
|
||||
|
||||
Esta guía cubre el despliegue, configuración y operación de Quasar en producción.
|
||||
|
||||
---
|
||||
|
||||
## Tabla de Contenidos
|
||||
|
||||
1. [Requisitos del Sistema](#requisitos-del-sistema)
|
||||
2. [Configuración de Producción](#configuración-de-producción)
|
||||
3. [Despliegue](#despliegue)
|
||||
4. [Monitoreo y Mantenimiento](#monitoreo-y-mantenimiento)
|
||||
5. [Actualizaciones](#actualizaciones)
|
||||
6. [Backup y Recuperación](#backup-y-recuperación)
|
||||
7. [Solución de Problemas](#solución-de-problemas)
|
||||
|
||||
---
|
||||
|
||||
## Requisitos del Sistema
|
||||
|
||||
### Hardware Mínimo
|
||||
|
||||
- **CPU:** 2 cores
|
||||
- **RAM:** 4GB
|
||||
- **Almacenamiento:** 20GB (para ROMs y metadata)
|
||||
- **Red:** Estable (para descargas de artwork)
|
||||
|
||||
### Software
|
||||
|
||||
- **Node.js 18+**
|
||||
- **Yarn 4.x**
|
||||
- **SQLite** (o PostgreSQL para producción)
|
||||
- **Nginx** (recomendado para reverse proxy)
|
||||
- **Certificado SSL** (HTTPS obligatorio)
|
||||
|
||||
### Dependencias Externas
|
||||
|
||||
- Claves API de IGDB, RAWG, TheGamesDB
|
||||
- Acceso a servicios de descarga de imágenes
|
||||
|
||||
---
|
||||
|
||||
## Configuración de Producción
|
||||
|
||||
### Variables de Entorno
|
||||
|
||||
Crear `.env.production` con:
|
||||
|
||||
```env
|
||||
# Database
|
||||
DATABASE_URL="file:./production.db"
|
||||
# Para PostgreSQL: postgresql://user:password@localhost:5432/quasar
|
||||
|
||||
# API Keys
|
||||
IGDB_CLIENT_ID=your_production_client_id
|
||||
IGDB_CLIENT_SECRET=your_production_client_secret
|
||||
RAWG_API_KEY=your_production_api_key
|
||||
THEGAMESDB_API_KEY=your_production_api_key
|
||||
SCREENSCRAPER_USERNAME=your_screenscraper_username
|
||||
SCREENSCRAPER_PASSWORD=your_screenscraper_password
|
||||
|
||||
# App Config
|
||||
NODE_ENV=production
|
||||
PORT=3000
|
||||
HOST=0.0.0.0
|
||||
LOG_LEVEL=info
|
||||
|
||||
# Security
|
||||
CORS_ORIGIN=https://yourdomain.com
|
||||
JWT_SECRET=your_secure_jwt_secret_here
|
||||
API_RATE_LIMIT=100
|
||||
|
||||
# Performance
|
||||
CACHE_TTL=86400
|
||||
MAX_CONCURRENT_API_REQUESTS=5
|
||||
```
|
||||
|
||||
### Configuración de Nginx
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name yourdomain.com;
|
||||
|
||||
# SSL Configuration
|
||||
ssl_certificate /path/to/cert.pem;
|
||||
ssl_certificate_key /path/to/key.pem;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
|
||||
# Security headers
|
||||
add_header X-Frame-Options DENY;
|
||||
add_header X-Content-Type-Options nosniff;
|
||||
add_header X-XSS-Protection "1; mode=block";
|
||||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||||
|
||||
# Frontend
|
||||
location / {
|
||||
root /var/www/quasar/frontend/dist;
|
||||
try_files $uri $uri/ /index.html;
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
|
||||
# Backend API
|
||||
location /api/ {
|
||||
proxy_pass http://localhost:3000;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection 'upgrade';
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_cache_bypass $http_upgrade;
|
||||
proxy_read_timeout 864s;
|
||||
}
|
||||
|
||||
# Static files
|
||||
location /static/ {
|
||||
root /var/www/quasar;
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Despliegue
|
||||
|
||||
### Opción 1: Docker (Recomendado)
|
||||
|
||||
```dockerfile
|
||||
# Dockerfile
|
||||
FROM node:18-alpine AS base
|
||||
|
||||
# Install dependencies only when needed
|
||||
FROM base AS deps
|
||||
RUN apk add --no-cache libc6-compat
|
||||
WORKDIR /app
|
||||
|
||||
# Install dependencies based on the preferred package manager
|
||||
COPY package.json yarn.lock* ./
|
||||
RUN yarn install --frozen-lockfile
|
||||
|
||||
# Rebuild the source code only when needed
|
||||
FROM base AS builder
|
||||
WORKDIR /app
|
||||
COPY --from=deps /app/node_modules ./node_modules
|
||||
COPY . .
|
||||
|
||||
# Build the application
|
||||
RUN yarn build
|
||||
|
||||
# Production image, copy all the files and run next
|
||||
FROM base AS runner
|
||||
WORKDIR /app
|
||||
|
||||
ENV NODE_ENV=production
|
||||
ENV PORT=3000
|
||||
|
||||
# Copy built application
|
||||
COPY --from=builder /app/dist ./dist
|
||||
COPY --from=builder /app/node_modules ./node_modules
|
||||
COPY --from=builder /app/package.json ./package.json
|
||||
|
||||
# Create non-root user
|
||||
RUN addgroup --system --gid 1001 nodejs
|
||||
RUN adduser --system --uid 1001 nextjs
|
||||
|
||||
USER nextjs
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
CMD ["node", "dist/server.js"]
|
||||
```
|
||||
|
||||
```yaml
|
||||
# docker-compose.yml
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
quasar-backend:
|
||||
build: ./backend
|
||||
ports:
|
||||
- '3000:3000'
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
- DATABASE_URL=file:./production.db
|
||||
- IGDB_CLIENT_ID=${IGDB_CLIENT_ID}
|
||||
- IGDB_CLIENT_SECRET=${IGDB_CLIENT_SECRET}
|
||||
- RAWG_API_KEY=${RAWG_API_KEY}
|
||||
- THEGAMESDB_API_KEY=${THEGAMESDB_API_KEY}
|
||||
volumes:
|
||||
- ./data:/app/data
|
||||
- ./backend/prisma:/app/prisma
|
||||
restart: unless-stopped
|
||||
|
||||
quasar-frontend:
|
||||
build: ./frontend
|
||||
ports:
|
||||
- '5173:5173'
|
||||
depends_on:
|
||||
- quasar-backend
|
||||
restart: unless-stopped
|
||||
|
||||
nginx:
|
||||
image: nginx:alpine
|
||||
ports:
|
||||
- '443:443'
|
||||
volumes:
|
||||
- ./nginx.conf:/etc/nginx/nginx.conf
|
||||
- ./ssl:/etc/nginx/ssl
|
||||
depends_on:
|
||||
- quasar-backend
|
||||
- quasar-frontend
|
||||
restart: unless-stopped
|
||||
```
|
||||
|
||||
### Opción 2: VPS Manual
|
||||
|
||||
```bash
|
||||
# 1. Setup server
|
||||
sudo apt update
|
||||
sudo apt install -y nodejs yarn nginx sqlite3
|
||||
|
||||
# 2. Clone repository
|
||||
git clone https://your-repo/quasar.git
|
||||
cd quasar
|
||||
|
||||
# 3. Install dependencies
|
||||
yarn install --production
|
||||
|
||||
# 4. Setup environment
|
||||
cp .env.example .env.production
|
||||
# Edit .env.production with real values
|
||||
|
||||
# 5. Build frontend
|
||||
cd frontend
|
||||
yarn build
|
||||
cd ..
|
||||
|
||||
# 6. Setup database
|
||||
cd backend
|
||||
npx prisma migrate deploy
|
||||
cd ..
|
||||
|
||||
# 7. Configure nginx
|
||||
sudo cp nginx.conf /etc/nginx/sites-available/quasar
|
||||
sudo ln -s /etc/nginx/sites-available/quasar /etc/nginx/sites-enabled/
|
||||
sudo nginx -t
|
||||
sudo systemctl reload nginx
|
||||
|
||||
# 8. Start services
|
||||
cd backend
|
||||
nohup yarn start > /var/log/quasar-backend.log 2>&1 &
|
||||
cd ../frontend
|
||||
nohup yarn start > /var/log/quasar-frontend.log 2>&1 &
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Monitoreo y Mantenimiento
|
||||
|
||||
### Health Checks
|
||||
|
||||
```bash
|
||||
# Backend health
|
||||
curl http://localhost:3000/health
|
||||
|
||||
# Database connection
|
||||
curl http://localhost:3000/api/health/database
|
||||
|
||||
# API rate limits status
|
||||
curl http://localhost:3000/api/health/rate-limits
|
||||
```
|
||||
|
||||
### Logging
|
||||
|
||||
Configurar logrotate:
|
||||
|
||||
```bash
|
||||
# /etc/logrotate.d/quasar
|
||||
/var/log/quasar/*.log {
|
||||
daily
|
||||
missingok
|
||||
rotate 7
|
||||
compress
|
||||
delaycompress
|
||||
notifempty
|
||||
copytruncate
|
||||
}
|
||||
```
|
||||
|
||||
### Monitoreo de API Keys
|
||||
|
||||
Crear script para verificar límites:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# check-api-limits.sh
|
||||
|
||||
# Check IGDB rate limits
|
||||
curl -s -I "https://api.igdb.com/v4/games" | grep -i "x-ratelimit"
|
||||
|
||||
# Check RAWG usage
|
||||
curl -s "https://api.rawg.io/api/games?key=$RAWG_API_KEY&search=test" | jq '.count'
|
||||
|
||||
# Log warnings
|
||||
echo "$(date): API rate limits checked" >> /var/log/quasar/api-monitor.log
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Actualizaciones
|
||||
|
||||
### Proceso de Actualización
|
||||
|
||||
```bash
|
||||
# 1. Backup
|
||||
./backup.sh
|
||||
|
||||
# 2. Stop services
|
||||
sudo systemctl stop quasar-backend
|
||||
sudo systemctl stop quasar-frontend
|
||||
|
||||
# 3. Pull latest code
|
||||
git pull origin main
|
||||
|
||||
# 4. Update dependencies
|
||||
yarn install --frozen-lockfile
|
||||
|
||||
# 5. Build frontend
|
||||
cd frontend && yarn build && cd ..
|
||||
|
||||
# 6. Run migrations
|
||||
cd backend && npx prisma migrate deploy && cd ..
|
||||
|
||||
# 7. Start services
|
||||
sudo systemctl start quasar-backend
|
||||
sudo systemctl start quasar-frontend
|
||||
```
|
||||
|
||||
### Actualizaciones de API Keys
|
||||
|
||||
1. Generar nuevas claves en cada servicio
|
||||
2. Actualizar variables de entorno
|
||||
3. Reiniciar servicios
|
||||
4. Monitorear errores durante 24h
|
||||
|
||||
---
|
||||
|
||||
## Backup y Recuperación
|
||||
|
||||
### Script de Backup
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# backup.sh
|
||||
|
||||
BACKUP_DIR="/backups/quasar"
|
||||
DATE=$(date +%Y%m%d_%H%M%S)
|
||||
DB_FILE="quasar_$DATE.db"
|
||||
ROMS_DIR="roms_$DATE"
|
||||
|
||||
# Create backup directory
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
|
||||
# Backup database
|
||||
cp backend/prisma/production.db "$BACKUP_DIR/$DB_FILE"
|
||||
|
||||
# Backup ROM metadata (not actual ROMs)
|
||||
cp -r data/roms_metadata "$BACKUP_DIR/$ROMS_DIR"
|
||||
|
||||
# Backup configuration
|
||||
cp .env.production "$BACKUP_DIR/env_$DATE"
|
||||
|
||||
# Compress backup
|
||||
tar -czf "$BACKUP_DIR/backup_$DATE.tar.gz" -C "$BACKUP_DIR" "$DB_FILE" "$ROMS_DIR" "env_$DATE"
|
||||
|
||||
# Clean up old backups (keep last 7 days)
|
||||
find "$BACKUP_DIR" -name "backup_*.tar.gz" -mtime +7 -delete
|
||||
|
||||
echo "Backup completed: $BACKUP_DIR/backup_$DATE.tar.gz"
|
||||
```
|
||||
|
||||
### Recuperación
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# restore.sh
|
||||
|
||||
BACKUP_FILE=$1
|
||||
BACKUP_DIR="/backups/quasar"
|
||||
|
||||
if [ ! -f "$BACKUP_DIR/$BACKUP_FILE" ]; then
|
||||
echo "Backup file not found: $BACKUP_DIR/$BACKUP_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Stop services
|
||||
sudo systemctl stop quasar-backend
|
||||
sudo systemctl stop quasar-frontend
|
||||
|
||||
# Extract backup
|
||||
cd "$BACKUP_DIR"
|
||||
tar -xzf "$BACKUP_FILE"
|
||||
|
||||
# Restore database
|
||||
cp "$DB_FILE" backend/prisma/production.db
|
||||
|
||||
# Restore ROM metadata
|
||||
cp -r "$ROMS_DIR"/* data/
|
||||
|
||||
# Restore configuration (optional)
|
||||
# cp "env_$DATE" .env.production
|
||||
|
||||
# Start services
|
||||
sudo systemctl start quasar-backend
|
||||
sudo systemctl start quasar-frontend
|
||||
|
||||
echo "Restore completed from: $BACKUP_FILE"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Solución de Problemas
|
||||
|
||||
### Problemas Comunes
|
||||
|
||||
#### 1. "Database connection failed"
|
||||
|
||||
```bash
|
||||
# Check database file
|
||||
ls -la backend/prisma/production.db
|
||||
|
||||
# Check permissions
|
||||
sudo chown -R nodejs:nodejs backend/prisma/
|
||||
|
||||
# Check database integrity
|
||||
sqlite3 backend/prisma/production.db "PRAGMA integrity_check;"
|
||||
```
|
||||
|
||||
#### 2. "API rate limit exceeded"
|
||||
|
||||
```bash
|
||||
# Check current rate limits
|
||||
curl -I "https://api.igdb.com/v4/games" | grep -i "x-ratelimit"
|
||||
|
||||
# Implement backoff strategy
|
||||
# Check logs for specific API errors
|
||||
tail -f /var/log/quasar/backend.log | grep "429"
|
||||
```
|
||||
|
||||
#### 3. "Frontend cannot connect to backend"
|
||||
|
||||
```bash
|
||||
# Check backend is running
|
||||
curl http://localhost:3000/health
|
||||
|
||||
# Check CORS configuration
|
||||
curl -H "Origin: https://yourdomain.com" -v http://localhost:3000/health
|
||||
|
||||
# Check nginx configuration
|
||||
sudo nginx -t
|
||||
```
|
||||
|
||||
#### 4. "ROM scanning fails"
|
||||
|
||||
```bash
|
||||
# Check directory permissions
|
||||
ls -la /path/to/roms/
|
||||
|
||||
# Check file formats
|
||||
find /path/to/roms/ -name "*.zip" -o -name "*.7z" -o -name "*.rar"
|
||||
|
||||
# Check disk space
|
||||
df -h
|
||||
```
|
||||
|
||||
### Diagnóstico Remoto
|
||||
|
||||
```bash
|
||||
# Create diagnostic script
|
||||
#!/bin/bash
|
||||
# diagnostic.sh
|
||||
|
||||
echo "=== Quasar Diagnostic Report ==="
|
||||
echo "Date: $(date)"
|
||||
echo "Node.js version: $(node --version)"
|
||||
echo "Yarn version: $(yarn --version)"
|
||||
echo ""
|
||||
|
||||
echo "=== System Resources ==="
|
||||
free -h
|
||||
df -h
|
||||
echo ""
|
||||
|
||||
echo "=== Services Status ==="
|
||||
systemctl status quasar-backend
|
||||
systemctl status quasar-frontend
|
||||
echo ""
|
||||
|
||||
echo "=== Database Status ==="
|
||||
sqlite3 backend/prisma/production.db "SELECT COUNT(*) FROM games;"
|
||||
sqlite3 backend/prisma/production.db "SELECT COUNT(*) FROM rom_files;"
|
||||
echo ""
|
||||
|
||||
echo "=== API Keys Status ==="
|
||||
echo "IGDB: ${IGDB_CLIENT_ID:0:10}..."
|
||||
echo "RAWG: ${RAWG_API_KEY:0:10}..."
|
||||
echo "TheGamesDB: ${THEGAMESDB_API_KEY:0:10}..."
|
||||
echo ""
|
||||
|
||||
echo "=== Recent Errors ==="
|
||||
tail -20 /var/log/quasar/backend.log | grep -i "error"
|
||||
tail -20 /var/log/quasar/frontend.log | grep -i "error"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Soporte
|
||||
|
||||
### Logs de Depuración
|
||||
|
||||
```bash
|
||||
# Backend logs
|
||||
tail -f /var/log/quasar/backend.log
|
||||
|
||||
# Frontend logs
|
||||
tail -f /var/log/quasar/frontend.log
|
||||
|
||||
# Nginx logs
|
||||
tail -f /var/log/nginx/access.log
|
||||
tail -f /var/log/nginx/error.log
|
||||
```
|
||||
|
||||
### Contacto
|
||||
|
||||
- **Issues:** Reportar en el repositorio de Gitea
|
||||
- **Emergencias:** Email: support@yourdomain.com
|
||||
- **Documentación:** Ver [docs/README.md](../../README.md)
|
||||
|
||||
---
|
||||
|
||||
_Última actualización: 2026-02-22_
|
||||
140
docs/API_KEYS.md
140
docs/API_KEYS.md
@@ -1,140 +0,0 @@
|
||||
# Obtaining API Keys
|
||||
|
||||
This guide explains how to get credentials for each metadata service.
|
||||
|
||||
## IGDB (Internet Game Database)
|
||||
|
||||
IGDB uses **OAuth 2.0 via Twitch**. Steps:
|
||||
|
||||
1. Go to [Twitch Developer Console](https://dev.twitch.tv/console/apps)
|
||||
2. Sign in with your Twitch account (create one if needed)
|
||||
3. Click "Create Application"
|
||||
- Name: "Quasar" (or your app name)
|
||||
- Category: Select relevant category
|
||||
- Accept terms, click Create
|
||||
4. You'll see:
|
||||
- **Client ID** — Copy this
|
||||
- Click "New Secret" to generate **Client Secret** — Copy this
|
||||
5. Go to Settings → OAuth Redirect URLs
|
||||
- Add: `http://localhost:3000/oauth/callback` (development)
|
||||
- For production: `https://yourdomain.com/oauth/callback`
|
||||
6. In your `.env` file:
|
||||
```
|
||||
IGDB_CLIENT_ID=your_client_id
|
||||
IGDB_CLIENT_SECRET=your_client_secret
|
||||
```
|
||||
7. Start Quasar, it will use IGDB automatically
|
||||
|
||||
**Rate Limit:** 4 requests/second
|
||||
|
||||
## RAWG (Rawg.io)
|
||||
|
||||
RAWG has a simpler **API Key** approach:
|
||||
|
||||
1. Go to [RAWG Settings](https://rawg.io/settings/account)
|
||||
2. Sign up if needed, then login
|
||||
3. Find "API Key" section
|
||||
4. Click "Create new key" (if needed) or copy existing key
|
||||
5. In your `.env` file:
|
||||
```
|
||||
RAWG_API_KEY=your_api_key_here
|
||||
```
|
||||
6. Start Quasar
|
||||
|
||||
**Rate Limit:** 20 requests/second (free tier)
|
||||
|
||||
**Note:** RAWG requires attribution in UI (include "Powered by RAWG" somewhere visible)
|
||||
|
||||
## TheGamesDB (thegamesdb.net)
|
||||
|
||||
TheGamesDB uses a simple **API Key**:
|
||||
|
||||
1. Go to [TheGamesDB API](https://thegamesdb.net/api)
|
||||
2. Find "API Key" section (free registration required)
|
||||
3. Register or login
|
||||
4. Copy your API key
|
||||
5. In your `.env` file:
|
||||
```
|
||||
THEGAMESDB_API_KEY=your_api_key_here
|
||||
```
|
||||
6. Start Quasar
|
||||
|
||||
**Rate Limit:** 1 request/second (free tier)
|
||||
|
||||
## Testing Without Real Keys
|
||||
|
||||
For development/testing:
|
||||
|
||||
- Leave API keys as `your_*_here` in `.env.local`
|
||||
- Quasar will gracefully degrade and show limited metadata
|
||||
- Frontend will still work with manual game entry
|
||||
|
||||
## Production Deployment
|
||||
|
||||
For production:
|
||||
|
||||
1. Generate new keys on each service (don't reuse dev keys)
|
||||
2. Store keys in **Gitea Secrets** (for automated CI/CD pipelines)
|
||||
3. Or use environment variables on your hosting provider
|
||||
4. Rotate keys every 3 months
|
||||
5. Monitor rate limits in service dashboards
|
||||
|
||||
## Gitea Actions CI/CD Setup
|
||||
|
||||
To enable automated testing with API keys in Gitea Actions:
|
||||
|
||||
### 1. Store Secrets in Gitea
|
||||
|
||||
Navigate to your repository settings:
|
||||
|
||||
```
|
||||
https://your-gitea-instance/your-org/quasar/settings/secrets/actions
|
||||
```
|
||||
|
||||
Add these secrets:
|
||||
|
||||
- `IGDB_CLIENT_ID` (from Twitch Developer Console)
|
||||
- `IGDB_CLIENT_SECRET` (from Twitch Developer Console)
|
||||
- `RAWG_API_KEY` (from RAWG settings)
|
||||
- `THEGAMESDB_API_KEY` (from TheGamesDB API)
|
||||
|
||||
### 2. Workflow Configuration
|
||||
|
||||
The `.gitea/workflows/ci.yml` workflow automatically:
|
||||
|
||||
- ✅ Installs dependencies
|
||||
- ✅ Runs linting checks
|
||||
- ✅ Executes backend tests (Vitest)
|
||||
- ✅ Executes frontend tests (Vitest)
|
||||
- ✅ Starts backend + frontend servers
|
||||
- ✅ Runs E2E tests (Playwright) with real metadata APIs
|
||||
- ✅ Uploads test reports on failure
|
||||
|
||||
### 3. Testing Flow
|
||||
|
||||
1. **Push** code to `main` or `develop`
|
||||
2. **Gitea Actions** picks up the `.gitea/workflows/ci.yml`
|
||||
3. **Secrets are injected** as `IGDB_CLIENT_ID`, `IGDB_CLIENT_SECRET`, `RAWG_API_KEY`, `THEGAMESDB_API_KEY`
|
||||
4. **E2E tests** fetch real metadata from APIs (using injected secrets)
|
||||
5. **Build fails** if any test fails (prevents broken code)
|
||||
|
||||
### 4. Local Development
|
||||
|
||||
For local testing, use `.env.local`:
|
||||
|
||||
```bash
|
||||
IGDB_CLIENT_ID=your_local_id
|
||||
IGDB_CLIENT_SECRET=your_local_secret
|
||||
RAWG_API_KEY=your_local_key
|
||||
THEGAMESDB_API_KEY=your_local_key
|
||||
```
|
||||
|
||||
**Note:** CI/CD uses Gitea Secrets (not `.env` files), so never commit real credentials.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**"IGDB_CLIENT_ID not found"** → Check `.env` file exists and has correct format
|
||||
|
||||
**"429 Too Many Requests"** → Rate limit exceeded, wait and retry
|
||||
|
||||
**"Invalid API Key"** → Copy key exactly (no spaces), verify it's active on service website
|
||||
92
docs/README.md
Normal file
92
docs/README.md
Normal file
@@ -0,0 +1,92 @@
|
||||
# Documentación del Proyecto Quasar 📚
|
||||
|
||||
Esta documentación está organizada en secciones lógicas para facilitar la navegación y mantenimiento.
|
||||
|
||||
## Estructura de la Documentación
|
||||
|
||||
```
|
||||
docs/
|
||||
├── README.md # Este archivo (índice general)
|
||||
├── 01-conceptos/ # Conceptos fundamentales y requisitos
|
||||
│ ├── requirements.md # Requisitos funcionales y no funcionales
|
||||
│ ├── architecture.md # Arquitectura técnica general
|
||||
│ └── data-model.md # Modelo de datos y esquema
|
||||
├── 02-tecnico/ # Documentación técnica detallada
|
||||
│ ├── apis.md # APIs del sistema (consolidado)
|
||||
│ ├── frontend.md # Documentación del frontend
|
||||
│ └── lessons-learned.md # Lecciones aprendidas y recomendaciones
|
||||
├── 03-analisis/ # Análisis comparativos y estudios
|
||||
│ └── competitive-analysis.md # Análisis competitivo
|
||||
└── 04-operaciones/ # Guías de operación y despliegue
|
||||
```
|
||||
|
||||
## Guía de Navegación
|
||||
|
||||
### 🎯 Para nuevos desarrolladores
|
||||
|
||||
1. Comienza con [`01-conceptos/requirements.md`](01-conceptos/requirements.md) para entender el propósito
|
||||
2. Lee [`01-conceptos/architecture.md`](01-conceptos/architecture.md) para la visión general
|
||||
3. Revisa [`01-conceptos/data-model.md`](01-conceptos/data-model.md) para entender los datos
|
||||
|
||||
### 🔧 Para trabajo técnico
|
||||
|
||||
1. Consulta [`02-tecnico/apis.md`](02-tecnico/apis.md) para APIs y configuración
|
||||
2. Revisa [`02-tecnico/frontend.md`](02-tecnico/frontend.md) para detalles del frontend
|
||||
3. Lee [`02-tecnico/lessons-learned.md`](02-tecnico/lessons-learned.md) para buenas prácticas
|
||||
|
||||
### 📊 Para análisis y decisiones
|
||||
|
||||
1. Revisa [`03-analisis/competitive-analysis.md`](03-analisis/competitive-analysis.md) para contexto del mercado
|
||||
|
||||
### 🚀 Para operaciones y despliegue
|
||||
|
||||
1. Las guías de operación están en desarrollo (sección `04-operaciones/`)
|
||||
|
||||
## Convenciones
|
||||
|
||||
### Formato de enlaces
|
||||
|
||||
Todos los enlaces internos usan formato markdown estándar:
|
||||
|
||||
```markdown
|
||||
[texto de enlace](ruta/al/archivo.md)
|
||||
```
|
||||
|
||||
### Nomenclatura de archivos
|
||||
|
||||
- Todos los usan `kebab-case.md`
|
||||
- Los prefijos numéricos indican orden de lectura
|
||||
|
||||
### Estructura de documentos
|
||||
|
||||
- Cada documento tiene tabla de contenidos (TOC)
|
||||
- Secciones numeradas para mejor navegación
|
||||
- Ejemplos de código con formato sintáctico
|
||||
|
||||
## Estado Actual
|
||||
|
||||
| Sección | Estado | Comentarios |
|
||||
| -------------- | ---------------- | ------------------------------------ |
|
||||
| 01-conceptos | ✅ Completa | Documentación fundamental estable |
|
||||
| 02-tecnico | ✅ Actualizada | APIs consolidados, frontend completo |
|
||||
| 03-analisis | ✅ Completa | Análisis competitivo actualizado |
|
||||
| 04-operaciones | 🚧 En desarrollo | Guías de operación pendientes |
|
||||
|
||||
## Próximos Pasos
|
||||
|
||||
- [ ] Añadir documentación de testing y CI/CD
|
||||
- [ ] Crear índice temático para búsqueda rápida
|
||||
- [ ] Documentar API REST detallada
|
||||
|
||||
## Contribuir
|
||||
|
||||
Al agregar nuevo contenido:
|
||||
|
||||
1. Coloca el documento en la sección adecuada
|
||||
2. Sigue las convenciones de nomenclatura
|
||||
3. Actualiza este README si agregas nuevas secciones
|
||||
4. Revisa y actualiza referencias cruzadas
|
||||
|
||||
---
|
||||
|
||||
_Última actualización: 2026-02-22_
|
||||
@@ -1,148 +0,0 @@
|
||||
# Integración de APIs externas — Prioridad y guía práctica
|
||||
|
||||
## Objetivo
|
||||
|
||||
Definir APIs prioritarias para el MVP, cómo obtener credenciales, ejemplos de uso y estrategias de robustez (rate limit, retries, fallback y normalización de datos).
|
||||
|
||||
---
|
||||
|
||||
## APIs priorizadas (MVP)
|
||||
|
||||
1. **IGDB (prioridad alta)**
|
||||
2. **RAWG (prioridad alta)**
|
||||
3. **TheGamesDB (prioridad media)**
|
||||
|
||||
---
|
||||
|
||||
## IGDB
|
||||
|
||||
- **Obtener credenciales**: registrar una app en Twitch Developer Console para obtener `CLIENT_ID` y `CLIENT_SECRET`. Obtener token con grant type `client_credentials` (POST a `https://id.twitch.tv/oauth2/token`).
|
||||
|
||||
- **Endpoints principales**: `POST https://api.igdb.com/v4/games` (consulta flexible via body con sintaxis IGDB), `POST https://api.igdb.com/v4/covers`, `POST https://api.igdb.com/v4/platforms`.
|
||||
|
||||
- **Ejemplo (buscar)**:
|
||||
|
||||
```bash
|
||||
# Obtener token
|
||||
curl -X POST 'https://id.twitch.tv/oauth2/token?client_id=$IGDB_CLIENT_ID&client_secret=$IGDB_CLIENT_SECRET&grant_type=client_credentials'
|
||||
|
||||
# Buscar juegos
|
||||
curl -X POST 'https://api.igdb.com/v4/games' \
|
||||
-H "Client-ID: $IGDB_CLIENT_ID" \
|
||||
-H "Authorization: Bearer $IGDB_TOKEN" \
|
||||
-H 'Accept: application/json' \
|
||||
--data 'fields id,name,first_release_date,platforms.name,genres.name,cover.url; search "zelda"; limit 5;'
|
||||
```
|
||||
|
||||
- **Respuesta (esquemática)**:
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": 12345,
|
||||
"name": "Ejemplo",
|
||||
"first_release_date": 1459468800,
|
||||
"platforms": [{ "name": "Nintendo Switch" }],
|
||||
"cover": { "url": "//images.igdb.com/...jpg" }
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
- **Límites y manejo**: la API puede devolver `429` o cabeceras de límite; implementar retries exponenciales (ej. 3 intentos) y respetar `Retry-After`. Implementar circuit breaker si la API falla repetidamente.
|
||||
- **Atribución**: mostrar origen de datos (ej. "Datos: IGDB") según términos del servicio.
|
||||
|
||||
---
|
||||
|
||||
## RAWG
|
||||
|
||||
- **Obtener credenciales**: registrarse en RAWG para obtener `RAWG_API_KEY` (https://rawg.io/apidocs).
|
||||
- **Endpoints principales**: `GET https://api.rawg.io/api/games?key=API_KEY&search=...`, `GET https://api.rawg.io/api/games/{id}`.
|
||||
- **Ejemplo**:
|
||||
|
||||
```bash
|
||||
curl 'https://api.rawg.io/api/games?key=$RAWG_API_KEY&search=zelda&page_size=5'
|
||||
```
|
||||
|
||||
- **Respuesta (esquemática)**:
|
||||
|
||||
```json
|
||||
{
|
||||
"count": 100,
|
||||
"results": [
|
||||
{ "id": 3498, "name": "GTA V", "released": "2013-09-17", "background_image": "https://..." }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
- **Límites y manejo**: RAWG suele tener límites por clave/plan; cachear y fallback a otros proveedores si falla.
|
||||
- **Atribución**: revisar condiciones y mostrar HTTP o texto de fuente si es requerido por el proveedor.
|
||||
|
||||
---
|
||||
|
||||
## TheGamesDB
|
||||
|
||||
- **Obtener credenciales**: crear cuenta y generar API Key en https://thegamesdb.net.
|
||||
- **Endpoints**: búsqueda por nombre y detalles (`/v1/Games/ByGameName?name=...`, `/v1/Games/ByGameID?id=...`).
|
||||
- **Ejemplo**:
|
||||
|
||||
```bash
|
||||
curl -H 'Authorization: Bearer $THEGAMESDB_KEY' 'https://api.thegamesdb.net/v1/Games/ByGameName?name=zelda'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Estrategia de fallback y normalización
|
||||
|
||||
- **Orden de prioridad**: IGDB → RAWG → TheGamesDB (configurable).
|
||||
- **Normalización (mapping)**:
|
||||
- `title` ← `name`
|
||||
- `platform` ← `platforms[].name`
|
||||
- `release_date` ← `first_release_date` / `released` → convertir a ISO 8601
|
||||
- `genres` ← `genres[].name`
|
||||
- `cover_url` ← `cover.url` / `background_image`
|
||||
- `external_ids` ← `{ igdb: id, rawg: id, thegamesdb: id }`
|
||||
|
||||
- **Fallback**: si IGDB no tiene portada, intentar RAWG; si falla, usar TheGamesDB. Registrar la fuente usada.
|
||||
|
||||
---
|
||||
|
||||
## Caché y almacenamiento de artwork
|
||||
|
||||
- **Caché metadata**: LRU en memoria o Redis con TTL (por ejemplo 24h) para evitar sobrecargar APIs.
|
||||
- **Almacenamiento de imágenes**: descargar y optimizar con `sharp` (crear versiones: thumb, medium), almacenar en `storage/artwork/{gameId}/cover.jpg` o S3.
|
||||
- **Servicio proxy**: servir imágenes desde backend para no exponer keys ni URLs externas.
|
||||
|
||||
---
|
||||
|
||||
## Manejo de errores y resiliencia
|
||||
|
||||
- Implementar **retries** exponenciales con jitter (3 intentos).
|
||||
- Implementar **circuit breaker** para desconectar llamadas a un proveedor fuera de servicio por N minutos.
|
||||
- Limitar concurrencia por proveedor (p. ej. 5 llamadas simultáneas) y usar colas para trabajos masivos (enriquecimiento masivo).
|
||||
|
||||
---
|
||||
|
||||
## Variables de entorno (ejemplos)
|
||||
|
||||
```
|
||||
IGDB_CLIENT_ID=...
|
||||
IGDB_CLIENT_SECRET=...
|
||||
RAWG_API_KEY=...
|
||||
THEGAMESDB_API_KEY=...
|
||||
EXTERNAL_API_CONCURRENCY=5
|
||||
```
|
||||
|
||||
> Nota: **Nunca** exponer estas claves en el cliente; siempre pasar por el backend.
|
||||
|
||||
---
|
||||
|
||||
## Fuentes
|
||||
|
||||
- IGDB API docs, RAWG API docs, TheGamesDB API docs.
|
||||
- Patrones: retries, circuit breakers (ej. libraries: `p-retry`, `cockatiel`).
|
||||
|
||||
---
|
||||
|
||||
**Metadatos**
|
||||
Autor: Quasar (investigación automatizada)
|
||||
Última actualización: 2026-02-07
|
||||
Reference in New Issue
Block a user