DraftKings es una aplicación móvil híbrida diseñada para los apasionados del fútbol. La plataforma permite a los usuarios gestionar una base de datos de jugadores, realizar un seguimiento de noticias detalladas, calificar el rendimiento mediante comentarios y valoraciones, y diseñar tácticamente su "Equipo Ideal".
Esta aplicación combina una interfaz moderna y fluida con varias arquitecturas de backend robustas y distribuida, garantizando escalabilidad, independencia entre los diferentes modulos de la aplicación y eficiencia en la gestión de datos deportivos.
- Gestión de Jugadores: Visualización, búsqueda con filtros avanzados y creación de nuevos perfiles con integración de cámara y geolocalización.
- Interacción Social: Sistema de comentarios y valoraciones (0-5 estrellas) para cada jugador.
- Módulo de Noticias: Creación y lectura de noticias vinculadas a jugadores específicos con soporte para etiquetas y resúmenes.
- Equipo Ideal: Herramienta interactiva para que el usuario configure su propia alineación táctica.
- Personalización: Soporte multi-idioma y modo claro/oscuro.
- Rúbrica CNSA docs/rúbricas/CNSA-rúbrica
- Rúbrica DAH docs/rúbricas/DAH-rúbrica
- Rúbrica DWSC docs/rúbricas/DWSC-rúbrica
- Rúbrica TRWM docs/rúbricas/TRWM-rúbrica
- Especificación de endpoints: docs/EndPoints.md
- SonarQube: http://sonarqube.cnsa-2026-dsa069.tech
- Front Ionic-Angular: https://draftkings-dev.cnsa-2026-dsa069.tech
- Back Node: https://dk-node-dev.cnsa-2026-dsa069.tech
- Corba: http://dk-corba-dev.cnsa-2026-dsa069.tech:8080/DK_News_Prod_Cons/servlet
- Back SpringBoot
- Gateway: https://dk-spring-dev.cnsa-2026-dsa069.tech
- Config: https://dk-spring-config-dev.cnsa-2026-dsa069.tech
- Eureka: https://dk-spring-eureka-dev.cnsa-2026-dsa069.tech
- User Client: https://eureka-client-user-dev-223788394742.us-east1.run.app
- Player Client: https://eureka-client-player-dev-223788394742.us-east1.run.app
- Review Client: https://eureka-client-review-dev-223788394742.us-east1.run.app
- Front Ionic-Angular: https://draftkings.cnsa-2026-dsa069.tech
- Back Node: https://dk-node.cnsa-2026-dsa069.tech
- Corba: http://dk-corba.cnsa-2026-dsa069.tech:8080/DK_News_Prod_Cons/servlet
- Back SpringBoot
- Gateway: https://dk-spring.cnsa-2026-dsa069.tech
- Config: https://dk-spring-config.cnsa-2026-dsa069.tech
- Eureka: https://dk-spring-eureka.cnsa-2026-dsa069.tech
- User Client: https://eureka-client-user-223788394742.us-east1.run.app
- Player Client: https://eureka-client-player-223788394742.us-east1.run.app
- Review Client: https://eureka-client-review-223788394742.us-east1.run.app
El proyecto utiliza un stack tecnológico avanzado para soportar una arquitectura híbrida y distribuida:
- Angular El proyecto se forma de Standalone components
- Ionic Framework: Desarrollo de la aplicación híbrida para garantizar una experiencia nativa en iOS y Android.
- Capacitor: Para el acceso a hardware nativo (Cámara y GPS).
- Componentes UI: Diseño basado en componentes deportivos de alto rendimiento.
- Node.js: Encargado de [mencionar función, ej: la lógica de noticias y tiempo real].
- Spring Boot (Java): Encargado de [mencionar función, ej: la gestión robusta de jugadores y autenticación].
- Mencionar diagramas
- CORBA (Common Object Request Broker Architecture): Utilizado como middleware para la interoperabilidad entre objetos distribuidos en una red heterogénea.
- ORB (Object Request Broker): Facilitador de la comunicación entre los servicios de backend y el almacenamiento de datos.
La aplicación sigue un modelo de arquitectura distribuida donde:
- El Frontend (Ionic) se comunica con las APIs REST de Node.js y Spring Boot.
- La capa de servicios utiliza CORBA para la comunicación entre componentes críticos del servidor, permitiendo que diferentes lenguajes y plataformas interactúen de forma transparente.
El backend de Spring Boot implementa una arquitectura de microservicios distribuidos con los siguientes componentes y patrones:
Función: Descubrimiento y registro dinámico de servicios.
-
Responsabilidades:
- Todos los microservicios se registran automáticamente en Eureka al iniciar
- Mantiene un registro actualizado de instancias disponibles
- Permite que los servicios se descubran entre sí mediante el nombre lógico
- Implementa health checks para detectar servicios caídos
-
Configuración:
- Escucha en puerto estándar (por defecto 8761)
- Replicas pueden crearse para alta disponibilidad
- Los clientes consultan periódicamente (cada 30 segundos) el estado de servicios
Función: Proporciona configuración externalizada y dinámica para todos los microservicios.
-
Responsabilidades:
- Almacena propiedades (application.yaml) de cada microservicio en este mismo repositorio GitHub (config.storage)
- Permite cambios de configuración sin redeploying
- Cada servicio obtiene su configuración específica al arrancar
- Comportamiento: Si está caído, los servicios siguen funcionando con su última configuración conocida
-
Archivos de configuración:
config-storage/<ms>-dev.yaml→ Configuración del microservico con perfil de desarollo en el despliegueconfig-storage/<ms>-prod.yaml→ Configuración del microservico con perfil de producción en el despliegueconfig-storage/<ms>.yaml→ Configuración del microservico por defecto
Función: Punto de entrada único y orquestador de tráfico.
-
Responsabilidades:
- Recibe TODAS las peticiones del cliente (Frontend Ionic)
- Redirige dinámicamente a los microservicios correspondientes basándose en rutas configuradas
- Implementa load balancing usando Eureka para saber dónde está cada servicio
- Maneja autenticación y autorización (JWT)
- Rate limiting y validación de peticiones
- Agrupa respuestas de múltiples servicios cuando es necesario
-
Patrones:
- TO DO
- El Gateway conoce la ubicación de cada servicio consultando Eureka
Función: Responsable de toda la lógica relacionada con jugadores.
-
Responsabilidades:
- CRUD completo de jugadores (Create, Read, Update, Delete)
- Filtros avanzados (por posición, equipo, rendimiento, etc.)
- Gestión de datos deportivos (estadísticas, goles, asistencias)
- Control de comentarios en jugadores: Relación 1:N con tabla
commentsse encarga de la comunicación con el micorservicio de reseñas y gestiona sus comentarios dado un jugador(comentarios específicos de jugadores) - Cálculo de promedios de valoraciones para jugadores
- Conexión directa a Base de Datos SQL (tabla:
players)
-
Dependencias:
- ✅ Se conecta a Eureka para registrarse
- ✅ Se conecta a Config Server para obtener conexión a BD
- ✅ Se comunica con Review MS (cuando necesita datos de reseñas de un jugador)
Función: Gestiona reseñas generales, valoraciones y comentarios independientes del contexto de jugadores.
-
Responsabilidades:
- CRUD de valoraciones generales (0-5 estrellas)
- Gestión de reseñas y comentarios independientes
- Calcular promedio de valoraciones
- Ordenar por más recientes, mejor valoradas, etc.
- Conexión directa a Base de Datos SQL (tabla:
reviews)
-
Dependencias:
- ✅ Se conecta a Eureka para registrarse
- ✅ Se conecta a Config Server para obtener conexión a BD
- ✅ Se comunica con Player MS (para validar contextos de jugadores si aplica)
TO-DO Refinar rutas
1. Frontend (Ionic) → Gateway
GET /api/gateway:8080/player/id/123/comentarios/234
2. Gateway → Eureka
"¿Dónde está Player MS?"
Respuesta: 192.168.1.5:8090
3. Gateway → Player MS
GET /player/id/123/comentarios/234
Respuesta: Jugador Identificado
4. Player MS → Eureka
"¿Dónde está Review MS?"
Respuesta: 192.168.1.7:8091
5. Player MS → Review MS (via Feign)
GET /comentarios/234 (Comentario del jugador)
Respuesta: Datos detallados del comentario del jugador
6. Player MS → Frontend (vía Gateway)
Respuesta completa: Comentario de un jugador específico
| Aspecto | Beneficio |
|---|---|
| Escalabilidad | Cada MS escala independientemente según demanda |
| Resiliencia | Si Review MS cae, Player MS sigue funcionando |
| Independencia | Equipos pueden trabajar en paralelo sin conflictos |
| Mantenibilidad | Cambios en un servicio no afectan a otros |
| Desacoplamiento | Comunicación vía APIs REST, no compartición de BD |
| Configuración Centralizada | Cambios sin redeploy gracias a Config Server |
| Discovery Automático | Eureka maneja el registro/desregistro dinámicamente |
La arquitectura de integración y despliegue continuo está diseñada para garantizar calidad, seguridad y automatización en cada sección del proyecto. A continuación, se describe en detalle el flujo para cada componente:
Archivo: ionic-ci-cd.yaml y ionic-ci-cd.prod.yml
Triggers:
- ✅ Se ejecuta en
pushamainydev - ✅ Se ejecuta en
pull_requestamainydev
Flujo de Integración Continua (CI):
-
Setup Node.js (v24)
- Instala dependencias usando npm con caché para optimizar build
-
Linting & Code Quality
- Ejecuta
npm run lint:jsonpara generar reporte de ESLint - Genera archivo
eslint-result.jsonpara análisis de PR
- Ejecuta
-
Build Web
- Dev:
npx ionic build(sin optimizaciones) - Prod:
npx ionic build --prod(optimizado para producción) - Genera carpeta
www/con assets compilados
- Dev:
-
Build Android APK
- Setup de Java (v21) y Android SDK (API 34)
- Adiciona plataforma Android via Capacitor
- Sincroniza Capacitor:
npx cap sync android - Dev: Genera APK en modo Debug (
app-debug.apk) - Prod: Genera APK en modo Release y lo firma digitalmente con keystore
-
E2E & Component Tests (solo Prod)
- Ejecuta Cypress con matriz de navegadores: Chrome y Firefox
- Valida funcionalidad de componentes críticos
-
Artefactos Generados:
www/- Build web compiladoDraftKings-debug.apkoDraftKings.apk- APK firmadoeslint-report- Reporte de linting
Archivo: node-ci.yaml, node-cd.yaml y node-cd.prod.yml
Triggers:
- ✅ Se ejecuta en
pushamainydev - ✅ Se ejecuta en
pull_requestamainydev
Flujo de Integración Continua (CI):
-
Setup Node.js (v24)
- Instala dependencias con npm caché
- Working directory:
./api-node
-
Build TypeScript
- Ejecuta
npm run build - Transpila TS → JS a carpeta
dist/
- Ejecuta
-
Code Quality
- Genera reporte ESLint:
npm run lint:json - Anotaciones en PR con resultados de linting
- Genera reporte ESLint:
-
Testing con Jest
- Ejecuta
npm run coverage - Genera:
junit.xmly reportes de cobertura - Integración con test-reporter de GitHub
- Ejecuta
-
Artefactos:
dist/- Código compiladopublic/- Assets estáticosjunit.xml- Resultados de testscoverage/- Reporte de cobertura
Flujo de Despliegue Continuo (CD):
Deployment a Dev (node-cd.yaml):
- Trigger: Se ejecuta si CI es exitoso en rama
dev - Steps:
- Descarga artefactos del build (dist/)
- Autentica en Google Cloud usando
GCP_SA_KEY - Crea/actualiza secret en GCP:
NODE_ENV_DEV - Login a Artifact Registry
- Build Docker: Usa
Dockerfileestándardocker build -t us-east1-docker.pkg.dev/cnsa-2026/draftkings/api-node:latest .
- Push a Artifact Registry
- Deploy a Cloud Run:
- Service:
dk-node-dev - Region:
us-east1 - Inyecta secreto:
/app/enviroments/env=NODE_ENV_DEV:latest - Flag:
--allow-unauthenticated
- Service:
Deployment a Prod (node-cd.prod.yml):
- Trigger: Se ejecuta si CI es exitoso en rama
main - Steps: Similares a Dev, con diferencias:
- Build Docker con tag versionado:
:1.0.0 - Usa
Dockerfile.prodoptimizado - Service:
dk-node(sin sufijo -dev) - Secret:
NODE_ENV_PROD
- Build Docker con tag versionado:
Archivo: spring-ci.yaml, spring-cd.yml y spring-cd.prod.yml
Triggers:
- ✅ Se ejecuta en
pushamainydev - ✅ Se ejecuta en
pull_requestamainydev
Microservicios Incluidos (Matrix Strategy):
1. eureka.server → Servidor de descubrimiento de servicios
2. config.server → Servidor centralizado de configuración
3. gateway → API Gateway para enrutamiento
4. eureka.client.player → Microservicio de Jugadores (con DB)
5. eureka.client.review → Microservicio de Reseñas (con DB)
Flujo de Integración Continua (CI):
-
Setup Java 17 (Temurin)
- Caché de Maven para optimizar builds
-
Build Paralelo (Matrix Strategy)
- Cada microservicio se compila en paralelo
- Si un servicio falla, los demás continúan (
fail-fast: false) - Working directory:
./api-spring/${{ matrix.service }}
-
Maven Build Completo por servicio:
mvn -B clean package
- Clean: Limpia builds anteriores
- Package: Compila, prueba y genera JAR
-
Code Quality & Coverage:
- JaCoCo Coverage: Análisis de cobertura de código (mínimo 60%)
- Checkstyle: Validación de estilo (Google Checks)
- Comentarios automáticos en PRs con resultados
-
Test Results:
- Genera:
target/surefire-reports/*.xml - Reporter de dorny/test-reporter para visualizar en GitHub
- Genera:
-
Artefactos Generados:
${{ service }}-jar- JAR ejecutable${{ service }}-test-results- Resultados de tests
Flujo de Despliegue Continuo (CD):
El deployment es manual y parametrizado usando workflow_call:
on:
workflow_call:
inputs:
service_name:
required: true
type: stringSteps de Deployment (Dev y Prod tienen la misma lógica):
- Descarga JAR del servicio especificado
- Autentica en GCP
- Transforma nombre del servicio:
eureka.client.player→eureka-client-player
- Crea/actualiza secretos en GCP (si aplica):
- Player y Review usan
DB_SQL_PASS_DEVoDB_SQL_PASS_PROD
- Player y Review usan
- Build Docker:
docker build -t us-east1-docker.pkg.dev/cnsa-2026/draftkings/${{ service_slug }}:latest .
- Push a Artifact Registry
- Deploy a Cloud Run:
- Service:
${{ service_slug }}-dev(o sin -dev en prod) - Inyecta secretos de DB solo si aplica
- Flags:
--allow-unauthenticated --ingress=all
- Service:
Diferencias Dev/Prod:
- Dev: Tag
:latest, secret_DEV - Prod: Tag
:1.0.0, secret_PROD, Dockerfile.prod
Archivo: corba-ci.yaml, corba-cd.yml y corba-cd.prod.yml
Triggers:
- ✅ Se ejecuta en
pushamainydev - ✅ Se ejecuta en
pull_requestamainydev
Particularidad: Java 8 (CORBA fue removido de Java 11+)
Flujo de Integración Continua (CI):
-
Setup Java 8 (Temurin - CRÍTICO para CORBA)
- CORBA no está disponible en Java 11+
- El compilador
idljes parte del JDK 8
-
Verificación del Compilador IDL:
- Verifica disponibilidad de
idlj(IDL Java Compiler) - Invocado automáticamente por Maven en fase
generate-sources
- Verifica disponibilidad de
-
Maven Build:
mvn -B clean verify
- generate-sources: Ejecuta
idljpara compilar archivos.idl - compile: Compila código Java generado + código fuente
- test: Ejecuta tests con JUnit 5
- Genera JAR con dependencias:
*-jar-with-dependencies.jar
- generate-sources: Ejecuta
-
Code Quality:
- JaCoCo Coverage: Mínimo 60%
- Checkstyle: Validación de estilo Google
-
Test Results:
- Reporte JUnit con dorny/test-reporter
-
Artefactos:
corba-jar- JAR ejecutable con dependenciastest-results- Reportes JUnit
Flujo de Despliegue Continuo (CD):
El deployment de CORBA es más complejo que otros servicios debido a infraestructura on-premise:
Steps de Deployment (Dev):
-
Docker Setup:
- Build imagen:
docker build -t us-east1-docker.pkg.dev/cnsa-2026/draftkings/corba-news-manager:latest . - Push a Artifact Registry
- Build imagen:
-
Resiliencia & VM Management:
- Verifica estado de VM en GCP:
- Si está
TERMINATED→ La enciende - Si está
RUNNING→ Continúa - Si
NOT_FOUND→ Lanza Terraform para crearla
- Si está
- Verifica estado de VM en GCP:
-
SSH Key Management:
- Instala clave privada SSH desde secrets
- Escanea fingerprints del host
-
Infrastructure as Code (Terraform) [SOLO SI VM NO EXISTE]:
- Setup Terraform
- Crea credenciales GCP y llaves SSH
- Inicia workspace:
$ENTORNO=dev - Aplica configuración:
terraform init -backend-config="prefix=terraform/state/$ENTORNO" terraform apply -auto-approve - Genera VM con permisos SSH habilitados
-
Espera VM lista:
- Retry up to 15 veces para SSH conectado
- Espera 10 segundos entre intentos
-
Despliegue en VM via SSH:
- Conecta a VM mediante SSH
- Pull imagen Docker
- Inicia contenedor CORBA
- Configura puerto de escucha
Deployment a Prod (corba-cd.prod.yml):
- VM:
dk-corba(sin sufijo -dev) - DNS:
dk-corba.cnsa-2026-dsa069.tech - Imagen: Tag
:1.0.0 - Usa
Dockerfile.prod
- Vista Login: Formulario de acceso con logo, campos de email/password y botón de acción. Enlace a registro.
- Vista Registro: Formulario extendido para nuevos usuarios.
-
Lista de Jugadores:
- Barra de búsqueda superior.
- Botón de filtros de búsqueda.
- Listado de tarjetas (cards) de jugadores.
-
Crear Jugador:
- Formulario: Nombre, posición, dorsal.
- Sección de imagen: Botón para "Tomar Foto" y placeholder de previsualización.
- Sección de Mapa: Componente de mapa interactivo para marcar ubicación.
-
Ver Jugador:
- Cabecera con imagen grande y datos estadísticos.
- Sección de Comentarios: Lista de comentarios que incluya: Autor, texto del comentario y sistema de valoración visual (0 a 5 estrellas).
- Crear Noticias: Formulario con campos: Encabezado, Resumen, Descripción larga, Autor, Fecha (selector),Jugador relacionado y Tags (etiquetas).
- Ver Noticias: Layout de lectura con jerarquía clara entre encabezado y cuerpo de la noticia, mostrando tags y jugador asociado.
- Vista Equipo Ideal:
- Representación visual de un campo de fútbol o lista táctica.
- Interfaz para seleccionar y posicionar jugadores existentes en el once inicial.
- Sección Perfil: Tarjeta de usuario con foto circular, visualización de correo y opción de cambiar contraseña.
- Sección Ajustes de App:
- Switch/Toggle para cambio de Idioma Español/Ingles.
- Switch/Toggle para Modo Claro / Modo Oscuro.
Para garantizar la escalabilidad y permitir que la aplicación conmute entre diferentes orígenes de datos (Backend en Node.js o Spring Boot), se ha implementado una arquitectura desacoplada basada en patrones de diseño clásicos y reactividad moderna.
El flujo de datos sigue una jerarquía estricta para asegurar el principio de responsabilidad única:
- Vistas (Pages/Components): Consumen el servicio a través de una clase abstracta. No conocen si los datos vienen de Node o Spring.
- ConfigService: Es la "fuente de verdad". Gestiona la preferencia del usuario mediante Signals de Angular y persiste la elección en
localStorage. - Factories: Funciones puras encargadas de la instanciación. Deciden qué implementación crear basándose en el estado del
ConfigService. - Clase Abstracta (Contrato): Define la estructura de los métodos (CRUD) y centraliza la lógica común, utilizando la función
inject()para la gestión de dependencias. - Implementaciones (Estrategias): Clases específicas que heredan de la abstracta y definen detalles particulares como la
apiUrl.
El proceso de cambio de backend se realiza de forma atómica para mantener la integridad de los datos:
- Selección: El usuario elige el backend en el
BackendToggleComponent(dentro de Login o Registro). - Confirmación: Al pulsar el botón de acción (Login/Register), se invoca al
ConfigService. - Persistencia y Reset: Si el backend ha cambiado, el servicio guarda la nueva configuración y dispara un
window.location.reload(). - Inyección Dinámica: Al reiniciar la app, el motor de Inyección de Dependencias de Angular ejecuta la Factory en el
main.ts, la cual provee la implementación correcta a toda la aplicación.
El sistema de autenticación implementa un flujo completo que utiliza Firebase Authentication como proveedor de identidad común, combinado con middleware específico en Node.js y Spring Boot para sincronizar usuarios en bases de datos locales. Este diseño garantiza seguridad, escalabilidad y la capacidad de cambiar entre backends transparentemente.
┌──────────────────────────────────────────────────────────────┐
│ CLIENTE (Ionic/Angular) │
│ - LoginPage / SignUpPage (formularios de auth) │
│ - AuthService (abstracta, patrón Strategy) │
│ - AuthInterceptor (adjunta JWT a peticiones HTTP) │
└─────────────────────────────────┬──────────────────────────────┘
│
┌─────────────▼──────────────┐
│ Firebase Authentication │
│ (Valida email/password) │
│ Genera JWT (ID Token) │
└─────────────┬──────────────┘
│
┌────────────────────────┴────────────────────────┐
│ │
┌────▼─────────────┐ ┌────────▼────────┐
│ NODE.JS API │ │ SPRING BOOT MS │
├────────────────┤ ├─────────────────┤
│ Middleware: │ │ Spring Security:│
│ - Valida JWT │ │ - Valida JWT │
│ - Crea usuario │ │ - Extrae claims │
│ automático │ │ │
├────────────────┤ ├─────────────────┤
│ MongoDB │ │ PostgreSQL │
│ (Users) │ │ (Users) │
└────────────────┘ └─────────────────┘
Características clave:
- Firebase como proveedor: Autentica credenciales y genera JWT
- Sincronización JIT (Just-In-Time): Node.js crea usuarios automáticamente en primer acceso
- Vinculación por UID: El Firebase UID es la clave primaria de referencia en ambas BDs
- Interceptor HTTP: Adjunta JWT a todas las peticiones automáticamente
- CORS configurado: Permite peticiones desde cliente Ionic
1. Usuario completa formulario:
├─ userName (nombre de usuario)
├─ email
├─ password (validado: 8 chars, 1 número, 1 símbolo)
└─ confirmPassword (debe coincidir)
2. SignUpPage.onRegister() inicia:
├─ Bloquea centinela temporalmente: localStorage['bypass_centinela'] = 'true'
└─ Evita interferencias mientras se registra
3. AuthService.registerFirebase(email, password):
├─ Firebase.createUserWithEmailAndPassword()
├─ Valida credenciales
├─ ✅ Si exitoso: Crea usuario en Firebase, genera JWT
└─ ❌ Si falla: Lanza excepción (email duplicado, etc.)
4. AuthService.registerBackend({ userName }):
├─ POST /api/user/sync (Node) o /userms/api/auth/sync-user (Spring)
├─ AuthInterceptor agrega: Authorization: Bearer {JWT}
│
├─── EN NODE.JS ───────────────────────────────────────┐
│ a) Middleware authorizeRequest: │
│ ├─ Decodifica JWT (Firebase Admin SDK) │
│ ├─ Extrae: firebaseUid, email, name │
│ ├─ Busca usuario en MongoDB │
│ ├─ SI NO EXISTE → Crea automáticamente │
│ │ Campos: firebaseUid, email, userName, │
│ │ role, is_active, blocked │
│ └─ SI EXISTE → Usa documento existente │
│ b) syncUser controller: │
│ ├─ Actualiza userName si se proporciona │
│ ├─ FindByIdAndUpdate en MongoDB │
│ └─ Devuelve 200 OK + usuario actualizado │
│ │
├─── EN SPRING BOOT ────────────────────────────────────┤
│ a) Spring Security valida JWT: │
│ ├─ Decodifica contra clave pública Firebase │
│ ├─ Verifica expiración │
│ ├─ Crea @AuthenticationPrincipal Jwt │
│ └─ Extrae claims (sub=firebaseUid, email) │
│ b) AuthController.syncUserToPostgres(): │
│ ├─ Busca usuario en PostgreSQL │
│ ├─ SI NO EXISTE → Crea con JPA: │
│ │ Campos: firebaseUid, email, userName, role │
│ │ save() a base de datos │
│ ├─ SI EXISTE → Devuelve 400 (ya sincronizado) │
│ └─ Devuelve 200 OK + mensaje │
└────────────────────────────────────────────────────┘
5. Si TODO es exitoso (200 OK en backend):
├─ Limpia localStorage:
│ ├─ pending_sync_data
│ ├─ execute_sync_on_reload
│ └─ bypass_centinela
├─ showToast('Registration successful!')
└─ navCtrl.navigateRoot('/tabs/players') ✅
6. Si FALLA en backend (error en sync):
├─ authService.deleteCurrentUser() → ROLLBACK Firebase
├─ Limpia localStorage
├─ showToast('Registration failed, please try again.')
└─ navCtrl.navigateRoot('/sign-up') (vuelve al formulario)
Importancia del Rollback: Garantiza que si el backend falla, el usuario NO queda registrado en Firebase. Integridad bidireccional.
1. Usuario introduce:
├─ email
└─ password
2. LoginPage.onLogin() inicia:
├─ Verifica si cambió backend desde localStorage
└─ Si cambió → window.location.reload() (el AppComponent termina el trabajo)
3. AuthService.loginFirebase(email, password):
├─ Firebase.signInWithEmailAndPassword()
├─ ✅ Exitoso: Genera JWT para la sesión
└─ ❌ Falla: Credenciales inválidas (excepción)
4. AuthService.verifyBackend():
├─ GET /api/user/profile (Node) o /userms/api/auth/me (Spring)
├─ AuthInterceptor agrega: Authorization: Bearer {JWT}
│
├─── EN NODE.JS ───────────────────────────────────────┐
│ a) Middleware authorizeRequestNoCreate: │
│ ├─ Decodifica JWT (Firebase Admin SDK) │
│ ├─ Extrae firebaseUid │
│ ├─ Busca usuario en MongoDB │
│ ├─ SI NO EXISTE → 401 Unauthorized (logout) │
│ ├─ SI EXISTE PERO bloqueado/inactivo → 401 │
│ └─ SI EXISTE Y activo → req.user = documento │
│ b) Retorna perfil del usuario (200 OK) │
│ │
├─── EN SPRING BOOT ────────────────────────────────────┤
│ a) Spring Security valida JWT │
│ b) AuthController.getMyProfile(): │
│ ├─ Extrae firebaseUid del JWT │
│ ├─ findByFirebaseUid(firebaseUid) │
│ ├─ SI EXISTE → 200 OK + User entity │
│ └─ SI NO EXISTE → 404 Not Found (logout) │
└────────────────────────────────────────────────────┘
5. Si verifyBackend() devuelve 200:
├─ showToast('Login successful!', 'success')
└─ navCtrl.navigateRoot('/tabs/players') ✅
6. Si verifyBackend() falla (401/404):
├─ authService.logout() → Cierra sesión Firebase
├─ Limpia localStorage
├─ showToast('Login failed. Please check your credentials.')
└─ navCtrl.navigateRoot('/login') (vuelve al formulario)
Diferencia clave: En login, el backend NO crea usuarios. Solo valida que existan. Esto previene que usuarios no registrados accedan.
Usado en: POST /api/user/sync (endpoint de sincronización)
// Flujo paso a paso:
1. Extrae token de header Authorization: Bearer {JWT}
2. Valida con Firebase Admin SDK:
├─ admin.auth().verifyIdToken(token)
├─ Decodifica JWT
├─ Verifica firma (clave pública Firebase)
└─ Obtiene decoded: { uid, email, name, ... }
3. Busca usuario en MongoDB:
├─ User.findOne({ firebaseUid: uid, is_active: true, blocked: false })
│
├─ SI EXISTE:
│ └─ req.user = documento (continúa)
│
└─ SI NO EXISTE (Primer registro):
├─ Crea nuevo documento:
│ {
│ firebaseUid: uid,
│ email: decoded.email,
│ userName: req.body.userName || null,
│ role: 'usuario',
│ is_active: true,
│ blocked: false,
│ createdAt: Date.now()
│ }
├─ Guarda en MongoDB
└─ req.user = documento creado (continúa)
4. next() → Continúa a controladorVentaja: Just-In-Time Provisioning. El usuario se crea automáticamente en primer acceso.
Usado en: GET /api/user/profile (obtener perfil)
1. Mismos pasos 1-2 (extrae y valida JWT)
2. Busca usuario en MongoDB (sin crear):
├─ SI EXISTE Y está activo:
│ ├─ req.user = documento
│ └─ next() (continúa)
│
└─ SI NO EXISTE O está inactivo/bloqueado:
├─ res.status(401).json({ error: 'No autorizado' })
└─ Detiene ejecución (no llama next()){
firebaseUid: String (unique, required) ← Clave de vinculación con Firebase
email: String (unique, required)
userName: String (opcional)
role: String (default: 'usuario')
is_active: Boolean (default: true)
blocked: Boolean (default: false)
timestamps: true ← createdAt, updatedAt automáticos
}POST /api/user/sync
├─ Middleware: authorizeRequest (CREA si no existe)
├─ Controller: syncUser
└─ Actualiza userName si se proporciona en body
GET /api/user/profile
├─ Middleware: authorizeRequestNoCreate (NO crea)
├─ Retorna: req.user directamente
└─ 401 si usuario no existe o está inactivoCors diseñado apra permitir solo conexiones específicas
Petición HTTP llega al gateway/microservicio:
│
├─ Authorization: Bearer {JWT}
│
▼
Spring Security Filter Chain:
│
├─ 1. CORS Filter: Valida origen (si es preflight, responde 200)
│
├─ 2. OAuth2 Resource Server Filter:
│ ├─ Extrae JWT del header
│ ├─ BearerTokenAuthenticationFilter decodifica
│ │
│ ├─ JwtDecoder valida firma:
│ │ ├─ Obtiene clave pública desde:
│ │ │ issuer-uri: https://securetoken.google.com/draftkings-a26c3
│ │ ├─ Verifica firma HMAC/RSA
│ │ ├─ Valida expiración (exp claim)
│ │ └─ Valida audience (aud claim, si aplica)
│ │
│ ├─ Si VÁLIDO:
│ │ ├─ Crea Jwt object (decoded JWT)
│ │ ├─ JwtAuthenticationConverter extrae claims:
│ │ │ ├─ sub (Firebase UID) → nombre principal
│ │ │ ├─ email
│ │ │ ├─ name
│ │ │ └─ otros claims custom
│ │ ├─ Crea Authentication con roles/authorities
│ │ └─ SecurityContext.setAuthentication(auth)
│ │
│ └─ Si INVÁLIDO:
│ ├─ JwtException (token expirado, firma inválida, etc.)
│ └─ 401 Unauthorized
│
├─ 3. AuthorizationFilter:
│ ├─ Verifica si ruta requiere autenticación
│ ├─ SI REQUIERE y no hay Authentication → 403 Forbidden
│ └─ SI REQUIERE y hay Authentication → Continúa
│
▼
Controlador recibe @AuthenticationPrincipal Jwt jwt
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id; // PK auto-incremental
@Column(unique = true, nullable = false)
private String firebaseUid; // ← Clave de vinculación con Firebase
@Column(nullable = false)
private String email;
private String userName;
private String role;
// Getters y setters...
}spring:
mvc:
cors:
configs:
'[/**]':
allowed-origins: "https://draftkings.cnsa-2026-dsa069.tech"
allowed-methods: "*"
allowed-headers: "*"
allow-credentials: true
security:
oauth2:
resourceserver:
jwt:
# Firebase proporciona la clave pública en este endpoint
issuer-uri: https://securetoken.google.com/draftkings-a26c3La clave del sistema es usar Firebase UID como identificador único en ambas bases de datos:
User {
firebaseUid: "abc123xyz..." (unique, required) ← Mismo UID de Firebase
email: "user@example.com"
userName: "juan_perez"
role: "usuario"
}User {
id: 1 (PK, auto-incremental)
firebaseUid: "abc123xyz..." (unique, required) ← Mismo UID de Firebase
email: "user@example.com"
userName: "juan_perez"
role: "ROLE_USER"
}Ventajas:
- ✅ Mismo usuario en ambas BDs sin duplicados
- ✅ Fácil migrar de un backend a otro
- ✅ Firebase UID es inmutable (no cambia nunca)
- ✅ Permite auditoría y rastreo de usuarios
Comportamiento:
- ✅ Obtiene token de Firebase (en SessionStorage/Memory)
- ✅ Si existe: Clona request y añade
Authorization: Bearer {JWT} - ✅ Si no existe: Envía request sin header
- ✅ Automático para TODAS las peticiones HTTP
| Aspecto | Node.js | Spring Boot |
|---|---|---|
| Validación JWT | Firebase Admin SDK | Spring Security oauth2ResourceServer |
| Creación de Usuario | Automática (JIT Provisioning) | Explícita (POST /sync-user) |
| Buscar Usuario | MongoDB findOne() |
JPA findByFirebaseUid() |
| Base de Datos | MongoDB | PostgreSQL |
| Endpoint Sync | POST /api/user/sync | POST /userms/api/auth/sync-user |
| Endpoint Verificación | GET /api/user/profile | GET /userms/api/auth/me |
| Si usuario NO existe en Login | 401 (authorizeRequestNoCreate) | 404 (Not Found) |
| Bloqueo de usuarios | Campo blocked + is_active |
Solo campos de Entity |
El AppComponent implementa un Centinela (efecto de Angular Signals) que:
- Monitorea autenticación: Vigila si
authService.isAuthenticated()cambia - Sincroniza en recargas: Si el usuario hace F5 o cierra/abre app
- Detecta cambios de backend: Si el usuario cambió backend en login/signup
- Ejecuta rollback: Si algo falla en la sincronización post-reload
Ver app.component.ts para detalles.
┌─────────────────────────────────────────────────────────────┐
│ USUARIO NUEVO │
│ (FLUJO DE REGISTRO) │
└─────────────────────────────────────────────────────────────┘
1. SignUpPage.onRegister()
└─ Recolecta: userName, email, password, confirmPassword
2. AuthService.registerFirebase(email, password)
└─ Firebase autentica → genera JWT → crea usuario Firebase
3. AuthService.registerBackend({ userName })
├─ POST a backend (Node o Spring) con JWT
│
├─ NODE.JS:
│ ├─ Middleware authorizeRequest valida JWT
│ ├─ Busca usuario → NO EXISTE → CREA en MongoDB
│ └─ syncUser actualiza userName → 200 OK
│
└─ SPRING BOOT:
├─ Spring Security valida JWT
├─ Busca usuario en PostgreSQL → NO EXISTE → CREA
└─ Devuelve 200 OK
4. Cliente recibe 200 OK
├─ Limpia localStorage
└─ Navega a /tabs/players ✅
┌─────────────────────────────────────────────────────────────┐
│ USUARIO EXISTENTE │
│ (FLUJO DE LOGIN) │
└─────────────────────────────────────────────────────────────┘
1. LoginPage.onLogin()
└─ Recolecta: email, password
2. AuthService.loginFirebase(email, password)
└─ Firebase autentica → genera JWT
3. AuthService.verifyBackend()
├─ GET a /api/user/profile (Node) o /userms/api/auth/me (Spring)
│
├─ NODE.JS:
│ ├─ Middleware authorizeRequestNoCreate valida JWT
│ ├─ Busca usuario → SI EXISTE → req.user
│ └─ Retorna perfil → 200 OK
│
└─ SPRING BOOT:
├─ Spring Security valida JWT
├─ Busca usuario en PostgreSQL
├─ SI EXISTE → 200 OK + perfil
└─ SI NO EXISTE → 404 Not Found
4. Cliente recibe respuesta
├─ Si 200: showToast('Login successful!')
└─ Navega a /tabs/players ✅
La app Android incluye configuraciones nativas personalizadas que se aplican durante el build:
- Branding Personalizado: Iconos y splash screens con el logotipo de DraftKings (corona + balón)
- Recursos Nativos Personalizados: Colores, estilos y layouts específicos en
android.custom/ - Firma Digital: APK firmado con keystore para producción (release build)
- Capacitor Integration: Conversión de aplicación web (Angular/Ionic) a aplicación nativa Android
- Manifest Personalizado: Configuración de permisos, actividades y proveedores en
AndroidManifest.xml
El build de la APK se realiza de forma automatizada en CI/CD (GitHub Actions):
# 1. Instalar dependencias
npm ci --legacy-peer-deps
# 2. Generar variables de entorno
npm run config
# 3. Compilar assets web (Ionic)
npx ionic build --prod
# 4. Agregar plataforma Android
npx cap add android
# 5. Generar iconos y splash screens nativos
npx capacitor-assets generate --android
# 6. Aplicar recursos personalizados
cp -r android.custom/* android/
# 7. Sincronizar Capacitor
npx cap sync android
# 8. Firmar APK (con keystore en base64)
echo "$KEYSTORE_BASE64" | base64 --decode > ./android/release-key.jks
# 9. Compilar APK en modo Release
cd android && ./gradlew assembleRelease
# 10. Verificar firma digital
apksigner verify --print-certs DraftKings.apkUbicación del APK generado: android/app/build/outputs/apk/release/DraftKings.apk
android.custom/
├── app/src/main/
│ ├── AndroidManifest.xml (Configuración nativa)
│ └── res/
│ ├── values/ (Colores, strings, estilos)
│ └── values-night/ (Tema oscuro)
Mencionar Dev Container para cada sección
- Node.js y npm
- Java JDK 11+
- Ionic CLI
- [Añadir cualquier dependencia de CORBA necesaria]
- Clonar el repositorio:
- Estilo: Moderno, deportivo, con alto contraste. Limpio y funcional.
- Navegación Principal: Implementar un Bottom Tab Bar (Barra de navegación inferior) presente en todas las vistas (excepto Login y Registro).
- Orden del Tab Bar (Izquierda a Derecha):
- 🏆 Equipo Ideal
- ⚽ Jugadores
- ⚙️ Ajustes