feat: Initial commit - Train tracking system
Some checks failed
Auto Tag on Merge to Main / auto-tag (push) Successful in 27s
CI - Lint and Build / lint-backend (push) Failing after 30s
CI - Lint and Build / lint-frontend (push) Failing after 2s
CI - Lint and Build / build-frontend (push) Has been skipped
CI - Lint and Build / docker-build-test (push) Has been skipped
Some checks failed
Auto Tag on Merge to Main / auto-tag (push) Successful in 27s
CI - Lint and Build / lint-backend (push) Failing after 30s
CI - Lint and Build / lint-frontend (push) Failing after 2s
CI - Lint and Build / build-frontend (push) Has been skipped
CI - Lint and Build / docker-build-test (push) Has been skipped
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>
This commit is contained in:
485
FASE1-MVP.md
Normal file
485
FASE1-MVP.md
Normal file
@@ -0,0 +1,485 @@
|
||||
# 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)
|
||||
|
||||
```bash
|
||||
# 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
|
||||
|
||||
```bash
|
||||
# 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
|
||||
|
||||
```bash
|
||||
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
|
||||
|
||||
```bash
|
||||
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
|
||||
|
||||
```bash
|
||||
# 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
|
||||
|
||||
```bash
|
||||
# Obtener todas las rutas
|
||||
GET /routes
|
||||
|
||||
# Obtener ruta específica
|
||||
GET /routes/:id
|
||||
```
|
||||
|
||||
### Estaciones
|
||||
|
||||
```bash
|
||||
# Obtener todas las estaciones
|
||||
GET /stations
|
||||
|
||||
# Obtener estaciones por tipo
|
||||
GET /stations?type=MAJOR
|
||||
|
||||
# Obtener estación específica
|
||||
GET /stations/:id
|
||||
```
|
||||
|
||||
### Estadísticas
|
||||
|
||||
```bash
|
||||
# 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
|
||||
|
||||
```javascript
|
||||
// Suscribirse a un tren específico
|
||||
socket.emit('subscribe:train', trainId);
|
||||
|
||||
// Desuscribirse de un tren
|
||||
socket.emit('unsubscribe:train', trainId);
|
||||
```
|
||||
|
||||
### Servidor → Cliente
|
||||
|
||||
```javascript
|
||||
// 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
|
||||
|
||||
```bash
|
||||
# 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
|
||||
|
||||
```bash
|
||||
# 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
|
||||
|
||||
```bash
|
||||
# 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
|
||||
|
||||
```bash
|
||||
# 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**:
|
||||
```bash
|
||||
# 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**:
|
||||
```bash
|
||||
# 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**:
|
||||
```bash
|
||||
# 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**:
|
||||
```bash
|
||||
# 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
|
||||
|
||||
```bash
|
||||
# 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:
|
||||
|
||||
```json
|
||||
{
|
||||
"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:
|
||||
|
||||
```bash
|
||||
# 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](arquitectura-sistema-tracking-trenes.md#roadmap-de-features).
|
||||
|
||||
---
|
||||
|
||||
## 📝 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](arquitectura-sistema-tracking-trenes.md)
|
||||
- [Fuentes de Datos](FUENTES_DATOS.md)
|
||||
- [README Principal](README.md)
|
||||
- [Makefile Commands](Makefile) - Ver `make help`
|
||||
|
||||
---
|
||||
|
||||
## 🤝 Contribuir
|
||||
|
||||
Si encuentras bugs o quieres proponer mejoras:
|
||||
|
||||
1. Crea un issue describiendo el problema/mejora
|
||||
2. Haz un fork del proyecto
|
||||
3. Crea una rama para tu feature
|
||||
4. Envía un pull request
|
||||
|
||||
---
|
||||
|
||||
**Estado**: Fase 1 MVP Completada ✅
|
||||
**Fecha**: 27 noviembre 2025
|
||||
**Próxima Fase**: Fase 2 - Enriquecimiento
|
||||
Reference in New Issue
Block a user