# 🗺️ Mapa de Arquitectura, Módulos y Referencia Técnica — "Eres Leyenda" (nodeapp)

Este documento sirve como la guía técnica definitiva para cualquier desarrollador que asuma el mantenimiento, escalabilidad o evolución de la plataforma **Eres Leyenda** (`nodeapp`).

---

## 📑 Tabla de Contenidos

1. [Visión General del Sistema](#1-visión-general-del-sistema)
2. [Estructura del Proyecto en Disco](#2-estructura-del-proyecto-en-disco)
3. [Flujo de Ejecución y Servidor Central (`server.js`)](#3-flujo-de-ejecución-y-servidor-central-serverjs)
4. [Capa de Datos y Configuración (`config/` y `db/`)](#4-capa-de-datos-y-configuración-config-y-db)
5. [Capa de Middlewares (`middleware/`)](#5-capa-de-middlewares-middleware)
6. [Capa de Servicios de Negocio (`services/`)](#6-capa-de-servicios-de-negocio-services)
7. [Catálogo Completo de Rutas y Endpoints API (`routes/api/`)](#7-catálogo-completo-de-rutas-y-endpoints-api-routesapi)
8. [Frontend SPA Moderno en Vue 3 (`frontend/`)](#8-frontend-spa-moderno-en-vue-3-frontend)
9. [Frontend Auxiliar y Vistas Públicas (`public/`)](#9-frontend-auxiliar-y-vistas-públicas-public)
10. [Protocolos de Telemetría (HTTP / UDP / WebSockets)](#10-protocolos-de-telemetría-http--udp--websockets)
11. [Guía Práctica para Nuevos Desarrolladores (Cómo Extender la Plataforma)](#11-guía-práctica-para-nuevos-desarrolladores-cómo-extender-la-plataforma)

---

## 1. Visión General del Sistema

**Eres Leyenda** es una plataforma SaaS multi-inquilino de cronometraje deportivo, telemetría GPS en vivo y gestión integral de competencias atléticas (Half Triatlón, Aguas Abiertas, Ciclismo, Atletismo y Relevos).

### Principales Capacidades
* **Multi-Tenancy (Marcas Blancas):** Permite aislar inquilinos con personalización de colores, logos, dominios y cuotas de atletas.
* **Control de Acceso Basado en Roles (RBAC):** 4 perfiles con permisos independientes (`athlete`, `staff`, `admin`, `superadmin`).
* **Ingesta Dual de Telemetría:**
  * **En Tiempo Real:** Conexión UDP (puerto 5005) para dispositivos GPS 4G físicos y Webhook HTTP (`/api/telemetry/ingest`), con emisión instantánea vía Socket.io.
  * **Asíncrona (Post-Carrera):** Carga y análisis automático de archivos `.gpx` y `.tcx`, con detección inteligente de cortes de ruta o atajos.
* **Logística de Pista (Staff):** Módulo para entrega de kits con escaneo/asignación de seriales GPS, recepción de chips en meta y reporte de incidentes/sanciones.
* **Sincronización con WordPress:** Recepción de inscripciones desde el plugin `carreras_checkout_by_sgl` de WooCommerce.
* **Base de Datos Agnóstica:** Capa de abstracción que funciona en **SQLite** (local sin configuración) o **MySQL/MariaDB** (producción/XAMPP) con traducción automática de dialectos (`INSERT IGNORE`, `REGEXP`, `CAST AS SIGNED`).

---

## 2. Estructura del Proyecto en Disco

```text
nodeapp/
├── server.js                      # Inicializador HTTP, Express, WebSockets y UDP
├── package.json                   # Dependencias y scripts de ejecución
├── .env / .env.example            # Variables de entorno
├── ARCHITECTURE_MAP.md            # Este mapa de arquitectura
├── README.md                      # Resumen ejecutivo de instalación rápida
│
├── config/
│   └── database.js                # Driver agnóstico de base de datos (SQLite / MySQL)
│
├── data/
│   └── eresleyenda.db             # Archivo de base de datos SQLite (si dialecto = sqlite)
│
├── db/
│   ├── schema.sql                 # Esquema DDL de 14 tablas relacionales con índices
│   └── seeds.js                   # Semillas de demostración, ruta Guatapé y credenciales RBAC
│
├── middleware/
│   ├── authMiddleware.js          # Autenticación JWT, verificación de roles (RBAC) y API Key
│   └── tenantMiddleware.js        # Detección y asignación del inquilino (Marca Blanca)
│
├── services/
│   ├── telemetryService.js        # Normalización GPS, geocercas, buffer en memoria y Socket.io
│   ├── timingEngine.js            # Reglas deportivas, ritmos (pace), distancias Haversine y categorías
│   ├── gpxParserService.js        # Parser XML de tracks GPX/TCX y detección de recortes de ruta
│   ├── gpsSummaryService.js       # Ingesta dual CSV/XML de resúmenes de tiempos del proveedor y splits
│   ├── simulatorService.js        # Generador de estrés concurrente (10 a 300 atletas)
│   └── 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
│   ├── leaderboard.js             # Tabla de posiciones y tiempos oficiales por disciplina
│   ├── simulator.js               # Controladores de inicio/parada de la simulación
│   ├── wpSync.js                  # Webhooks e importación masiva de WordPress
│   └── certificate.js             # Extracción de datos para diploma de finalista
│
├── frontend/                      # Aplicación 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 (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 RBAC
│       ├── stores/
│       │   ├── auth.js            # Estado reactivo del usuario autenticado y roles
│       │   └── tenant.js          # Estado y configuración visual del inquilino
│       ├── Layouts/
│       │   └── AdminLayout.vue    # Layout con barra de navegación, identidad y menú RBAC
│       ├── components/
│       │   └── Navbar.vue         # Barra superior
│       └── views/
│           ├── auth/ (LoginView.vue, RegisterView.vue)
│           ├── athlete/ (Profile, Races, Performance, Track, Tickets)
│           ├── staff/ (Checkin, Returns, Incidents)
│           ├── admin/ (Events, Staff, Athletes, Tickets)
│           └── superadmin/ (SuperAdminView.vue)
│
├── public/                        # Archivos estáticos servidos directamente por Express
│   ├── app/                       # Compilado de producción del frontend Vue (/app/*)
│   ├── index.html                 # Hub central de bienvenida y métricas con layout de menú lateral
│   ├── live.html & js/live-tracker.js    # Visor GPS en mapa Leaflet con Socket.io
│   ├── leaderboard.html & js/leaderboard.js # Tabla de posiciones en vivo con menú lateral
│   ├── certificate.html           # Diploma digital oficial para imprimir/descargar
│   ├── admin.html & js/admin.js   # Consola de dirección de carrera con menú lateral y tabs reactivos
│   ├── superadmin.html & js/superadmin.js # Consola SaaS con menú lateral y simulador 300 GPS
│   ├── css/                       # Estilos (main.css, admin.css, live.css, sidebar-layout.css)
│   ├── js/                        # Scripts compartidos (theme.js, sidebar.js)
│   └── logo-leyenda.png           # Logotipo institucional
│
├── 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
```

---

## 3. Flujo de Ejecución y Servidor Central (`server.js`)

`server.js` es el punto neurálgico donde convergen HTTP, WebSockets y UDP.

### Inicialización y Componentes
1. **Configuración de Entorno:** Carga `.env` mediante `dotenv`.
2. **Servidor HTTP & Socket.io:**
   * Crea la instancia `http.createServer(app)`.
   * Inicializa `Socket.io` permitiendo CORS universal.
   * Conecta Socket.io con el servicio de telemetría: `telemetryService.setSocketIO(io)`.
   * Maneja salas por evento: `socket.join('event_' + eventId)`.
3. **Middlewares Globales:**
   * `cors()`: Permite peticiones cross-origin de aplicaciones externas y WordPress.
   * `express.json({ limit: '25mb' })` y `urlencoded`: Permite payloads pesados (ej. sincronizaciones por lotes o polilíneas GPX).
   * `tenantMiddleware`: Inyecta `req.tenant` y `res.locals.tenant`.
4. **Enrutamiento Estático y SPA:**
   * `public/` servido como estático.
   * Rutas `/app/*` redirigen a `public/app/index.html` (para el enrutamiento client-side de Vue 3).
5. **Receptor UDP (Puerto 5005):**
   * Escucha datagramas `dgram.createSocket('udp4')`.
   * Procesa tanto JSON como cadenas delimitadas por comas (`IMEI,lat,lon,speed,bat`).
   * Pasa el paquete inmediatamente a `telemetryService.processTelemetry(payload)`.
6. **Bootstrap (`bootstrap()`):**
   * Ejecuta `db.initSchema()` para crear o migrar tablas.
   * Ejecuta `seedDatabase()` para sembrar registros si la base está limpia.
   * Levanta el puerto (por defecto `3000`).

---

## 4. Capa de Datos y Configuración (`config/` y `db/`)

### `config/database.js`
Ofrece una interfaz unificada basada en Promesas (`async/await`) tanto para SQLite como para MySQL:

* `all(sql, params)`: Devuelve un array de filas coincidentes.
* `get(sql, params)`: Devuelve una sola fila (o `null`).
* `run(sql, params)`: Para sentencias `INSERT`, `UPDATE`, `DELETE`. Retorna `{ lastID, changes }` con compatibilidad automática `INSERT IGNORE` en MySQL/MariaDB.
* `exec(sqlScript)`: Ejecuta bloques DDL completos (separados por `;`) manejando índices duplicados de forma tolerante.
* `initSchema()`: Aplica `schema.sql` y verifica migraciones preventivas.
* **Base de datos activa por defecto:** MariaDB/MySQL en `127.0.0.1:3306`, base de datos `legend_tracker`, usuario `root` sin contraseña. SQLite se mantiene como opción de contingencia automática mediante la variable `DB_DIALECT=sqlite`.

### `db/schema.sql` (14 Tablas Relacionales)

| # | Tabla | Propósito y Llaves Principales |
|---|---|---|
| 1 | `tenants` | Inquilinos / Marcas Blancas (`id`, `slug`, `domain`, `colors`, `quota`, `api_key`). |
| 2 | `events` | Eventos deportivos (`id`, `tenant_id`, `sport_type`, `gpx_route_json`, `status`). |
| 3 | `event_segments` | Disciplinas por carrera (`id`, `event_id`, `name`, `distance_km`, `pace_unit`). |
| 4 | `checkpoints` | Puntos de control y geocercas (`id`, `event_id`, `lat`, `lon`, `radius_meters`). |
| 5 | `categories` | Categorías por género y edad (`id`, `event_id`, `name`, `gender`, `is_team`). |
| 6 | `teams` | Equipos para modalidad de relevos (`id`, `event_id`, `bib_number`, `chip_code`). |
| 7 | `users` | Usuarios con RBAC (`id`, `tenant_id`, `role`, `email`, `doc_number`, `avatar_url`, `password_hash`). |
| 8 | `athletes` | Padrón central de atletas sincronizados con WordPress (`id`, `bib_number`, `chip_code`, `avatar_url`, `medical_data`, `user_id`, `wp_order_id`). |
| 9 | `split_times` | Tiempos parciales por checkpoint (`id`, `athlete_id`, `checkpoint_id`, `split_time_seconds`, `pace_formatted`). |
| 10 | `telemetry_logs` | Histórico y buffer de coordenadas GPS emitidas (`id`, `athlete_id`, `lat`, `lon`, `speed_kmh`, `battery`). |
| 11 | `gpx_uploads` | Auditoría de tracks GPX cargados y estado de corte de ruta (`id`, `athlete_id`, `route_cut_detected`). |
| 12 | `kit_deliveries` | Logística en carpa: entrega de kits, notas de entrega (`delivery_notes`), asignación de GPS (`gps_serial_assigned`), recepción en meta y notas de devolución (`return_notes`). |
| 13 | `race_incidents` | Incidentes de pista (`id`, `athlete_id`, `staff_user_id`, `incident_type`, `penalty_seconds`). |
| 14 | `support_tickets` & `ticket_messages` | Sistema de PQRS / Soporte técnico de atletas hacia el staff y administradores. |

### `db/seeds.js`
* Define el circuito oficial con waypoints geográficos en **Guatapé, Antioquia** (`GUATAPE_ROUTE`).
* Crea 2 inquilinos de prueba: `eres-leyenda` y `triatlon-calima`.
* Crea 3 eventos con diferentes modalidades deportivas.
* **Usuarios preconfigurados (Contraseñas de desarrollo):**
  * **Superadmin:** `superadmin@eresleyenda.com` / `superadmin123`
  * **Admin:** `admin@eresleyenda.com` / `admin123`
  * **Staff:** `staff@eresleyenda.com` / `staff123`
  * **Atleta:** `atleta@eresleyenda.com` / `atleta123` (Cédula: `1020456789`)

---

## 5. Capa de Middlewares (`middleware/`)

### `tenantMiddleware.js`
Identifica el inquilino correspondiente a cada solicitud entrante mediante el siguiente orden de precedencia:
1. Query param: `?tenant=slug`
2. Cabeceras HTTP: `x-tenant-slug` o `x-tenant-id`
3. Subdominio HTTP del host (ej. `calima.eresleyenda.com` -> `calima`)
4. Fallback por defecto al tenant principal `eres-leyenda`.

Adjunta el objeto resuelto en `req.tenant` y `res.locals.tenant`.

### `authMiddleware.js`
1. `authenticateToken(req, res, next)`:
   * Extrae el token Bearer de la cabecera `Authorization` o `x-auth-token`.
   * Verifica la firma JWT (`JWT_SECRET`).
   * Consulta al usuario en la base de datos para confirmar que sigue activo (`is_active = 1`).
   * Inyecta los datos del usuario en `req.user`.
2. `requireRole(roles)`:
   * Valida si `req.user.role` está en la lista de roles permitidos.
   * `superadmin` posee bypass universal en cualquier ruta protegida.
3. `requireTenant(req, res, next)`:
   * Garantiza que un usuario no pueda consultar datos de un inquilino ajeno a su `tenant_id` (excepto `superadmin`).
4. `apiKeyAuth(req, res, next)`:
   * Valida la clave `x-api-key` para integraciones entre servidores y plugins de WordPress.

---

## 6. Capa de Servicios de Negocio (`services/`)

### `telemetryService.js`
* **Buffer de Alta Concurrencia:** Acumula coordenadas en memoria y las vuelca a la base de datos en lotes cada 2.5 segundos mediante `setInterval`.
* **Throughput Monitor:** Mide paquetes por segundo recibidos.
* **`processTelemetry(rawPayload)`:**
  1. Normaliza el payload (`normalizePayload`) aceptando identificadores (`chip_code`, `imei`, `bib_number`).
  2. Localiza al atleta y recupera los checkpoints del evento.
  3. Comprueba proximidad geodésica (`timingEngine.calculateDistanceMeters`). Si entra en el radio (ej. 45 m) de un checkpoint no registrado, registra automáticamente el split en `split_times`.
  4. Si cruza la meta final, cambia el estado a `finished`.
  5. Actualiza la posición y telemetría del atleta en la tabla `athletes`.
  6. Emite evento por WebSockets vía `io.to('event_' + eventId).emit('telemetry:point', liveUpdate)`.

### `timingEngine.js`
* **`formatDuration(seconds)`:** Convierte segundos a formato estándar `HH:MM:SS` o `MM:SS`.
* **`formatPace(discipline, distanceKm, timeSeconds)`:**
  * Natación (`swim` / `open_water`): Minutos por cada 100 metros (`min/100m`).
  * Ciclismo (`bike`): Velocidad media en `km/h`.
  * Atletismo (`run`): Minutos por kilómetro (`min/km`).
* **`calculateAgeGroup(birthDateStr, eventYear)`:** Calcula la categoría etaria oficial (ej. `18-24`, `25-29`, `30-34`, etc.).
* **`calculateDistanceMeters(lat1, lon1, lat2, lon2)`:** Distancia geodésica mediante la fórmula de Haversine.
* **`getHalfTriathlonSegment(distanceKm)`:** Determina la disciplina activa según la distancia recorrida en un 70.3 (Swim 1.9k -> T1 200m -> Bike 90k -> T2 200m -> Run 21.1k -> Finish).

### `gpxParserService.js`
* **`parseGpxFile(filePath, officialCheckpoints)`:**
  * Parsea archivos `.gpx` y `.tcx` a estructuras JSON homogéneas.
  * Calcula distancia total real recorrida, desnivel acumulado positivo (`elevationGainM`) y velocidad máxima filtrando saltos anómalos de GPS.
  * **Detector de Corte de Ruta / Atajos:** Compara cada punto del track contra la lista de checkpoints oficiales del evento. Si el atleta no estuvo dentro del radio de tolerancia de algún punto de control obligatorio, marca `routeCutDetected = true` e indica exactamente qué checkpoint omitió.

### `simulatorService.js`
* Generador de pruebas de estrés y demostración:
  * Genera o utiliza de 10 a 300 atletas concurrentes.
  * **`interpolateRoutePosition(progress)`:** Calcula coordenadas matemáticas realistas a lo largo del circuito de Guatapé aplicando variación de velocidad por disciplina y un ligero factor aleatorio de separación (jitter).
  * **`startSimulation(count, multiplier)` / `stopSimulation()`:** Bucle recurrente que inyecta telemetría como si proviniera de chips GPS físicos reales.

### `gpsSummaryService.js`
* **Parser Dual de Tiempos del Proveedor GPS (CSV y XML):**
  * Procesa archivos consolidados como `Final.csv` y `Final.xml` generados por los sistemas de cronometraje del proveedor GPS.
  * **`parseTimeStrToSeconds(str)`:** Convierte cadenas de tiempo (`HH:MM:SS` o `MM:SS`) a segundos absolutos de forma robusta.
  * **Conciliación Inteligente con la Base de Datos:** Localiza a cada atleta por su dorsal oficial (`NumOrg` / `bib_number`) o chip físico (`Chip` / `chip_code`) en el evento.
  * **Generación de Splits Oficiales:** Registra los 5 parciales del evento (`swim`, `t1`, `bike`, `t2`, `run`) con su respectivo cálculo de ritmo/velocidad formateado (`pace_formatted`).
  * **Cierre de Meta Oficial:** Actualiza el atleta a estado `finished`, registrando `finish_time`, `total_time_seconds` y el orden de llegada.
  * **Modo Simulación (`dry_run`):** Permite previsualizar la cantidad de atletas y parciales a importar antes de persistir los cambios en base de datos.

### `wpSyncService.js`
* **`mapCheckoutMetadata(rawItem, eventId, tenantId)`:** Mapea los metadatos de checkout de WooCommerce guardados por `carreras_checkout_by_sgl` (nombres, documento, EPS, talla, contacto de emergencia, alergias, tipo de sangre).
* **`getNextBibNumber(eventId)`:** Consulta el dorsal numérico más alto y asigna automáticamente el siguiente correlativo libre.
* **`ingestAthleteFromCheckout(payload, eventId, tenantId)`:** Inserción o actualización idempotente (evita duplicados si el atleta vuelve a comprar o actualizar datos).

---

## 7. Catálogo Completo de Rutas y Endpoints API (`routes/api/`)

A continuación se listan todos los endpoints expuestos por el backend, su método HTTP, archivo fuente, parámetros requeridos y roles necesarios:

### 7.1 Autenticación y Perfil (`/api/auth`)
| Método | Endpoint | Archivo | Roles | Descripción |
|---|---|---|---|---|
| `POST` | `/api/auth/login` | `routes/api/auth.js` | Público | Autentica con email/cédula y contraseña; retorna JWT y objeto `user`. |
| `POST` | `/api/auth/register` | `routes/api/auth.js` | Público | Registro de nuevo usuario (rol por defecto `athlete`). |
| `GET` | `/api/auth/me` | `routes/api/auth.js` | Todos | Retorna datos del usuario en sesión y su información de atleta vinculada. |
| `PUT` | `/api/auth/profile` | `routes/api/auth.js` | Todos | Actualiza nombres, teléfono, EPS, talla de camiseta, foto y datos médicos. |
| `POST` | `/api/auth/avatar` | `routes/api/auth.js` | Todos | Sube y actualiza la foto de perfil (`avatar_url`) del atleta/usuario en `uploads/avatars/`. |
| `DELETE` | `/api/auth/avatar` | `routes/api/auth.js` | Todos | Elimina la foto de perfil personalizada y restaura el avatar por iniciales. |

### 7.2 Portal del Atleta (`/api/athlete`)
| Método | Endpoint | Archivo | Roles | Descripción |
|---|---|---|---|---|
| `GET` | `/api/athlete/races` | `routes/api/athlete.js` | `athlete`, `admin` | Historial de carreras donde el atleta está inscrito o ha competido. |
| `GET` | `/api/athlete/performance/:eventId` | `routes/api/athlete.js` | `athlete`, `admin` | Detalle de tiempos parciales (splits), posiciones, incidentes y kit. |
| `GET` | `/api/athlete/track/:eventId` | `routes/api/athlete.js` | `athlete`, `admin` | Coordenadas GPX de la ruta y hasta 500 puntos de su telemetría en vivo. |

### 7.3 Logística y Operaciones de Staff (`/api/staff`)
| Método | Endpoint | Archivo | Roles | Descripción |
|---|---|---|---|---|
| `GET` | `/api/staff/athletes` | `routes/api/staff.js` | `staff`, `admin` | Búsqueda por dorsal, cédula o nombre; estado de entrega y devolución de kit. |
| `POST` | `/api/staff/kit-delivery` | `routes/api/staff.js` | `staff`, `admin` | Registra entrega física de kit, observaciones (`delivery_notes`) y asocia serial GPS al dorsal en tiempo real. |
| `POST` | `/api/staff/kit-return` | `routes/api/staff.js` | `staff`, `admin` | Registra retorno del chip GPS y observaciones de recepción (`return_notes`) al cruzar la meta oficial. |
| `POST` | `/api/staff/manual-split` | `routes/api/staff.js` | `staff`, `admin` | Registra tiempo manual de paso en checkpoint (split), calcula ritmo, actualiza meta e incidentes con notas. |
| `POST` | `/api/staff/incidents` | `routes/api/staff.js` | `staff`, `admin` | Reporta incidente (médico, mecánico, penalización, DNF, descalificación). |
| `GET` | `/api/staff/incidents` | `routes/api/staff.js` | `staff`, `admin` | Listado de incidentes reportados durante el evento con filtros. |

### 7.4 Soporte Técnico y PQRS (`/api/tickets`)
| Método | Endpoint | Archivo | Roles | Descripción |
|---|---|---|---|---|
| `GET` | `/api/tickets` | `routes/api/tickets.js` | Todos | Lista tickets de soporte (atletas ven los suyos; staff/admin ven los del tenant). |
| `POST` | `/api/tickets` | `routes/api/tickets.js` | Todos | Crea un nuevo ticket (Petición, Queja, Reclamo o Soporte Técnico). |
| `GET` | `/api/tickets/:id` | `routes/api/tickets.js` | Todos | Retorna el detalle del ticket y el hilo cronológico de respuestas. |
| `POST` | `/api/tickets/:id/messages` | `routes/api/tickets.js` | Todos | Envía un mensaje dentro de la conversación del ticket. |
| `PATCH` | `/api/tickets/:id/status` | `routes/api/tickets.js` | `staff`, `admin` | Cambia el estado del ticket (`open`, `in_progress`, `resolved`, `closed`). |

### 7.5 Administración de Carrera y Asignación de Jueces (`/api/admin`)
| Método | Endpoint | Archivo | Roles | Descripción |
|---|---|---|---|---|
| `GET` | `/api/admin/staff` | `routes/api/admin.js` | `admin` | Lista el personal de logística (`staff`) y jueces de carrera (`judge`) con sus carreras asignadas (`assigned_events`). |
| `POST` | `/api/admin/staff` | `routes/api/admin.js` | `admin` | Da de alta un nuevo miembro de `staff` o `judge` (y lo asigna a carrera específica en `judge_assignments` si aplica). |
| `POST` | `/api/admin/judges/assign` | `routes/api/admin.js` | `admin` | Asigna o actualiza la asignación de un juez (`judge_user_id`) a una carrera específica (`event_id`) y punto de control (`checkpoint_id`). |
| `DELETE` | `/api/admin/judges/assign/:assignmentId` | `routes/api/admin.js` | `admin` | Retira la asignación de un juez a una carrera específica. |
| `PATCH` | `/api/admin/staff/:id/toggle` | `routes/api/admin.js` | `admin` | Activa o desactiva credenciales de un miembro de staff o juez. |
| `POST` | `/api/admin/events` | `routes/api/admin.js` | `admin` | Crea un nuevo evento deportivo con sus segmentos y checkpoints predefinidos. |

### 7.5b Mesa Oficial de Jueces de Carrera (`/api/judge`)
| Método | Endpoint | Archivo | Roles | Descripción |
|---|---|---|---|---|
| `GET` | `/api/judge/my-events` | `routes/api/judge.js` | `judge`, `admin` | Retorna únicamente las carreras específicas asignadas al juez autenticado en `judge_assignments` con sus segmentos y checkpoints. |
| `GET` | `/api/judge/events/:eventId/athletes` | `routes/api/judge.js` | `judge`, `admin` | Valida que el juez esté asignado a `:eventId` y retorna el padrón de atletas de esa carrera con su avance y splits registrados. |
| `POST` | `/api/judge/events/:eventId/progress` | `routes/api/judge.js` | `judge`, `admin` | Registra el avance de un atleta en la carrera asignada (paso por checkpoint, segmento, tiempo parcial/acumulado, km recorrido, llegada a meta o DNF/DQ) y emite actualización en vivo por WebSocket (`judge:progress`). |

### 7.6 Eventos y Padrón de Atletas (`/api/events` y `/api/athletes`)
| Método | Endpoint | Archivo | Roles | Descripción |
|---|---|---|---|---|
| `GET` | `/api/events` | `routes/api/events.js` | Público | Lista los eventos deportivos disponibles en el inquilino. |
| `GET` | `/api/events/:id` | `routes/api/events.js` | Público | Detalle de un evento, configuración y segmentos. |
| `PUT` | `/api/events/:id/status` | `routes/api/events.js` | `admin` | Modifica estado del evento (`draft`, `scheduled`, `live`, `finished`). |
| `GET` | `/api/athletes` | `routes/api/athletes.js` | `admin` | Listado paginado de atletas con filtros por estado y categoría. |
| `GET` | `/api/athletes/:id` | `routes/api/athletes.js` | `admin` | Ficha técnica y médica completa de un atleta. |
| `POST` | `/api/athletes` | `routes/api/athletes.js` | `admin` | Inserción manual de un atleta. |
| `PUT` | `/api/athletes/:id` | `routes/api/athletes.js` | `admin` | Actualización de datos de carrera o información de contacto. |

### 7.7 Telemetría, Clasificaciones y GPX
| Método | Endpoint | Archivo | Roles | Descripción |
|---|---|---|---|---|
| `POST` | `/api/telemetry/ingest` | `routes/api/telemetry.js` | API Key / Púb. | Ingesta de paquete GPS vía webhook HTTP (JSON o estándar de tracker). |
| `GET` | `/api/telemetry/stats` | `routes/api/telemetry.js` | Todos | Métricas de rendimiento: paquetes/segundo y tamaño del buffer. |
| `GET` | `/api/telemetry/history/:athleteId`| `routes/api/telemetry.js`| Todos | Historial de puntos y coordenadas emitidas por un atleta. |
| `GET` | `/api/leaderboard/:eventId` | `routes/api/leaderboard.js`| Público | Clasificación oficial, posiciones overall/categoría y ritmo por disciplina. |
| `POST` | `/api/gpx/upload` | `routes/api/gpx.js` | Todos | Subida con `multer` de track `.gpx`/`.tcx`, análisis y verificación de cortes. |
| `POST` | `/api/gpx/import-summary` | `routes/api/gpx.js` | `admin`, `superadmin` | Importa archivo de resumen de tiempos (`Final.csv` o `Final.xml`), genera splits oficiales y actualiza clasificaciones. |
| `POST` | `/api/gpx/athlete-track` | `routes/api/gpx.js` | `athlete`, `staff`, `admin` | Carga de archivo GPX individual del atleta (`Morning_*.gpx`), persistencia en disco, cálculo de desnivel y auditoría de ruta. |
| `GET` | `/api/gpx/history` | `routes/api/gpx.js` | Todos | Historial de archivos GPX analizados para un evento. |
| `GET` | `/api/certificate/:athleteId` | `routes/api/certificate.js`| Público | Datos oficiales para generar el diploma de finalista. |

### 7.8 Inquilinos Multi-Tenant (Empresas), Marca Blanca y Simulación (`/api/tenants` y `/api/simulator`)
| Método | Endpoint | Archivo | Roles | Descripción |
|---|---|---|---|---|
| `GET` | `/api/tenants/current` | `routes/api/tenants.js` | Público | Información sanitizada de la marca blanca/empresa activa por subdominio o sesión (nunca expone otros tenants ni `api_key`). |
| `GET` | `/api/tenants/my-branding` | `routes/api/tenants.js` | `admin` | Obtiene la configuración de subdominio, logotipo, colores y métricas propias de la empresa del Administrador. |
| `PUT` | `/api/tenants/my-branding` | `routes/api/tenants.js` | `admin` | Personaliza el nombre, subdominio único (`subdomain`), dominio, logotipo y colores corporativos de la empresa. |
| `POST` | `/api/tenants/my-branding/logo` | `routes/api/tenants.js` | `admin` | Sube una imagen de logotipo personalizado (`uploads/tenants/`) para la empresa activa. |
| `GET` | `/api/tenants` | `routes/api/tenants.js` | `superadmin` | 🔒 **Exclusivo SuperAdmin:** Lista todas las empresas (tenants) del sistema SaaS (ningún tenant puede ver a los demás). |
| `POST` | `/api/tenants` | `routes/api/tenants.js` | `superadmin` | 🔒 **Exclusivo SuperAdmin:** Crea una nueva empresa con subdominio, logo, colores y su usuario Administrador inicial. |
| `PUT` | `/api/tenants/:id` | `routes/api/tenants.js` | `admin`, `superadmin` | Actualiza subdominio, logotipo, colores y cuota de atletas validando propiedad estricta del tenant. |
| `POST` | `/api/simulator/start` | `routes/api/simulator.js` | `admin` | Inicia simulación concurrente (parámetros: `count`, `multiplier`). |
| `POST` | `/api/simulator/stop` | `routes/api/simulator.js` | `admin` | Detiene el simulador en tiempo real. |
| `GET` | `/api/simulator/status` | `routes/api/simulator.js` | `admin` | Consulta el estado activo del simulador y atletas en carrera. |

### 7.9 Sincronización WordPress (`/api/sync/wordpress`)
| Método | Endpoint | Archivo | Roles | Descripción |
|---|---|---|---|---|
| `POST` | `/api/sync/wordpress/webhook` | `routes/api/wpSync.js` | API Key / Púb. | Recibe webhooks automáticos de compras en WooCommerce (individuales y relevos, con validación opcional `WC_WEBHOOK_SECRET`). |
| `POST` | `/api/sync/wordpress/pull` | `routes/api/wpSync.js` | `admin`, `superadmin` | 🔒 **Acceso estricto:** Importación desde la API REST de WooCommerce (`/wp-json/wc/v3/orders`) leyendo `WP_SITE_URL`, `WC_CONSUMER_KEY` y `WC_CONSUMER_SECRET` desde `.env`. |
| `GET` | `/api/sync/wordpress/config` | `routes/api/wpSync.js` | `admin`, `superadmin` | Estado actual de las variables de conexión WooCommerce configuradas en `nodeapp/.env`. |
| `GET` | `/api/sync/wordpress/schema` | `routes/api/wpSync.js` | Todos | Muestra el esquema de metadatos requerido por el checkout. |

---

## 8. Frontend SPA Moderno en Vue 3 (`frontend/`)

El frontend está construido con **Vue 3 (Composition API)**, **Vite**, **Pinia** y **Vue Router 4**. Se compila directamente a `public/app/`.

### Estructura de Vistas por Rol
* **Autenticación:**
  * `views/auth/LoginView.vue`: Acceso unificado para los 4 roles con redirección automática inteligente.
  * `views/auth/RegisterView.vue`: Formulario de registro con captura de documento y datos de contacto.
* **Módulo Atleta:**
  * `views/athlete/AthleteProfileView.vue`: Ficha médica, foto de perfil personalizable (`avatar_url`), contacto de emergencia, EPS, talla y panel **"Mis Equipos y Modalidad según la Carrera"** (`enrollments` con modalidad `individual` o `relay`, nombre del equipo en `teams`, segmento de relevo `relay_role` y compañeros de equipo).
  * `views/athlete/AthleteRacesView.vue`: Listado de carreras con distinción de modalidad Individual vs. Relevos por Equipos, integrantes del equipo, dorsal asignado, estado del kit y acceso al visor/diploma.
  * `views/athlete/AthletePerformanceView.vue`: Grilla de splits, modalidad/equipo y cronometraje oficial.
  * `views/athlete/AthleteTrackView.vue`: Visor Leaflet personal que compara el track recorrido con la ruta del evento.
  * `views/athlete/AthleteTicketsView.vue`: Módulo para radicar y responder tickets de soporte.
* **Módulo Staff en Pista:**
  * `views/staff/StaffCheckinView.vue`: Entrega de kits y escaneo de seriales GPS con validación en vivo.
  * `views/staff/StaffReturnsView.vue`: Retorno de equipos en zona de meta.
  * `views/staff/StaffIncidentsView.vue`: Reporte de incidentes médicos, mecánicos o sanciones de jueces.
* **Módulo Admin de Carrera:**
  * `views/admin/AdminEventsView.vue`: Control de eventos y paso a estado `live`.
  * `views/admin/AdminStaffView.vue`: Alta y control de personal de pista.
  * `views/admin/AdminAthletesView.vue`: Padrón central de competidores.
  * `views/admin/AdminTicketsView.vue`: Panel de resolución de PQRS.
* **Módulo Super-Admin SaaS:**
  * `views/superadmin/SuperAdminView.vue`: Control de marcas blancas, cuotas y simulador de 300 atletas.

### Compilación y Desarrollo del Frontend
* **Modo desarrollo (Hot Reload):**
  ```bash
  cd frontend
  npm run dev
  ```
* **Compilación de producción hacia `public/app/`:**
  ```bash
  npm run build:frontend
  ```
  *(Desde la raíz de `nodeapp` ejecuta `npm --prefix frontend run build`).*

---

## 9. Frontend Auxiliar y Vistas Públicas (`public/`)

Para compatibilidad universal y máxima ligereza, se incluyen vistas HTML y Vanilla JS optimizadas con control de acceso RBAC estricto (`public/js/sidebar.js` + `data-require-role`):
* `live.html` + `public/js/live-tracker.js`: Visor de carrera en pantalla gigante o móvil con Leaflet y Socket.io en tiempo real.
* `leaderboard.html` + `public/js/leaderboard.js`: Clasificaciones públicas con actualización automática y filtrado por categoría y dorsal.
* `certificate.html`: Diploma de finalista con formato A4 listo para imprimir en PDF o compartir.
* `admin.html` (`staff`, `admin`, `superadmin`) y `superadmin.html` / `architecture_map.html` (`superadmin`): Consolas auxiliares protegidas por rol tanto en enlaces de menú lateral/superior como en acceso directo a página.
* `public/js/sidebar.js` & `frontend/src/Layouts/AdminLayout.vue`: Depuración estricta de menús laterales y superiores para garantizar que ningún rol (`guest`, `athlete`, `staff`, `admin`, `superadmin`) vea ni acceda a enlaces fuera de sus permisos.

---

## 10. Protocolos de Telemetría (HTTP / UDP / WebSockets)

### Ingesta UDP (Puerto 5005)
Acepta datagramas directos de firmware GPS.
* **Formato 1 (CSV estándar de rastreadores):**
  `IMEI,lat,lon,speed,battery`
  *Ejemplo:* `CHIP-GPS-101,6.2338,-75.1612,38.5,92`
* **Formato 2 (JSON):**
  `{"chip_code": "CHIP-GPS-101", "lat": 6.2338, "lon": -75.1612, "speed_kmh": 38.5, "battery": 92}`

### Ingesta HTTP Webhook
* **Endpoint:** `POST /api/telemetry/ingest`
* **Payload:**
  ```json
  {
    "chip_code": "CHIP-GPS-101",
    "bib_number": "101",
    "lat": 6.2205,
    "lon": -75.1795,
    "speed": 39.2,
    "battery": 88
  }
  ```

### Emisión WebSockets (Socket.io)
Los clientes conectados reciben los siguientes eventos en tiempo real:
* `telemetry:point`: Emite `{ athleteId, bibNumber, fullName, lat, lon, speedKmh, battery, distanceKm, segment, status }`.
* `checkpoint:crossed`: Emite `{ athlete, checkpoint }` cuando un atleta ingresa a la geocerca de un punto de cronometraje.
* `athlete:finished`: Emite cuando el atleta cruza el último checkpoint (Arco de Meta).

---

## 11. Guía Práctica para Nuevos Desarrolladores (Cómo Extender la Plataforma)

### ¿Cómo agregar una nueva modalidad deportiva?
1. Abre `services/timingEngine.js`:
   * Agrega las reglas de ritmo en `formatPace()` (ej. si agregas `rowing` o `patinaje`).
   * Define la función de segmentación por distancias si requiere splits dinámicos.
2. Abre `db/schema.sql` y actualiza el comentario de `sport_type` en la tabla `events`.
3. En `db/seeds.js` o mediante `/api/admin/events`, registra el nuevo evento configurando las filas correspondientes en `event_segments` y `checkpoints`.

### ¿Cómo agregar un nuevo endpoint de API?
1. Crea o modifica el router en `routes/api/<tu_modulo>.js`.
2. Aplica los middlewares necesarios:
   ```javascript
   const { authenticateToken, requireRole } = require('../../middleware/authMiddleware');
   router.get('/ruta', authenticateToken, requireRole(['admin']), async (req, res) => { ... });
   ```
3. Registra el router en `server.js` con su prefijo:
   ```javascript
   app.use('/api/nuevo', require('./routes/api/nuevo'));
   ```

### ¿Cómo agregar una nueva vista en el frontend Vue 3?
1. Crea el componente en `frontend/src/views/<rol>/MiNuevaVista.vue`.
2. Regístralo en `frontend/src/router/index.js` asignándole `meta: { requiresAuth: true, roles: [...] }`.
3. Si requiere enlace en el menú lateral, agrégalo en `frontend/src/Layouts/AdminLayout.vue`.
4. Compila los cambios con `npm run build:frontend`.

---
*Documento generado y verificado para la plataforma "Eres Leyenda" — Todos los derechos reservados.*
