GameSage es una plataforma web centrada en videojuegos que integra en un solo proyecto el frontend, el backend y la base de datos. La aplicación está construida como una aplicación Angular con renderizado en servidor (SSR) y un backend HTTP que se ejecuta en el mismo proceso Node.js, de modo que no hace falta desplegar por separado una SPA y una API: un único servidor atiende tanto las páginas web como las peticiones a la API REST.
El proyecto está pensado para ofrecer una experiencia completa de principio a fin: catálogo de juegos, usuarios y autenticación, carrito y compras, favoritos, subida de imágenes (media), y un chat asistido por IA. Todo ello con persistencia en PostgreSQL, documentación de la API (Swagger), tests de integración del backend y posibilidad de despliegue en entornos como Vercel. Esta documentación describe la arquitectura, el flujo de ejecución, la estructura del código y los pasos para arrancar el proyecto desde cero o trabajar con él en desarrollo.
/api/* (misma URL base y mismo puerto)./api/...), evitando dependencias de URLs absolutas.Accept-Language o cookie) e inyecta al vuelo las meta-etiquetas SEO traducidas (description, keywords, OpenGraph, Twitter Cards, etc.) y el atributo lang en el index.html antes de enviarlo al cliente. Esto permite tener una única SPA que se comporta como múltiples sitios localizados para los motores de búsqueda.start, dev, serve:ssr, serve:ssr:https) llaman internamente a scripts/free-port.mjs para liberar puertos solo cuando están ocupados (4200, 3000 y 3443 según el comando), evitando errores EADDRINUSE sin mostrar mensajes innecesarios.La aplicación sigue un modelo monolítico en el que un único proceso Node.js atiende tanto las peticiones de la aplicación web (incluyendo el renderizado en servidor de las páginas Angular) como las peticiones a la API REST. No hay separación de dominios por puerto: todo se expone en el mismo host y puerto configurado (por defecto 3000). Esta decisión simplifica el despliegue y evita problemas de CORS y de configuración de URLs entre frontend y backend.
El flujo típico de una petición es el siguiente: la petición llega al servidor Express. Primero se inyectan las cabeceras de seguridad unificadas (CSP, HSTS, X-Frame-Options, etc.). Luego, si la ruta comienza por /api, la maneja el backend montado en Express (controladores, servicios, base de datos); en caso contrario, se sirven primero los archivos estáticos del build de Angular (incluyendo el sitemap.xml dinámico y estático). Si no hay coincidencia, se delega en el motor de Angular SSR para generar la respuesta HTML, en cuyo proceso se intercepta la respuesta para traducir dinámicamente el index.html (metadatos SEO) al idioma del usuario antes de enviarla. Así, la SPA y la API coexisten en el mismo origen de forma segura y optimizada.
El frontend está construido con Angular (v20) y @angular/ssr. El CLI de Angular está configurado con outputMode: "server" y un entry point de servidor (src/server.ts), de modo que el build genera tanto el bundle del navegador como el del servidor. En el servidor se utiliza AngularNodeAppEngine de @angular/ssr/node para renderizar las rutas de la aplicación en Node y devolver HTML, mejorando el SEO y la percepción de carga inicial.
El backend HTTP está implementado con Express (v4). La aplicación Express se crea en src/backend/app.ts, se monta bajo la aplicación principal en src/server.ts (cuando el backend no está deshabilitado) y concentra todo el enrutado bajo /api/*, middlewares globales (CORS, JSON, helmet, rate limiting en producción, logging, serialización de respuestas) y el manejador de errores al final de la cadena.
El acceso a datos se realiza mediante Prisma. El esquema está en prisma/schema.prisma y define los modelos (User, Game, Developer, Publisher, Genre, Platform, Media, Favorite, CartItem, Purchase, PurchaseItem, ChatSession, ChatMessage) y sus relaciones. La URL de conexión se obtiene de la variable de entorno POSTGRES_PRISMA_URL. Las migraciones se gestionan con prisma migrate; el cliente se genera con prisma generate y se usa en los servicios del backend.
Las imágenes y el media asociado a juegos o usuarios se almacenan y gestionan con Cloudinary. Las variables CLOUDINARY_CLOUD_NAME, CLOUDINARY_API_KEY y CLOUDINARY_API_SECRET configuran el cliente. El módulo de media (src/backend/modules/media) utiliza Cloudinary para subir y eliminar recursos; los scripts de seed pueden poblar media desde una carpeta local o desde URLs.
El chat con IA utiliza Google Generative AI (y dependencias como @google/generative-ai o @ai-sdk/google). La API key se configura con GOOGLE_GENERATIVE_AI_API_KEY. El módulo de chat (src/backend/modules/chat) expone endpoints para sesiones y mensajes, y delega en el servicio de IA para generar respuestas.
validate).Authorization: Bearer ..., hash de contraseñas)./api-docs..env al arrancar el backend.eslint-plugin-security para prevenir inyección de objetos, expresiones regulares inseguras y ejecución de comandos no seguros.El proyecto aplica buenas prácticas OWASP: unificación de la inyección de cabeceras de seguridad centralizadas (security-headers.ts) en todo el proyecto Node.js, Content-Security-Policy estricto generado en vuelo dependiendo del entorno (unsafe-inline denegado en eval), X-Frame-Options (SAMEORIGIN), X-Content-Type-Options (nosniff) y Referrer-Policy en el servidor. CORS restringido en producción mediante CORS_ORIGIN; en desarrollo se permiten automáticamente las URL de origen seguro; redirección a HTTPS en producción; validación Zod de tipos; y bloqueo contra ejecución de objetos/comandos detectado con ESLint.
La aplicación ha sido auditada con OWASP ZAP (Zed Attack Proxy). Tras una profunda revisión, se mitigaron exitosamente errores de enrutamiento con wildcards en SSR, vulnerabilidades de secuencias de bytes (Null Byte Injection mitigados recursivamente), falsos positivos en las validaciones de Zod (mapados correctamente a Bad Request), y problemas de memoria Allocation failed - JavaScript heap out of memory detectados en procesos pesados de compilación (NODE_OPTIONS=--max-old-space-size=8192). Actualmente, la aplicación cuenta con un score completamente limpio en ZAP.
src/server.tsEs el punto de entrada del servidor cuando se ejecuta el build SSR (por ejemplo node dist/game-sage/server/server.mjs). Realiza lo siguiente:
applySecurityHeaders) a todas las peticiones desde el principio.AngularNodeAppEngine.SSR_DISABLE_BACKEND no está definida, importa dinámicamente ./backend/app y monta la aplicación Express del backend antes que los estáticos y el SSR. Esto asegura que las rutas de API (/api/*) tengan prioridad y no sean interceptadas por el router de Angular.express.static sobre la carpeta del build del navegador (dist/.../browser) configurando caché a largo plazo.processAngularResponse) que delega en angularApp.handle(req). Tras recibir la respuesta HTML generada por Angular, la intercepta para inyectar dinámicamente el idioma y los metadatos SEO (traducción de description, keywords, og:title, etc.) basándose en la cookie app-language o la cabecera Accept-Language.reqHandler para integración con Vercel o para arrancar el servidor HTTP.El orden de montaje es crítico: Security Headers -> Backend (/api) -> Estáticos -> Angular SSR (con intercepción SEO).
src/backend/app.tsConstruye la aplicación Express del backend. No inicia un servidor propio; se exporta por defecto y se monta en server.ts. Contiene:
trust proxy para correcto uso de IP detrás de proxies.x-forwarded-proto o req.secure).crossOriginResourcePolicy: 'cross-origin'.http://localhost:PORT y http://localhost:4200; en producción solo los orígenes definidos en CORS_ORIGIN (separados por coma). Si no se define CORS_ORIGIN en producción, se acepta cualquier origen.express.json con límite 10MB, middleware de logging de peticiones, middleware de serialización de respuestas (conversión de tipos Prisma como Decimal y Date a valores JSON serializables)./api/health y /api/diagnostic./api-docs con especificación dinámica según entorno y host./api/auth con authLimiter (5 intentos por hora por IP, sin contar exitosos) y authRoutes./api/users, /api/games, /api/developers, /api/publishers, /api/genres, /api/platforms, /api/media, /api/favorites, /api/cart, /api/purchases, /api/chat.Además, se reemplaza console.warn para silenciar mensajes deprecados o de limpieza que no aportan en runtime.
src/backend/index.tsPunto de entrada alternativo cuando se ejecuta solo el backend (por ejemplo con nodemon sobre src/backend). Crea un servidor llamando a app.listen(env.PORT) y registra manejadores de SIGTERM y SIGINT para cerrar el servidor de forma ordenada. En el flujo SSR normal este fichero no se usa; el servidor se levanta desde server.ts.
src/main.ts (navegador)Punto de entrada del bundle del navegador. Ejecuta bootstrapApplication(AppComponent, appConfig) con la configuración definida en src/app/app.config.ts. Se usa cuando la aplicación se ejecuta en el cliente (tras la hidratación o en modo solo cliente con ng serve).
src/main.server.tsPunto de entrada del bundle del servidor para Angular. Exporta una función bootstrap que recibe un BootstrapContext y arranca la aplicación con bootstrapApplication(AppComponent, config, context), usando la configuración de servidor (app.config.server). El CLI de Angular utiliza este archivo para renderizar las rutas en el servidor.
Llegada al proceso Node: La petición entra en la aplicación Express definida en server.ts.
Backend (rutas /api/*): Si la ruta coincide con algún handler del backend montado primero (por ejemplo /api/health, /api/auth/login, /api/games, etc.), la cadena de middlewares de Express se ejecuta: CORS, body parser, request logger, response serializer, rate limiter (si aplica), y luego el router correspondiente. En rutas protegidas, el middleware auth verifica el JWT y adjunta req.user; el middleware adminOnly comprueba que el usuario esté en la lista de administradores. Los controladores llaman a los servicios, que usan Prisma para leer o escribir en PostgreSQL. La respuesta se serializa (Decimal, Date, etc. convertidos) y se envía. Si se lanza un error, el errorHandler centralizado devuelve un JSON de error y registra el fallo.
Archivos estáticos: Si la URL no fue manejada por el backend, Express intenta servir un archivo desde la carpeta browser del build. Las peticiones a JS, CSS, imágenes, etc. se resuelven aquí.
SSR (resto de rutas): Si no hubo respuesta del backend ni archivo estático, el middleware de Angular llama a angularApp.handle(req). Angular ejecuta el router de la aplicación, renderiza el componente en servidor y devuelve una respuesta HTTP que incluye un index.html. Esta respuesta se intercepta en el middleware processAngularResponse, que extrae el idioma (lang) de las cookies del usuario o de Accept-Language, e inyecta metadatos SEO traducidos en tiempo real (keywords, descriptions, title) junto con el <html> dinámico.
Cliente: El navegador recibe este HTML localizado y totalmente adaptado. A partir de aquí, Angular carga los scripts del bundle, arranca ngx-translate, hidrata el DOM y permite cambiar el idioma al vuelo manipulando reactivamente el document.documentElement.lang.
src/security-headers.tsArchivo centralizado que exporta las funciones applySecurityHeaders y applyNoCacheHeaders. Concentra toda la configuración de cabeceras estrictas de seguridad (HSTS, CSP, X-Frame-Options, Permissions-Policy, etc.). El servidor SSR de Angular y todos los middlewares de Express importan y usan estas funciones, lo que garantiza una política de seguridad uniforme, evita duplicidad de código y previene alertas de seguridad (por ejemplo, en OWASP ZAP) independientemente de si la petición la atiende la SPA, un archivo estático o la API.
src/backend/config/dotenv desde .env y exporta un objeto env con las variables necesarias (PORT, NODE_ENV, JWT_SECRET, JWT_EXPIRES_IN, JWT_ISSUER, JWT_AUDIENCE, CORS_ORIGIN opcional, POSTGRES_PRISMA_URL, BCRYPT_SALT_ROUNDS, Cloudinary, GOOGLE_GENERATIVE_AI_API_KEY, ADMIN_EMAILS, ADMIN_PASSWORDS, ADMIN_NAMES). Valida la presencia de las obligatorias y lanza si falta alguna; las opcionales (JWT_*, CORS_ORIGIN) tienen valores por defecto o quedan undefined.PrismaClient para reutilizar la conexión.auth.routes.ts, games.routes.ts) documentan los endpoints y se integran con swagger-jsdoc.src/backend/middleware/Authorization: Bearer <token>, verifica el JWT con JWT_SECRET y opciones issuer y audience (desde env), y si es válido asigna req.user con { sub, email, isAdmin }. Si falta o es inválido, responde 401.adminOnly comprueba que req.user exista y que o bien req.user.isAdmin sea true o bien el email esté en la lista ADMIN_EMAILS. Si no, responde 403.schema.safeParse(req.body); si falla, responde 400 con los errores aplanados; si tiene éxito, reemplaza req.body por el resultado tipado y llama a next().generalLimiter (100 peticiones / 15 min por IP) y authLimiter (5 intentos / hora por IP para rutas de auth, sin contar las exitosas). Se aplican en app.ts según el entorno.res.json para que, antes de enviar, pase el payload por serializePrisma. Así se convierten tipos no serializables de Prisma (Decimal, BigInt, Date) a tipos JSON válidos.src/backend/utils/serializePrisma(value) que convierte Decimal (objetos con toNumber), Date (a ISO string), arrays y objetos anidados a valores seguros para JSON.stringify.src/backend/modules/Cada dominio (auth, users, games, developers, publishers, genres, platforms, media, favorites, cart, purchases, chat) sigue una estructura similar:
auth, adminOnly, validate(schema)) y delega en controladores. Incluye anotaciones JSDoc para Swagger.req y res, extraen parámetros y cuerpo, llaman a los servicios y envían la respuesta (JSON o código de estado). No contienen lógica de negocio pesada.Ejemplo de cadena en un endpoint protegido: petición → auth (opcional) → validate(schema) → adminOnly (opcional) → controlador → servicio → Prisma → respuesta JSON (serializada por el middleware global).
notificationsEl módulo src/backend/modules/notifications es un módulo de dominio interno orientado a eventos (no expone rutas HTTP propias), por eso no tiene *.routes.ts ni *.controller.ts.
notifications.service.ts: motor de notificaciones (lógica de negocio, jobs programados, deduplicación y envío).notifications.types.ts: tipos compartidos del dominio de notificaciones.index.ts: punto de entrada público del módulo para imports limpios desde otros módulos.Este módulo lo consumen favorites, games, cart, purchases y app.ts (scheduler), manteniendo la misma arquitectura por capas: controladores de dominio disparan eventos y notifications centraliza el comportamiento de correo.
src/backend/scripts/ADMIN_EMAILS, ADMIN_PASSWORDS (opcional) y ADMIN_NAMES (opcional). Usado en el pipeline build:full para asegurar que existan admins tras el despliegue.backend-data/ o variables como MEDIA_BASE_PATH).src/backend/tests/Tests de integración con Jest y supertest. Se ejecutan con npm run test:backend (NODE_ENV=test). Cada archivo (auth.test.ts, users.test.ts, games.test.ts, etc.) levanta la aplicación Express y realiza peticiones HTTP a los endpoints, comprobando códigos de estado y cuerpos de respuesta. La configuración de Jest está en jest.backend.config.js; la base de datos de test debe estar configurada en .env (misma variable POSTGRES_PRISMA_URL o una URL específica de test).
El esquema en prisma/schema.prisma define:
Las migraciones en prisma/migrations/ reflejan la evolución del esquema (campos añadidos, índices, etc.). En entornos que usan pooler (por ejemplo Neon), directUrl con POSTGRES_URL_NON_POOLING se usa para migraciones.
src/app/app.config.tsConfiguración de la aplicación Angular: Zone.js, router (routes), HttpClient con withFetch() y el interceptor serverConnectionInterceptor, animaciones, Translate (ngx-translate) con loader HTTP desde /assets/i18n/, fallback y idioma por defecto (es), y una gran cantidad de providers para repositorios y servicios.
Los tokens inyectables definen nombres de recurso (games, developers, genres, etc.), URLs base de API (todas con prefijo environment.apiUrl que en producción y desarrollo suele ser '', resultando en rutas relativas /api/...) y factories de repositorios y mapeos. Los servicios de dominio (GameService, UserService, CartItemService, PurchaseService, FavoriteService, ChatService, etc.) y el AuthenticationService se registran aquí. También se habilita la hidratación del cliente con provideClientHydration(withEventReplay()).
src/app/app.routes.tsDefine las rutas de la SPA: home (/), login, register, dashboard (protegida con authGuard y customerGuard), product/:id, aichat, favourites, cart, settings, help, contact, privacy, conditions, cookies, search, y admin (protegida con authGuard y adminGuard) con rutas hijas para gestión de géneros, desarrolladores, plataformas, editores y juegos. Las rutas de admin cargan componentes con loadComponent (lazy loading).
La aplicación soporta autenticación y registro social con Google y GitHub usando flujo de redirección completa.
login y register, los botones sociales construyen la URL del proveedor y redirigen con window.location.assign(...).client_id al backend (/api/auth/google/client-id y /api/auth/github/client-id) para no exponer configuración fija en el cliente.sessionStorage (state, nonce, destino target) para validar el retorno y preservar contexto.GET /api/auth/google/callback.GET /api/auth/github/callback.login o register con los parámetros OAuth necesarios y marca _skip_loader=1 para mantener una vuelta visual fluida.login.component.ts y register.component.ts detectan code/state (GitHub) o id_token/state (Google), validan state/nonce y completan sesión contra backend (POST /api/auth/github o POST /api/auth/google).code) para conflictos/validaciones de registro (ERR_AUTH_*) y el frontend los traduce vía i18n.GOOGLE_CLIENT_IDGITHUB_CLIENT_IDGITHUB_CLIENT_SECRETEl área /admin está pensada para gestión de catálogo y está protegida por authGuard + adminGuard en frontend y por auth + adminOnly en backend para operaciones sensibles.
media.POST/PATCH/DELETE /api/games/:id?POST/PATCH/DELETE /api/developers/:id?POST/PATCH/DELETE /api/publishers/:id?POST/PATCH/DELETE /api/genres/:id?POST/PATCH/DELETE /api/platforms/:id?El frontend abstrae las llamadas HTTP en repositorios (por recurso) que construyen las URLs a partir de los tokens (apiUrl + nombre de recurso). Los servicios de dominio utilizan estos repositorios y exponen métodos de alto nivel (listar juegos, añadir al carrito, crear compra, etc.). Los mapeos transforman los DTOs del backend al modelo que usa la aplicación. Esta capa permite cambiar la implementación (HTTP, mock) sin tocar los componentes.
En src/environments/ están environment.ts (desarrollo) y environment.prod.ts (producción). Contienen production: boolean y apiUrl: string. Con apiUrl: '', las peticiones se hacen a rutas relativas, por lo que funcionan contra el mismo origen (SSR o proxy en desarrollo). El build de producción reemplaza el fichero de environment mediante fileReplacements en angular.json.
Durante ng build con configuración SSR, Angular puede ejecutar el servidor de entrada (src/server.ts) para descubrir rutas y generar recursos. En ese contexto, el backend importa Prisma, Cloudinary, dotenv, etc., y puede fallar si no hay .env, base de datos o variables configuradas. Para evitar eso, el script build:ssr define SSR_DISABLE_BACKEND=1 (por ejemplo con cross-env). Así, en server.ts la condición if (!process.env['SSR_DISABLE_BACKEND']) es falsa y no se monta el backend durante el build. El artefacto generado (server.mjs) sí monta el backend cuando se ejecuta en runtime, porque entonces la variable no está definida.
/ (y el resto de rutas de la SPA).GET /api/health (respuesta { ok: true }).GET /api/diagnostic.GET /robots.txt: Reglas para crawlers.GET /sitemap.xml: Índice de sitemaps.GET /sitemap-static.xml: Rutas estáticas de la web.GET /sitemap-products.xml: Sitemap dinámico generado desde la BD con los productos actuales.GET /api-docs.POST /api/auth/register, POST /api/auth/login.GET /api/auth/google/client-idGET /api/auth/github/client-idGET /api/auth/google/callbackGET /api/auth/github/callbackPOST /api/auth/googlePOST /api/auth/githubGET /api/users/me (requiere JWT).GET /api/games, GET /api/games/:id, POST /api/games, PATCH /api/games/:id, DELETE /api/games/:id (creación/edición/borrado con auth y admin)./api/developers, /api/publishers, /api/genres, /api/platforms./api/media./api/favorites, /api/cart, /api/purchases./api/chat (sesiones y mensajes con IA).La documentación detallada de parámetros, cuerpos y respuestas está en Swagger (/api-docs) y en los JSDoc de cada ruta.
La aplicación integra una pasarela de pago embebida con Stripe para cerrar compras sin salir del sitio. El flujo está diseñado para mantener consistencia entre estado de pago, carrito, compras del usuario y stock por plataforma.
POST /api/cart/checkout-session.mode: payment y ui_mode: embedded_page.clientSecret, sessionId y publishableKey, que el frontend usa para montar el checkout embebido.POST /api/cart/checkout/confirm./help) incluye una guía extensa de activación por plataforma (PC, PlayStation, Xbox y Switch), navegación por secciones, capturas reales y una sección FAQ integrada y traducida para todos los idiomas soportados.enter/leave), con comportamiento estable y consistente con el patrón visual de filtros de búsqueda.Escape), siguiendo el mismo patrón visual y de interacción que las capturas del detalle de producto.Purchase y PurchaseItem) para que aparezcan en el historial del usuario.key (clave de producto digital) para mostrarla de forma segura en el dashboard con modo oculto/visible./product/:id), el frontend muestra confirmación contextual con enlaces a dashboard y help#faq.POST /api/purchases/:id/refund.PATCH /api/purchases/:id para clientes antiguos.refunded,.env.npm run serve:ssr:https y abrir https://localhost:3443.La aplicación incorpora un sistema de notificaciones por email en backend, centralizado en src/backend/modules/notifications.
settings (frontend): activación global, email destino, idioma, frecuencia (immediate/daily/weekly), horas silenciosas, pausa temporal y toggles por tipo.User (Prisma): campos emailNotificationsEnabled, notificationEmail, emailNotificationLanguage, emailNotificationFrequency, emailNotificationTopics, emailNotificationPausedUntil, emailQuietHoursStart, emailQuietHoursEnd, emailRecommendationIntervalDays, emailNotificationMeta, lastSeenAt.es, en, fr, de, it para asunto, títulos y cuerpos.Hola/Hi... <nombre>), bloque de contenido y footer fijo con enlace a ajustes.https://gamingsage.vercel.app/settings.En compra y reembolso se adjunta PDF (application/pdf) con referencia, fecha, total y líneas de artículos:
PUR-<id> / REF-<id> para compras/reembolsos internos.STRIPE-<sessionId> en confirmaciones de checkout Stripe.startEmailNotificationScheduler) fuera de entorno test.runEmailJobsNow).SMTP_HOSTSMTP_PORT (opcional, por defecto 587)SMTP_USERSMTP_PASSSMTP_FROM (opcional, por defecto no-reply@gamesage.local)SMTP_FROM_NAME (opcional, por defecto Game Sage; nombre visual del remitente)PUBLIC_APP_URL o FRONTEND_URL o APP_URL (opcionales; recomendados para construir URLs absolutas de imagen en correos)Si no se define SMTP, el sistema omite el envío sin romper la ejecución del backend.
Antes de ejecutar determinados comandos (build, serve, dev, tests, seeds, etc.), el proyecto ejecuta comprobaciones previas que evitan fallos confusos y muestran errores legibles.
scripts/check-toolchain.mjs: Comprueba que exista node_modules y binarios necesarios (Angular CLI, Prisma). Si faltan dependencias o herramientas de build, muestra el comando recomendado según el estado actual (npm run setup:secure o npm install --include=dev --no-audit).
scripts/check-prisma.mjs: Comprueba que el cliente de Prisma esté generado (existe node_modules/@prisma/client/index.d.ts). Si no se ha ejecutado npx prisma generate, se muestra un error claro antes de que el compilador de TypeScript falle con mensajes sobre campos o tipos faltantes.
scripts/serve-ssr.mjs: Antes de arrancar el servidor SSR compilado, valida que exista el build (dist/game-sage/server/server.mjs) y que exista un .env configurado. Si falta alguno, muestra un error directo indicando qué comando o paso previo ejecutar.
scripts/serve-ssr-watch-safe.mjs: Wrapper de arranque usado en modo watch SSR. Espera a que el build de dist/game-sage/server esté estable y con imports presentes antes de arrancar el servidor, y reintenta automáticamente si detecta un ERR_MODULE_NOT_FOUND transitorio de chunks durante una recompilación.
scripts/free-port.mjs: Utilidad interna que verifica si un puerto concreto está en uso y, solo en ese caso, mata el proceso asociado. Se usa desde los scripts start, dev y serve:ssr para evitar errores EADDRINUSE sin imprimir mensajes cuando el puerto ya está libre.
Estas verificaciones se ejecutan automáticamente en los scripts de package.json que las requieren (build, serve:ssr, dev:backend, test, seed:admin, format, etc.). No aplican a clean:full ni a reinstall, ya que esos comandos borran o reinstalan dependencias por diseño.
Esta sección describe los pasos necesarios para poner en marcha el proyecto en una máquina sin tener preinstalado Node.js, base de datos ni ninguna herramienta del stack.
Node.js incluye el runtime de JavaScript y npm (gestor de paquetes). Sin Node no se puede ejecutar el servidor ni el CLI de Angular.
node -v (debe mostrar la versión, por ejemplo v20.x o v22.x).npm -v (debe mostrar la versión de npm).Versiones recomendadas: Node 20.x o 22.x; el proyecto está probado con estas versiones.
Si el código se obtiene desde un repositorio Git, hace falta tener Git instalado.
git --version.Si el proyecto se recibe como ZIP o carpeta, este paso puede omitirse.
git clone <url> angular-ssr y luego cd angular-ssr.package.json).El backend usa PostgreSQL a través de Prisma. Hay que disponer de una instancia accesible y de las URLs de conexión en el .env.
gamesage / gamesage).postgresql://usuario:password@localhost:5432/nombre_bd. Esa URL y, si aplica, la URL directa sin pooler se configuran en el paso de variables de entorno.POSTGRES_PRISMA_URL y POSTGRES_URL_NON_POOLING (o la que corresponda para migraciones). Esas URLs se copian al .env y se gestionan desde Vercel; no hace falta crear la base manualmente en neon.tech.En cualquier caso, las URLs de conexión se definen en el archivo .env en el paso siguiente.
En la raíz del proyecto debe existir un archivo .env. No está versionado por seguridad.
cp .env.example .env (en Windows: copy .env.example .env)..env y rellenar cada variable:postgresql://user:pass@host:5432/dbname).POSTGRES_PRISMA_URL.development para desarrollo; en Vercel u otro hosting de producción usar production.7d, game-sage y game-sage-users. Útiles para endurecer la emisión/verificación de tokens.https://tu-app.vercel.app). En desarrollo se permiten automáticamente http://localhost:PORT y http://localhost:4200.admin@example.com).Guardar el archivo. Sin estas variables, el backend no arrancará (env.ts lanza si falta alguna obligatoria).
En Vercel: configurar las mismas variables en el proyecto (Settings → Environment Variables). El build usa vercel-build (prisma generate + build:ssr) definido en vercel.json.
En la raíz del proyecto ejecutar:
Example :npm run setup:securenpm run setup:secure instala dependencias con --no-audit, aplica correcciones de seguridad en dependencias de producción, restaura automáticamente las devDependencies necesarias para compilar y testear, y finaliza validando 0 vulnerabilities para producción (npm audit --omit=dev).
Si en Windows PowerShell aparece "running scripts is disabled on this system" al ejecutar npm:
npm.cmd run setup:secure (y en general npm.cmd en lugar de npm para el resto de comandos), oSet-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope LocalMachine para permitir scripts locales como npm.ps1.Generar el cliente de Prisma (necesario para que el backend compile y ejecute):
Example :npx prisma generateAplicar el esquema a la base de datos (crear tablas y migraciones pendientes):
Example :npx prisma migrate deploySi es la primera vez y se usa migrate dev en lugar de deploy (por ejemplo en desarrollo local), se pueden crear migraciones desde el estado actual del schema con:
npx prisma migrate devComprobar que la base tiene tablas con npx prisma studio (opcional).
Para poder acceder al área de administración:
Example :npm run seed:adminEsto crea o actualiza los usuarios cuyos emails están en ADMIN_EMAILS (y opcionalmente contraseñas y nombres desde .env).
Para cargar juegos, géneros, plataformas, etc. de ejemplo:
Example :npm run seed:dataEjecuta primero una limpieza y luego el seed completo. Requiere que las variables de Cloudinary y, si aplica, la ruta de media local estén configuradas (ver documentación de los scripts en src/backend/scripts/).
Compilar la aplicación (frontend + servidor):
Example :npm run build:ssrAl finalizar, arrancar el servidor SSR compilado (modo estable):
Example :npm run serve:ssr:prodEl servidor quedará escuchando en http://localhost:<PORT> (por defecto 3000). Ahí se sirve la web y la API (/api/*). Abrir el navegador en esa URL para usar la aplicación.
cp .env.example .env
# Editar .env con BD, Cloudinary, JWT, etc.
npm run setup:secure
npx prisma generate
npx prisma migrate deploy
npm run seed:admin
npm run build:ssr
npm run serve:ssr:prodLas variables de entorno se definen en .env (ver plantilla en .env.example) y se cargan con dotenv desde src/backend/config/env.ts. En ese fichero se valida la presencia de variables críticas y se tipan para consumo interno.
Variables habituales:
PORT, NODE_ENVJWT_SECRET, BCRYPT_SALT_ROUNDS; opcionales JWT_EXPIRES_IN, JWT_ISSUER, JWT_AUDIENCECORS_ORIGIN (solo en producción; orígenes permitidos separados por coma). En desarrollo se permiten http://localhost:PORT y http://localhost:4200 sin configurarlo.POSTGRES_PRISMA_URLCLOUDINARY_CLOUD_NAME, CLOUDINARY_API_KEY, CLOUDINARY_API_SECRETGOOGLE_GENERATIVE_AI_API_KEYGOOGLE_CLIENT_ID, GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRETADMIN_EMAILS (y opcionalmente ADMIN_PASSWORDS, ADMIN_NAMES)src/environments)Angular utiliza los ficheros de environment para seleccionar configuración en build (por ejemplo, reemplazos en producción). En este proyecto los environments son deliberadamente mínimos:
production: booleanapiUrl: ''Con apiUrl vacío, todas las URLs de API construidas en src/app/app.config.ts quedan como rutas relativas (/api/...). Esto permite que el frontend funcione correctamente en SSR y despliegues sin necesitar reconfigurar URLs absolutas.
Script: src/backend/scripts/postgreToExcel.py
POSTGRES_PRISMA_URL configurada en .envpy -3 src/backend/scripts/postgreToExcel.pyEl comando usa POSTGRES_PRISMA_URL de tu .env.
Si faltan dependencias, el script intenta instalarlas automaticamente y, si no puede, te indica el comando exacto para resolverlo.
Opcional:
--write-mode replace para sobrescribir siempre backend-data/postgres_export.xlsx.--write-mode incremental (por defecto) para guardar en backend-data/exports/postgres_export_1.xlsx, postgres_export_2.xlsx, etc.--schema public para elegir esquema.--output ruta/archivo.xlsx para salida personalizada.Script: src/backend/scripts/jsonToExcel.py
Este script demuestra el uso intensivo de la librería Pandas para la limpieza, transformación y análisis de los datos del proyecto (ficheros JSON).
pandas, openpyxl. Instalables con python -m pip install pandas openpyxl.python src/backend/scripts/jsonToExcel.pybackend-data/exports/processed_database.xlsx con los resultados y una pestaña específica para la búsqueda.Los comandos están definidos en package.json. Para un arranque desde cero completo, ver la sección Arrancar el proyecto desde cero.
Nota: Los comandos start, dev, serve:ssr, serve:ssr:https, serve:ssr:prod, dev:ssr y dev:ssr:https ejecutan automáticamente node scripts/free-port.mjs <puerto> cuando corresponde para liberar puertos de desarrollo solo si están en uso, evitando conflictos sin generar ruido en la consola.
npm start
Ejecuta ng serve (desarrollo de frontend en modo SPA).
npm run dev:backend
Levanta el backend con nodemon + tsx, observando src/backend.
npm run dev
Alias de npm run dev:backend.
npm run serve:ssr
Modo desarrollo SSR con recarga automática: compila en watch y reinicia el servidor SSR cuando cambia dist. El reinicio usa un wrapper seguro para evitar caídas/ruido por chunks temporales no disponibles durante recompilaciones.
npm run serve:ssr:https
Igual que serve:ssr, pero además levanta un proxy HTTPS local (https://localhost:3443 -> http://localhost:3000) para pruebas en contexto seguro (por ejemplo Stripe en local).
Consideraciones reales de este modo:
http://localhost:3000; para navegar la app debes abrir https://localhost:3443.chrome-error://chromewebdata); abrir una pestaña nueva y acceder directamente a https://localhost:3443.serve:ssr (HTTP) suele ser más estable y simple.npm run serve:ssr:prod
Ejecuta el servidor SSR compilado desde dist/game-sage/server/server.mjs (con 8GB de memoria asignada).
npm run start:ssr
Alias de npm run serve:ssr:prod.
npm run build
Build estándar de Angular (browser) con 8GB de memoria.
npm run build:ssr
Build SSR (browser + server) con 8GB de memoria. Durante el build se utiliza SSR_DISABLE_BACKEND=1 para evitar que el backend se evalúe en la fase de extracción de rutas.
npm run build:full
Pipeline completo:
prisma generateseed:adminbuild:ssrTras una clonación o reinstalación, usa npm run setup:secure para dejar el entorno listo y con validación de 0 vulnerabilities en producción.
npm test
Ejecuta la suite de backend (npm run test:backend). Se mantiene como comando principal para CI/validación rápida.
npm run test:backend
Ejecuta Jest sobre src/backend/tests. Se configura NODE_ENV=test y se silencia el ruido de warnings de Node para una salida limpia.
npm run test:frontend
Ejecuta ng test (Karma/Jasmine). Útil si se añaden *.spec.ts del frontend.
npm run seed:admin
Ejecuta src/backend/scripts/seedAdmin.ts.
npm run clean:data
Ejecuta src/backend/scripts/cleanData.ts y limpia por completo los datos de negocio y la media asociada en Cloudinary.
npm run seed:data
Ejecuta primero la limpieza completa (npm run clean:data) y, a continuación, src/backend/scripts/seedData.ts, que rellena la base de datos y la media a partir de backend-data/.
npx prisma studionpx prisma migrate devnpx prisma generatenpx prisma db pushnpm run docs: levanta la documentación web de Compodoc en local (usando docs/).npm run docs:build: genera la documentación de Compodoc en la carpeta docs/.npm run docs:serve: levanta la documentación web de Compodoc en local para abrirla en el navegador (usando docs/).npm run audit: ejecuta npm audit --audit-level=high --omit=dev para revisar vulnerabilidades en dependencias de producción (OWASP A06).npm run lint: ejecuta el análisis estático de código buscando vulnerabilidades de seguridad (vía ESLint y plugin de seguridad).npm run audit:fix:prod: corrige vulnerabilidades en dependencias de producción, restaura devDependencies y valida npm audit --omit=dev al final.npm run setup:secure: flujo recomendado tras clonar; instala dependencias y deja el entorno listo para build con validación de seguridad de producción.py -3 src/backend/scripts/postgreToExcel.py: exporta tablas PostgreSQL a un unico Excel para analisis en Power BI (por defecto en backend-data, modo incremental).npm run watch: build en watch (config development).npm run format: formateo con Prettier.npm run clean: borra dist, logs, coverage.npm run clean:full: borra dist, node_modules, logs, coverage, docs, documentation y .venv.npm run reinstall: reinstalación completa (clean:full + npm install); después ejecutar npm run setup:secure.npm run serve:ssr), Stripe puede mostrar el aviso:You may test your Stripe.js integration over HTTP. However, live Stripe.js integrations must use HTTPS.npm run serve:ssr:https y abrir https://localhost:3443.Los siguientes comandos cubren la mayoría de flujos habituales:
npm run setup:securenpm run build:ssr
npm run serve:ssr:prodnpm run dev:backendnpm run build:fullnpm testEl repositorio incluye una base compactada de personalización para Cursor Agent:
uvx mcp-atlassian.GITHUB_TOKENPOSTGRES_PRISMA_URLJIRA_URLJIRA_USERNAMEJIRA_API_TOKENStory Points.Story Points representan horas estimadas y deben ser enteros.Rules, Skills, Commands y Hooks del proyecto se cargan automáticamente al abrir el repositorio en Cursor; no requieren pasos adicionales para estar disponibles.MCP: para usar GitHub, PostgreSQL o Jira, hay que tener definidas las variables de entorno correspondientes.Settings -> MCP y habilitar manualmente los servidores github, jira y postgres si aparecen en Disabled..env o variables de entorno del sistema son válidas, pero Cursor debe poder resolverlas al arrancar.En Windows, el flujo recomendado es:
.env local.npm run setup:cursor-accessEste script instala uv si falta y copia las variables MCP necesarias desde .env al entorno de usuario de Windows para que Cursor pueda resolverlas al arrancar los servidores MCP.
Variables sincronizadas:
GITHUB_TOKENPOSTGRES_PRISMA_URLJIRA_URLJIRA_USERNAMEJIRA_API_TOKENDespués de ejecutar el script, hay que cerrar y volver a abrir Cursor.
Settings -> MCP aparece MCP configuration errors con mensaje de JSON inválido en un servidor externo (por ejemplo extension-GitKraken), pulsar Open JSON y corregir o eliminar la entrada rota.Developer: Reload Window en Cursor y volver a comprobar que github, jira y postgres estén en Enabled.Not connected o devolver Connection closed; si pasa, cerrar y abrir Cursor y volver a revisar Settings -> MCP.Settings -> MCP, y después ejecutar Developer: Reload Window.npm run build:ssr, npm run lint, npm audit --omit=dev.npm run vercel-build en local: ese comando está orientado al pipeline de Vercel y puede regenerar artefactos no deseados (por ejemplo contenido en docs/).La integración de Jira en este proyecto usa un servidor MCP local lanzado con:
Example :uvx mcp-atlassianPor tanto, antes de usar Jira en Cursor hay que tener uv instalado en el sistema. En Windows:
powershell -c "irm https://astral.sh/uv/install.ps1 | iex"Después, verificar:
Example :uv --version