feat: phase 3 redesign, game images, auth system, vm guides, service isolation
Some checks failed
Deploy Multi-VM / Deploy VM Web (push) Has been cancelled
Deploy Multi-VM / Deploy VM Auth (push) Has been cancelled
Deploy Multi-VM / Deploy Game Servers (docker-compose.fusionfall.yml, VM_FUSIONFALL_HOST, VM_FUSIONFALL_SSH_KEY, VM_FUSIONFALL_USER, fusionfall) (push) Has been cancelled
Deploy Multi-VM / Deploy Game Servers (docker-compose.maple2.yml, VM_MAPLE2_HOST, VM_MAPLE2_SSH_KEY, VM_MAPLE2_USER, maple2) (push) Has been cancelled
Deploy Multi-VM / Deploy Game Servers (docker-compose.minecraft.yml, VM_MINECRAFT_HOST, VM_MINECRAFT_SSH_KEY, VM_MINECRAFT_USER, minecraft) (push) Has been cancelled
Deploy Multi-VM / Deploy Game Servers (docker-compose.retro.yml, VM_RETRO_HOST, VM_RETRO_SSH_KEY, VM_RETRO_USER, retro) (push) Has been cancelled

- Redesign all internal pages to warm/gold aesthetic (catalog, game detail,
  documentary, about, donate, community, guides, contact, server-status,
  login, profile, admin, not-found)
- Add real cover images for all 4 games via Strapi CMS with getImageUrl helper
- Integrate NextAuth v5 with Authentik OIDC authentication
- Add new public pages: community, guides, contact, server-status
- Add new protected pages: login, profile, admin dashboard
- Remove legacy AFC/MercadoPago system entirely
- Add Docker Compose split files for service isolation (main, auth, fusionfall, nier)
- Add OpenFusion VM deployment configs (config.vm.ini, systemd service, README-VM)
- Add NieR Reincarnation server guide and desktop client guide
- Add architecture docs for multi-VM deployment
- Add healthcheck, SSE, contact, newsletter, admin API routes
- Add reusable UI components, skeleton loaders, activity feed, bookmark system
- Update deployment and game server documentation
This commit is contained in:
consultoria-as
2026-04-28 05:15:38 +00:00
parent ea142501fa
commit 449c02eadc
151 changed files with 10053 additions and 2312 deletions

View File

