diff --git a/README.md b/README.md new file mode 100644 index 0000000..563abc7 --- /dev/null +++ b/README.md @@ -0,0 +1,198 @@ +# Project Afterlife + +Plataforma de preservacion de videojuegos con documentales interactivos. Servidores privados de juegos que ya no existen, acompanados de documentales narrativos que cuentan su historia. + +## Estado Actual + +| Servicio | Estado | Puerto | RAM | +|----------|--------|--------|-----| +| **Next.js 15** (frontend) | En linea | 3000 | ~111 MB | +| **Strapi 5** (CMS) | En linea | 1337 | ~179 MB | +| **PostgreSQL 16** | En linea | 5432 | ~57 MB | +| **MinIO** (almacenamiento) | En linea | 9000/9001 | ~144 MB | +| **OpenFusion** (FusionFall) | En linea | 23000-23001 | ~254 MB | +| **MapleStory 2 - World** | En linea | 21001 | ~126 MB | +| **MapleStory 2 - Login** | En linea | 20001 | ~100 MB | +| **MapleStory 2 - Web** | En linea | 4000 | ~70 MB | +| **MapleStory 2 - Game Ch0** | En linea | 20002/21002 | ~341 MB | +| **MapleStory 2 - MySQL** | En linea | 3307 | ~733 MB | +| **Minecraft FTB Infinity** | En linea | 25565 | ~3.5 GB | + +**Total**: ~5.6 GB RAM / 40 GB disponibles | 31 GB disco / 96 GB disponibles + +## Juegos Preservados + +### FusionFall (Cartoon Network Universe) +- **Emulador**: [OpenFusion](https://github.com/OpenFusionProject/OpenFusion) (C++) +- **Conexion**: `192.168.10.234:23000` +- **Cliente**: [FusionFall Retro Client](https://github.com/OpenFusionProject) +- **Documental**: "FusionFall: El Mundo Que No Queriamos Perder" (7 capitulos) + +### MapleStory 2 +- **Emulador**: [Maple2](https://github.com/MS2Community/Maple2) (C# .NET 8) +- **Conexion**: `192.168.10.234:20001` (Login Server) +- **Cliente**: MapleStory 2 Global Client + XML Patches +- **Documental**: "MapleStory 2: El Mundo Que Construimos Juntos" (7 capitulos) + +### Minecraft: FTB Infinity Evolved +- **Servidor**: [itzg/minecraft-server](https://github.com/itzg/docker-minecraft-server) (Java 8) +- **Conexion**: `192.168.10.234:25565` +- **Cliente**: FTB App o launcher compatible con FTB Infinity Evolved v3.1.0 +- **Modpack**: 175+ mods, Minecraft 1.7.10 + Forge 10.13.4.1614 + +## Arquitectura + +``` +project-afterlife/ +├── apps/ +│ ├── cms/ # Strapi 5 CMS (React 18) +│ └── web/ # Next.js 15 frontend (React 19) +├── packages/ +│ └── shared/ # Tipos TypeScript compartidos +├── servers/ +│ ├── openfusion/ # Servidor FusionFall (C++) +│ └── maple2/ # Servidor MapleStory 2 (C# .NET 8) +├── docker/ +│ ├── docker-compose.dev.yml # Stack local (web + CMS + juegos) +│ ├── docker-compose.maple2.yml # MapleStory 2 (separado) +│ ├── docker-compose.yml # Produccion (con Nginx + SSL) +│ └── nginx/ # Configuracion Nginx +├── docs/ # Documentacion del proyecto +└── .github/workflows/ # CI/CD deployment +``` + +### Stack Tecnologico + +| Componente | Tecnologia | Version | +|-----------|-----------|---------| +| Frontend | Next.js + TypeScript | 15.x | +| UI | Tailwind CSS | v4 | +| i18n | next-intl | 4.8.3 | +| CMS | Strapi | 5.36.0 | +| Base de datos (CMS) | PostgreSQL | 16 | +| Base de datos (MS2) | MySQL | 8.0 | +| Almacenamiento | MinIO (S3) | Latest | +| Audio | Howler.js | 2.2.4 | +| Animaciones | Framer Motion | 12.x | +| Monorepo | npm workspaces + Turborepo | - | +| CI/CD | GitHub Actions | - | +| Reverse Proxy | Nginx | Alpine | + +## Inicio Rapido + +### Requisitos +- Docker y Docker Compose v2+ +- 8 GB RAM minimo (16 GB recomendado con todos los servidores) +- 50 GB disco libre + +### 1. Clonar y configurar + +```bash +git clone https://git.consultoria-as.com/consultoria-as/project-afterlife.git +cd project-afterlife +``` + +### 2. Crear archivo de entorno + +```bash +cp docker/.env.example docker/.env +# Editar docker/.env con las claves necesarias +``` + +Variables requeridas en `docker/.env`: +```env +# Base de datos +DATABASE_NAME=afterlife +DATABASE_USERNAME=afterlife +DATABASE_PASSWORD=afterlife + +# MinIO +MINIO_ROOT_USER=afterlife +MINIO_ROOT_PASSWORD=afterlife123 + +# Strapi (generar con openssl rand -base64 32) +APP_KEYS= +API_TOKEN_SALT= +ADMIN_JWT_SECRET= +TRANSFER_TOKEN_SALT= +JWT_SECRET= + +# API Token (crear en Strapi Admin > Settings > API Tokens) +STRAPI_API_TOKEN= + +# Strapi URL publica +PUBLIC_STRAPI_URL=http://localhost:1337 + +# OpenFusion +OPENFUSION_SHARD_IP=192.168.10.234 +``` + +### 3. Levantar servicios base + +```bash +cd docker + +# Stack principal (CMS + Web + OpenFusion + Minecraft FTB) +docker compose -f docker-compose.dev.yml up -d + +# MapleStory 2 (requiere setup previo, ver docs/game-servers.md) +docker compose -f docker-compose.maple2.yml up -d +``` + +### 4. Setup inicial de Strapi + +1. Abrir http://localhost:1337/admin +2. Crear usuario administrador +3. Ir a Settings > API Tokens > Create new API Token +4. Tipo: Full access, copiar el token a `STRAPI_API_TOKEN` en `.env` +5. Reiniciar el servicio web: `docker compose -f docker-compose.dev.yml restart web` + +### 5. Verificar + +- **Frontend**: http://localhost:3000 +- **CMS Admin**: http://localhost:1337/admin +- **MinIO Console**: http://localhost:9001 + +## Documentacion Completa + +| Documento | Descripcion | +|-----------|------------| +| [README.md](README.md) | Este archivo — vision general y estado | +| [docs/architecture.md](docs/architecture.md) | Arquitectura tecnica detallada | +| [docs/game-servers.md](docs/game-servers.md) | Setup y operacion de servidores de juegos | +| [docs/cms-content.md](docs/cms-content.md) | Modelo de contenido CMS y documentales | +| [docs/deployment.md](docs/deployment.md) | Guia de despliegue a produccion | +| [docs/plans/](docs/plans/) | Documentos de diseno e implementacion | + +## Rutas de la Web + +| Ruta | Descripcion | +|------|------------| +| `/es` o `/en` | Pagina principal con hero y ultimos juegos | +| `/es/catalog` | Catalogo de juegos con filtros | +| `/es/about` | Sobre el proyecto | +| `/es/donate` | Pagina de donaciones | +| `/es/games/[slug]` | Pagina individual de juego | +| `/es/games/[slug]/documentary` | Documental interactivo | + +## Contenido en Base de Datos + +### Juegos +| Slug | Titulo | Estado | Documental | +|------|--------|--------|------------| +| `fusionfall` | FusionFall | Online | 7 capitulos | +| `maplestory2` | MapleStory 2 | Online | 7 capitulos | +| `minecraft-ftb-infinity` | Minecraft: FTB Infinity Evolved | Online | Pendiente | + +### Documentales +| Juego | Titulo | Capitulos | +|-------|--------|-----------| +| FusionFall | "El Mundo Que No Queriamos Perder" | 7 | +| MapleStory 2 | "El Mundo Que Construimos Juntos" | 7 | + +Cada documental tiene sus 7 capitulos publicados en ambos idiomas (ES/EN). + +## Licencia + +Proyecto privado. Todos los derechos reservados. +Los emuladores de juegos utilizados son proyectos open-source independientes con sus propias licencias. diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 0000000..3460753 --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,161 @@ +# Arquitectura Tecnica + +## Vision General + +Project Afterlife es un monorepo que combina una plataforma web de preservacion de videojuegos con los servidores de los juegos preservados. La infraestructura se gestiona completamente con Docker Compose. + +## Diagrama de Servicios + +``` + ┌─────────────────────────────────────┐ + │ USUARIO / CLIENTE │ + └───────────┬───────────┬─────────────┘ + │ │ + ┌───────────▼───┐ ┌─────▼─────────────┐ + │ Next.js :3000│ │ Clientes de juegos │ + └───────┬───────┘ └──┬──────┬─────┬───┘ + │ │ │ │ + ┌───────▼───────┐ │ │ │ + │ Strapi :1337 │ │ │ │ + └───┬───────┬───┘ │ │ │ + │ │ │ │ │ + ┌───────▼──┐ ┌──▼────┐ │ │ │ + │ PG :5432 │ │ MinIO │ │ │ │ + │ │ │ :9000 │ │ │ │ + └──────────┘ └───────┘ │ │ │ + │ │ │ + ┌──────────────────────────────┘ │ │ + │ │ │ + ┌───────▼──────────┐ ┌──────────────────┐ │ │ + │ OpenFusion │ │ MapleStory 2 │ │ │ + │ :23000-23001 │ │ Login :20001 │ │ │ + └──────────────────┘ │ World :21001 │ │ │ + │ Game :20002 │ │ │ + │ Web :4000 │ │ │ + │ MySQL :3307 │ │ │ + └──────────────────┘ │ │ + │ │ + ┌──────────────────────▼─┐ │ + │ Minecraft FTB :25565 │ │ + └────────────────────────┘ │ + │ + (Juegos futuros...) ◄──┘ +``` + +## Componentes + +### Frontend (Next.js 15) + +**Ubicacion**: `apps/web/` + +- **Framework**: Next.js 15 con App Router +- **React**: 19 (forzado via `overrides` en root `package.json`) +- **Estilos**: Tailwind CSS v4 con `@tailwindcss/postcss` +- **i18n**: next-intl con prefijo de ruta (`/es/`, `/en/`) +- **Audio**: Howler.js para reproductor de documentales +- **Animaciones**: Framer Motion + +**Estructura de rutas**: +``` +src/app/ +├── layout.tsx # Pass-through (return children) +├── globals.css # Tailwind v4 imports +├── not-found.tsx # 404 page +└── [locale]/ + ├── layout.tsx # ,
, providers + ├── page.tsx # Home + ├── about/page.tsx # About + ├── catalog/page.tsx # Game catalog + ├── donate/page.tsx # Donations + └── games/ + └── [slug]/ + ├── page.tsx # Game detail + └── documentary/page.tsx # Interactive documentary +``` + +**Patron de layout**: El root `layout.tsx` es un pass-through que solo retorna `children`. El layout real con ``, ``, y `NextIntlClientProvider` esta en `[locale]/layout.tsx`. Esto es necesario para que next-intl funcione correctamente con el App Router. + +### CMS (Strapi 5) + +**Ubicacion**: `apps/cms/` + +- **Version**: Strapi 5.36.0 +- **Base de datos**: PostgreSQL 16 +- **Almacenamiento**: MinIO (compatible con S3) +- **React**: 18 (admin panel, separado del frontend) +- **i18n**: Plugin nativo con locales ES/EN + +**Content Types**: +- `Game` — Entrada de juego con metadata, screenshots, estado del servidor +- `Documentary` — Documental con titulo, descripcion, relacion 1:1 con Game +- `Chapter` — Capitulo con contenido rich text, audio opcional, orden + +**Nota sobre schemas**: Los archivos `schema.json` de Strapi 5 deben copiarse manualmente al directorio `dist/` durante el build. El Dockerfile del CMS incluye este fix. + +### Tipos Compartidos + +**Ubicacion**: `packages/shared/` + +Paquete TypeScript puro (`@afterlife/shared`) con las interfaces compartidas entre web y CMS: + +```typescript +// Game, Documentary, Chapter, StrapiMedia, StrapiResponse, etc. +``` + +### Base de Datos + +**PostgreSQL 16** para el CMS: +- 44 tablas (contenido + sistema Strapi) +- Modelo i18n: cada contenido tiene filas separadas por locale (en/es) y estado (draft/published) +- Relaciones via tablas `_lnk` (e.g., `games_documentary_lnk`) + +**MySQL 8.0** para MapleStory 2: +- Dos databases: `maple-data` (datos del juego, read-only) y `game-server` (datos de jugadores) +- Puerto 3307 para evitar conflicto con PostgreSQL 5432 + +### Almacenamiento (MinIO) + +MinIO corre como servicio S3-compatible para almacenar: +- Imagenes de portada de juegos +- Screenshots +- Archivos de audio para documentales +- Cualquier otro media subido al CMS + +**Puertos**: 9000 (API), 9001 (consola web) + +## Docker Compose: Tres Archivos + +| Archivo | Proposito | Servicios | +|---------|----------|-----------| +| `docker-compose.dev.yml` | Desarrollo local | PG, MinIO, CMS, Web, OpenFusion, Minecraft FTB | +| `docker-compose.maple2.yml` | MapleStory 2 | MySQL, World, Login, Game, Web, File-Ingest | +| `docker-compose.yml` | Produccion | PG, MinIO, CMS, Web, Nginx, Certbot | + +MapleStory 2 tiene su propio compose porque son 6 servicios (demasiados para mezclar con el stack principal) y requiere su propia base de datos MySQL. + +## Red Docker + +Todos los servicios del mismo archivo compose comparten una red Docker implicita. Los servicios se referencian entre si por nombre de servicio (e.g., `cms` desde `web`, `maple2-mysql` desde `maple2-world`). + +Las variables de entorno como `DB_IP`, `GRPC_WORLD_IP` se configuran en cada servicio para apuntar al nombre de contenedor correcto dentro de la red Docker. + +## CI/CD + +**GitHub Actions** (`.github/workflows/deploy.yml`): +1. Push a `main` dispara el deploy +2. SSH al VPS +3. Pull, build, restart de servicios Docker + +## Decisiones Arquitectonicas + +### React 19 vs 18 +El monorepo tiene dos versiones de React: 19 para Next.js (web) y 18 para Strapi (admin). Resuelto con `overrides` en el root `package.json` que solo afecta al workspace de web. El CMS corre en su propio contenedor Docker con sus propias dependencias. + +### Tailwind v4 +Usa la nueva sintaxis `@import "tailwindcss"` en `globals.css` con el plugin `@tailwindcss/postcss`. No hay `tailwind.config.js`. + +### i18n con Strapi 5 +Strapi 5 maneja i18n con filas separadas por locale y un `document_id` compartido. Cada documento tiene 4 filas: draft EN, draft ES, published EN, published ES. Las relaciones (`_lnk`) conectan las filas del mismo estado/locale. + +### Servidores de juegos separados +Los servidores de juegos no son parte del build del monorepo. Son proyectos externos (OpenFusion en C++, Maple2 en C#) que se clonan en `servers/` y se ejecutan via Docker. El `.gitignore` excluye `servers/maple2/` (14 GB de datos de cliente) y los binarios de OpenFusion. diff --git a/docs/cms-content.md b/docs/cms-content.md new file mode 100644 index 0000000..719e476 --- /dev/null +++ b/docs/cms-content.md @@ -0,0 +1,145 @@ +# Modelo de Contenido CMS + +## Strapi 5 — Content Types + +### Game + +Entrada principal de cada juego preservado. + +| Campo | Tipo | Requerido | Localizado | Descripcion | +|-------|------|-----------|------------|-------------| +| title | string | si | si | Nombre del juego | +| slug | uid | si | no | URL-friendly, auto-generado desde title | +| description | richtext | no | si | Descripcion larga del juego | +| genre | enum | si | no | MMORPG, FPS, Casual, Strategy, Sports, Other | +| releaseYear | integer | si | no | Ano de lanzamiento original | +| shutdownYear | integer | si | no | Ano de cierre de servidores | +| developer | string | si | no | Estudio desarrollador | +| publisher | string | no | no | Publisher/distribuidor | +| screenshots | media[] | no | no | Capturas de pantalla (solo imagenes) | +| coverImage | media | si | no | Imagen de portada principal | +| serverStatus | enum | no | no | online, maintenance, coming_soon (default) | +| serverLink | string | no | no | IP:puerto para conectarse | +| documentary | relation | no | no | oneToOne con Documentary | + +### Documentary + +Documental interactivo asociado a un juego. + +| Campo | Tipo | Requerido | Localizado | Descripcion | +|-------|------|-----------|------------|-------------| +| title | string | si | si | Titulo del documental | +| description | text | no | si | Descripcion/subtitulo | +| game | relation | no | no | oneToOne con Game | +| chapters | relation | no | no | oneToMany con Chapter (ordenados) | + +### Chapter + +Capitulo individual de un documental. + +| Campo | Tipo | Requerido | Localizado | Descripcion | +|-------|------|-----------|------------|-------------| +| title | string | si | si | Titulo del capitulo | +| content | richtext | si | si | Contenido narrativo completo | +| audioFile | media | no | no | Archivo de audio (narracion) | +| audioDuration | integer | no | no | Duracion en segundos | +| order | integer | si | no | Orden de aparicion (1, 2, 3...) | +| coverImage | media | no | no | Imagen de portada del capitulo | +| documentary | relation | no | no | manyToOne con Documentary | + +## Modelo i18n de Strapi 5 + +Strapi 5 maneja la internacionalizacion con **filas separadas por locale**. Cada documento tiene un `document_id` compartido y multiples filas: + +``` +document_id: "abc123" +├── id: 1 (locale: en, draft) +├── id: 2 (locale: en, published) +├── id: 3 (locale: es, draft) +└── id: 4 (locale: es, published) +``` + +Las **relaciones** (`_lnk` tables) conectan las filas del mismo estado. Un juego publicado en ES se conecta al documental publicado en ES, no al draft ni al EN. + +### Tablas de enlace +- `games_documentary_lnk` — game_id ↔ documentary_id +- `chapters_documentary_lnk` — chapter_id ↔ documentary_id + chapter_ord + +## Contenido Actual + +### Juegos (3) +| Slug | document_id | Titulo | Genre | Release | Shutdown | Server | +|------|-------------|--------|-------|---------|----------|--------| +| fusionfall | sx17hshy2d... | FusionFall | MMORPG | 2009 | 2013 | online | +| maplestory2 | ms2maple2d... | MapleStory 2 | MMORPG | 2015 | 2020 | online | +| minecraft-ftb-infinity | mcftbinfd... | Minecraft: FTB Infinity Evolved | Sandbox | 2011 | - | online | + +### Documentales (2) + +#### FusionFall: "El Mundo Que No Queriamos Perder" +| # | Titulo | Contenido | +|---|--------|-----------| +| 1 | El Sueno Imposible | Origenes, Cartoon Network, Grigon Entertainment | +| 2 | Cuando los Mundos Colisionaron | Desarrollo, motor Unity, estilo anime de Midori Foo | +| 3 | Bienvenido al Futuro | Lanzamiento, viajes en el tiempo, sistema de Nanos | +| 4 | La Caida de Grigon | Quiebra del estudio, CN asume el desarrollo | +| 5 | La Academia | Free-to-play, The Academy, Adventure Time | +| 6 | Seis Dias | Cierre con 6 dias de aviso, agosto 2013 | +| 7 | Afterlife | Comunidad, FusionFall Retro/Legacy, OpenFusion | + +#### MapleStory 2: "El Mundo Que Construimos Juntos" +| # | Titulo | Contenido | +|---|--------|-----------| +| 1 | El Siguiente Nivel | De MapleStory 1 a la vision 3D de NSquare | +| 2 | Un Mundo de Cubos y Color | Arte voxel, UGC, musica, housing | +| 3 | El Amanecer Coreano | Lanzamiento Korea julio 2015, primeros problemas | +| 4 | La Conquista Global | Lanzamiento global octubre 2018, hype de Twitch | +| 5 | La Tormenta Perfecta | Declive: RNG, limites semanales, exodo | +| 6 | El Ultimo Despertar | Expansion Awakening, demasiado tarde | +| 7 | Afterlife | Cierre mayo 2020, emulador MS2Community | + +## Crear Contenido Nuevo + +### Via Strapi Admin (recomendado) +1. Ir a http://localhost:1337/admin +2. Content Manager > Game / Documentary / Chapter +3. Crear en un idioma, luego usar "Localization" para traducir + +### Via PostgreSQL (insercion directa) +Cuando la API de Strapi tiene restricciones de permisos, se puede insertar directamente en PostgreSQL. Cada contenido necesita 4 filas: + +```sql +-- Draft EN +INSERT INTO games (document_id, title, slug, ..., locale) +VALUES ('unique_doc_id', 'Title', 'slug', ..., 'en'); + +-- Draft ES +INSERT INTO games (document_id, title, slug, ..., locale) +VALUES ('unique_doc_id', 'Titulo', 'slug', ..., 'es'); + +-- Published EN (con published_at) +INSERT INTO games (document_id, title, slug, ..., published_at, locale) +VALUES ('unique_doc_id', 'Title', 'slug', ..., NOW(), 'en'); + +-- Published ES (con published_at) +INSERT INTO games (document_id, title, slug, ..., published_at, locale) +VALUES ('unique_doc_id', 'Titulo', 'slug', ..., NOW(), 'es'); +``` + +Para documentales, ademas de los inserts hay que crear las relaciones en las tablas `_lnk`: +```sql +INSERT INTO games_documentary_lnk (game_id, documentary_id) VALUES (game_id, doc_id); +INSERT INTO chapters_documentary_lnk (chapter_id, documentary_id, chapter_ord) VALUES (ch_id, doc_id, 1); +``` + +## API Endpoints + +La API de Strapi se consume desde Next.js via funciones en `apps/web/src/lib/api.ts`: + +| Funcion | Descripcion | +|---------|------------| +| `getGames(locale)` | Lista todos los juegos con portada | +| `getGameBySlug(slug, locale)` | Juego con documental y capitulos | +| `getDocumentaryByGameSlug(slug, locale)` | Documental completo de un juego | + +Todas las llamadas requieren el header `Authorization: Bearer