Complete real-time train tracking system for Spanish railways (Renfe/Cercanías): - Backend API (Node.js/Express) with GTFS-RT polling workers - Frontend dashboard (React/Vite) with Leaflet maps - Real-time updates via Socket.io WebSocket - PostgreSQL/PostGIS database with Flyway migrations - Redis caching layer - Docker Compose configuration for development and production - Gitea CI/CD workflows (lint, auto-tag, release) - Production deployment with nginx + Let's Encrypt SSL 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
10 KiB
Fase 1: MVP - Sistema de Tracking de Trenes en Tiempo Real
Estado: ✅ COMPLETADO
La Fase 1 del roadmap ha sido implementada exitosamente. Este documento describe lo que se ha construido y cómo probarlo.
✨ Características Implementadas
Backend
- ✅ Worker GTFS-RT que recolecta posiciones cada 30 segundos
- ✅ API REST con endpoints para trenes, rutas, estaciones y estadísticas
- ✅ WebSocket server para actualizaciones en tiempo real
- ✅ Integración con PostgreSQL + PostGIS
- ✅ Cache Redis para posiciones actuales
- ✅ Sistema de logs con Pino
- ✅ Gestión de errores y reconexión automática
Frontend
- ✅ Mapa interactivo con Leaflet.js y OpenStreetMap
- ✅ Visualización de trenes en tiempo real
- ✅ Panel de información detallada de cada tren
- ✅ Conexión WebSocket con reconexión automática
- ✅ Timeline básico (UI preparado, funcionalidad fase 2)
- ✅ Estadísticas en header (trenes activos, última actualización)
- ✅ Diseño responsivo
📁 Estructura del Proyecto
trenes/
├── backend/
│ ├── src/
│ │ ├── api/
│ │ │ ├── routes/
│ │ │ │ ├── trains.js # Endpoints de trenes
│ │ │ │ ├── routes.js # Endpoints de rutas
│ │ │ │ ├── stations.js # Endpoints de estaciones
│ │ │ │ └── stats.js # Endpoints de estadísticas
│ │ │ └── server.js # Servidor API + WebSocket
│ │ ├── worker/
│ │ │ └── gtfs-poller.js # Worker GTFS-RT
│ │ ├── lib/
│ │ │ ├── db.js # Cliente PostgreSQL
│ │ │ ├── redis.js # Cliente Redis
│ │ │ └── logger.js # Logger Pino
│ │ └── config/
│ │ └── index.js # Configuración
│ ├── package.json
│ ├── Dockerfile
│ └── .env.example
│
├── frontend/
│ ├── src/
│ │ ├── components/
│ │ │ ├── TrainMap.jsx # Mapa Leaflet
│ │ │ ├── TrainInfo.jsx # Panel de información
│ │ │ └── Timeline.jsx # Timeline (UI)
│ │ ├── hooks/
│ │ │ └── useTrains.js # Hook WebSocket
│ │ ├── styles/
│ │ │ └── index.css # Estilos globales
│ │ ├── App.jsx # Componente principal
│ │ └── main.jsx # Entry point
│ ├── package.json
│ ├── Dockerfile
│ └── vite.config.js
│
├── database/
│ ├── init/ # Scripts iniciales
│ └── migrations/ # Migraciones Flyway
│
├── docker-compose.yml
├── Makefile
└── README.md
🚀 Cómo Ejecutar el MVP
Prerrequisitos
- Docker y Docker Compose instalados
- Puerto 80, 3000, 5432, 6379 disponibles
- (Opcional) Make para comandos simplificados
Opción 1: Usando Make (Recomendado)
# 1. Configurar variables de entorno
cp .env.example .env
# Editar .env si es necesario
# 2. Ejecutar migraciones
make migrate
# 3. Iniciar todos los servicios
make start
# 4. Ver logs
make logs
Opción 2: Docker Compose Manual
# 1. Configurar variables de entorno
cp .env.example .env
# 2. Ejecutar migraciones
docker-compose --profile migration up flyway
# 3. Iniciar servicios
docker-compose up -d
# 4. Ver logs
docker-compose logs -f
Opción 3: Desarrollo Local (sin Docker)
Backend
cd backend
# Instalar dependencias
npm install
# Configurar .env
cp .env.example .env
# Ajustar DATABASE_URL y REDIS_URL a localhost
# Ejecutar worker en una terminal
npm run dev:worker
# Ejecutar API en otra terminal
npm run dev
Frontend
cd frontend
# Instalar dependencias
npm install
# Ejecutar en modo desarrollo
npm run dev
🌐 Acceder a la Aplicación
Una vez iniciados los servicios:
- Aplicación Web: http://localhost
- API REST: http://localhost/api o http://localhost:3000
- Health Check: http://localhost/health o http://localhost:3000/health
📡 Endpoints de la API
Trenes
# Obtener todos los trenes activos
GET /trains/current
# Obtener información de un tren específico
GET /trains/:id
# Obtener histórico de un tren
GET /trains/:id/history?from=2025-11-27T00:00:00Z&to=2025-11-27T23:59:59Z&limit=100
# Obtener trayectoria de un tren
GET /trains/:id/path?from=2025-11-27T10:00:00Z&to=2025-11-27T11:00:00Z
# Obtener trenes en un área geográfica
GET /trains/area?minLat=40.0&minLon=-4.0&maxLat=41.0&maxLon=-3.0
Rutas
# Obtener todas las rutas
GET /routes
# Obtener ruta específica
GET /routes/:id
Estaciones
# Obtener todas las estaciones
GET /stations
# Obtener estaciones por tipo
GET /stations?type=MAJOR
# Obtener estación específica
GET /stations/:id
Estadísticas
# Obtener estadísticas del sistema
GET /stats
# Obtener estadísticas de un tren
GET /stats/train/:id?from=2025-11-27T00:00:00Z&to=2025-11-27T23:59:59Z
🔌 WebSocket Events
Cliente → Servidor
// Suscribirse a un tren específico
socket.emit('subscribe:train', trainId);
// Desuscribirse de un tren
socket.emit('unsubscribe:train', trainId);
Servidor → Cliente
// Actualización de todos los trenes (cada 2 segundos)
socket.on('trains:update', (positions) => {
console.log('Posiciones actualizadas:', positions);
});
// Actualización de un tren específico (si estás suscrito)
socket.on('train:update', (position) => {
console.log('Tren actualizado:', position);
});
🧪 Probar el Sistema
1. Verificar que el Worker está funcionando
# Ver logs del worker
make logs-worker
# O con docker-compose
docker-compose logs -f worker
# Deberías ver mensajes como:
# "Polling GTFS-RT feed..."
# "Processed vehicle positions: {trains: 50, duration: 1234}"
2. Verificar API
# Health check
curl http://localhost:3000/health
# Obtener trenes actuales
curl http://localhost:3000/trains/current | jq
# Obtener estadísticas
curl http://localhost:3000/stats | jq
3. Verificar Base de Datos
# Conectar a PostgreSQL
make psql
# Ver trenes almacenados
SELECT COUNT(*) FROM trains;
# Ver posiciones de las últimas 24 horas
SELECT COUNT(*) FROM train_positions WHERE recorded_at > NOW() - INTERVAL '24 hours';
# Ver estaciones
SELECT * FROM stations LIMIT 10;
4. Verificar Redis
# Conectar a Redis
make redis-cli
# Ver trenes activos
SMEMBERS trains:active
# Ver posición actual de un tren
GET trains:current:TRAIN_ID
🐛 Troubleshooting
No se ven trenes en el mapa
Causa: El feed GTFS-RT puede no tener datos o el worker no está corriendo.
Solución:
# Verificar logs del worker
make logs-worker
# Verificar si hay trenes en Redis
make redis-cli
> SMEMBERS trains:active
# Si Redis está vacío, verificar PostgreSQL
make psql
> SELECT COUNT(*) FROM train_positions WHERE recorded_at > NOW() - INTERVAL '1 hour';
Error de conexión WebSocket
Causa: CORS o URL incorrecta.
Solución:
# Verificar que VITE_WS_URL está configurado correctamente
# En .env.testing o variables de entorno del frontend
# Debería ser: http://localhost:3000 (desarrollo) o ws://localhost/ws (producción)
La base de datos no tiene datos
Causa: Migraciones no ejecutadas o feed GTFS-RT sin datos.
Solución:
# Ejecutar migraciones
make migrate
# Verificar estado de migraciones
make migrate-info
# Ver datos iniciales
make psql
> SELECT * FROM stations LIMIT 5;
Error "PostgreSQL not connected"
Causa: PostgreSQL no está corriendo o configuración incorrecta.
Solución:
# Verificar que PostgreSQL está corriendo
docker-compose ps postgres
# Reiniciar PostgreSQL
docker-compose restart postgres
# Verificar logs
docker-compose logs postgres
📊 Métricas y Monitorización
Logs del Sistema
# Ver todos los logs
make logs
# Ver logs específicos
make logs-api # API
make logs-worker # Worker
make logs-db # PostgreSQL
Estadísticas del Worker
El worker registra estadísticas cada 60 segundos:
{
"totalPolls": 120,
"successfulPolls": 118,
"failedPolls": 2,
"totalTrains": 45,
"lastPollTime": "2025-11-27T10:30:00.000Z",
"successRate": "98.33%"
}
Panel de Administración
Para acceder a herramientas de administración:
# Iniciar con modo debug
make debug-start
# Acceder a:
# - Adminer (PostgreSQL): http://localhost:8080
# - Redis Commander: http://localhost:8081
🎯 Próximos Pasos (Fase 2)
La Fase 2 incluirá:
- Integración GTFS Static (rutas, horarios)
- Trip Updates (retrasos, cancelaciones)
- Service Alerts (incidencias)
- Timeline funcional con reproducción histórica
- Monitor de puntualidad
- Panel de incidencias
Para más información, consultar el roadmap completo.
📝 Notas Técnicas
Fuente de Datos
El sistema consume el feed GTFS-RT de Renfe:
- URL: https://gtfsrt.renfe.com/vehicle_positions.pb
- Formato: Protocol Buffer (GTFS Realtime)
- Frecuencia: 30 segundos
- Cobertura: Principalmente Cercanías
Almacenamiento
- PostgreSQL: Histórico completo de posiciones (particionado por mes)
- Redis: Cache de últimas posiciones (TTL 5 minutos)
- WebSocket: Broadcast en tiempo real (cada 2 segundos)
Rendimiento
- Polling: 30 segundos (configurable via
POLLING_INTERVAL) - Broadcast WS: 2 segundos
- Particiones DB: Mensuales (nov 2025 - mar 2027)
- Retención: 90 días (configurable, usar
cleanup_old_positions())
📚 Documentación Adicional
- Arquitectura Completa
- Fuentes de Datos
- README Principal
- Makefile Commands - Ver
make help
🤝 Contribuir
Si encuentras bugs o quieres proponer mejoras:
- Crea un issue describiendo el problema/mejora
- Haz un fork del proyecto
- Crea una rama para tu feature
- Envía un pull request
Estado: Fase 1 MVP Completada ✅ Fecha: 27 noviembre 2025 Próxima Fase: Fase 2 - Enriquecimiento