@@ -1,9 +1,20 @@
# Database
# =============================================================================
# Project Afterlife - Multi-VM Architecture Environment Variables
# =============================================================================
# Copy this file to docker/.env and fill in all values before deployment.
# Each VM has its own compose file and uses only the variables it needs.
# =============================================================================
# VM 1: Web Platform (vm-web - 10.0.0.10)
# Services: Next.js, Strapi CMS, PostgreSQL, MinIO, Nginx, Certbot
# =============================================================================
# Database (PostgreSQL 16)
DATABASE_NAME=afterlife
DATABASE_USERNAME=afterlife
DATABASE_PASSWORD=change_me_in_production
# Strapi
# Strapi Secrets (generate with: openssl rand -base64 32)
APP_KEYS=key1,key2,key3,key4
API_TOKEN_SALT=change_me
ADMIN_JWT_SECRET=change_me
@@ -11,29 +22,133 @@ TRANSFER_TOKEN_SALT=change_me
JWT_SECRET=change_me
STRAPI_API_TOKEN=your_api_token_after_first_boot
# MinIO
# MinIO (S3-compatible storage)
MINIO_ROOT_USER=afterlife
MINIO_ROOT_PASSWORD=change_me_in_production
# Public URL (for frontend image/media URLs)
PUBLIC_STRAPI_URL=http://yourdomain.com
# Public URLs
PUBLIC_STRAPI_URL=https://play.consultoria-as.com
NEXT_PUBLIC_SITE_URL=https://play.consultoria-as.com
# Public hostname for game servers (DDNS)
PUBLIC_HOST=play.yourdomain.com
# Cloudflare API Token (create at https://dash.cloudflare.com/profile/api-tokens)
# Permissions needed: Zone > DNS > Edit
# Cloudflare DDNS (optional, for dynamic IPs)
# Create token at: https://dash.cloudflare.com/profile/api-tokens
# Permissions: Zone > DNS > Edit
PUBLIC_HOST=play.consultoria-as.com
CF_API_TOKEN=your_cloudflare_api_token
# AfterCoin Blockchain (private Ethereum chain for casino tokens)
# =============================================================================
# VM PRINCIPAL — Autenticacion (todo en vm-main - 10.0.0.10)
# Services: Authentik Server, Worker, PostgreSQL, Redis
# NOTE: Authentik runs on the same VM as Web + CMS (consolidated)
# =============================================================================
# Authentik (generate strong secrets — min 50 chars)
# You can generate with: openssl rand -base64 60
AUTHENTIK_SECRET_KEY=generate_a_very_long_random_string_minimum_50_characters_long
AUTHENTIK_POSTGRES_PASSWORD=change_me_in_production
AUTHENTIK_POSTGRES_USER=authentik
AUTHENTIK_POSTGRES_DB=authentik
# Authentik OIDC Clients (create these in Authentik Admin after first boot)
# Internal URL (Docker network). Nginx proxies /auth to authentik-server:9000
AUTHENTIK_URL=http://authentik-server:9000
# Web App (for players)
AUTHENTIK_CLIENT_ID_WEB=afterlife-web
AUTHENTIK_CLIENT_SECRET_WEB=change_me
AUTHENTIK_REDIRECT_URI_WEB=https://play.consultoria-as.com/api/auth/callback/authentik
# CMS Admin (for content editors)
AUTHENTIK_CLIENT_ID_CMS=afterlife-cms
AUTHENTIK_CLIENT_SECRET_CMS=change_me
AUTHENTIK_REDIRECT_URI_CMS=https://play.consultoria-as.com/admin/auth/callback/authentik
# =============================================================================
# VM 3: OpenFusion (vm-fusionfall - 10.0.0.30)
# Services: OpenFusion Server
# =============================================================================
# Public IP where players connect (your domain or public IP)
OPENFUSION_SHARD_IP=play.consultoria-as.com
OPENFUSION_MOTD="Welcome to Project Afterlife - FusionFall"
# =============================================================================
# VM 6: NieR Reincarnation (vm-nier - 10.0.0.70)
# Services: MariesWonderland Server (.NET 10)
# =============================================================================
# ResourcesBaseUrl must be exactly 43 characters after the host
# Example: http://play.consultoria-as.com/aaaaaaaaaaaaaaaaaaaaaaaa
NIER_RESOURCES_BASE_URL=http://play.consultoria-as.com/aaaaaaaaaaaaaaaaaaaaaaaa
NIER_MASTER_DATA_VERSION=20240404193219
# =============================================================================
# VM 7: Dragon Ball Online (vm-dbo - 10.0.0.80)
# Services: DBO Global Server (C++) + MariaDB
# =============================================================================
# MariaDB root password for DBO
DBO_DB_PASSWORD=dboglobal
# =============================================================================
# VM 4: MapleStory 2 (vm-maple2 - 10.0.0.40)
# Services: MySQL, World, Login, Game, Web
# =============================================================================
# MySQL root password
MAPLE2_DB_PASSWORD=maplestory
# Path to MapleStory 2 client data (14GB, NOT in git)
MAPLE2_DATA_FOLDER=../servers/maple2/client-data/Data
# Public IP for game client connections
MAPLE2_PUBLIC_IP=play.consultoria-as.com
# Internal .env for Maple2 servers (see servers/maple2/.env.example)
# GAME_IP and LOGIN_IP should be set to MAPLE2_PUBLIC_IP in servers/maple2/.env
# =============================================================================
# VM 5: Minecraft + AfterCoin Blockchain (vm-minecraft - 10.0.0.50)
# Services: Minecraft FTB, Geth, AFC Bridge, RPC SSL Proxy
# =============================================================================
# AfterCoin Admin Wallet
# Generate with: node -e "const {ethers}=require('ethers');const w=ethers.Wallet.createRandom();console.log(w.address,w.privateKey)"
AFC_ADMIN_ADDRESS=0xYOUR_ADMIN_ADDRESS
AFC_ADMIN_PRIVATE_KEY=your_private_key_without_0x_prefix
AFC_BRIDGE_SECRET=change_me_in_production
# AFC Store (MercadoPago integration)
# MercadoPago (AFC Store payments)
MERCADOPAGO_ACCESS_TOKEN=your_mp_access_token
MERCADOPAGO_WEBHOOK_SECRET=your_mp_webhook_secret
MERCADOPAGO_WEBHOOK_URL=https://yourdomain.com/api/afc/webhook
MERCADOPAGO_WEBHOOK_URL=https://play.consultoria-as.com/api/afc/webhook
AFC_PRICE_MXN=15
NEXT_PUBLIC_SITE_URL=http://localhost:3000
# =============================================================================
# VM 6: Retro Consoles (vm-retro - 10.0.0.60)
# Services: SM64 Coop DX, N64 Netplay, Dolphin Traversal
# =============================================================================
# SM64 Coop DX
SM64_PORT=7777
SM64_PLAYERS=16
# N64 Netplay (Mario Party)
G64NS_NAME="Afterlife N64 - Mario Party"
G64NS_PORT=45000
G64NS_MAXGAMES=4
G64NS_MOTD="Welcome to Project Afterlife N64 Netplay"
G64NS_DISABLEBROADCAST=false
G64NS_ENABLEAUTH=false
# =============================================================================
# Game Server Public IPs (displayed on website to players)
# These can be the same domain if you use port-based routing,
# or different subdomains/IPs per VM.
# =============================================================================
FUSIONFALL_PUBLIC_IP=play.consultoria-as.com
MAPLE2_PUBLIC_IP=play.consultoria-as.com
MINECRAFT_PUBLIC_IP=play.consultoria-as.com
SM64_PUBLIC_IP=play.consultoria-as.com
N64_PUBLIC_IP=play.consultoria-as.com
DOLPHIN_PUBLIC_IP=play.consultoria-as.com

