Preparación del Entorno de Desarrollo
Esta guía detallada te ayudará a configurar tu entorno de desarrollo local para trabajar en UnoSportClub.
Requisitos del Sistema
Software Requerido
-
Node.js: Versión 24.x (alineada con CI en
.github/workflows/ci.yml) -
npm: Versión 9 o superior
-
Git: Para control de versiones
-
PostgreSQL: Versión 14 o superior (local o acceso a instancia remota)
-
Angular CLI: Incluido como dependencia del proyecto
Configuración Inicial del Proyecto
Configuración de Base de Datos Local
Instalar PostgreSQL
Fedora/RHEL:
sudo dnf install postgresql postgresql-server postgresql-contrib
sudo postgresql-setup --initdb
sudo systemctl enable postgresql
sudo systemctl start postgresql
Ubuntu/Debian:
sudo apt update
sudo apt install postgresql postgresql-contrib
sudo systemctl start postgresql
sudo systemctl enable postgresql
macOS (Homebrew):
brew install postgresql@14
brew services start postgresql@14
Windows:
Descarga el instalador desde https://www.postgresql.org/download/windows/
Configurar Autenticación (Fedora/RHEL)
En Fedora, PostgreSQL usa autenticación ident por defecto. Para permitir autenticación por contraseña, configura pg_hba.conf:
# Obtener la ruta del archivo de configuración
sudo -u postgres psql -t -c "SHOW hba_file;"
# Hacer backup del archivo
sudo cp /var/lib/pgsql/data/pg_hba.conf /var/lib/pgsql/data/pg_hba.conf.backup
# Cambiar ident por md5 para conexiones locales
sudo sed -i 's/^host.*all.*all.*127.0.0.1\/32.*ident$/host all all 127.0.0.1\/32 md5/' /var/lib/pgsql/data/pg_hba.conf
sudo sed -i 's/^host.*all.*all.*::1\/128.*ident$/host all all ::1\/128 md5/' /var/lib/pgsql/data/pg_hba.conf
# Recargar PostgreSQL
sudo systemctl reload postgresql
Crear Usuario y Bases de Datos
# Conectar como usuario postgres
sudo -u postgres psql
# Crear usuario con contraseña
CREATE USER unosportclub WITH PASSWORD 'unosportclub_password';
# Crear bases de datos (desarrollo y test)
CREATE DATABASE unosportclub OWNER unosportclub;
CREATE DATABASE unosportclub_test OWNER unosportclub;
# Otorgar permisos
GRANT ALL PRIVILEGES ON DATABASE unosportclub TO unosportclub;
GRANT ALL PRIVILEGES ON DATABASE unosportclub_test TO unosportclub;
# Salir
\q
Instalar Extensión btree_gist
La extensión btree_gist es requerida para algunas migraciones:
# Fedora/RHEL: Instalar paquete contrib
sudo dnf install postgresql-contrib
# Crear extensión en ambas bases de datos
sudo -u postgres psql -d unosportclub -c "CREATE EXTENSION IF NOT EXISTS btree_gist;"
sudo -u postgres psql -d unosportclub_test -c "CREATE EXTENSION IF NOT EXISTS btree_gist;"
Configurar Variables de Entorno
Crea el archivo functions/.env:
cd functions
cp .env.example .env
Edita functions/.env con tu configuración local:
# Base de datos de desarrollo
DB_HOST=localhost
DB_PORT=5432
DB_NAME=unosportclub
DB_USER=unosportclub
DB_PASSWORD=unosportclub_password
# Base de datos de test
DB_TEST_HOST=localhost
DB_TEST_PORT=5432
DB_TEST_NAME=unosportclub_test
DB_TEST_USER=unosportclub
DB_TEST_PASSWORD=unosportclub_password
# SSL (false para desarrollo local)
DB_SSL=false
# Alternativa: Usar DATABASE_URL
# DATABASE_URL=postgresql://unosportclub:unosportclub_password@localhost:5432/unosportclub
# DATABASE_TEST_URL=postgresql://unosportclub:unosportclub_password@localhost:5432/unosportclub_test
Ejecutar Migraciones y Seeders
# Desde la raíz del proyecto
npm run db:migrate
Esto creará todas las tablas y cargará los datos iniciales.
Verificar Instalación de Base de Datos
# Verificar conexión
PGPASSWORD=unosportclub_password psql -h localhost -U unosportclub -d unosportclub -c "SELECT version();"
# Ver tablas creadas
PGPASSWORD=unosportclub_password psql -h localhost -U unosportclub -d unosportclub -c "\dt"
# O usando el script de prueba del proyecto
npm run db:test
Deberías ver las siguientes tablas:
-
user -
client -
document_type -
court_type -
court -
reservation_type -
reservation_status -
reservation -
tariff -
tariff_class -
payment_type -
payment -
discount -
class -
enrollment -
schema_migrations
OAuth2 local y API
La autenticación usa OAuth2 local (OIDC discovery + JWT RS256). Configura las variables en functions/.env (OAUTH2_*, SIGNER_KEY, SALT_SEPARATOR).
Pruebas locales: Testing con OAuth2 local.
MCP (Cursor / clientes OAuth usuario)
El endpoint POST /api/mcp usa OAuth2 de usuario (authorization_code + PKCE), no credenciales M2M de /sudo/machines.
Variables en functions/.env:
MCP_OAUTH_CLIENT_ID=unosport-mcp
MCP_OAUTH_REDIRECT_URIS=http://localhost:*,http://localhost:*/callback,https://vscode.dev/redirect
UNOSPORT_PANEL_URL=http://localhost:6101
Flujo local:
-
El cliente MCP (p. ej. Cursor) abre
GET /api/authorize?client_id=…&redirect_uri=…&response_type=code&code_challenge=…&code_challenge_method=S256&scope=mcp+panel+openid. -
Redirección al login del panel (
/login?oauth_state=…). -
Tras login, el panel llama
POST /oauth/authorize/completey redirige alredirect_uriconcode. -
Intercambio en
POST /oauth/token(grant_type=authorization_code,code_verifier,client_id). -
Llamadas MCP con
Authorization: Bearer(JWT de usuario con scopemcpy permisopanel).
Metadatos RFC 9728: GET /.well-known/oauth-protected-resource/mcp.
Descubrimiento OIDC: GET /.well-known/openid-configuration (incluye authorization_endpoint, grant_types_supported con authorization_code, scope mcp).
Desarrollo local (todas las apps)
Desde la raíz del monorepo:
npm run dev:serve
Levanta API (:6000) y las cinco SPAs (:6100–:6104). Alternativa por proyecto: npm start, npm run start:panel, npm run start:trainer, npm run start:sudo, npm run start:accounting.
Configuración del Editor
Visual Studio Code
Crea o edita .vscode/settings.json:
{
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
"typescript.preferences.importModuleSpecifier": "relative",
"files.exclude": {
"**/.git": true,
"**/.DS_Store": true,
"**/node_modules": true,
"**/dist": true
},
"search.exclude": {
"**/node_modules": true,
"**/dist": true,
"**/.angular": true
}
}
Estructura del Proyecto
Directorios Principales
unosportclub/
├── src/ # App cliente (6100)
├── projects/
│ ├── panel/ # Panel operador (6101)
│ ├── trainer/ # Panel entrenador (6102)
│ ├── sudo/ # Panel sudo (6103)
│ ├── accounting/ # Contabilidad NIIF (6104)
│ └── common/ # @cortex-ia-com-co/common
├── functions/ # API Express (6000)
│ ├── server.js
│ ├── routes/
│ ├── db/
│ └── utils/
├── docs/ # Antora
├── docker/ # Imagen unificada
└── Dockerfile
Scripts de Desarrollo
Scripts Principales
npm run dev:serve # API + 5 SPAs (recomendado)
npm start # Solo app cliente
npm run start:panel # Solo panel
npm test # Suite completa (common + functions + Angular)
npm run build:production:all
npm run db:migrate # Migraciones + seeders
Scripts del Backend
cd functions
# Desarrollo
node server.js # API en :6000 (o server_wrapper.js con --watch)
npm run lint # Verificar código con ESLint
# Testing
npm test # Ejecutar tests
npm run test:watch # Tests en modo watch
npm run test:coverage # Tests con cobertura
Variables de Entorno del Backend
El servidor backend requiere las siguientes variables de entorno en functions/.env:
# Base de datos
DATABASE_URL=postgresql://usuario:password@host:puerto/database
# Redis (opcional, para escalado horizontal del Relay)
REDIS_URL=redis://localhost:6379
# Sistema Relay (opcional)
REALTIME_ENABLED=true
# Puerto del servidor (opcional, default: 6000)
PORT=6000
Almacenamiento de comprobantes de pago (S3/R2)
Los comprobantes de pago de reserva (payment.foto) se guardan en un bucket S3-compatible mediante PaymentReceiptStorage (functions/services/payment-receipt-storage.js). El bucket debe permanecer privado; el panel y las integraciones descargan la imagen con GET /admin/booking/{bookingId}/payment/{paymentId}/foto.
En functions/.env (ver también functions/.env.example):
AWS_ACCESS_KEY_ID=your-key-id
AWS_SECRET_ACCESS_KEY=your-secret-access-key
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=your-bucket-name
AWS_ENDPOINT=https://your-account.r2.cloudflarestorage.com
AWS_USE_PATH_STYLE_ENDPOINT=true
AWS_PUBLIC_BASE_URL=
-
AWS_BUCKET: nombre del bucket (obligatorio para subida/descarga). -
AWS_ENDPOINT+AWS_USE_PATH_STYLE_ENDPOINT=true: habitual en Cloudflare R2. -
AWS_PUBLIC_BASE_URL: opcional; si se define, la URL guardada en BD usa ese prefijo en lugar del endpoint path-style.
Tras configurar, aplica migraciones (npm run db:migrate en functions/) para la columna payment.foto.
Prueba manual (multipart):
-
Obtén sesión y CSRF:
GET /api/admin/…con Bearer; lee cookieXSRF-TOKEN. -
POST /api/admin/booking/{id}/paymentcon-F foto=@archivo.jpgy campos de pago (ver Comprobantes de pago). -
Verifica en el panel
/booking/payment/:idcon Ver, oGET …/payment/{paymentId}/foto. -
Para quitar el comprobante:
DELETE …/payment/{paymentId}/foto(elimina el objeto en el bucket y ponepayment.foto = null). -
Para eliminar el pago completo:
DELETE …/payment/{paymentId}(sipayment.fototiene valor, también elimina el objeto en el bucket).
Si S3 no está configurado, POST multipart, GET …/foto, DELETE …/foto y DELETE …/payment/{paymentId} (con comprobante) responden 503.
Servidor Backend Local
Iniciar Servidor Backend
cd functions
node server.js
El servidor estará disponible en:
* API REST: http://localhost:6000/api
* Health Check: http://localhost:6000/health
* Swagger Docs: http://localhost:6000/api/api-docs
* Sistema Relay: http://localhost:6000/realtime (si está habilitado)
Flujo de Trabajo de Desarrollo
2. Desarrollo Local
# Terminal 1: Servidor Angular
npm start
# Terminal 2: Servidor Backend (Express)
cd functions
node server.js
# Terminal 3: Base de datos (si es necesario)
psql -U unosportclub -d unosportclub
# Opcional: Redis (si usas escalado horizontal del Relay)
redis-server
Solución de Problemas Comunes
Error: Puerto ya en uso
Si el puerto 4200 (Angular) o 6000 (Backend) está en uso:
# Encontrar proceso usando el puerto
lsof -i :4200
lsof -i :6000
# Matar el proceso
kill -9 <PID>
# O cambiar el puerto del backend
cd functions
PORT=3001 node server.js
Error: Variables de Entorno No Cargadas
Asegúrate de que:
* El archivo functions/.env existe
* Las variables están correctamente formateadas
* No hay espacios alrededor del = en las variables
Error: Base de Datos No Conecta
Verifica:
* PostgreSQL está corriendo: sudo systemctl status postgresql
* Las credenciales en .env son correctas
* El usuario tiene permisos: GRANT ALL PRIVILEGES ON DATABASE unosportclub TO unosportclub;
* En Fedora, verifica que pg_hba.conf use md5 en lugar de ident para conexiones locales
* La extensión btree_gist está instalada: sudo dnf install postgresql-contrib
Contabilidad y cierre de caja
El módulo accounting (DB_NAME_ACCOUNTING) gestiona períodos contables y el cierre NIIF (IAS 1).
Período contable y caja (panel)
-
Cada cierre de caja en el panel registra un asiento contable (bóveda ↔ cajero) solo si existe un período abierto que cubra la fecha de cierre.
-
Si no hay período, la API responde con un mensaje en español indicando que debe abrirse o extenderse un período en contabilidad.
-
El cierre de período en accounting valida: balance de comprobación, cierre de resultados (4.x/5.x → 3.1) y ecuación patrimonial.
Cierre de período (flujo operativo)
-
Operaciones del período: pagos (D cajero/banco, C 4.1), cierres de caja (traslados 1.x).
-
Cierre de resultados: saldar ingresos/gastos y trasladar utilidad a patrimonio (cuenta
3.1). -
Cierre administrativo: bloquear nuevos asientos cuando activo + pasivo + patrimonio = 0.
API relevante:
-
GET /accounting/periods/:id/close-readiness— checklist previo al cierre. -
POST /accounting/periods/:id/close-results— cierre explícito de resultados a patrimonio. -
POST /accounting/periods/:id/close— cierra el período (ejecuta cierre de resultados si aplica).
Instalación BD contabilidad: node functions/db/install-accounting.js (ver functions/db/README.md).
Hora mural y zonas horarias
Las columnas reservation.checking y checkout son TIMESTAMP WITHOUT TIME ZONE con hora mural América/Bogotá.
-
Backend: serialización con
TO_CHAR(checking_date,checking_time, …); entrada normalizada connormalizeTimestampForDb. -
Frontend: utilidades
reservation-wall-timeen@cortex-ia-com-co/common. -
No usar
new Date(cadenaConZ).toLocaleString()para mostrar horarios de cancha o eventos.
Referencias: xref:api-reference.adoc#_fechas_y_horas_hora_mural, xref:data-model.adoc#_2_8_reservation, xref:coding-standards.adoc#_fechas_de_reserva_y_eventos.
Publicación de releases
Este checklist operativo forma parte del marco descrito en Entornos, versiones y ciclos de lanzamiento. Pipeline técnico: CI/CD y DevOps.
-
Commit
chore(release): vX.Y.Zen rama de línea (ej.0.4) actualizandoRELEASE_NOTES.mdypackage.json. -
Push de la rama
0.4. -
gh release create vX.Y.Z— el tag dispara jobsbuildyrelease(imagen GHCR). -
Sincronizar
developcon la rama de release.
Documentación Antora (docs/): añadir entrada en docs/RELEASE_NOTES.md y ejecutar npm run build:local antes de publicar.
Próximos Pasos
Una vez configurado tu entorno:
-
Revisa la Arquitectura del Software
-
Familiarízate con el Modelo de Datos
-
Explora la Referencia de API
-
Revisa los Flujos de Trabajo principales
-
Consulta el Metodología de Desarrollo Ágil