🌳 Estructura de Directorios en Disco
nodeapp/
├── server.js # Inicializador HTTP, Express, WebSockets y receptor UDP (:5005)
├── package.json # Dependencias y scripts de ejecución (start, dev, build:frontend)
├── .env / .env.example # Variables de entorno (DB_DIALECT, puertos, JWT_SECRET)
├── ARCHITECTURE_MAP.md # Mapa de arquitectura en formato Markdown
├── README.md # Resumen ejecutivo de instalación rápida
│
├── config/
│ └── database.js # Abstracción agnóstica de base de datos (SQLite / MySQL)
│
├── data/
│ └── eresleyenda.db # Archivo de base de datos local SQLite
│
├── db/
│ ├── schema.sql # Esquema DDL con 14 tablas relacionales e índices
│ └── seeds.js # Semillas de demostración, ruta Guatapé y credenciales RBAC
│
├── middleware/
│ ├── authMiddleware.js # Validación JWT, requireRole(['staff','admin',...]) y API Keys
│ └── tenantMiddleware.js # Detección y aislamiento de Marcas Blancas por slug/dominio
│
├── services/
│ ├── telemetryService.js # Normalización GPS, geocercas, buffer en memoria y Socket.io
│ ├── timingEngine.js # Reglas de Half Triatlón, distancias Haversine y cálculo de Pace
│ ├── gpxParserService.js # Parser XML GPX/TCX y detector de atajos / cortes de ruta
│ ├── gpsSummaryService.js # Ingesta dual CSV/XML de resúmenes de tiempos y splits oficiales
│ ├── simulatorService.js # Generador de estrés concurrente (10 a 300 atletas simultáneos)
│ └── wpSyncService.js # Mapeo de campos de WooCommerce e ingesta de inscripciones
│
├── routes/api/
│ ├── auth.js # Registro, Login, perfil /me y edición de usuario
│ ├── athlete.js # Historial de carreras, splits y visor del track individual
│ ├── athletes.js # CRUD administrativo del padrón de atletas
│ ├── staff.js # Búsqueda en carpa, entrega de kits, retorno de GPS e incidentes
│ ├── tickets.js # Sistema PQRS / Soporte con hilos de mensajería
│ ├── admin.js # Gestión de personal de staff y alta de eventos
│ ├── events.js # Listado de eventos y cambio de estados (scheduled/live/finished)
│ ├── tenants.js # CRUD de inquilinos, cuotas y estilos de marca blanca
│ ├── telemetry.js # Ingesta HTTP de coordenadas GPS y estadísticas
│ ├── gpx.js # Subida de tracks GPX/TCX y auditoría de atajos
│ ├── leaderboard.js # Tabla de posiciones y tiempos oficiales por disciplina
│ ├── simulator.js # Controladores de inicio/parada de la simulación masiva
│ ├── wpSync.js # Webhooks e importación masiva de WordPress
│ └── certificate.js # Extracción de datos para diploma oficial de finalista
│
├── frontend/ # SPA moderna en Vue 3 + Pinia + Vite
│ ├── index.html # Template base de Vite
│ ├── package.json # Dependencias del frontend
│ ├── vite.config.js # Configuración de compilación con base '/app/'
│ └── src/
│ ├── main.js # Bootstrap de Vue 3, Pinia y Router
│ ├── App.vue # Componente raíz con Layout dinámico
│ ├── style.css # Estilos globales y paleta de diseño deportivo
│ ├── router/
│ │ └── index.js # Vue Router con Navigation Guards y roles RBAC
│ ├── stores/
│ │ ├── auth.js # Estado reactivo del usuario y roles
│ │ └── tenant.js # Estado de la Marca Blanca e identidad visual
│ ├── Layouts/
│ │ └── AdminLayout.vue # Layout con barra de navegación y menú RBAC
│ └── views/
│ ├── auth/ # LoginView.vue y RegisterView.vue
│ ├── athlete/ # Profile, Races, Performance, Track, Tickets
│ ├── staff/ # Checkin (kits/chips), Returns y Reporte de Incidentes
│ ├── admin/ # Gestión de Eventos, Staff, Atletas y PQRS
│ └── superadmin/ # SuperAdminView.vue (Gestor SaaS de Marcas Blancas)
│
├── public/ # Archivos estáticos servidos directamente por Express
│ ├── app/ # Compilado de producción de la SPA Vue 3 (/app/*)
│ ├── index.html # Dashboard y hub central con arquitectura de menú lateral
│ ├── architecture_map.html # Este mapa interactivo de arquitectura
│ ├── live.html # Visor cartográfico en tiempo real con Leaflet
│ ├── leaderboard.html # Tabla de posiciones y tiempos en vivo con menú lateral
│ ├── certificate.html # Diploma digital de finalista para impresión/descarga
│ ├── admin.html # Consola de dirección de carrera con menú lateral y tabs reactivos
│ ├── superadmin.html # Consola SaaS con menú lateral y simulador de 300 atletas
│ ├── css/ # Estilos (main.css, admin.css, live.css, sidebar-layout.css)
│ └── js/ # Scripts (live-tracker.js, leaderboard.js, admin.js, sidebar.js)
│
├── docker/
│ ├── Dockerfile # Contenedor Node.js optimizado
│ └── docker-compose.yml # Orquestación con contenedor MySQL y Node
│
└── uploads/ # Directorio de subida temporal de archivos GPX/TCX
⚙️ Servidor Central (server.js)
Punto de convergencia en tiempo real donde conviven 3 protocolos de transporte:
- Servidor HTTP Express: Expone la API REST en
/api/*y sirve la SPA Vue 3 en/app/*. - Servidor Socket.io: Canal bidireccional que transmite eventos a clientes suscritos por sala de carrera (
event_{id}). - Receptor UDP en Puerto 5005: Escucha datagramas directos de chips GPS 4G con decodificación de tramas JSON y CSV (
IMEI,lat,lon,speed,bat). - Bootstrap Secuencial: Ejecuta
db.initSchema()seguido deseedDatabase()asegurando que la base de datos esté lista antes de aceptar tráfico.
🗄️ Capa de Base de Datos Relacional
config/database.js abstrae las operaciones con métodos unificados con Promesas: all(), get(), run() y exec(). Actualmente configurado y conectado a MariaDB (DBngin) en 127.0.0.1:3306 con la base de datos legend_tracker (usuario root), con SQLite disponible como respaldo transparente si DB_DIALECT=sqlite.
Marcas blancas con slug, dominio, cuota de atletas y paleta de colores personalizada.
Eventos deportivos con modalidad (Half Triatlón, Aguas Abiertas, etc.), ruta GPX y disciplinas.
Puntos de control con latitud, longitud y radio en metros para registrar splits automáticos.
Usuarios con roles RBAC (athlete, judge, staff, admin, superadmin), asignación de jueces por carrera específica (judge_assignments), equipos de relevos (teams) y atletas con avance por punto de control.
Registro de entrega de kit, notas de entrega (delivery_notes), serial GPS asignado, devolución y notas de recepción (return_notes).
Sistema de tickets y mensajes bidireccionales de soporte entre competidores, staff y dirección.
🛡️ Middlewares y Control de Acceso
Resuelve el inquilino correspondiente inspeccionando en orden:
- 1. Parámetro
?tenant=slug - 2. Headers
x-tenant-slugox-tenant-id - 3. Subdominio del Host HTTP
- 4. Fallback al inquilino maestro
eres-leyenda
Seguridad por capas con JSON Web Tokens:
authenticateToken: Valida cabecera Bearer y actividad del usuario.requireRole(['staff','admin',...]): Protección RBAC con bypass para superadmin.requireTenant: Impide el acceso cruzado entre distintas marcas blancas.
🚀 Servicios y Motores de Negocio
Cerebro de la ingesta en vivo. Normaliza datos de proveedores GPS, comprueba geocercas geodésicas, actualiza al atleta y emite por WebSockets.
Motor de reglas deportivas. Calcula ritmo en min/100m (nado), km/h (bici) o min/km (carrera), distancias Haversine y categorías etarias.
Procesador asíncrono de tracks XML. Extrae métricas de desnivel y ejecuta auditoría de paso obligatorio por checkpoints para detectar recortes de ruta.
Generador de estrés para pruebas de carga. Simula hasta 300 competidores emitiendo paquetes concurrentes a velocidad configurable.
Parser dual CSV/XML de hojas de tiempos del proveedor (Final.csv/Final.xml). Concilia dorsales, genera splits oficiales y actualiza tiempos de llegada.
Integración con WooCommerce y el plugin carreras_checkout_by_sgl. Mapea metadatos sanitizados y asigna dorsales en tiempo real.
🌐 Catálogo de Endpoints de la API REST
| Método | Endpoint URL | Rol Mínimo | Módulo | Propósito / Descripción |
|---|---|---|---|---|
| POST | /api/auth/login | Público | auth.js |
Autentica por correo o documento y entrega Token JWT. |
| POST | /api/auth/register | Público | auth.js |
Registro de nuevos competidores. |
| GET | /api/auth/me | Autenticado | auth.js |
Devuelve usuario en sesión y su ficha atlética vinculada. |
| PUT | /api/auth/profile | Autenticado | auth.js |
Actualiza datos de contacto, EPS, foto y ficha médica. |
| POST | /api/auth/avatar | Autenticado | auth.js |
Sube y actualiza la foto de perfil (avatar_url) del atleta o usuario. |
| DELETE | /api/auth/avatar | Autenticado | auth.js |
Elimina la foto de perfil actual y restaura el avatar por iniciales. |
| GET | /api/athlete/races | athlete | athlete.js |
Historial de carreras inscritas con estado de kit y chip. |
| GET | /api/athlete/performance/:eventId | athlete | athlete.js |
Tiempos parciales por split, ritmo, posición en podio e incidentes. |
| GET | /api/athlete/track/:eventId | athlete | athlete.js |
Ruta oficial del evento y puntos de telemetría personal en mapa. |
| GET | /api/judge/my-events | judge | judge.js |
Retorna únicamente las carreras específicas asignadas al juez autenticado con sus checkpoints. |
| GET | /api/judge/events/:eventId/athletes | judge | judge.js |
Lista atletas de la carrera asignada al juez con su avance actual y splits registrados. |
| POST | /api/judge/events/:eventId/progress | judge | judge.js |
Registra el avance del atleta (punto de control, segmento, km, tiempo parcial/acumulado, llegada a meta o DNF/DQ). |
| POST | /api/admin/judges/assign | admin | admin.js |
Asigna un juez de carrera a una competencia específica (event_id) y punto de control opcional. |
| DELETE | /api/admin/judges/assign/:assignmentId | admin | admin.js |
Retira la asignación de un juez a una carrera específica. |
| GET | /api/staff/athletes | staff | staff.js |
Búsqueda en carpa para entrega y devolución de kits. |
| POST | /api/staff/kit-delivery | staff | staff.js |
Registra entrega de kit, notas de entrega (delivery_notes) y asocia serial GPS en vivo. |
| POST | /api/staff/kit-return | staff | staff.js |
Registra recepción del chip GPS en meta con notas de devolución (return_notes). |
| POST | /api/staff/manual-split | staff | staff.js |
Registro manual de tiempos y avances por jueces/staff en checkpoints con notas. |
| POST | /api/staff/incidents | staff | staff.js |
Reporte de emergencias médicas, mecánicas, penalización o DNF. |
| GET | /api/staff/incidents | staff | staff.js |
Lista de incidentes reportados en la carrera. |
| GET | /api/tickets | Autenticado | tickets.js |
Listado de solicitudes PQRS del usuario o del inquilino. |
| POST | /api/tickets | Autenticado | tickets.js |
Apertura de nuevo ticket (Petición, Queja, Reclamo, Soporte). |
| GET | /api/tickets/:id | Autenticado | tickets.js |
Detalle de ticket y trazabilidad completa de mensajes. |
| POST | /api/tickets/:id/messages | Autenticado | tickets.js |
Envía una respuesta dentro del hilo de mensajería. |
| PATCH | /api/tickets/:id/status | staff | tickets.js |
Cambia el estado de un ticket (en progreso, resuelto, cerrado). |
| GET | /api/admin/staff | admin | admin.js |
Lista todo el personal de logística del inquilino. |
| POST | /api/admin/staff | admin | admin.js |
Da de alta un nuevo miembro de staff con contraseña bcrypt. |
| PATCH | /api/admin/staff/:id/toggle | admin | admin.js |
Habilita o inhabilita credenciales de un miembro de staff. |
| POST | /api/admin/events | admin | admin.js |
Crea un nuevo evento con segmentos y checkpoints predefinidos. |
| GET | /api/events | Público | events.js |
Catálogo de eventos activos de la marca blanca. |
| PUT | /api/events/:id/status | admin | events.js |
Modifica estado del evento (draft, scheduled, live, finished). |
| GET | /api/athletes | admin | athletes.js |
Padrón completo de competidores inscritos con filtros. |
| POST | /api/telemetry/ingest | API Key | telemetry.js |
Ingesta de paquete GPS y retransmisión por WebSockets. |
| GET | /api/telemetry/stats | Público | telemetry.js |
Métricas de throughput en tiempo real (paquetes por segundo). |
| GET | /api/leaderboard/:eventId | Público | leaderboard.js |
Clasificaciones oficiales, ritmos por disciplina y gaps. |
| POST | /api/gpx/upload | Autenticado | gpx.js |
Carga y análisis asíncrono de tracks GPX con auditoría de atajos. |
| POST | /api/gpx/import-summary | admin | gpx.js |
Importa resumen de tiempos del proveedor (Final.csv o Final.xml) y genera splits oficiales. |
| POST | /api/gpx/athlete-track | athlete | gpx.js |
Carga track individual GPX del atleta, persiste archivo en disco y audita cortes de ruta. |
| GET | /api/certificate/:athleteId | Público | certificate.js |
Datos oficiales para renderizado del diploma de finalista. |
| POST | /api/simulator/start | admin | simulator.js |
Inicia simulación masiva (conteo de atletas y multiplicador de velocidad). |
| POST | /api/simulator/stop | admin | simulator.js |
Detiene inmediatamente el generador de telemetría simulada. |
| GET | /api/tenants/my-branding | admin | tenants.js |
Obtiene subdominio, logotipo, colores y métricas exclusivas de la empresa del Administrador. |
| PUT | /api/tenants/my-branding | admin | tenants.js |
Personaliza el subdominio único, logotipo y colores corporativos de la empresa activa. |
| POST | /api/tenants/my-branding/logo | admin | tenants.js |
Sube un archivo de logotipo (PNG/JPG/WEBP/SVG) para la empresa organizadora. |
| GET | /api/tenants | superadmin | tenants.js |
Exclusivo SuperAdmin: Lista todas las empresas del sistema SaaS (aislado de otros roles). |
| POST | /api/tenants | superadmin | tenants.js |
Exclusivo SuperAdmin: Crea una nueva empresa con subdominio, logo, colores y su Admin inicial. |
| POST | /api/sync/wordpress/webhook | API Key / Púb. | wpSync.js |
Webhook de compras de carreras_checkout_by_sgl y WooCommerce (individuales y relevos). |
| GET | /api/sync/wordpress/config | admin, superadmin | wpSync.js |
Consulta el estado de las credenciales API REST de WooCommerce definidas en .env. |
| POST | /api/sync/wordpress/pull | admin, superadmin | wpSync.js |
🔒 Importación desde API REST de WooCommerce usando WC_CONSUMER_KEY y WC_CONSUMER_SECRET de .env (con recarga en caliente de .env). |
💻 Frontend SPA Moderno (Vue 3 + Pinia + Vite)
Ubicado en frontend/ y servido en producción desde public/app/ bajo la ruta base /app/*.
Pinia Stores
stores/auth.js: Token JWT, usuario activo, cierre de sesión y helpers reactivos (isAthlete, isStaff, isAdmin, isSuperAdmin).
stores/tenant.js: Identidad visual, colores primarios/secundarios y logos del inquilino activo.
Navigation Guards & Menú RBAC Estricto
router/index.js & Layouts/AdminLayout.vue: Aislamiento estricto por rol en menú lateral, barra superior y rutas (ningún usuario visualiza enlaces ni accede a vistas que no correspondan a su rol: athlete, staff, admin, superadmin).
public/js/sidebar.js: Aplica directivas [data-require-role] sin parpadeo y redirige automáticamente si un visitante o usuario sin permisos intenta abrir admin.html, superadmin.html o architecture_map.html.
📡 Protocolos de Telemetría
1. Receptor UDP (Puerto 5005)
Admite tramas de dispositivos de telemetría física con formato CSV o JSON:
2. WebSockets (Socket.io)
Eventos emitidos hacia visores Leaflet y pantallas de meta:
telemetry:point: Posición, velocidad, batería y distancia actualizada.checkpoint:crossed: Disparado automáticamente cuando el competidor entra al radio de la geocerca.athlete:finished: Cruce de la meta oficial.
📘 Guía Práctica para Nuevos Desarrolladores
¿Cómo extender la aplicación de forma segura?
-
Agregar una nueva disciplina deportiva:
Editar
services/timingEngine.jsy registrar la fórmula de ritmo (pace) dentro deformatPace(). -
Crear un nuevo endpoint API:
Crear o modificar la ruta en
routes/api/y aplicar los middlewaresauthenticateTokenyrequireRole(['...']). -
Compilar el Frontend:
Tras modificar cualquier vista Vue en
frontend/src/views/, ejecutar desde la raíz de nodeapp:npm run build:frontend -
Regla de Oro de Documentación:
Siempre que añadas o modifiques rutas, tablas o servicios, actualiza tanto
ARCHITECTURE_MAP.mdcomo este archivopublic/architecture_map.html.