View File

@@ -0,0 +1,100 @@
# VM 2: Autenticacion y Administracion
# IP Privada: 10.0.0.20
# Puertos Publicos: 9000 (Authentik via Nginx proxy from vm-web)
# Tecnologia: Authentik (OIDC/OAuth2 provider)
# Servicios: Authentik Server, Authentik Worker, PostgreSQL, Redis
services:
authentik-postgres:
image: docker.io/library/postgres:16-alpine
restart: unless-stopped
container_name: auth-postgres
healthcheck:
test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}"]
start_period: 20s
interval: 30s
retries: 5
timeout: 5s
volumes:
- authentik_postgres_data:/var/lib/postgresql/data
environment:
POSTGRES_PASSWORD: ${AUTHENTIK_POSTGRES_PASSWORD:-authentik}
POSTGRES_USER: ${AUTHENTIK_POSTGRES_USER:-authentik}
POSTGRES_DB: ${AUTHENTIK_POSTGRES_DB:-authentik}
networks:
- auth-internal
authentik-redis:
image: docker.io/library/redis:alpine
restart: unless-stopped
container_name: auth-redis
healthcheck:
test: ["CMD-SHELL", "redis-cli ping | grep PONG"]
start_period: 20s
interval: 30s
retries: 5
timeout: 3s
volumes:
- authentik_redis_data:/data
networks:
- auth-internal
authentik-server:
image: ghcr.io/goauthentik/server:latest
restart: unless-stopped
container_name: auth-server
command: server
environment:
AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY}
AUTHENTIK_REDIS__HOST: authentik-redis
AUTHENTIK_POSTGRESQL__HOST: authentik-postgres
AUTHENTIK_POSTGRESQL__NAME: ${AUTHENTIK_POSTGRES_DB:-authentik}
AUTHENTIK_POSTGRESQL__USER: ${AUTHENTIK_POSTGRES_USER:-authentik}
AUTHENTIK_POSTGRESQL__PASSWORD: ${AUTHENTIK_POSTGRES_PASSWORD:-authentik}
# Authentik listen on all interfaces so it's reachable from other VMs
AUTHENTIK_LISTEN__HTTP: 0.0.0.0:9000
AUTHENTIK_LISTEN__HTTPS: 0.0.0.0:9443
ports:
- "10.0.0.20:9000:9000"
- "10.0.0.20:9443:9443"
volumes:
- authentik_media:/media
- authentik_custom_templates:/templates
depends_on:
- authentik-postgres
- authentik-redis
networks:
- auth-internal
authentik-worker:
image: ghcr.io/goauthentik/server:latest
restart: unless-stopped
container_name: auth-worker
command: worker
environment:
AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY}
AUTHENTIK_REDIS__HOST: authentik-redis
AUTHENTIK_POSTGRESQL__HOST: authentik-postgres
AUTHENTIK_POSTGRESQL__NAME: ${AUTHENTIK_POSTGRES_DB:-authentik}
AUTHENTIK_POSTGRESQL__USER: ${AUTHENTIK_POSTGRES_USER:-authentik}
AUTHENTIK_POSTGRESQL__PASSWORD: ${AUTHENTIK_POSTGRES_PASSWORD:-authentik}
user: root
volumes:
- authentik_media:/media
- authentik_custom_templates:/templates
- /var/run/docker.sock:/var/run/docker.sock
depends_on:
- authentik-postgres
- authentik-redis
networks:
- auth-internal
volumes:
authentik_postgres_data:
authentik_redis_data:
authentik_media:
authentik_custom_templates:
networks:
auth-internal:
driver: bridge

View File

@@ -60,184 +60,14 @@ services:
restart: unless-stopped
depends_on:
- cms
- afc-bridge
environment:
STRAPI_URL: http://cms:1337
STRAPI_API_TOKEN: ${STRAPI_API_TOKEN:-}
NEXT_PUBLIC_STRAPI_URL: ${PUBLIC_STRAPI_URL:-http://localhost:1337}
AFC_BRIDGE_URL: http://afc-bridge:3001
AFC_BRIDGE_SECRET: ${AFC_BRIDGE_SECRET}
MERCADOPAGO_ACCESS_TOKEN: ${MERCADOPAGO_ACCESS_TOKEN:-}
MERCADOPAGO_WEBHOOK_SECRET: ${MERCADOPAGO_WEBHOOK_SECRET:-}
MERCADOPAGO_WEBHOOK_URL: ${MERCADOPAGO_WEBHOOK_URL:-}
AFC_PRICE_MXN: ${AFC_PRICE_MXN:-15}
NEXT_PUBLIC_SITE_URL: ${NEXT_PUBLIC_SITE_URL:-http://localhost:3000}
ports:
- "3000:3000"
cloudflare-ddns:
image: favonia/cloudflare-ddns:latest
restart: unless-stopped
environment:
CF_API_TOKEN: ${CF_API_TOKEN}
DOMAINS: ${PUBLIC_HOST:-play.consultoria-as.com}
PROXIED: "false"
IP6_PROVIDER: none
openfusion:
build:
context: ../servers/openfusion
dockerfile: Dockerfile
restart: unless-stopped
environment:
SHARD_IP: 192.168.10.234
MOTD: ${OPENFUSION_MOTD:-Bienvenido a Project Afterlife - FusionFall Academy}
ports:
- "23000:23000"
- "23001:23001"
volumes:
- openfusion_data:/usr/src/app/data
minecraft-ftb:
image: itzg/minecraft-server:java21
restart: unless-stopped
container_name: minecraft-ftb
environment:
EULA: "TRUE"
TYPE: FTBA
FTB_MODPACK_ID: 125
FTB_MODPACK_VERSION_ID: 100181
MEMORY: 6G
MAX_MEMORY: 6G
MOTD: "Project Afterlife - FTB Evolution"
DIFFICULTY: normal
MAX_PLAYERS: 20
VIEW_DISTANCE: 10
ENABLE_COMMAND_BLOCK: "true"
MAX_TICK_TIME: -1
ports:
- "25565:25565"
volumes:
- minecraft_ftb_data:/data
deploy:
resources:
limits:
memory: 8G
geth:
build:
context: ../blockchain
dockerfile: Dockerfile
restart: unless-stopped
environment:
ADMIN_PRIVATE_KEY: ${AFC_ADMIN_PRIVATE_KEY}
ADMIN_ADDRESS: ${AFC_ADMIN_ADDRESS}
ports:
- "8545:8545"
- "8546:8546"
volumes:
- geth_data:/data
deploy:
resources:
limits:
memory: 1G
rpc-ssl:
image: nginx:alpine
restart: unless-stopped
depends_on:
- geth
volumes:
- ./nginx/rpc-ssl.conf:/etc/nginx/nginx.conf:ro
- certbot_etc:/etc/letsencrypt:ro
ports:
- "8443:8443"
afc-bridge:
build:
context: ../services/afc-bridge
dockerfile: Dockerfile
restart: unless-stopped
depends_on:
- geth
environment:
GETH_RPC_URL: http://geth:8545
ADMIN_PRIVATE_KEY: ${AFC_ADMIN_PRIVATE_KEY}
BRIDGE_SECRET: ${AFC_BRIDGE_SECRET}
PORT: 3001
DB_PATH: /data/bridge.db
GAS_FUND_AMOUNT: "0.01"
ports:
- "3001:3001"
volumes:
- afc_bridge_data:/data
sm64coopdx:
build:
context: ../servers/sm64coopdx
dockerfile: Dockerfile
restart: unless-stopped
container_name: sm64coopdx
environment:
SM64_PORT: ${SM64_PORT:-7777}
SM64_PLAYERS: ${SM64_PLAYERS:-16}
ports:
- "7777:7777/udp"
volumes:
- sm64_save:/server/save
- sm64_mods:/server/mods
deploy:
resources:
limits:
memory: 2G
n64-netplay:
image: k4rian/gopher64-netplay-server:latest
container_name: n64-netplay
restart: unless-stopped
environment:
G64NS_NAME: "Afterlife N64 - Mario Party"
G64NS_PORT: 45000
G64NS_MAXGAMES: 4
G64NS_MOTD: "Bienvenido a Project Afterlife - Mario Party N64"
G64NS_LOGPATH: "gopher64-server.log"
G64NS_DISABLEBROADCAST: "false"
G64NS_ENABLEAUTH: "false"
ports:
- "45000-45004:45000-45004/tcp"
- "45000-45004:45000-45004/udp"
volumes:
- n64_netplay_data:/home/gopher64
- /etc/localtime:/etc/localtime:ro
deploy:
resources:
limits:
memory: 128M
dolphin-traversal:
build:
context: ../servers/dolphin-traversal
dockerfile: Dockerfile
container_name: dolphin-traversal
restart: unless-stopped
ports:
- "6262:6262/udp"
- "6226:6226/udp"
deploy:
resources:
limits:
memory: 64M
volumes:
postgres_data:
minio_data:
openfusion_data:
minecraft_ftb_data:
geth_data:
afc_bridge_data:
sm64_save:
sm64_mods:
n64_netplay_data:
certbot_etc:
external: true
name: docker_certbot_etc

View File

@@ -0,0 +1,36 @@
version: "3.8"
services:
fusionfall:
build:
context: ../servers/openfusion
dockerfile: Dockerfile
container_name: fusionfall
restart: unless-stopped
ports:
- "23000:23000/tcp"
- "23001:23001/tcp"
volumes:
- ../servers/openfusion/data:/usr/src/app/data
- ../servers/openfusion/tdata:/usr/src/app/tdata:ro
- ../servers/openfusion/config.ini:/usr/src/app/config.ini
environment:
- SHARD_IP=${FUSIONFALL_SHARD_IP:-192.168.10.84}
- MOTD=${FUSIONFALL_MOTD:-Bienvenido a Project Afterlife - FusionFall Academy}
- ACCOUNT_LEVEL=${FUSIONFALL_ACCOUNT_LEVEL:-1}
networks:
- fusionfall
# Aislamiento: sin acceso saliente a internet (red interna)
# Los clientes locales se conectan via ports mapeados en el host
sysctls:
- net.ipv4.ip_unprivileged_port_start=23000
healthcheck:
test: ["CMD-SHELL", "ss -tln | grep -q ':23000' || exit 1"]
interval: 30s
timeout: 5s
retries: 3
networks:
fusionfall:
driver: bridge
internal: false

View File

@@ -0,0 +1,264 @@
# =============================================================================
# VM PRINCIPAL — Project Afterlife (Soft Launch)
# =============================================================================
# IP Privada: 10.0.0.10
# Puertos Publicos: 80, 443
# Servicios: Next.js, Strapi CMS, Authentik SSO, PostgreSQL x2, Redis, MinIO, Nginx, Certbot
#
# Esta es la VM principal donde corre toda la plataforma web, autenticacion
# y administracion. Los servidores de juegos corren en VMs separadas.
# =============================================================================
services:
# ---------------------------------------------------------------------------
# PostgreSQL — Base de datos para Strapi CMS
# ---------------------------------------------------------------------------
postgres:
image: postgres:16-alpine
restart: unless-stopped
container_name: main-postgres
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
POSTGRES_DB: ${DATABASE_NAME:-afterlife}
POSTGRES_USER: ${DATABASE_USERNAME:-afterlife}
POSTGRES_PASSWORD: ${DATABASE_PASSWORD:-afterlife}
ports:
- "127.0.0.1:5432:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${DATABASE_USERNAME:-afterlife}"]
interval: 5s
timeout: 5s
retries: 5
networks:
- main-internal
# ---------------------------------------------------------------------------
# PostgreSQL — Base de datos para Authentik
# ---------------------------------------------------------------------------
authentik-postgres:
image: docker.io/library/postgres:16-alpine
restart: unless-stopped
container_name: main-authentik-postgres
healthcheck:
test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}"]
start_period: 20s
interval: 30s
retries: 5
timeout: 5s
volumes:
- authentik_postgres_data:/var/lib/postgresql/data
environment:
POSTGRES_PASSWORD: ${AUTHENTIK_POSTGRES_PASSWORD:-authentik}
POSTGRES_USER: ${AUTHENTIK_POSTGRES_USER:-authentik}
POSTGRES_DB: ${AUTHENTIK_POSTGRES_DB:-authentik}
ports:
- "127.0.0.1:5433:5432"
networks:
- main-internal
# ---------------------------------------------------------------------------
# Redis — Cache para Authentik
# ---------------------------------------------------------------------------
authentik-redis:
image: docker.io/library/redis:alpine
restart: unless-stopped
container_name: main-authentik-redis
healthcheck:
test: ["CMD-SHELL", "redis-cli ping | grep PONG"]
start_period: 20s
interval: 30s
retries: 5
timeout: 3s
volumes:
- authentik_redis_data:/data
networks:
- main-internal
# ---------------------------------------------------------------------------
# MinIO — Almacenamiento S3-compatible
# ---------------------------------------------------------------------------
minio:
image: minio/minio:latest
restart: unless-stopped
container_name: main-minio
command: server /data --console-address ":9001"
volumes:
- minio_data:/data
environment:
MINIO_ROOT_USER: ${MINIO_ROOT_USER:-afterlife}
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:-afterlife123}
ports:
- "127.0.0.1:9000:9000"
- "127.0.0.1:9001:9001"
networks:
- main-internal
# ---------------------------------------------------------------------------
# Authentik Server — SSO / Identity Provider
# ---------------------------------------------------------------------------
authentik-server:
image: ghcr.io/goauthentik/server:latest
restart: unless-stopped
container_name: main-authentik-server
command: server
environment:
AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY}
AUTHENTIK_REDIS__HOST: authentik-redis
AUTHENTIK_POSTGRESQL__HOST: authentik-postgres
AUTHENTIK_POSTGRESQL__NAME: ${AUTHENTIK_POSTGRES_DB:-authentik}
AUTHENTIK_POSTGRESQL__USER: ${AUTHENTIK_POSTGRES_USER:-authentik}
AUTHENTIK_POSTGRESQL__PASSWORD: ${AUTHENTIK_POSTGRES_PASSWORD:-authentik}
AUTHENTIK_LISTEN__HTTP: 0.0.0.0:9000
AUTHENTIK_LISTEN__HTTPS: 0.0.0.0:9443
expose:
- "9000"
- "9443"
volumes:
- authentik_media:/media
- authentik_custom_templates:/templates
depends_on:
- authentik-postgres
- authentik-redis
networks:
- main-internal
# ---------------------------------------------------------------------------
# Authentik Worker — Tareas en background
# ---------------------------------------------------------------------------
authentik-worker:
image: ghcr.io/goauthentik/server:latest
restart: unless-stopped
container_name: main-authentik-worker
command: worker
environment:
AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY}
AUTHENTIK_REDIS__HOST: authentik-redis
AUTHENTIK_POSTGRESQL__HOST: authentik-postgres
AUTHENTIK_POSTGRESQL__NAME: ${AUTHENTIK_POSTGRES_DB:-authentik}
AUTHENTIK_POSTGRESQL__USER: ${AUTHENTIK_POSTGRES_USER:-authentik}
AUTHENTIK_POSTGRESQL__PASSWORD: ${AUTHENTIK_POSTGRES_PASSWORD:-authentik}
user: root
volumes:
- authentik_media:/media
- authentik_custom_templates:/templates
- /var/run/docker.sock:/var/run/docker.sock
depends_on:
- authentik-postgres
- authentik-redis
networks:
- main-internal
# ---------------------------------------------------------------------------
# Strapi CMS — Content Management
# ---------------------------------------------------------------------------
cms:
build:
context: ../apps/cms
dockerfile: Dockerfile
restart: unless-stopped
container_name: main-cms
depends_on:
postgres:
condition: service_healthy
environment:
HOST: 0.0.0.0
PORT: 1337
DATABASE_HOST: postgres
DATABASE_PORT: 5432
DATABASE_NAME: ${DATABASE_NAME:-afterlife}
DATABASE_USERNAME: ${DATABASE_USERNAME:-afterlife}
DATABASE_PASSWORD: ${DATABASE_PASSWORD:-afterlife}
APP_KEYS: ${APP_KEYS}
API_TOKEN_SALT: ${API_TOKEN_SALT}
ADMIN_JWT_SECRET: ${ADMIN_JWT_SECRET}
TRANSFER_TOKEN_SALT: ${TRANSFER_TOKEN_SALT}
JWT_SECRET: ${JWT_SECRET}
# Authentik OIDC for CMS admin SSO
AUTHENTIK_URL: ${AUTHENTIK_URL:-http://authentik-server:9000}
AUTHENTIK_CLIENT_ID: ${AUTHENTIK_CLIENT_ID_CMS:-}
AUTHENTIK_CLIENT_SECRET: ${AUTHENTIK_CLIENT_SECRET_CMS:-}
AUTHENTIK_REDIRECT_URI: ${AUTHENTIK_REDIRECT_URI_CMS:-}
PUBLIC_STRAPI_URL: ${PUBLIC_STRAPI_URL:-https://play.consultoria-as.com}
expose:
- "1337"
networks:
- main-internal
# ---------------------------------------------------------------------------
# Next.js — Frontend Web
# ---------------------------------------------------------------------------
web:
build:
context: ../
dockerfile: apps/web/Dockerfile
restart: unless-stopped
container_name: main-web
depends_on:
- cms
- authentik-server
environment:
STRAPI_URL: http://cms:1337
STRAPI_API_TOKEN: ${STRAPI_API_TOKEN:-}
NEXT_PUBLIC_STRAPI_URL: ${PUBLIC_STRAPI_URL:-https://play.consultoria-as.com}
# Authentik OIDC
AUTHENTIK_URL: ${AUTHENTIK_URL:-http://authentik-server:9000}
AUTHENTIK_CLIENT_ID: ${AUTHENTIK_CLIENT_ID_WEB:-}
AUTHENTIK_CLIENT_SECRET: ${AUTHENTIK_CLIENT_SECRET_WEB:-}
AUTHENTIK_REDIRECT_URI: ${AUTHENTIK_REDIRECT_URI_WEB:-}
# Public game server IPs (displayed to players)
NEXT_PUBLIC_NIER_IP: ${NIER_PUBLIC_IP:-play.consultoria-as.com}
NEXT_PUBLIC_DBO_IP: ${DBO_PUBLIC_IP:-play.consultoria-as.com}
NEXT_PUBLIC_MAPLE2_IP: ${MAPLE2_PUBLIC_IP:-play.consultoria-as.com}
NEXT_PUBLIC_FUSIONFALL_IP: ${FUSIONFALL_PUBLIC_IP:-play.consultoria-as.com}
NEXT_PUBLIC_SITE_URL: ${NEXT_PUBLIC_SITE_URL:-https://play.consultoria-as.com}
expose:
- "3000"
networks:
- main-internal
# ---------------------------------------------------------------------------
# Nginx — Reverse Proxy + SSL
# ---------------------------------------------------------------------------
nginx:
image: nginx:alpine
restart: unless-stopped
container_name: main-nginx
depends_on:
- web
- cms
- authentik-server
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx.main.conf:/etc/nginx/nginx.conf:ro
- certbot_certs:/etc/letsencrypt:ro
- certbot_www:/var/www/certbot:ro
networks:
- main-internal
# ---------------------------------------------------------------------------
# Certbot — SSL automático
# ---------------------------------------------------------------------------
certbot:
image: certbot/certbot
container_name: main-certbot
volumes:
- certbot_certs:/etc/letsencrypt
- certbot_www:/var/www/certbot
entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h; done'"
volumes:
postgres_data:
authentik_postgres_data:
authentik_redis_data:
authentik_media:
authentik_custom_templates:
minio_data:
certbot_certs:
certbot_www:
networks:
main-internal:
driver: bridge

View File

@@ -1,120 +0,0 @@
services:
maple2-mysql:
image: mysql:8.0
restart: unless-stopped
container_name: maple2-db
environment:
MYSQL_ROOT_PASSWORD: ${MAPLE2_DB_PASSWORD:-maplestory}
volumes:
- maple2_mysql:/var/lib/mysql
ports:
- "3307:3306"
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-p${MAPLE2_DB_PASSWORD:-maplestory}"]
interval: 10s
timeout: 5s
retries: 10
maple2-file-ingest:
container_name: maple2-file-ingest
image: mcr.microsoft.com/dotnet/sdk:8.0
working_dir: /app/Maple2.File.Ingest
entrypoint: ["dotnet", "run"]
depends_on:
maple2-mysql:
condition: service_healthy
env_file:
- ../servers/maple2/.env
environment:
DB_IP: maple2-mysql
MS2_DATA_FOLDER: /ClientData
volumes:
- ../servers/maple2:/app
- ${MAPLE2_DATA_FOLDER:-../servers/maple2/client-data/Data}:/ClientData
- maple2_dotnet_tools:/root/.dotnet/tools
profiles:
- ingest
maple2-world:
build:
context: ../servers/maple2
dockerfile: ./Maple2.Server.World/Dockerfile
container_name: maple2-world
image: maple2/world
command: dotnet Maple2.Server.World.dll
restart: unless-stopped
depends_on:
maple2-mysql:
condition: service_healthy
ports:
- "21001:21001"
env_file:
- ../servers/maple2/.env
environment:
DB_IP: maple2-mysql
GRPC_LOGIN_IP: maple2-login
maple2-login:
build:
context: ../servers/maple2
dockerfile: ./Maple2.Server.Login/Dockerfile
container_name: maple2-login
image: maple2/login
command: dotnet Maple2.Server.Login.dll
restart: unless-stopped
depends_on:
maple2-mysql:
condition: service_healthy
maple2-world:
condition: service_started
ports:
- "20001:20001"
env_file:
- ../servers/maple2/.env
environment:
DB_IP: maple2-mysql
GRPC_WORLD_IP: maple2-world
maple2-web:
build:
context: ../servers/maple2
dockerfile: ./Maple2.Server.Web/Dockerfile
container_name: maple2-web
image: maple2/web
command: dotnet Maple2.Server.Web.dll
restart: unless-stopped
depends_on:
maple2-mysql:
condition: service_healthy
ports:
- "4000:4000"
env_file:
- ../servers/maple2/.env
environment:
DB_IP: maple2-mysql
maple2-game-ch0:
build:
context: ../servers/maple2
dockerfile: ./Maple2.Server.Game/Dockerfile
image: maple2/game
restart: unless-stopped
depends_on:
maple2-mysql:
condition: service_healthy
maple2-world:
condition: service_started
ports:
- "20003:20003"
- "21003:21003"
env_file:
- ../servers/maple2/.env
environment:
DB_IP: maple2-mysql
GRPC_GAME_IP: maple2-game-ch0
GRPC_WORLD_IP: maple2-world
INSTANCED_CONTENT: "false"
volumes:
maple2_mysql:
maple2_dotnet_tools:

View File

@@ -0,0 +1,29 @@
# =============================================================================
# VM: NieR Reincarnation Server
# IP Privada: 10.0.0.70
# Puertos Publicos: 80, 443
# =============================================================================
services:
nier-server:
build:
context: ../servers/nier-reincarnation
dockerfile: Dockerfile
image: nier/server:latest
container_name: nier-server
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- nier_data:/data
environment:
ASPNETCORE_ENVIRONMENT: Production
ASPNETCORE_URLS: "http://0.0.0.0:80;https://0.0.0.0:443"
deploy:
resources:
limits:
memory: 1536M
volumes:
nier_data:

View File

@@ -2,6 +2,10 @@ services:
postgres:
image: postgres:16-alpine
restart: unless-stopped
deploy:
resources:
limits:
memory: 512M
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
@@ -15,6 +19,10 @@ services:
image: minio/minio:latest
restart: unless-stopped
command: server /data --console-address ":9001"
deploy:
resources:
limits:
memory: 512M
volumes:
- minio_data:/data
environment:
@@ -29,6 +37,12 @@ services:
context: ../apps/cms
dockerfile: Dockerfile
restart: unless-stopped
deploy:
resources:
limits:
memory: 1536M
volumes:
- cms_uploads:/app/public/uploads
depends_on:
- postgres
- minio
@@ -40,6 +54,8 @@ services:
DATABASE_NAME: ${DATABASE_NAME:-afterlife}
DATABASE_USERNAME: ${DATABASE_USERNAME:-afterlife}
DATABASE_PASSWORD: ${DATABASE_PASSWORD:-afterlife}
REDIS_HOST: redis
REDIS_PORT: 6379
APP_KEYS: ${APP_KEYS}
API_TOKEN_SALT: ${API_TOKEN_SALT}
ADMIN_JWT_SECRET: ${ADMIN_JWT_SECRET}
@@ -53,12 +69,21 @@ services:
context: ../
dockerfile: apps/web/Dockerfile
restart: unless-stopped
deploy:
resources:
limits:
memory: 1536M
depends_on:
- cms
environment:
STRAPI_URL: http://cms:1337
STRAPI_API_TOKEN: ${STRAPI_API_TOKEN}
NEXT_PUBLIC_STRAPI_URL: ${PUBLIC_STRAPI_URL:-http://localhost:1337}
DATABASE_HOST: postgres
DATABASE_PORT: 5432
DATABASE_NAME: ${DATABASE_NAME:-afterlife}
DATABASE_USERNAME: ${DATABASE_USERNAME:-afterlife}
DATABASE_PASSWORD: ${DATABASE_PASSWORD:-afterlife}
ports:
- "3000:3000"
@@ -76,6 +101,19 @@ services:
- certbot_certs:/etc/letsencrypt:ro
- certbot_www:/var/www/certbot:ro
redis:
image: redis:7-alpine
restart: unless-stopped
command: redis-server --maxmemory 256mb --maxmemory-policy allkeys-lru --appendonly yes
deploy:
resources:
limits:
memory: 256M
volumes:
- redis_data:/data
ports:
- "6379:6379"
certbot:
image: certbot/certbot
volumes:
@@ -85,5 +123,7 @@ services:
volumes:
postgres_data:
minio_data:
cms_uploads:
redis_data:
certbot_certs:
certbot_www:

View File

@@ -0,0 +1,102 @@
events {
worker_connections 1024;
}
http {
upstream web {
server web:3000;
}
upstream cms {
server cms:1337;
}
upstream authentik {
server authentik-server:9000;
}
server {
listen 80;
server_name _;
client_max_body_size 100M;
# Redirect all HTTP to HTTPS (optional — remove if no SSL yet)
# location / {
# return 301 https://$host$request_uri;
# }
# --- Authentik (SSO) accessible at /auth ---
location /auth/ {
proxy_pass http://authentik;
proxy_http_version 1.1;
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;
}
# --- Next.js Auth API routes ---
location /api/auth/ {
proxy_pass http://web;
proxy_http_version 1.1;
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;
}
# --- AFC Store API (if enabled) ---
location /api/afc/ {
proxy_pass http://web;
proxy_http_version 1.1;
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;
}
# --- Strapi API ---
location /api/ {
proxy_pass http://cms;
proxy_http_version 1.1;
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;
}
# --- Strapi Admin Panel ---
location /admin {
proxy_pass http://cms;
proxy_http_version 1.1;
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;
}
# --- Strapi Uploads ---
location /uploads/ {
proxy_pass http://cms;
proxy_set_header Host $host;
}
# --- Next.js Frontend (catch-all) ---
location / {
proxy_pass http://web;
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;
}
# --- Certbot challenge ---
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
}
}