FlotillasGPS - Sistema completo de monitoreo de flotillas GPS
Sistema completo para monitoreo y gestion de flotas de vehiculos con: - Backend FastAPI con PostgreSQL/TimescaleDB - Frontend React con TypeScript y TailwindCSS - App movil React Native con Expo - Soporte para dispositivos GPS, Meshtastic y celulares - Video streaming en vivo con MediaMTX - Geocercas, alertas, viajes y reportes - Autenticacion JWT y WebSockets en tiempo real Documentacion completa y guias de usuario incluidas.
This commit is contained in:
264
deploy/README.md
Normal file
264
deploy/README.md
Normal file
@@ -0,0 +1,264 @@
|
||||
# Deploy - Sistema de Flotillas
|
||||
|
||||
Scripts y configuraciones para desplegar el sistema de flotillas en produccion.
|
||||
|
||||
## Estructura
|
||||
|
||||
```
|
||||
deploy/
|
||||
├── proxmox/ # Crear VM en Proxmox VE
|
||||
│ └── vm-setup.sh
|
||||
├── scripts/ # Scripts de utilidad
|
||||
│ ├── install.sh # Instalacion completa
|
||||
│ ├── backup.sh # Backup automatico
|
||||
│ ├── restore.sh # Restaurar backup
|
||||
│ ├── update.sh # Actualizar aplicacion
|
||||
│ ├── health-check.sh # Verificar salud
|
||||
│ ├── status.sh # Estado del sistema
|
||||
│ └── logs.sh # Visor de logs
|
||||
├── services/ # Servicios systemd
|
||||
│ ├── flotillas-api.service
|
||||
│ ├── flotillas-web.service
|
||||
│ ├── mediamtx.service
|
||||
│ └── cloudflared.service
|
||||
├── cloudflare/ # Configuracion tunnel
|
||||
│ └── config.yml
|
||||
├── traccar/ # Configuracion GPS
|
||||
│ └── traccar.xml
|
||||
├── mediamtx/ # Configuracion streaming
|
||||
│ └── mediamtx.yml
|
||||
└── postgres/ # Base de datos
|
||||
└── init.sql
|
||||
```
|
||||
|
||||
## Requisitos
|
||||
|
||||
- **SO**: Ubuntu 22.04 LTS
|
||||
- **RAM**: Minimo 4GB (recomendado 8GB)
|
||||
- **Disco**: Minimo 50GB SSD
|
||||
- **CPU**: 4 cores
|
||||
|
||||
## Instalacion Rapida
|
||||
|
||||
### 1. En Proxmox (opcional)
|
||||
|
||||
```bash
|
||||
# Crear VM automaticamente
|
||||
./deploy/proxmox/vm-setup.sh --vmid 200 --name flotillas --memory 8192
|
||||
```
|
||||
|
||||
### 2. En Ubuntu
|
||||
|
||||
```bash
|
||||
# Clonar repositorio
|
||||
git clone https://github.com/tuorg/flotillas.git /opt/flotillas
|
||||
cd /opt/flotillas
|
||||
|
||||
# Ejecutar instalador
|
||||
sudo ./deploy/scripts/install.sh
|
||||
```
|
||||
|
||||
El instalador:
|
||||
- Actualiza el sistema
|
||||
- Instala PostgreSQL 15 + TimescaleDB + PostGIS
|
||||
- Instala Redis
|
||||
- Instala Python 3.11 y Node.js 20
|
||||
- Instala Traccar GPS Server
|
||||
- Instala MediaMTX para video
|
||||
- Configura servicios systemd
|
||||
- Configura firewall (solo puerto 5055 publico)
|
||||
- Genera credenciales aleatorias
|
||||
|
||||
## Post-Instalacion
|
||||
|
||||
### Verificar estado
|
||||
|
||||
```bash
|
||||
./deploy/scripts/status.sh
|
||||
./deploy/scripts/health-check.sh
|
||||
```
|
||||
|
||||
### Ver logs
|
||||
|
||||
```bash
|
||||
./deploy/scripts/logs.sh api -f # API en tiempo real
|
||||
./deploy/scripts/logs.sh traccar # Traccar GPS
|
||||
./deploy/scripts/logs.sh all -f # Todos los servicios
|
||||
```
|
||||
|
||||
### Configurar Cloudflare Tunnel
|
||||
|
||||
1. Instalar cloudflared:
|
||||
```bash
|
||||
curl -L https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb -o cloudflared.deb
|
||||
dpkg -i cloudflared.deb
|
||||
```
|
||||
|
||||
2. Autenticarse:
|
||||
```bash
|
||||
cloudflared tunnel login
|
||||
```
|
||||
|
||||
3. Crear tunnel:
|
||||
```bash
|
||||
cloudflared tunnel create flotillas
|
||||
```
|
||||
|
||||
4. Configurar DNS:
|
||||
```bash
|
||||
cloudflared tunnel route dns flotillas flotillas.tudominio.com
|
||||
```
|
||||
|
||||
5. Copiar config y habilitar servicio:
|
||||
```bash
|
||||
mkdir -p /etc/cloudflared
|
||||
cp /opt/flotillas/deploy/cloudflare/config.yml /etc/cloudflared/
|
||||
systemctl enable cloudflared
|
||||
systemctl start cloudflared
|
||||
```
|
||||
|
||||
## Mantenimiento
|
||||
|
||||
### Backup
|
||||
|
||||
```bash
|
||||
# Backup manual
|
||||
./deploy/scripts/backup.sh
|
||||
|
||||
# Backup completo (incluye archivos)
|
||||
./deploy/scripts/backup.sh --full
|
||||
|
||||
# Backup y subir a S3
|
||||
./deploy/scripts/backup.sh --upload
|
||||
```
|
||||
|
||||
Backups automaticos: diariamente a las 3 AM (configurado por install.sh)
|
||||
|
||||
### Restaurar
|
||||
|
||||
```bash
|
||||
# Listar backups disponibles
|
||||
./deploy/scripts/restore.sh --list
|
||||
|
||||
# Restaurar ultimo backup
|
||||
./deploy/scripts/restore.sh --latest
|
||||
|
||||
# Restaurar backup especifico
|
||||
./deploy/scripts/restore.sh --db /var/backups/flotillas/daily/flotillas_20240115_db.sql.gz
|
||||
```
|
||||
|
||||
### Actualizar
|
||||
|
||||
```bash
|
||||
# Actualizar a ultima version
|
||||
./deploy/scripts/update.sh
|
||||
|
||||
# Forzar actualizacion (descarta cambios locales)
|
||||
./deploy/scripts/update.sh --force
|
||||
|
||||
# Solo actualizar backend
|
||||
./deploy/scripts/update.sh --backend
|
||||
```
|
||||
|
||||
## Servicios
|
||||
|
||||
| Servicio | Puerto | Descripcion |
|
||||
|----------|--------|-------------|
|
||||
| flotillas-api | 8000 | Backend FastAPI |
|
||||
| flotillas-web | 3000 | Frontend |
|
||||
| postgresql | 5432 | Base de datos |
|
||||
| redis | 6379 | Cache |
|
||||
| traccar | 5055 | GPS Server |
|
||||
| mediamtx | 8554/8889/8888 | Video RTSP/WebRTC/HLS |
|
||||
| mosquitto | 1883 | MQTT |
|
||||
|
||||
### Comandos systemd
|
||||
|
||||
```bash
|
||||
# Estado
|
||||
systemctl status flotillas-api
|
||||
|
||||
# Reiniciar
|
||||
systemctl restart flotillas-api
|
||||
|
||||
# Logs
|
||||
journalctl -u flotillas-api -f
|
||||
|
||||
# Habilitar/Deshabilitar
|
||||
systemctl enable flotillas-api
|
||||
systemctl disable flotillas-api
|
||||
```
|
||||
|
||||
## Seguridad
|
||||
|
||||
- **Firewall**: Solo puerto 5055 (GPS) esta abierto
|
||||
- **Acceso web**: Via Cloudflare Tunnel (HTTPS)
|
||||
- **Base de datos**: Solo acceso local
|
||||
- **Redis**: Autenticacion con password
|
||||
- **Fail2ban**: Proteccion contra fuerza bruta
|
||||
|
||||
## Puertos
|
||||
|
||||
| Puerto | Uso | Acceso |
|
||||
|--------|-----|--------|
|
||||
| 22 | SSH | Firewall |
|
||||
| 5055 | Traccar GPS | Publico |
|
||||
| 3000 | Frontend | Tunnel |
|
||||
| 8000 | API | Tunnel |
|
||||
| 5432 | PostgreSQL | Local |
|
||||
| 6379 | Redis | Local |
|
||||
| 8554 | RTSP | Tunnel |
|
||||
| 8889 | WebRTC | Tunnel |
|
||||
| 8888 | HLS | Tunnel |
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### API no inicia
|
||||
|
||||
```bash
|
||||
# Ver logs
|
||||
journalctl -u flotillas-api -n 100
|
||||
|
||||
# Verificar puerto
|
||||
ss -tlnp | grep 8000
|
||||
|
||||
# Verificar base de datos
|
||||
psql -h localhost -U flotillas -d flotillas -c "SELECT 1"
|
||||
```
|
||||
|
||||
### Traccar no recibe datos
|
||||
|
||||
```bash
|
||||
# Verificar puerto GPS
|
||||
ss -tlnp | grep 5055
|
||||
|
||||
# Ver logs Traccar
|
||||
tail -f /opt/traccar/logs/tracker-server.log
|
||||
|
||||
# Probar conexion
|
||||
nc -zv localhost 5055
|
||||
```
|
||||
|
||||
### Problemas de memoria
|
||||
|
||||
```bash
|
||||
# Ver uso de memoria por servicio
|
||||
systemctl status flotillas-api --no-pager | grep Memory
|
||||
|
||||
# Reducir workers de API
|
||||
# Editar /etc/systemd/system/flotillas-api.service
|
||||
# Cambiar --workers 4 a --workers 2
|
||||
systemctl daemon-reload
|
||||
systemctl restart flotillas-api
|
||||
```
|
||||
|
||||
## Credenciales
|
||||
|
||||
Las credenciales se generan durante la instalacion y se guardan en:
|
||||
- `/root/flotillas-credentials.txt`
|
||||
|
||||
**IMPORTANTE**: Guardar en lugar seguro y eliminar el archivo despues.
|
||||
|
||||
## Soporte
|
||||
|
||||
Para soporte, crear un issue en el repositorio o contactar al equipo de desarrollo.
|
||||
135
deploy/cloudflare/config.yml
Normal file
135
deploy/cloudflare/config.yml
Normal file
@@ -0,0 +1,135 @@
|
||||
# ============================================
|
||||
# Cloudflare Tunnel - Configuracion
|
||||
# ============================================
|
||||
# Documentacion: https://developers.cloudflare.com/cloudflare-one/connections/connect-apps
|
||||
#
|
||||
# Para usar esta configuracion:
|
||||
# 1. Instalar cloudflared: https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/installation
|
||||
# 2. Autenticarse: cloudflared tunnel login
|
||||
# 3. Crear tunnel: cloudflared tunnel create flotillas
|
||||
# 4. Obtener el UUID del tunnel y actualizar este archivo
|
||||
# 5. Crear registros DNS: cloudflared tunnel route dns flotillas flotillas.tudominio.com
|
||||
# 6. Copiar credenciales a /etc/cloudflared/
|
||||
# ============================================
|
||||
|
||||
# ID del tunnel (reemplazar con tu UUID)
|
||||
tunnel: TUNNEL_UUID_AQUI
|
||||
|
||||
# Archivo de credenciales
|
||||
credentials-file: /etc/cloudflared/TUNNEL_UUID_AQUI.json
|
||||
|
||||
# Configuracion de logging
|
||||
loglevel: info
|
||||
logfile: /var/log/cloudflared.log
|
||||
|
||||
# Metricas (opcional)
|
||||
metrics: localhost:60123
|
||||
|
||||
# No auto-actualizar
|
||||
no-autoupdate: true
|
||||
|
||||
# Configuracion de ingress (rutas)
|
||||
ingress:
|
||||
# ----------------------------------------
|
||||
# API Backend - /api/* y /docs
|
||||
# ----------------------------------------
|
||||
- hostname: flotillas.tudominio.com
|
||||
path: /api/*
|
||||
service: http://localhost:8000
|
||||
originRequest:
|
||||
connectTimeout: 30s
|
||||
noTLSVerify: false
|
||||
|
||||
- hostname: flotillas.tudominio.com
|
||||
path: /docs
|
||||
service: http://localhost:8000
|
||||
|
||||
- hostname: flotillas.tudominio.com
|
||||
path: /redoc
|
||||
service: http://localhost:8000
|
||||
|
||||
- hostname: flotillas.tudominio.com
|
||||
path: /openapi.json
|
||||
service: http://localhost:8000
|
||||
|
||||
# ----------------------------------------
|
||||
# WebSocket - /ws/*
|
||||
# ----------------------------------------
|
||||
- hostname: flotillas.tudominio.com
|
||||
path: /ws/*
|
||||
service: http://localhost:8000
|
||||
originRequest:
|
||||
# Importante para WebSocket
|
||||
noTLSVerify: false
|
||||
# Mantener conexion abierta
|
||||
keepAliveConnections: 100
|
||||
keepAliveTimeout: 90s
|
||||
|
||||
# ----------------------------------------
|
||||
# Video Streaming - WebRTC/HLS
|
||||
# ----------------------------------------
|
||||
- hostname: stream.flotillas.tudominio.com
|
||||
path: /*
|
||||
service: http://localhost:8889
|
||||
originRequest:
|
||||
noTLSVerify: false
|
||||
|
||||
- hostname: hls.flotillas.tudominio.com
|
||||
path: /*
|
||||
service: http://localhost:8888
|
||||
|
||||
# ----------------------------------------
|
||||
# API de MediaMTX (interno/admin)
|
||||
# ----------------------------------------
|
||||
- hostname: mediamtx-api.flotillas.tudominio.com
|
||||
path: /*
|
||||
service: http://localhost:9997
|
||||
originRequest:
|
||||
# Solo acceso interno
|
||||
noTLSVerify: false
|
||||
|
||||
# ----------------------------------------
|
||||
# Frontend Web - Todo lo demas
|
||||
# ----------------------------------------
|
||||
- hostname: flotillas.tudominio.com
|
||||
service: http://localhost:3000
|
||||
originRequest:
|
||||
noTLSVerify: false
|
||||
|
||||
# ----------------------------------------
|
||||
# Catch-all (requerido)
|
||||
# ----------------------------------------
|
||||
- service: http_status:404
|
||||
|
||||
# ============================================
|
||||
# Notas de configuracion
|
||||
# ============================================
|
||||
#
|
||||
# DOMINIOS RECOMENDADOS:
|
||||
# - flotillas.tudominio.com -> Frontend + API
|
||||
# - stream.flotillas.tudominio.com -> Video WebRTC
|
||||
# - hls.flotillas.tudominio.com -> Video HLS
|
||||
#
|
||||
# INSTALACION RAPIDA CON TOKEN:
|
||||
# Si prefieres usar token en lugar de archivo de config:
|
||||
# 1. Ir a Cloudflare Zero Trust Dashboard
|
||||
# 2. Access -> Tunnels -> Crear tunnel
|
||||
# 3. Copiar token
|
||||
# 4. Ejecutar: cloudflared tunnel run --token TU_TOKEN
|
||||
#
|
||||
# PUERTOS EXPUESTOS A TRAVES DEL TUNNEL:
|
||||
# - 3000: Frontend (serve)
|
||||
# - 8000: Backend API (uvicorn)
|
||||
# - 8889: MediaMTX WebRTC
|
||||
# - 8888: MediaMTX HLS
|
||||
# - 9997: MediaMTX API (admin)
|
||||
#
|
||||
# PUERTO NO EXPUESTO (acceso directo):
|
||||
# - 5055: Traccar GPS (dispositivos GPS se conectan directamente)
|
||||
#
|
||||
# SEGURIDAD ADICIONAL:
|
||||
# Configura Access Policies en Cloudflare Zero Trust para:
|
||||
# - Proteger /docs y /redoc (solo administradores)
|
||||
# - Proteger mediamtx-api (solo interno)
|
||||
# - Requerir autenticacion para rutas sensibles
|
||||
# ============================================
|
||||
237
deploy/mediamtx/mediamtx.yml
Normal file
237
deploy/mediamtx/mediamtx.yml
Normal file
@@ -0,0 +1,237 @@
|
||||
# ============================================
|
||||
# MediaMTX - Configuracion para Sistema de Flotillas
|
||||
# ============================================
|
||||
# Documentacion: https://github.com/bluenviron/mediamtx
|
||||
#
|
||||
# MediaMTX es un servidor de streaming multimedia que soporta:
|
||||
# - RTSP (recibir streams de camaras IP)
|
||||
# - WebRTC (streaming en navegadores)
|
||||
# - HLS (streaming adaptativo)
|
||||
# - RTMP (compatibilidad con OBS, etc.)
|
||||
# ============================================
|
||||
|
||||
# ========================================
|
||||
# Configuracion General
|
||||
# ========================================
|
||||
|
||||
# Nivel de log: debug, info, warn, error
|
||||
logLevel: info
|
||||
|
||||
# Destino de logs
|
||||
logDestinations: [stdout]
|
||||
|
||||
# Archivo de log (si se habilita file en logDestinations)
|
||||
# logFile: /var/log/mediamtx/mediamtx.log
|
||||
|
||||
# Timeout de lectura/escritura
|
||||
readTimeout: 10s
|
||||
writeTimeout: 10s
|
||||
|
||||
# Timeout de lectura para UDP
|
||||
readBufferCount: 512
|
||||
|
||||
# ========================================
|
||||
# API REST
|
||||
# ========================================
|
||||
|
||||
api: yes
|
||||
apiAddress: 127.0.0.1:9997
|
||||
|
||||
# Metricas para Prometheus
|
||||
metrics: yes
|
||||
metricsAddress: 127.0.0.1:9998
|
||||
|
||||
# ========================================
|
||||
# RTSP Server
|
||||
# ========================================
|
||||
# Recibe streams de camaras IP
|
||||
|
||||
rtsp: yes
|
||||
protocols: [udp, multicast, tcp]
|
||||
|
||||
# Puertos RTSP
|
||||
rtspAddress: :8554
|
||||
|
||||
# Rango de puertos UDP para RTP
|
||||
rtpAddress: :8000
|
||||
rtcpAddress: :8001
|
||||
|
||||
# Multicast (opcional)
|
||||
multicastIPRange: 224.1.0.0/16
|
||||
multicastRTPPort: 8002
|
||||
multicastRTCPPort: 8003
|
||||
|
||||
# ========================================
|
||||
# RTMP Server
|
||||
# ========================================
|
||||
# Compatibilidad con OBS, FFmpeg, etc.
|
||||
|
||||
rtmp: yes
|
||||
rtmpAddress: :1935
|
||||
|
||||
# Encriptacion RTMPS (requiere certificados)
|
||||
rtmpEncryption: "no"
|
||||
# rtmpServerKey: server.key
|
||||
# rtmpServerCert: server.crt
|
||||
|
||||
# ========================================
|
||||
# HLS Server
|
||||
# ========================================
|
||||
# Streaming adaptativo para navegadores antiguos
|
||||
|
||||
hls: yes
|
||||
hlsAddress: :8888
|
||||
|
||||
# Permitir origen cruzado (CORS)
|
||||
hlsAlwaysRemux: no
|
||||
hlsVariant: lowLatency
|
||||
hlsSegmentCount: 7
|
||||
hlsSegmentDuration: 1s
|
||||
hlsPartDuration: 200ms
|
||||
hlsSegmentMaxSize: 50M
|
||||
hlsAllowOrigin: '*'
|
||||
|
||||
# Directorio para segmentos HLS
|
||||
hlsDirectory: ''
|
||||
|
||||
# ========================================
|
||||
# WebRTC Server
|
||||
# ========================================
|
||||
# Streaming de baja latencia en navegadores modernos
|
||||
|
||||
webrtc: yes
|
||||
webrtcAddress: :8889
|
||||
|
||||
# CORS para WebRTC
|
||||
webrtcAllowOrigin: '*'
|
||||
|
||||
# Configuracion ICE (NAT traversal)
|
||||
webrtcICEServers2: []
|
||||
# Usar servidores STUN/TURN si hay NAT
|
||||
# webrtcICEServers2:
|
||||
# - urls: [stun:stun.l.google.com:19302]
|
||||
|
||||
# Puertos ICE UDP
|
||||
webrtcICEUDPMuxAddress: :8189
|
||||
webrtcICETCPMuxAddress: :8189
|
||||
|
||||
# ========================================
|
||||
# SRT Server (Secure Reliable Transport)
|
||||
# ========================================
|
||||
|
||||
srt: no
|
||||
srtAddress: :8890
|
||||
|
||||
# ========================================
|
||||
# Grabacion
|
||||
# ========================================
|
||||
# Guardar streams a disco
|
||||
|
||||
record: no
|
||||
recordPath: ./recordings/%path/%Y-%m-%d_%H-%M-%S-%f
|
||||
recordFormat: fmp4
|
||||
recordPartDuration: 100ms
|
||||
recordSegmentDuration: 1h
|
||||
recordDeleteAfter: 24h
|
||||
|
||||
# ========================================
|
||||
# Autenticacion
|
||||
# ========================================
|
||||
# Proteger acceso a streams
|
||||
|
||||
# Metodos de autenticacion: internal, http, jwt
|
||||
authMethod: internal
|
||||
|
||||
# Usuarios internos (basico)
|
||||
authInternalUsers:
|
||||
# Usuario admin - acceso total
|
||||
- user: admin
|
||||
pass: CHANGE_ME_ADMIN_PASSWORD
|
||||
permissions:
|
||||
- action: publish
|
||||
path: ''
|
||||
- action: read
|
||||
path: ''
|
||||
- action: playback
|
||||
path: ''
|
||||
- action: api
|
||||
|
||||
# Usuario para publicar (camaras)
|
||||
# Las camaras usan este usuario para enviar stream
|
||||
- user: camera
|
||||
pass: CHANGE_ME_CAMERA_PASSWORD
|
||||
permissions:
|
||||
- action: publish
|
||||
path: ''
|
||||
|
||||
# Usuario para ver (clientes)
|
||||
- user: viewer
|
||||
pass: CHANGE_ME_VIEWER_PASSWORD
|
||||
permissions:
|
||||
- action: read
|
||||
path: ''
|
||||
- action: playback
|
||||
path: ''
|
||||
|
||||
# Usuario anonimo (solo lectura, opcional)
|
||||
# - user: ''
|
||||
# pass: ''
|
||||
# permissions:
|
||||
# - action: read
|
||||
# path: ''
|
||||
|
||||
# ========================================
|
||||
# Paths (Streams)
|
||||
# ========================================
|
||||
# Configuracion de paths/streams individuales
|
||||
|
||||
paths:
|
||||
# Path por defecto - permite cualquier stream
|
||||
all_others:
|
||||
|
||||
# Stream de ejemplo - camara fija
|
||||
# camara1:
|
||||
# source: rtsp://192.168.1.100:554/stream1
|
||||
# sourceOnDemand: yes
|
||||
# sourceOnDemandStartTimeout: 10s
|
||||
# sourceOnDemandCloseAfter: 10s
|
||||
|
||||
# Stream desde FFmpeg (si necesitas transcodificar)
|
||||
# stream_transcoded:
|
||||
# runOnInit: ffmpeg -i rtsp://source -c:v libx264 -preset ultrafast -tune zerolatency -f rtsp rtsp://localhost:$RTSP_PORT/$MTX_PATH
|
||||
# runOnInitRestart: yes
|
||||
|
||||
# Stream de camara vehicular (ejemplo)
|
||||
# vehiculo_001:
|
||||
# source: rtsp://usuario:password@192.168.1.101:554/h264
|
||||
# sourceOnDemand: yes
|
||||
# runOnDemand: ''
|
||||
# runOnDemandRestart: no
|
||||
# runOnDemandStartTimeout: 10s
|
||||
# runOnDemandCloseAfter: 10s
|
||||
|
||||
# ============================================
|
||||
# Notas de Integracion
|
||||
# ============================================
|
||||
#
|
||||
# PUBLICAR STREAM (desde camara o FFmpeg):
|
||||
# rtsp://camera:password@servidor:8554/nombre_stream
|
||||
#
|
||||
# VER STREAM:
|
||||
# - RTSP: rtsp://viewer:password@servidor:8554/nombre_stream
|
||||
# - WebRTC: http://servidor:8889/nombre_stream
|
||||
# - HLS: http://servidor:8888/nombre_stream/index.m3u8
|
||||
#
|
||||
# API REST (ejemplos):
|
||||
# - Listar streams: curl http://localhost:9997/v3/paths/list
|
||||
# - Info de stream: curl http://localhost:9997/v3/paths/get/nombre_stream
|
||||
# - Kick conexion: curl -X POST http://localhost:9997/v3/paths/kick/nombre_stream
|
||||
#
|
||||
# INTEGRACION CON FRONTEND:
|
||||
# Usar libreria como hls.js o adaptador WebRTC para reproducir en navegador
|
||||
#
|
||||
# SEGURIDAD:
|
||||
# 1. Cambiar passwords por defecto
|
||||
# 2. En produccion, usar authMethod: http para validar contra tu API
|
||||
# 3. Configurar CORS apropiadamente
|
||||
# ============================================
|
||||
581
deploy/proxmox/vm-setup.sh
Normal file
581
deploy/proxmox/vm-setup.sh
Normal file
@@ -0,0 +1,581 @@
|
||||
#!/bin/bash
|
||||
# ============================================
|
||||
# Sistema de Flotillas - Crear VM en Proxmox
|
||||
# ============================================
|
||||
# Este script crea una VM en Proxmox VE lista para
|
||||
# instalar el sistema de flotillas
|
||||
#
|
||||
# Ejecutar en el HOST de Proxmox (no en una VM)
|
||||
#
|
||||
# Uso: ./vm-setup.sh [--vmid ID] [--name NOMBRE] [--memory MB] [--cores N]
|
||||
#
|
||||
# Requisitos:
|
||||
# - Proxmox VE 7.x o 8.x
|
||||
# - Almacenamiento local o compartido disponible
|
||||
# - Acceso a internet para descargar ISO
|
||||
# ============================================
|
||||
|
||||
set -e
|
||||
set -o pipefail
|
||||
|
||||
# ---------------------------------------------
|
||||
# Colores
|
||||
# ---------------------------------------------
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
# ---------------------------------------------
|
||||
# Configuracion por defecto
|
||||
# ---------------------------------------------
|
||||
|
||||
# VM
|
||||
VMID="${VMID:-200}"
|
||||
VM_NAME="${VM_NAME:-flotillas}"
|
||||
VM_MEMORY="${VM_MEMORY:-4096}" # MB
|
||||
VM_CORES="${VM_CORES:-4}"
|
||||
VM_DISK_SIZE="${VM_DISK_SIZE:-50}" # GB
|
||||
VM_SOCKETS="${VM_SOCKETS:-1}"
|
||||
|
||||
# Red
|
||||
VM_BRIDGE="${VM_BRIDGE:-vmbr0}"
|
||||
VM_VLAN="${VM_VLAN:-}" # Dejar vacio si no usa VLAN
|
||||
VM_IP="${VM_IP:-dhcp}" # O IP estatica: 192.168.1.100/24
|
||||
VM_GATEWAY="${VM_GATEWAY:-}" # Solo si IP estatica
|
||||
VM_DNS="${VM_DNS:-8.8.8.8}"
|
||||
|
||||
# Almacenamiento
|
||||
STORAGE="${STORAGE:-local-lvm}"
|
||||
ISO_STORAGE="${ISO_STORAGE:-local}"
|
||||
|
||||
# Ubuntu
|
||||
UBUNTU_VERSION="22.04.4"
|
||||
UBUNTU_ISO="ubuntu-${UBUNTU_VERSION}-live-server-amd64.iso"
|
||||
UBUNTU_URL="https://releases.ubuntu.com/22.04/${UBUNTU_ISO}"
|
||||
|
||||
# Cloud-init (para configuracion automatica)
|
||||
USE_CLOUD_INIT="${USE_CLOUD_INIT:-true}"
|
||||
CI_USER="${CI_USER:-flotillas}"
|
||||
CI_PASSWORD="${CI_PASSWORD:-}" # Se genera si esta vacio
|
||||
CI_SSH_KEY="${CI_SSH_KEY:-}" # Ruta a archivo de clave publica
|
||||
|
||||
# ---------------------------------------------
|
||||
# Funciones
|
||||
# ---------------------------------------------
|
||||
log_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}[OK]${NC} $1"
|
||||
}
|
||||
|
||||
log_warn() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# Generar password aleatorio
|
||||
generate_password() {
|
||||
openssl rand -base64 16 | tr -dc 'a-zA-Z0-9' | head -c 16
|
||||
}
|
||||
|
||||
# Verificar si comando existe
|
||||
command_exists() {
|
||||
command -v "$1" &> /dev/null
|
||||
}
|
||||
|
||||
# ---------------------------------------------
|
||||
# Parsear argumentos
|
||||
# ---------------------------------------------
|
||||
parse_args() {
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--vmid)
|
||||
VMID="$2"
|
||||
shift 2
|
||||
;;
|
||||
--name)
|
||||
VM_NAME="$2"
|
||||
shift 2
|
||||
;;
|
||||
--memory)
|
||||
VM_MEMORY="$2"
|
||||
shift 2
|
||||
;;
|
||||
--cores)
|
||||
VM_CORES="$2"
|
||||
shift 2
|
||||
;;
|
||||
--disk)
|
||||
VM_DISK_SIZE="$2"
|
||||
shift 2
|
||||
;;
|
||||
--storage)
|
||||
STORAGE="$2"
|
||||
shift 2
|
||||
;;
|
||||
--bridge)
|
||||
VM_BRIDGE="$2"
|
||||
shift 2
|
||||
;;
|
||||
--ip)
|
||||
VM_IP="$2"
|
||||
shift 2
|
||||
;;
|
||||
--gateway)
|
||||
VM_GATEWAY="$2"
|
||||
shift 2
|
||||
;;
|
||||
--ssh-key)
|
||||
CI_SSH_KEY="$2"
|
||||
shift 2
|
||||
;;
|
||||
--no-cloud-init)
|
||||
USE_CLOUD_INIT=false
|
||||
shift
|
||||
;;
|
||||
--help|-h)
|
||||
show_help
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
log_error "Opcion desconocida: $1"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
show_help() {
|
||||
echo "Sistema de Flotillas - Crear VM en Proxmox"
|
||||
echo ""
|
||||
echo "Uso: $0 [opciones]"
|
||||
echo ""
|
||||
echo "Opciones:"
|
||||
echo " --vmid ID ID de la VM (default: 200)"
|
||||
echo " --name NOMBRE Nombre de la VM (default: flotillas)"
|
||||
echo " --memory MB Memoria RAM en MB (default: 4096)"
|
||||
echo " --cores N Numero de cores (default: 4)"
|
||||
echo " --disk GB Tamanio de disco en GB (default: 50)"
|
||||
echo " --storage NAME Almacenamiento Proxmox (default: local-lvm)"
|
||||
echo " --bridge NAME Bridge de red (default: vmbr0)"
|
||||
echo " --ip IP/CIDR IP estatica o 'dhcp' (default: dhcp)"
|
||||
echo " --gateway IP Gateway (requerido si IP estatica)"
|
||||
echo " --ssh-key FILE Archivo de clave SSH publica"
|
||||
echo " --no-cloud-init No usar cloud-init"
|
||||
echo ""
|
||||
echo "Ejemplos:"
|
||||
echo " $0 --vmid 200 --name flotillas --memory 8192 --cores 4"
|
||||
echo " $0 --ip 192.168.1.100/24 --gateway 192.168.1.1"
|
||||
}
|
||||
|
||||
# ---------------------------------------------
|
||||
# Verificar requisitos
|
||||
# ---------------------------------------------
|
||||
check_requirements() {
|
||||
log_info "Verificando requisitos..."
|
||||
|
||||
# Verificar que estamos en Proxmox
|
||||
if ! command_exists pvesh; then
|
||||
log_error "Este script debe ejecutarse en un host Proxmox"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verificar version de Proxmox
|
||||
PVE_VERSION=$(pveversion --verbose | grep "pve-manager" | awk '{print $2}')
|
||||
log_info "Proxmox VE version: $PVE_VERSION"
|
||||
|
||||
# Verificar que VMID no existe
|
||||
if qm status $VMID &> /dev/null; then
|
||||
log_error "Ya existe una VM con ID $VMID"
|
||||
log_error "Usa --vmid para especificar otro ID"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verificar almacenamiento
|
||||
if ! pvesm status | grep -q "^${STORAGE}"; then
|
||||
log_error "Almacenamiento '$STORAGE' no encontrado"
|
||||
log_error "Almacenamientos disponibles:"
|
||||
pvesm status
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verificar bridge de red
|
||||
if ! ip link show "$VM_BRIDGE" &> /dev/null; then
|
||||
log_warn "Bridge '$VM_BRIDGE' no encontrado"
|
||||
log_warn "Bridges disponibles:"
|
||||
ip link show type bridge
|
||||
fi
|
||||
|
||||
log_success "Requisitos verificados"
|
||||
}
|
||||
|
||||
# ---------------------------------------------
|
||||
# Descargar ISO de Ubuntu
|
||||
# ---------------------------------------------
|
||||
download_ubuntu_iso() {
|
||||
log_info "Verificando ISO de Ubuntu..."
|
||||
|
||||
ISO_PATH="/var/lib/vz/template/iso/${UBUNTU_ISO}"
|
||||
|
||||
if [[ -f "$ISO_PATH" ]]; then
|
||||
log_success "ISO ya existe: $ISO_PATH"
|
||||
return
|
||||
fi
|
||||
|
||||
log_info "Descargando Ubuntu ${UBUNTU_VERSION}..."
|
||||
log_info "URL: $UBUNTU_URL"
|
||||
|
||||
# Crear directorio si no existe
|
||||
mkdir -p /var/lib/vz/template/iso
|
||||
|
||||
# Descargar con wget
|
||||
wget -q --show-progress -O "$ISO_PATH" "$UBUNTU_URL"
|
||||
|
||||
if [[ ! -f "$ISO_PATH" ]]; then
|
||||
log_error "Error al descargar ISO"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_success "ISO descargada: $ISO_PATH"
|
||||
}
|
||||
|
||||
# ---------------------------------------------
|
||||
# Descargar imagen Cloud-Init (alternativa)
|
||||
# ---------------------------------------------
|
||||
download_cloud_image() {
|
||||
log_info "Descargando imagen Ubuntu Cloud..."
|
||||
|
||||
CLOUD_IMAGE="ubuntu-22.04-server-cloudimg-amd64.img"
|
||||
CLOUD_URL="https://cloud-images.ubuntu.com/jammy/current/${CLOUD_IMAGE}"
|
||||
CLOUD_PATH="/var/lib/vz/template/iso/${CLOUD_IMAGE}"
|
||||
|
||||
if [[ -f "$CLOUD_PATH" ]]; then
|
||||
log_success "Imagen cloud ya existe"
|
||||
return
|
||||
fi
|
||||
|
||||
wget -q --show-progress -O "$CLOUD_PATH" "$CLOUD_URL"
|
||||
|
||||
log_success "Imagen descargada: $CLOUD_PATH"
|
||||
}
|
||||
|
||||
# ---------------------------------------------
|
||||
# Crear VM
|
||||
# ---------------------------------------------
|
||||
create_vm() {
|
||||
log_info "Creando VM..."
|
||||
|
||||
# Configuracion de red
|
||||
NET_CONFIG="virtio,bridge=${VM_BRIDGE}"
|
||||
if [[ -n "$VM_VLAN" ]]; then
|
||||
NET_CONFIG="${NET_CONFIG},tag=${VM_VLAN}"
|
||||
fi
|
||||
|
||||
# Crear VM base
|
||||
qm create $VMID \
|
||||
--name "$VM_NAME" \
|
||||
--description "Sistema de Flotillas GPS" \
|
||||
--ostype l26 \
|
||||
--machine q35 \
|
||||
--bios ovmf \
|
||||
--cpu host \
|
||||
--sockets $VM_SOCKETS \
|
||||
--cores $VM_CORES \
|
||||
--memory $VM_MEMORY \
|
||||
--balloon 0 \
|
||||
--net0 "$NET_CONFIG" \
|
||||
--scsihw virtio-scsi-pci \
|
||||
--agent enabled=1
|
||||
|
||||
log_success "VM creada con ID: $VMID"
|
||||
}
|
||||
|
||||
# ---------------------------------------------
|
||||
# Agregar disco
|
||||
# ---------------------------------------------
|
||||
add_disk() {
|
||||
log_info "Creando disco de ${VM_DISK_SIZE}GB..."
|
||||
|
||||
# Agregar disco SCSI
|
||||
qm set $VMID \
|
||||
--scsi0 "${STORAGE}:${VM_DISK_SIZE},discard=on,ssd=1" \
|
||||
--boot order=scsi0
|
||||
|
||||
# Agregar EFI disk
|
||||
qm set $VMID \
|
||||
--efidisk0 "${STORAGE}:1,efitype=4m,pre-enrolled-keys=1"
|
||||
|
||||
log_success "Disco agregado"
|
||||
}
|
||||
|
||||
# ---------------------------------------------
|
||||
# Agregar ISO o Cloud-Init
|
||||
# ---------------------------------------------
|
||||
add_boot_media() {
|
||||
if [[ "$USE_CLOUD_INIT" == "true" ]]; then
|
||||
setup_cloud_init
|
||||
else
|
||||
attach_iso
|
||||
fi
|
||||
}
|
||||
|
||||
attach_iso() {
|
||||
log_info "Adjuntando ISO de instalacion..."
|
||||
|
||||
ISO_PATH="${ISO_STORAGE}:iso/${UBUNTU_ISO}"
|
||||
|
||||
qm set $VMID \
|
||||
--ide2 "$ISO_PATH,media=cdrom" \
|
||||
--boot order="ide2;scsi0"
|
||||
|
||||
log_success "ISO adjuntada"
|
||||
log_warn "Deberas completar la instalacion manualmente"
|
||||
}
|
||||
|
||||
setup_cloud_init() {
|
||||
log_info "Configurando Cloud-Init..."
|
||||
|
||||
# Generar password si no se especifico
|
||||
if [[ -z "$CI_PASSWORD" ]]; then
|
||||
CI_PASSWORD=$(generate_password)
|
||||
log_info "Password generado: $CI_PASSWORD"
|
||||
fi
|
||||
|
||||
# Agregar drive de cloud-init
|
||||
qm set $VMID --ide2 "${STORAGE}:cloudinit"
|
||||
|
||||
# Configurar cloud-init
|
||||
qm set $VMID \
|
||||
--ciuser "$CI_USER" \
|
||||
--cipassword "$CI_PASSWORD"
|
||||
|
||||
# Configurar red
|
||||
if [[ "$VM_IP" == "dhcp" ]]; then
|
||||
qm set $VMID --ipconfig0 ip=dhcp
|
||||
else
|
||||
qm set $VMID --ipconfig0 "ip=${VM_IP},gw=${VM_GATEWAY}"
|
||||
fi
|
||||
|
||||
# DNS
|
||||
qm set $VMID --nameserver "$VM_DNS"
|
||||
|
||||
# SSH key si se especifico
|
||||
if [[ -n "$CI_SSH_KEY" ]] && [[ -f "$CI_SSH_KEY" ]]; then
|
||||
qm set $VMID --sshkeys "$CI_SSH_KEY"
|
||||
log_info "SSH key configurada"
|
||||
fi
|
||||
|
||||
# Importar imagen cloud
|
||||
CLOUD_IMAGE="/var/lib/vz/template/iso/ubuntu-22.04-server-cloudimg-amd64.img"
|
||||
|
||||
if [[ -f "$CLOUD_IMAGE" ]]; then
|
||||
log_info "Importando imagen cloud al disco..."
|
||||
qm importdisk $VMID "$CLOUD_IMAGE" $STORAGE
|
||||
qm set $VMID --scsi0 "${STORAGE}:vm-${VMID}-disk-1"
|
||||
qm resize $VMID scsi0 ${VM_DISK_SIZE}G
|
||||
else
|
||||
log_warn "Imagen cloud no encontrada, usando ISO tradicional"
|
||||
attach_iso
|
||||
return
|
||||
fi
|
||||
|
||||
log_success "Cloud-Init configurado"
|
||||
}
|
||||
|
||||
# ---------------------------------------------
|
||||
# Configurar opciones adicionales
|
||||
# ---------------------------------------------
|
||||
configure_vm_options() {
|
||||
log_info "Configurando opciones adicionales..."
|
||||
|
||||
# Habilitar QEMU guest agent
|
||||
qm set $VMID --agent enabled=1
|
||||
|
||||
# Configurar arranque automatico
|
||||
qm set $VMID --onboot 1
|
||||
|
||||
# Configurar proteccion (evitar eliminacion accidental)
|
||||
# qm set $VMID --protection 1
|
||||
|
||||
# Tags para organizacion
|
||||
qm set $VMID --tags "flotillas,gps,produccion"
|
||||
|
||||
log_success "Opciones configuradas"
|
||||
}
|
||||
|
||||
# ---------------------------------------------
|
||||
# Crear script de post-instalacion
|
||||
# ---------------------------------------------
|
||||
create_post_install_script() {
|
||||
log_info "Creando script de post-instalacion..."
|
||||
|
||||
POST_INSTALL_DIR="/var/lib/vz/snippets"
|
||||
mkdir -p "$POST_INSTALL_DIR"
|
||||
|
||||
cat > "${POST_INSTALL_DIR}/flotillas-postinstall.sh" <<'SCRIPT'
|
||||
#!/bin/bash
|
||||
# Script de post-instalacion para Sistema de Flotillas
|
||||
# Ejecutar despues de instalar Ubuntu
|
||||
|
||||
set -e
|
||||
|
||||
echo "=== Actualizando sistema ==="
|
||||
apt-get update && apt-get upgrade -y
|
||||
|
||||
echo "=== Instalando QEMU Guest Agent ==="
|
||||
apt-get install -y qemu-guest-agent
|
||||
systemctl enable qemu-guest-agent
|
||||
systemctl start qemu-guest-agent
|
||||
|
||||
echo "=== Instalando dependencias basicas ==="
|
||||
apt-get install -y \
|
||||
curl \
|
||||
wget \
|
||||
git \
|
||||
htop \
|
||||
net-tools \
|
||||
ufw
|
||||
|
||||
echo "=== Configurando firewall basico ==="
|
||||
ufw default deny incoming
|
||||
ufw default allow outgoing
|
||||
ufw allow ssh
|
||||
ufw allow 5055/tcp # Traccar GPS
|
||||
ufw --force enable
|
||||
|
||||
echo "=== Listo! ==="
|
||||
echo "Ahora ejecuta el script de instalacion:"
|
||||
echo " cd /opt && git clone REPO_URL flotillas"
|
||||
echo " cd flotillas/deploy/scripts"
|
||||
echo " sudo ./install.sh"
|
||||
SCRIPT
|
||||
|
||||
chmod +x "${POST_INSTALL_DIR}/flotillas-postinstall.sh"
|
||||
|
||||
log_success "Script creado en: ${POST_INSTALL_DIR}/flotillas-postinstall.sh"
|
||||
}
|
||||
|
||||
# ---------------------------------------------
|
||||
# Generar archivo de credenciales
|
||||
# ---------------------------------------------
|
||||
save_credentials() {
|
||||
CREDS_FILE="/root/vm-${VMID}-credentials.txt"
|
||||
|
||||
cat > "$CREDS_FILE" <<EOF
|
||||
# ============================================
|
||||
# Credenciales VM Sistema de Flotillas
|
||||
# ============================================
|
||||
# Generadas: $(date)
|
||||
|
||||
VM_ID: $VMID
|
||||
VM_NAME: $VM_NAME
|
||||
VM_IP: $VM_IP
|
||||
|
||||
# Acceso SSH
|
||||
Usuario: $CI_USER
|
||||
Password: $CI_PASSWORD
|
||||
|
||||
# Despues de iniciar la VM:
|
||||
# 1. Conectarse: ssh ${CI_USER}@IP_DE_LA_VM
|
||||
# 2. Ejecutar script de instalacion
|
||||
|
||||
# Comandos utiles:
|
||||
# - Iniciar VM: qm start $VMID
|
||||
# - Consola: qm terminal $VMID
|
||||
# - Detener: qm shutdown $VMID
|
||||
# - Estado: qm status $VMID
|
||||
EOF
|
||||
|
||||
chmod 600 "$CREDS_FILE"
|
||||
|
||||
log_success "Credenciales guardadas en: $CREDS_FILE"
|
||||
}
|
||||
|
||||
# ---------------------------------------------
|
||||
# Mostrar resumen
|
||||
# ---------------------------------------------
|
||||
show_summary() {
|
||||
echo ""
|
||||
echo -e "${GREEN}============================================${NC}"
|
||||
echo -e "${GREEN} VM CREADA EXITOSAMENTE${NC}"
|
||||
echo -e "${GREEN}============================================${NC}"
|
||||
echo ""
|
||||
echo -e "${BLUE}Configuracion:${NC}"
|
||||
echo " ID: $VMID"
|
||||
echo " Nombre: $VM_NAME"
|
||||
echo " Memoria: ${VM_MEMORY}MB"
|
||||
echo " Cores: $VM_CORES"
|
||||
echo " Disco: ${VM_DISK_SIZE}GB"
|
||||
echo " Red: $VM_BRIDGE"
|
||||
echo ""
|
||||
|
||||
if [[ "$USE_CLOUD_INIT" == "true" ]]; then
|
||||
echo -e "${BLUE}Cloud-Init:${NC}"
|
||||
echo " Usuario: $CI_USER"
|
||||
echo " Password: $CI_PASSWORD"
|
||||
echo " IP: $VM_IP"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
echo -e "${BLUE}Siguientes pasos:${NC}"
|
||||
echo " 1. Iniciar la VM:"
|
||||
echo " qm start $VMID"
|
||||
echo ""
|
||||
|
||||
if [[ "$USE_CLOUD_INIT" == "true" ]]; then
|
||||
echo " 2. Esperar a que inicie y conectarse:"
|
||||
echo " ssh ${CI_USER}@<IP_DE_LA_VM>"
|
||||
echo ""
|
||||
else
|
||||
echo " 2. Abrir consola y completar instalacion de Ubuntu:"
|
||||
echo " qm terminal $VMID"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
echo " 3. Ejecutar script de instalacion del sistema:"
|
||||
echo " git clone <REPO_URL> /opt/flotillas"
|
||||
echo " cd /opt/flotillas/deploy/scripts"
|
||||
echo " sudo ./install.sh"
|
||||
echo ""
|
||||
echo -e "${GREEN}============================================${NC}"
|
||||
}
|
||||
|
||||
# ---------------------------------------------
|
||||
# Main
|
||||
# ---------------------------------------------
|
||||
main() {
|
||||
parse_args "$@"
|
||||
|
||||
echo ""
|
||||
echo -e "${BLUE}============================================${NC}"
|
||||
echo -e "${BLUE} CREAR VM PARA SISTEMA DE FLOTILLAS${NC}"
|
||||
echo -e "${BLUE}============================================${NC}"
|
||||
echo ""
|
||||
|
||||
check_requirements
|
||||
|
||||
if [[ "$USE_CLOUD_INIT" == "true" ]]; then
|
||||
download_cloud_image
|
||||
else
|
||||
download_ubuntu_iso
|
||||
fi
|
||||
|
||||
create_vm
|
||||
add_disk
|
||||
add_boot_media
|
||||
configure_vm_options
|
||||
create_post_install_script
|
||||
save_credentials
|
||||
|
||||
show_summary
|
||||
}
|
||||
|
||||
# Ejecutar
|
||||
main "$@"
|
||||
486
deploy/scripts/backup.sh
Normal file
486
deploy/scripts/backup.sh
Normal file
@@ -0,0 +1,486 @@
|
||||
#!/bin/bash
|
||||
# ============================================
|
||||
# Sistema de Flotillas - Script de Backup
|
||||
# ============================================
|
||||
# Realiza backup de base de datos y configuracion
|
||||
#
|
||||
# Uso: ./backup.sh [--full] [--upload] [--keep-days N]
|
||||
#
|
||||
# Opciones:
|
||||
# --full Incluir backup completo de archivos
|
||||
# --upload Subir a S3/remote despues del backup
|
||||
# --keep-days Dias de retencion (default: 7)
|
||||
# ============================================
|
||||
|
||||
set -e
|
||||
set -o pipefail
|
||||
|
||||
# ---------------------------------------------
|
||||
# Colores
|
||||
# ---------------------------------------------
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
# ---------------------------------------------
|
||||
# Variables de Configuracion
|
||||
# ---------------------------------------------
|
||||
INSTALL_DIR="${INSTALL_DIR:-/opt/flotillas}"
|
||||
BACKUP_DIR="${BACKUP_DIR:-/var/backups/flotillas}"
|
||||
RETENTION_DAYS="${BACKUP_RETENTION_DAYS:-7}"
|
||||
|
||||
# Cargar variables de entorno
|
||||
if [[ -f "$INSTALL_DIR/.env" ]]; then
|
||||
export $(grep -v '^#' "$INSTALL_DIR/.env" | xargs)
|
||||
fi
|
||||
|
||||
# Base de datos
|
||||
DB_HOST="${POSTGRES_HOST:-localhost}"
|
||||
DB_PORT="${POSTGRES_PORT:-5432}"
|
||||
DB_NAME="${POSTGRES_DB:-flotillas}"
|
||||
DB_USER="${POSTGRES_USER:-flotillas}"
|
||||
DB_PASSWORD="${POSTGRES_PASSWORD:-}"
|
||||
|
||||
# S3 (opcional)
|
||||
S3_ENABLED="${S3_ENABLED:-false}"
|
||||
S3_BUCKET="${S3_BUCKET:-}"
|
||||
S3_ENDPOINT="${S3_ENDPOINT:-https://s3.amazonaws.com}"
|
||||
|
||||
# Timestamp para este backup
|
||||
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
||||
BACKUP_NAME="flotillas_${TIMESTAMP}"
|
||||
|
||||
# Flags
|
||||
FULL_BACKUP=false
|
||||
UPLOAD_BACKUP=false
|
||||
|
||||
# ---------------------------------------------
|
||||
# Funciones
|
||||
# ---------------------------------------------
|
||||
log_info() {
|
||||
echo -e "${BLUE}[$(date '+%Y-%m-%d %H:%M:%S')] [INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}[$(date '+%Y-%m-%d %H:%M:%S')] [OK]${NC} $1"
|
||||
}
|
||||
|
||||
log_warn() {
|
||||
echo -e "${YELLOW}[$(date '+%Y-%m-%d %H:%M:%S')] [WARN]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[$(date '+%Y-%m-%d %H:%M:%S')] [ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# Calcular tamanio de archivo
|
||||
get_file_size() {
|
||||
du -h "$1" 2>/dev/null | cut -f1
|
||||
}
|
||||
|
||||
# Verificar espacio disponible
|
||||
check_disk_space() {
|
||||
local required_gb=$1
|
||||
local free_space=$(df -BG "$BACKUP_DIR" | awk 'NR==2 {print $4}' | tr -d 'G')
|
||||
|
||||
if [[ $free_space -lt $required_gb ]]; then
|
||||
log_error "Espacio insuficiente: ${free_space}GB disponible, se requieren ${required_gb}GB"
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# ---------------------------------------------
|
||||
# Parsear argumentos
|
||||
# ---------------------------------------------
|
||||
parse_args() {
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--full)
|
||||
FULL_BACKUP=true
|
||||
shift
|
||||
;;
|
||||
--upload)
|
||||
UPLOAD_BACKUP=true
|
||||
shift
|
||||
;;
|
||||
--keep-days)
|
||||
RETENTION_DAYS="$2"
|
||||
shift 2
|
||||
;;
|
||||
--help|-h)
|
||||
echo "Uso: $0 [--full] [--upload] [--keep-days N]"
|
||||
echo ""
|
||||
echo "Opciones:"
|
||||
echo " --full Backup completo (DB + archivos)"
|
||||
echo " --upload Subir a S3 despues del backup"
|
||||
echo " --keep-days Dias de retencion (default: 7)"
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
log_error "Opcion desconocida: $1"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
# ---------------------------------------------
|
||||
# Crear directorio de backup
|
||||
# ---------------------------------------------
|
||||
prepare_backup_dir() {
|
||||
log_info "Preparando directorio de backup..."
|
||||
|
||||
# Crear directorio si no existe
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
mkdir -p "$BACKUP_DIR/daily"
|
||||
mkdir -p "$BACKUP_DIR/temp"
|
||||
|
||||
# Verificar permisos
|
||||
if [[ ! -w "$BACKUP_DIR" ]]; then
|
||||
log_error "No hay permisos de escritura en $BACKUP_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verificar espacio (minimo 5GB)
|
||||
check_disk_space 5 || exit 1
|
||||
|
||||
log_success "Directorio listo: $BACKUP_DIR"
|
||||
}
|
||||
|
||||
# ---------------------------------------------
|
||||
# Backup de PostgreSQL
|
||||
# ---------------------------------------------
|
||||
backup_database() {
|
||||
log_info "Iniciando backup de PostgreSQL..."
|
||||
|
||||
local db_backup_file="$BACKUP_DIR/temp/${BACKUP_NAME}_db.sql"
|
||||
local db_backup_compressed="$BACKUP_DIR/daily/${BACKUP_NAME}_db.sql.gz"
|
||||
|
||||
# Configurar password para pg_dump
|
||||
export PGPASSWORD="$DB_PASSWORD"
|
||||
|
||||
# Realizar dump
|
||||
log_info "Exportando base de datos ${DB_NAME}..."
|
||||
|
||||
pg_dump \
|
||||
-h "$DB_HOST" \
|
||||
-p "$DB_PORT" \
|
||||
-U "$DB_USER" \
|
||||
-d "$DB_NAME" \
|
||||
--format=plain \
|
||||
--no-owner \
|
||||
--no-acl \
|
||||
--verbose \
|
||||
-f "$db_backup_file" 2>/dev/null
|
||||
|
||||
# Comprimir
|
||||
log_info "Comprimiendo backup..."
|
||||
gzip -9 -c "$db_backup_file" > "$db_backup_compressed"
|
||||
|
||||
# Limpiar archivo temporal
|
||||
rm -f "$db_backup_file"
|
||||
|
||||
# Limpiar variable de password
|
||||
unset PGPASSWORD
|
||||
|
||||
local size=$(get_file_size "$db_backup_compressed")
|
||||
log_success "Backup de BD completado: $db_backup_compressed ($size)"
|
||||
|
||||
# Backup de Traccar DB si existe
|
||||
if [[ -n "${TRACCAR_DB_NAME:-}" ]]; then
|
||||
log_info "Exportando base de datos Traccar..."
|
||||
|
||||
local traccar_backup="$BACKUP_DIR/daily/${BACKUP_NAME}_traccar.sql.gz"
|
||||
|
||||
export PGPASSWORD="$DB_PASSWORD"
|
||||
|
||||
pg_dump \
|
||||
-h "$DB_HOST" \
|
||||
-p "$DB_PORT" \
|
||||
-U "$DB_USER" \
|
||||
-d "${TRACCAR_DB_NAME}" \
|
||||
--format=plain \
|
||||
--no-owner \
|
||||
--no-acl \
|
||||
2>/dev/null | gzip -9 > "$traccar_backup"
|
||||
|
||||
unset PGPASSWORD
|
||||
|
||||
local traccar_size=$(get_file_size "$traccar_backup")
|
||||
log_success "Backup de Traccar completado: $traccar_backup ($traccar_size)"
|
||||
fi
|
||||
}
|
||||
|
||||
# ---------------------------------------------
|
||||
# Backup de configuracion
|
||||
# ---------------------------------------------
|
||||
backup_config() {
|
||||
log_info "Respaldando archivos de configuracion..."
|
||||
|
||||
local config_backup="$BACKUP_DIR/daily/${BACKUP_NAME}_config.tar.gz"
|
||||
|
||||
# Lista de archivos a respaldar
|
||||
local files_to_backup=(
|
||||
"$INSTALL_DIR/.env"
|
||||
"$INSTALL_DIR/deploy"
|
||||
"/opt/traccar/conf/traccar.xml"
|
||||
"/opt/mediamtx/mediamtx.yml"
|
||||
"/etc/mosquitto/conf.d/flotillas.conf"
|
||||
"/etc/systemd/system/flotillas-*.service"
|
||||
"/etc/systemd/system/mediamtx.service"
|
||||
)
|
||||
|
||||
# Crear archivo temporal con lista de archivos existentes
|
||||
local file_list=$(mktemp)
|
||||
|
||||
for file in "${files_to_backup[@]}"; do
|
||||
if [[ -e "$file" ]]; then
|
||||
echo "$file" >> "$file_list"
|
||||
fi
|
||||
done
|
||||
|
||||
# Crear tarball
|
||||
tar -czf "$config_backup" -T "$file_list" 2>/dev/null || true
|
||||
|
||||
# Limpiar
|
||||
rm -f "$file_list"
|
||||
|
||||
local size=$(get_file_size "$config_backup")
|
||||
log_success "Backup de configuracion completado: $config_backup ($size)"
|
||||
}
|
||||
|
||||
# ---------------------------------------------
|
||||
# Backup completo (archivos)
|
||||
# ---------------------------------------------
|
||||
backup_full() {
|
||||
if [[ "$FULL_BACKUP" != "true" ]]; then
|
||||
return
|
||||
fi
|
||||
|
||||
log_info "Iniciando backup completo de archivos..."
|
||||
|
||||
local full_backup="$BACKUP_DIR/daily/${BACKUP_NAME}_full.tar.gz"
|
||||
|
||||
# Excluir directorios grandes innecesarios
|
||||
tar -czf "$full_backup" \
|
||||
--exclude="$INSTALL_DIR/backend/venv" \
|
||||
--exclude="$INSTALL_DIR/frontend/node_modules" \
|
||||
--exclude="$INSTALL_DIR/.git" \
|
||||
--exclude="*.log" \
|
||||
--exclude="*.pyc" \
|
||||
--exclude="__pycache__" \
|
||||
"$INSTALL_DIR" 2>/dev/null
|
||||
|
||||
local size=$(get_file_size "$full_backup")
|
||||
log_success "Backup completo: $full_backup ($size)"
|
||||
}
|
||||
|
||||
# ---------------------------------------------
|
||||
# Rotar backups antiguos
|
||||
# ---------------------------------------------
|
||||
rotate_backups() {
|
||||
log_info "Rotando backups antiguos (retencion: ${RETENTION_DAYS} dias)..."
|
||||
|
||||
local deleted=0
|
||||
|
||||
# Encontrar y eliminar backups mas antiguos que RETENTION_DAYS
|
||||
while IFS= read -r -d '' file; do
|
||||
rm -f "$file"
|
||||
((deleted++))
|
||||
done < <(find "$BACKUP_DIR/daily" -type f -name "flotillas_*.gz" -mtime +${RETENTION_DAYS} -print0 2>/dev/null)
|
||||
|
||||
if [[ $deleted -gt 0 ]]; then
|
||||
log_info "Eliminados $deleted backups antiguos"
|
||||
fi
|
||||
|
||||
# Mostrar espacio usado
|
||||
local space_used=$(du -sh "$BACKUP_DIR" 2>/dev/null | cut -f1)
|
||||
log_info "Espacio total usado por backups: $space_used"
|
||||
}
|
||||
|
||||
# ---------------------------------------------
|
||||
# Subir a S3
|
||||
# ---------------------------------------------
|
||||
upload_to_s3() {
|
||||
if [[ "$UPLOAD_BACKUP" != "true" ]]; then
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ "$S3_ENABLED" != "true" ]]; then
|
||||
log_warn "S3 no esta habilitado. Configura S3_ENABLED=true en .env"
|
||||
return
|
||||
fi
|
||||
|
||||
if ! command -v aws &> /dev/null; then
|
||||
log_warn "AWS CLI no instalado. Instalando..."
|
||||
pip3 install awscli -q
|
||||
fi
|
||||
|
||||
log_info "Subiendo backups a S3..."
|
||||
|
||||
# Configurar credenciales
|
||||
export AWS_ACCESS_KEY_ID="${S3_ACCESS_KEY}"
|
||||
export AWS_SECRET_ACCESS_KEY="${S3_SECRET_KEY}"
|
||||
|
||||
# Subir archivos del dia
|
||||
for file in "$BACKUP_DIR/daily/${BACKUP_NAME}"*.gz; do
|
||||
if [[ -f "$file" ]]; then
|
||||
local filename=$(basename "$file")
|
||||
log_info "Subiendo: $filename"
|
||||
|
||||
aws s3 cp "$file" "s3://${S3_BUCKET}/backups/$(date +%Y/%m)/${filename}" \
|
||||
--endpoint-url "$S3_ENDPOINT" \
|
||||
--quiet
|
||||
|
||||
log_success "Subido: $filename"
|
||||
fi
|
||||
done
|
||||
|
||||
# Limpiar credenciales
|
||||
unset AWS_ACCESS_KEY_ID
|
||||
unset AWS_SECRET_ACCESS_KEY
|
||||
|
||||
log_success "Backup subido a S3"
|
||||
}
|
||||
|
||||
# ---------------------------------------------
|
||||
# Crear indice de backups
|
||||
# ---------------------------------------------
|
||||
create_backup_index() {
|
||||
log_info "Actualizando indice de backups..."
|
||||
|
||||
local index_file="$BACKUP_DIR/backup_index.txt"
|
||||
|
||||
# Cabecera
|
||||
cat > "$index_file" <<EOF
|
||||
# Indice de Backups - Sistema de Flotillas
|
||||
# Generado: $(date)
|
||||
# Retencion: ${RETENTION_DAYS} dias
|
||||
#
|
||||
# Formato: fecha | tipo | archivo | tamanio
|
||||
#
|
||||
EOF
|
||||
|
||||
# Listar backups
|
||||
for file in $(ls -t "$BACKUP_DIR/daily"/*.gz 2>/dev/null); do
|
||||
local filename=$(basename "$file")
|
||||
local size=$(get_file_size "$file")
|
||||
local date=$(stat -c %y "$file" 2>/dev/null | cut -d' ' -f1)
|
||||
local type="unknown"
|
||||
|
||||
case "$filename" in
|
||||
*_db.sql.gz) type="database" ;;
|
||||
*_config.tar.gz) type="config" ;;
|
||||
*_full.tar.gz) type="full" ;;
|
||||
*_traccar.sql.gz) type="traccar" ;;
|
||||
esac
|
||||
|
||||
echo "$date | $type | $filename | $size" >> "$index_file"
|
||||
done
|
||||
|
||||
log_success "Indice actualizado: $index_file"
|
||||
}
|
||||
|
||||
# ---------------------------------------------
|
||||
# Verificar integridad del backup
|
||||
# ---------------------------------------------
|
||||
verify_backup() {
|
||||
log_info "Verificando integridad de backups..."
|
||||
|
||||
local errors=0
|
||||
|
||||
for file in "$BACKUP_DIR/daily/${BACKUP_NAME}"*.gz; do
|
||||
if [[ -f "$file" ]]; then
|
||||
if gzip -t "$file" 2>/dev/null; then
|
||||
log_success "OK: $(basename "$file")"
|
||||
else
|
||||
log_error "CORRUPTO: $(basename "$file")"
|
||||
((errors++))
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ $errors -gt 0 ]]; then
|
||||
log_error "Se encontraron $errors archivos corruptos"
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# ---------------------------------------------
|
||||
# Enviar notificacion
|
||||
# ---------------------------------------------
|
||||
send_notification() {
|
||||
local status="$1"
|
||||
local message="$2"
|
||||
|
||||
# Telegram (si esta configurado)
|
||||
if [[ -n "${TELEGRAM_BOT_TOKEN:-}" ]] && [[ -n "${TELEGRAM_CHAT_ID:-}" ]]; then
|
||||
local emoji="✅"
|
||||
[[ "$status" == "error" ]] && emoji="❌"
|
||||
|
||||
curl -s -X POST \
|
||||
"https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
|
||||
-d chat_id="${TELEGRAM_CHAT_ID}" \
|
||||
-d text="${emoji} Backup Flotillas: ${message}" \
|
||||
> /dev/null 2>&1
|
||||
fi
|
||||
}
|
||||
|
||||
# ---------------------------------------------
|
||||
# Mostrar resumen
|
||||
# ---------------------------------------------
|
||||
show_summary() {
|
||||
echo ""
|
||||
log_success "=========================================="
|
||||
log_success "BACKUP COMPLETADO"
|
||||
log_success "=========================================="
|
||||
echo ""
|
||||
echo "Archivos creados:"
|
||||
|
||||
for file in "$BACKUP_DIR/daily/${BACKUP_NAME}"*.gz; do
|
||||
if [[ -f "$file" ]]; then
|
||||
echo " - $(basename "$file") ($(get_file_size "$file"))"
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "Ubicacion: $BACKUP_DIR/daily/"
|
||||
echo "Retencion: ${RETENTION_DAYS} dias"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# ---------------------------------------------
|
||||
# Main
|
||||
# ---------------------------------------------
|
||||
main() {
|
||||
parse_args "$@"
|
||||
|
||||
log_info "=========================================="
|
||||
log_info "INICIANDO BACKUP - $(date)"
|
||||
log_info "=========================================="
|
||||
|
||||
# Ejecutar pasos
|
||||
prepare_backup_dir
|
||||
backup_database
|
||||
backup_config
|
||||
backup_full
|
||||
rotate_backups
|
||||
verify_backup || true
|
||||
upload_to_s3
|
||||
create_backup_index
|
||||
show_summary
|
||||
|
||||
# Notificar exito
|
||||
send_notification "success" "Backup completado exitosamente"
|
||||
}
|
||||
|
||||
# Manejo de errores global
|
||||
trap 'log_error "Backup fallido en linea $LINENO"; send_notification "error" "Backup fallido"; exit 1' ERR
|
||||
|
||||
# Ejecutar
|
||||
main "$@"
|
||||
247
deploy/scripts/health-check.sh
Normal file
247
deploy/scripts/health-check.sh
Normal file
@@ -0,0 +1,247 @@
|
||||
#!/bin/bash
|
||||
# ============================================
|
||||
# Sistema de Flotillas - Health Check
|
||||
# ============================================
|
||||
# Verifica el estado de todos los servicios
|
||||
#
|
||||
# Uso: ./health-check.sh [--verbose] [--json]
|
||||
# ============================================
|
||||
|
||||
set -o pipefail
|
||||
|
||||
# ---------------------------------------------
|
||||
# Colores
|
||||
# ---------------------------------------------
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
# Variables
|
||||
INSTALL_DIR="${INSTALL_DIR:-/opt/flotillas}"
|
||||
VERBOSE=false
|
||||
JSON_OUTPUT=false
|
||||
EXIT_CODE=0
|
||||
|
||||
# Cargar variables de entorno
|
||||
if [[ -f "$INSTALL_DIR/.env" ]]; then
|
||||
export $(grep -v '^#' "$INSTALL_DIR/.env" | xargs)
|
||||
fi
|
||||
|
||||
# ---------------------------------------------
|
||||
# Funciones
|
||||
# ---------------------------------------------
|
||||
parse_args() {
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--verbose|-v) VERBOSE=true; shift ;;
|
||||
--json|-j) JSON_OUTPUT=true; shift ;;
|
||||
--help|-h)
|
||||
echo "Uso: $0 [--verbose] [--json]"
|
||||
exit 0
|
||||
;;
|
||||
*) shift ;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
check_service() {
|
||||
local service="$1"
|
||||
local name="$2"
|
||||
|
||||
if systemctl is-active --quiet "$service" 2>/dev/null; then
|
||||
echo "ok"
|
||||
else
|
||||
echo "fail"
|
||||
fi
|
||||
}
|
||||
|
||||
check_port() {
|
||||
local port="$1"
|
||||
|
||||
if nc -z localhost "$port" 2>/dev/null; then
|
||||
echo "ok"
|
||||
else
|
||||
echo "fail"
|
||||
fi
|
||||
}
|
||||
|
||||
check_url() {
|
||||
local url="$1"
|
||||
local timeout="${2:-5}"
|
||||
|
||||
if curl -sf --max-time "$timeout" "$url" > /dev/null 2>&1; then
|
||||
echo "ok"
|
||||
else
|
||||
echo "fail"
|
||||
fi
|
||||
}
|
||||
|
||||
check_db() {
|
||||
local host="${POSTGRES_HOST:-localhost}"
|
||||
local port="${POSTGRES_PORT:-5432}"
|
||||
local db="${POSTGRES_DB:-flotillas}"
|
||||
local user="${POSTGRES_USER:-flotillas}"
|
||||
|
||||
if PGPASSWORD="${POSTGRES_PASSWORD}" psql -h "$host" -p "$port" -U "$user" -d "$db" -c "SELECT 1" > /dev/null 2>&1; then
|
||||
echo "ok"
|
||||
else
|
||||
echo "fail"
|
||||
fi
|
||||
}
|
||||
|
||||
check_redis() {
|
||||
local password="${REDIS_PASSWORD:-}"
|
||||
|
||||
if [[ -n "$password" ]]; then
|
||||
if redis-cli -a "$password" ping 2>/dev/null | grep -q "PONG"; then
|
||||
echo "ok"
|
||||
else
|
||||
echo "fail"
|
||||
fi
|
||||
else
|
||||
if redis-cli ping 2>/dev/null | grep -q "PONG"; then
|
||||
echo "ok"
|
||||
else
|
||||
echo "fail"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
print_status() {
|
||||
local name="$1"
|
||||
local status="$2"
|
||||
local details="$3"
|
||||
|
||||
if [[ "$JSON_OUTPUT" == "true" ]]; then
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ "$status" == "ok" ]]; then
|
||||
echo -e " ${GREEN}[OK]${NC} $name"
|
||||
else
|
||||
echo -e " ${RED}[FAIL]${NC} $name"
|
||||
EXIT_CODE=1
|
||||
fi
|
||||
|
||||
if [[ "$VERBOSE" == "true" ]] && [[ -n "$details" ]]; then
|
||||
echo -e " $details"
|
||||
fi
|
||||
}
|
||||
|
||||
# ---------------------------------------------
|
||||
# Main
|
||||
# ---------------------------------------------
|
||||
main() {
|
||||
parse_args "$@"
|
||||
|
||||
# Resultados para JSON
|
||||
declare -A results
|
||||
|
||||
if [[ "$JSON_OUTPUT" != "true" ]]; then
|
||||
echo ""
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo -e "${BLUE} HEALTH CHECK - Sistema de Flotillas${NC}"
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo ""
|
||||
echo -e "${BLUE}Servicios Systemd:${NC}"
|
||||
fi
|
||||
|
||||
# Servicios systemd
|
||||
results[flotillas_api]=$(check_service "flotillas-api" "API Backend")
|
||||
print_status "flotillas-api" "${results[flotillas_api]}"
|
||||
|
||||
results[flotillas_web]=$(check_service "flotillas-web" "Frontend Web")
|
||||
print_status "flotillas-web" "${results[flotillas_web]}"
|
||||
|
||||
results[postgresql]=$(check_service "postgresql" "PostgreSQL")
|
||||
print_status "postgresql" "${results[postgresql]}"
|
||||
|
||||
results[redis]=$(check_service "redis-server" "Redis")
|
||||
print_status "redis" "${results[redis]}"
|
||||
|
||||
results[traccar]=$(check_service "traccar" "Traccar GPS")
|
||||
print_status "traccar" "${results[traccar]}"
|
||||
|
||||
results[mediamtx]=$(check_service "mediamtx" "MediaMTX")
|
||||
print_status "mediamtx" "${results[mediamtx]}"
|
||||
|
||||
results[mosquitto]=$(check_service "mosquitto" "Mosquitto MQTT")
|
||||
print_status "mosquitto" "${results[mosquitto]}"
|
||||
|
||||
if [[ "$JSON_OUTPUT" != "true" ]]; then
|
||||
echo ""
|
||||
echo -e "${BLUE}Conectividad:${NC}"
|
||||
fi
|
||||
|
||||
# Puertos
|
||||
results[port_api]=$(check_port "${API_PORT:-8000}")
|
||||
print_status "API (puerto ${API_PORT:-8000})" "${results[port_api]}"
|
||||
|
||||
results[port_frontend]=$(check_port "${FRONTEND_PORT:-3000}")
|
||||
print_status "Frontend (puerto ${FRONTEND_PORT:-3000})" "${results[port_frontend]}"
|
||||
|
||||
results[port_traccar]=$(check_port "${TRACCAR_PORT:-5055}")
|
||||
print_status "Traccar (puerto ${TRACCAR_PORT:-5055})" "${results[port_traccar]}"
|
||||
|
||||
results[port_rtsp]=$(check_port 8554)
|
||||
print_status "MediaMTX RTSP (puerto 8554)" "${results[port_rtsp]}"
|
||||
|
||||
if [[ "$JSON_OUTPUT" != "true" ]]; then
|
||||
echo ""
|
||||
echo -e "${BLUE}Base de Datos:${NC}"
|
||||
fi
|
||||
|
||||
# Base de datos
|
||||
results[db_connection]=$(check_db)
|
||||
print_status "PostgreSQL conexion" "${results[db_connection]}"
|
||||
|
||||
results[redis_connection]=$(check_redis)
|
||||
print_status "Redis conexion" "${results[redis_connection]}"
|
||||
|
||||
if [[ "$JSON_OUTPUT" != "true" ]]; then
|
||||
echo ""
|
||||
echo -e "${BLUE}APIs:${NC}"
|
||||
fi
|
||||
|
||||
# APIs
|
||||
results[api_health]=$(check_url "http://localhost:${API_PORT:-8000}/health")
|
||||
print_status "API /health" "${results[api_health]}"
|
||||
|
||||
results[mediamtx_api]=$(check_url "http://localhost:9997/v3/paths/list")
|
||||
print_status "MediaMTX API" "${results[mediamtx_api]}"
|
||||
|
||||
# JSON output
|
||||
if [[ "$JSON_OUTPUT" == "true" ]]; then
|
||||
echo "{"
|
||||
echo " \"timestamp\": \"$(date -Iseconds)\","
|
||||
echo " \"status\": \"$([ $EXIT_CODE -eq 0 ] && echo 'healthy' || echo 'unhealthy')\","
|
||||
echo " \"checks\": {"
|
||||
|
||||
first=true
|
||||
for key in "${!results[@]}"; do
|
||||
if [[ "$first" != "true" ]]; then
|
||||
echo ","
|
||||
fi
|
||||
first=false
|
||||
printf " \"%s\": \"%s\"" "$key" "${results[$key]}"
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo " }"
|
||||
echo "}"
|
||||
else
|
||||
echo ""
|
||||
if [[ $EXIT_CODE -eq 0 ]]; then
|
||||
echo -e "${GREEN}Estado general: SALUDABLE${NC}"
|
||||
else
|
||||
echo -e "${RED}Estado general: PROBLEMAS DETECTADOS${NC}"
|
||||
fi
|
||||
echo ""
|
||||
fi
|
||||
|
||||
exit $EXIT_CODE
|
||||
}
|
||||
|
||||
main "$@"
|
||||
1080
deploy/scripts/install.sh
Normal file
1080
deploy/scripts/install.sh
Normal file
File diff suppressed because it is too large
Load Diff
133
deploy/scripts/logs.sh
Normal file
133
deploy/scripts/logs.sh
Normal file
@@ -0,0 +1,133 @@
|
||||
#!/bin/bash
|
||||
# ============================================
|
||||
# Sistema de Flotillas - Visor de Logs
|
||||
# ============================================
|
||||
# Muestra logs de los diferentes servicios
|
||||
#
|
||||
# Uso: ./logs.sh [servicio] [--follow] [--lines N]
|
||||
#
|
||||
# Servicios: api, web, traccar, mediamtx, postgres, redis, all
|
||||
# ============================================
|
||||
|
||||
# Variables
|
||||
LINES="${LINES:-100}"
|
||||
FOLLOW=false
|
||||
SERVICE="api"
|
||||
|
||||
# Colores
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
# ---------------------------------------------
|
||||
# Parsear argumentos
|
||||
# ---------------------------------------------
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
api|web|traccar|mediamtx|postgres|redis|mosquitto|all)
|
||||
SERVICE="$1"
|
||||
shift
|
||||
;;
|
||||
-f|--follow)
|
||||
FOLLOW=true
|
||||
shift
|
||||
;;
|
||||
-n|--lines)
|
||||
LINES="$2"
|
||||
shift 2
|
||||
;;
|
||||
--help|-h)
|
||||
echo "Sistema de Flotillas - Visor de Logs"
|
||||
echo ""
|
||||
echo "Uso: $0 [servicio] [opciones]"
|
||||
echo ""
|
||||
echo "Servicios:"
|
||||
echo " api - Backend FastAPI"
|
||||
echo " web - Frontend"
|
||||
echo " traccar - Traccar GPS"
|
||||
echo " mediamtx - Video streaming"
|
||||
echo " postgres - Base de datos"
|
||||
echo " redis - Cache"
|
||||
echo " mosquitto - MQTT"
|
||||
echo " all - Todos los servicios"
|
||||
echo ""
|
||||
echo "Opciones:"
|
||||
echo " -f, --follow Seguir logs en tiempo real"
|
||||
echo " -n, --lines N Numero de lineas (default: 100)"
|
||||
echo ""
|
||||
echo "Ejemplos:"
|
||||
echo " $0 api -f # Seguir logs de API"
|
||||
echo " $0 traccar -n 500 # Ultimas 500 lineas de Traccar"
|
||||
echo " $0 all -f # Todos los logs en tiempo real"
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "Opcion desconocida: $1"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# ---------------------------------------------
|
||||
# Mostrar logs
|
||||
# ---------------------------------------------
|
||||
show_logs() {
|
||||
local unit="$1"
|
||||
local name="$2"
|
||||
|
||||
echo -e "${BLUE}=== Logs de $name ===${NC}"
|
||||
|
||||
local cmd="journalctl -u $unit -n $LINES --no-pager"
|
||||
|
||||
if [[ "$FOLLOW" == "true" ]]; then
|
||||
cmd="journalctl -u $unit -f"
|
||||
fi
|
||||
|
||||
$cmd 2>/dev/null || echo "Servicio no disponible o sin logs"
|
||||
echo ""
|
||||
}
|
||||
|
||||
case $SERVICE in
|
||||
api)
|
||||
show_logs "flotillas-api" "API Backend"
|
||||
;;
|
||||
web)
|
||||
show_logs "flotillas-web" "Frontend"
|
||||
;;
|
||||
traccar)
|
||||
show_logs "traccar" "Traccar GPS"
|
||||
# Tambien mostrar log de archivo si existe
|
||||
if [[ -f "/opt/traccar/logs/tracker-server.log" ]]; then
|
||||
echo -e "${BLUE}=== Log de archivo Traccar ===${NC}"
|
||||
tail -n $LINES /opt/traccar/logs/tracker-server.log
|
||||
fi
|
||||
;;
|
||||
mediamtx)
|
||||
show_logs "mediamtx" "MediaMTX"
|
||||
;;
|
||||
postgres)
|
||||
show_logs "postgresql" "PostgreSQL"
|
||||
;;
|
||||
redis)
|
||||
show_logs "redis-server" "Redis"
|
||||
;;
|
||||
mosquitto)
|
||||
show_logs "mosquitto" "Mosquitto MQTT"
|
||||
;;
|
||||
all)
|
||||
if [[ "$FOLLOW" == "true" ]]; then
|
||||
echo "Mostrando todos los logs en tiempo real..."
|
||||
echo "Presiona Ctrl+C para salir"
|
||||
echo ""
|
||||
journalctl -u flotillas-api -u flotillas-web -u traccar -u mediamtx -u mosquitto -f
|
||||
else
|
||||
show_logs "flotillas-api" "API Backend"
|
||||
show_logs "flotillas-web" "Frontend"
|
||||
show_logs "traccar" "Traccar GPS"
|
||||
show_logs "mediamtx" "MediaMTX"
|
||||
show_logs "mosquitto" "Mosquitto MQTT"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
549
deploy/scripts/restore.sh
Normal file
549
deploy/scripts/restore.sh
Normal file
@@ -0,0 +1,549 @@
|
||||
#!/bin/bash
|
||||
# ============================================
|
||||
# Sistema de Flotillas - Script de Restauracion
|
||||
# ============================================
|
||||
# Restaura backups de base de datos y configuracion
|
||||
#
|
||||
# Uso: ./restore.sh [--db backup.sql.gz] [--config config.tar.gz] [--list]
|
||||
#
|
||||
# Opciones:
|
||||
# --db FILE Restaurar backup de base de datos
|
||||
# --config FILE Restaurar backup de configuracion
|
||||
# --list Listar backups disponibles
|
||||
# --latest Restaurar el backup mas reciente
|
||||
# --date YYYYMMDD Restaurar backup de fecha especifica
|
||||
# ============================================
|
||||
|
||||
set -e
|
||||
set -o pipefail
|
||||
|
||||
# ---------------------------------------------
|
||||
# Colores
|
||||
# ---------------------------------------------
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
# ---------------------------------------------
|
||||
# Variables
|
||||
# ---------------------------------------------
|
||||
INSTALL_DIR="${INSTALL_DIR:-/opt/flotillas}"
|
||||
BACKUP_DIR="${BACKUP_DIR:-/var/backups/flotillas}"
|
||||
|
||||
# Cargar variables de entorno
|
||||
if [[ -f "$INSTALL_DIR/.env" ]]; then
|
||||
export $(grep -v '^#' "$INSTALL_DIR/.env" | xargs)
|
||||
fi
|
||||
|
||||
# Base de datos
|
||||
DB_HOST="${POSTGRES_HOST:-localhost}"
|
||||
DB_PORT="${POSTGRES_PORT:-5432}"
|
||||
DB_NAME="${POSTGRES_DB:-flotillas}"
|
||||
DB_USER="${POSTGRES_USER:-flotillas}"
|
||||
DB_PASSWORD="${POSTGRES_PASSWORD:-}"
|
||||
|
||||
# Opciones
|
||||
DB_BACKUP=""
|
||||
CONFIG_BACKUP=""
|
||||
LIST_ONLY=false
|
||||
USE_LATEST=false
|
||||
RESTORE_DATE=""
|
||||
|
||||
# ---------------------------------------------
|
||||
# Funciones
|
||||
# ---------------------------------------------
|
||||
log_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}[OK]${NC} $1"
|
||||
}
|
||||
|
||||
log_warn() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# ---------------------------------------------
|
||||
# Parsear argumentos
|
||||
# ---------------------------------------------
|
||||
parse_args() {
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--db)
|
||||
DB_BACKUP="$2"
|
||||
shift 2
|
||||
;;
|
||||
--config)
|
||||
CONFIG_BACKUP="$2"
|
||||
shift 2
|
||||
;;
|
||||
--list)
|
||||
LIST_ONLY=true
|
||||
shift
|
||||
;;
|
||||
--latest)
|
||||
USE_LATEST=true
|
||||
shift
|
||||
;;
|
||||
--date)
|
||||
RESTORE_DATE="$2"
|
||||
shift 2
|
||||
;;
|
||||
--help|-h)
|
||||
show_help
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
log_error "Opcion desconocida: $1"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
show_help() {
|
||||
echo "Sistema de Flotillas - Restauracion de Backup"
|
||||
echo ""
|
||||
echo "Uso: $0 [opciones]"
|
||||
echo ""
|
||||
echo "Opciones:"
|
||||
echo " --db FILE Restaurar backup de base de datos"
|
||||
echo " --config FILE Restaurar backup de configuracion"
|
||||
echo " --list Listar backups disponibles"
|
||||
echo " --latest Restaurar el backup mas reciente"
|
||||
echo " --date YYYYMMDD Restaurar backup de fecha especifica"
|
||||
echo ""
|
||||
echo "Ejemplos:"
|
||||
echo " $0 --list"
|
||||
echo " $0 --latest"
|
||||
echo " $0 --date 20240115"
|
||||
echo " $0 --db /var/backups/flotillas/daily/flotillas_20240115_030000_db.sql.gz"
|
||||
}
|
||||
|
||||
# ---------------------------------------------
|
||||
# Listar backups disponibles
|
||||
# ---------------------------------------------
|
||||
list_backups() {
|
||||
echo ""
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo -e "${BLUE} BACKUPS DISPONIBLES${NC}"
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo ""
|
||||
|
||||
if [[ ! -d "$BACKUP_DIR/daily" ]]; then
|
||||
log_warn "No hay backups disponibles"
|
||||
return
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}Base de Datos:${NC}"
|
||||
echo "---------------------------------------------"
|
||||
printf "%-40s %10s %s\n" "Archivo" "Tamanio" "Fecha"
|
||||
echo "---------------------------------------------"
|
||||
|
||||
for file in $(ls -t "$BACKUP_DIR/daily"/*_db.sql.gz 2>/dev/null); do
|
||||
local name=$(basename "$file")
|
||||
local size=$(du -h "$file" | cut -f1)
|
||||
local date=$(stat -c %y "$file" | cut -d' ' -f1)
|
||||
printf "%-40s %10s %s\n" "$name" "$size" "$date"
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo -e "${GREEN}Configuracion:${NC}"
|
||||
echo "---------------------------------------------"
|
||||
|
||||
for file in $(ls -t "$BACKUP_DIR/daily"/*_config.tar.gz 2>/dev/null); do
|
||||
local name=$(basename "$file")
|
||||
local size=$(du -h "$file" | cut -f1)
|
||||
local date=$(stat -c %y "$file" | cut -d' ' -f1)
|
||||
printf "%-40s %10s %s\n" "$name" "$size" "$date"
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo -e "${GREEN}Backups Completos:${NC}"
|
||||
echo "---------------------------------------------"
|
||||
|
||||
for file in $(ls -t "$BACKUP_DIR/daily"/*_full.tar.gz 2>/dev/null); do
|
||||
local name=$(basename "$file")
|
||||
local size=$(du -h "$file" | cut -f1)
|
||||
local date=$(stat -c %y "$file" | cut -d' ' -f1)
|
||||
printf "%-40s %10s %s\n" "$name" "$size" "$date"
|
||||
done
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
# ---------------------------------------------
|
||||
# Encontrar backup mas reciente
|
||||
# ---------------------------------------------
|
||||
find_latest_backup() {
|
||||
local type="$1" # db, config, full
|
||||
|
||||
local pattern="*_${type}.*gz"
|
||||
|
||||
local latest=$(ls -t "$BACKUP_DIR/daily"/$pattern 2>/dev/null | head -1)
|
||||
|
||||
if [[ -z "$latest" ]]; then
|
||||
log_error "No se encontro backup de tipo: $type"
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "$latest"
|
||||
}
|
||||
|
||||
# ---------------------------------------------
|
||||
# Encontrar backup por fecha
|
||||
# ---------------------------------------------
|
||||
find_backup_by_date() {
|
||||
local type="$1"
|
||||
local date="$2"
|
||||
|
||||
local pattern="flotillas_${date}*_${type}.*gz"
|
||||
|
||||
local found=$(ls -t "$BACKUP_DIR/daily"/$pattern 2>/dev/null | head -1)
|
||||
|
||||
if [[ -z "$found" ]]; then
|
||||
log_error "No se encontro backup de tipo '$type' para fecha: $date"
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "$found"
|
||||
}
|
||||
|
||||
# ---------------------------------------------
|
||||
# Detener servicios
|
||||
# ---------------------------------------------
|
||||
stop_services() {
|
||||
log_info "Deteniendo servicios..."
|
||||
|
||||
systemctl stop flotillas-api 2>/dev/null || true
|
||||
systemctl stop flotillas-web 2>/dev/null || true
|
||||
|
||||
# Esperar a que se detengan
|
||||
sleep 2
|
||||
|
||||
log_success "Servicios detenidos"
|
||||
}
|
||||
|
||||
# ---------------------------------------------
|
||||
# Iniciar servicios
|
||||
# ---------------------------------------------
|
||||
start_services() {
|
||||
log_info "Iniciando servicios..."
|
||||
|
||||
systemctl start flotillas-api 2>/dev/null || true
|
||||
systemctl start flotillas-web 2>/dev/null || true
|
||||
|
||||
log_success "Servicios iniciados"
|
||||
}
|
||||
|
||||
# ---------------------------------------------
|
||||
# Restaurar base de datos
|
||||
# ---------------------------------------------
|
||||
restore_database() {
|
||||
local backup_file="$1"
|
||||
|
||||
if [[ ! -f "$backup_file" ]]; then
|
||||
log_error "Archivo no encontrado: $backup_file"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_info "Restaurando base de datos desde: $(basename "$backup_file")"
|
||||
|
||||
# Verificar integridad
|
||||
log_info "Verificando integridad del archivo..."
|
||||
if ! gzip -t "$backup_file" 2>/dev/null; then
|
||||
log_error "El archivo de backup esta corrupto"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Confirmar
|
||||
echo ""
|
||||
echo -e "${YELLOW}ADVERTENCIA: Esto reemplazara TODOS los datos actuales${NC}"
|
||||
echo -e "${YELLOW}Base de datos: ${DB_NAME}${NC}"
|
||||
echo ""
|
||||
read -p "Continuar? (escribir 'SI' para confirmar): " confirm
|
||||
|
||||
if [[ "$confirm" != "SI" ]]; then
|
||||
log_warn "Restauracion cancelada"
|
||||
return 1
|
||||
fi
|
||||
|
||||
stop_services
|
||||
|
||||
# Exportar password
|
||||
export PGPASSWORD="$DB_PASSWORD"
|
||||
|
||||
# Crear backup de seguridad antes de restaurar
|
||||
log_info "Creando backup de seguridad..."
|
||||
local safety_backup="$BACKUP_DIR/temp/pre_restore_$(date +%Y%m%d_%H%M%S).sql.gz"
|
||||
mkdir -p "$BACKUP_DIR/temp"
|
||||
|
||||
pg_dump -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" 2>/dev/null | gzip > "$safety_backup" || true
|
||||
|
||||
log_info "Backup de seguridad: $safety_backup"
|
||||
|
||||
# Terminar conexiones activas
|
||||
log_info "Cerrando conexiones activas..."
|
||||
psql -h "$DB_HOST" -p "$DB_PORT" -U postgres -c "
|
||||
SELECT pg_terminate_backend(pid)
|
||||
FROM pg_stat_activity
|
||||
WHERE datname = '${DB_NAME}' AND pid <> pg_backend_pid();
|
||||
" 2>/dev/null || true
|
||||
|
||||
# Recrear base de datos
|
||||
log_info "Recreando base de datos..."
|
||||
|
||||
# Eliminar y recrear BD
|
||||
psql -h "$DB_HOST" -p "$DB_PORT" -U postgres <<EOF
|
||||
DROP DATABASE IF EXISTS ${DB_NAME};
|
||||
CREATE DATABASE ${DB_NAME} OWNER ${DB_USER};
|
||||
EOF
|
||||
|
||||
# Conectar y crear extensiones
|
||||
psql -h "$DB_HOST" -p "$DB_PORT" -U postgres -d "$DB_NAME" <<EOF
|
||||
CREATE EXTENSION IF NOT EXISTS postgis;
|
||||
CREATE EXTENSION IF NOT EXISTS timescaledb CASCADE;
|
||||
CREATE EXTENSION IF NOT EXISTS pg_trgm;
|
||||
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
||||
GRANT ALL ON DATABASE ${DB_NAME} TO ${DB_USER};
|
||||
GRANT ALL ON SCHEMA public TO ${DB_USER};
|
||||
EOF
|
||||
|
||||
# Restaurar datos
|
||||
log_info "Restaurando datos..."
|
||||
gunzip -c "$backup_file" | psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -q
|
||||
|
||||
unset PGPASSWORD
|
||||
|
||||
log_success "Base de datos restaurada exitosamente"
|
||||
|
||||
start_services
|
||||
}
|
||||
|
||||
# ---------------------------------------------
|
||||
# Restaurar configuracion
|
||||
# ---------------------------------------------
|
||||
restore_config() {
|
||||
local backup_file="$1"
|
||||
|
||||
if [[ ! -f "$backup_file" ]]; then
|
||||
log_error "Archivo no encontrado: $backup_file"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_info "Restaurando configuracion desde: $(basename "$backup_file")"
|
||||
|
||||
# Verificar integridad
|
||||
if ! gzip -t "$backup_file" 2>/dev/null; then
|
||||
log_error "El archivo de backup esta corrupto"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Confirmar
|
||||
echo ""
|
||||
echo -e "${YELLOW}ADVERTENCIA: Esto sobrescribira archivos de configuracion${NC}"
|
||||
echo ""
|
||||
read -p "Continuar? (y/N): " -n 1 -r
|
||||
echo
|
||||
|
||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||
log_warn "Restauracion cancelada"
|
||||
return 1
|
||||
fi
|
||||
|
||||
stop_services
|
||||
|
||||
# Crear directorio temporal
|
||||
local temp_dir=$(mktemp -d)
|
||||
|
||||
# Extraer
|
||||
log_info "Extrayendo configuracion..."
|
||||
tar -xzf "$backup_file" -C "$temp_dir"
|
||||
|
||||
# Restaurar archivos
|
||||
log_info "Restaurando archivos..."
|
||||
|
||||
# .env
|
||||
if [[ -f "$temp_dir$INSTALL_DIR/.env" ]]; then
|
||||
cp "$temp_dir$INSTALL_DIR/.env" "$INSTALL_DIR/.env"
|
||||
log_info "Restaurado: .env"
|
||||
fi
|
||||
|
||||
# Traccar
|
||||
if [[ -f "$temp_dir/opt/traccar/conf/traccar.xml" ]]; then
|
||||
cp "$temp_dir/opt/traccar/conf/traccar.xml" /opt/traccar/conf/traccar.xml
|
||||
log_info "Restaurado: traccar.xml"
|
||||
fi
|
||||
|
||||
# MediaMTX
|
||||
if [[ -f "$temp_dir/opt/mediamtx/mediamtx.yml" ]]; then
|
||||
cp "$temp_dir/opt/mediamtx/mediamtx.yml" /opt/mediamtx/mediamtx.yml
|
||||
log_info "Restaurado: mediamtx.yml"
|
||||
fi
|
||||
|
||||
# Servicios systemd
|
||||
for service in $temp_dir/etc/systemd/system/flotillas-*.service; do
|
||||
if [[ -f "$service" ]]; then
|
||||
cp "$service" /etc/systemd/system/
|
||||
log_info "Restaurado: $(basename "$service")"
|
||||
fi
|
||||
done
|
||||
|
||||
# Recargar systemd
|
||||
systemctl daemon-reload
|
||||
|
||||
# Limpiar
|
||||
rm -rf "$temp_dir"
|
||||
|
||||
log_success "Configuracion restaurada"
|
||||
|
||||
start_services
|
||||
}
|
||||
|
||||
# ---------------------------------------------
|
||||
# Restaurar backup completo
|
||||
# ---------------------------------------------
|
||||
restore_full() {
|
||||
local backup_file="$1"
|
||||
|
||||
if [[ ! -f "$backup_file" ]]; then
|
||||
log_error "Archivo no encontrado: $backup_file"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_info "Restaurando backup completo desde: $(basename "$backup_file")"
|
||||
|
||||
echo ""
|
||||
echo -e "${RED}ADVERTENCIA: Esto reemplazara TODO el directorio de la aplicacion${NC}"
|
||||
echo ""
|
||||
read -p "Continuar? (escribir 'SI' para confirmar): " confirm
|
||||
|
||||
if [[ "$confirm" != "SI" ]]; then
|
||||
log_warn "Restauracion cancelada"
|
||||
return 1
|
||||
fi
|
||||
|
||||
stop_services
|
||||
|
||||
# Backup actual
|
||||
log_info "Respaldando instalacion actual..."
|
||||
local current_backup="$BACKUP_DIR/temp/pre_restore_full_$(date +%Y%m%d_%H%M%S).tar.gz"
|
||||
tar -czf "$current_backup" "$INSTALL_DIR" 2>/dev/null || true
|
||||
|
||||
# Extraer
|
||||
log_info "Extrayendo backup completo..."
|
||||
tar -xzf "$backup_file" -C /
|
||||
|
||||
log_success "Backup completo restaurado"
|
||||
|
||||
# Reinstalar dependencias
|
||||
log_info "Reinstalando dependencias..."
|
||||
|
||||
cd "$INSTALL_DIR/backend"
|
||||
source venv/bin/activate
|
||||
pip install -r requirements.txt -q 2>/dev/null || pip install -e . -q
|
||||
deactivate
|
||||
|
||||
cd "$INSTALL_DIR/frontend"
|
||||
npm install 2>/dev/null || true
|
||||
|
||||
start_services
|
||||
}
|
||||
|
||||
# ---------------------------------------------
|
||||
# Main
|
||||
# ---------------------------------------------
|
||||
main() {
|
||||
parse_args "$@"
|
||||
|
||||
# Solo listar
|
||||
if [[ "$LIST_ONLY" == "true" ]]; then
|
||||
list_backups
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Modo interactivo si no se especificaron opciones
|
||||
if [[ -z "$DB_BACKUP" ]] && [[ -z "$CONFIG_BACKUP" ]] && [[ "$USE_LATEST" != "true" ]] && [[ -z "$RESTORE_DATE" ]]; then
|
||||
echo ""
|
||||
echo "Sistema de Flotillas - Restauracion"
|
||||
echo "===================================="
|
||||
echo ""
|
||||
echo "Selecciona una opcion:"
|
||||
echo " 1) Restaurar backup mas reciente (DB + Config)"
|
||||
echo " 2) Restaurar solo base de datos"
|
||||
echo " 3) Restaurar solo configuracion"
|
||||
echo " 4) Listar backups disponibles"
|
||||
echo " 5) Cancelar"
|
||||
echo ""
|
||||
read -p "Opcion: " option
|
||||
|
||||
case $option in
|
||||
1)
|
||||
USE_LATEST=true
|
||||
;;
|
||||
2)
|
||||
list_backups
|
||||
echo ""
|
||||
read -p "Ingresa ruta del archivo de BD: " DB_BACKUP
|
||||
;;
|
||||
3)
|
||||
list_backups
|
||||
echo ""
|
||||
read -p "Ingresa ruta del archivo de config: " CONFIG_BACKUP
|
||||
;;
|
||||
4)
|
||||
list_backups
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "Cancelado"
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Restaurar por fecha
|
||||
if [[ -n "$RESTORE_DATE" ]]; then
|
||||
log_info "Buscando backups para fecha: $RESTORE_DATE"
|
||||
|
||||
DB_BACKUP=$(find_backup_by_date "db.sql" "$RESTORE_DATE") || exit 1
|
||||
CONFIG_BACKUP=$(find_backup_by_date "config.tar" "$RESTORE_DATE") || true
|
||||
fi
|
||||
|
||||
# Restaurar mas reciente
|
||||
if [[ "$USE_LATEST" == "true" ]]; then
|
||||
log_info "Buscando backups mas recientes..."
|
||||
|
||||
DB_BACKUP=$(find_latest_backup "db.sql") || exit 1
|
||||
CONFIG_BACKUP=$(find_latest_backup "config.tar") || true
|
||||
fi
|
||||
|
||||
# Ejecutar restauraciones
|
||||
if [[ -n "$DB_BACKUP" ]]; then
|
||||
restore_database "$DB_BACKUP"
|
||||
fi
|
||||
|
||||
if [[ -n "$CONFIG_BACKUP" ]]; then
|
||||
restore_config "$CONFIG_BACKUP"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
log_success "=========================================="
|
||||
log_success "RESTAURACION COMPLETADA"
|
||||
log_success "=========================================="
|
||||
echo ""
|
||||
echo "Verifica que los servicios esten funcionando:"
|
||||
echo " systemctl status flotillas-api"
|
||||
echo " systemctl status flotillas-web"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Ejecutar
|
||||
main "$@"
|
||||
214
deploy/scripts/status.sh
Normal file
214
deploy/scripts/status.sh
Normal file
@@ -0,0 +1,214 @@
|
||||
#!/bin/bash
|
||||
# ============================================
|
||||
# Sistema de Flotillas - Estado del Sistema
|
||||
# ============================================
|
||||
# Muestra informacion completa del estado
|
||||
# ============================================
|
||||
|
||||
# Colores
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
CYAN='\033[0;36m'
|
||||
NC='\033[0m'
|
||||
|
||||
INSTALL_DIR="${INSTALL_DIR:-/opt/flotillas}"
|
||||
|
||||
# Cargar variables
|
||||
if [[ -f "$INSTALL_DIR/.env" ]]; then
|
||||
export $(grep -v '^#' "$INSTALL_DIR/.env" | xargs)
|
||||
fi
|
||||
|
||||
clear
|
||||
|
||||
echo ""
|
||||
echo -e "${CYAN}╔══════════════════════════════════════════════════════════════╗${NC}"
|
||||
echo -e "${CYAN}║ SISTEMA DE FLOTILLAS - ESTADO DEL SISTEMA ║${NC}"
|
||||
echo -e "${CYAN}╚══════════════════════════════════════════════════════════════╝${NC}"
|
||||
echo ""
|
||||
|
||||
# ---------------------------------------------
|
||||
# Informacion del servidor
|
||||
# ---------------------------------------------
|
||||
echo -e "${BLUE}┌─ Servidor ─────────────────────────────────────────────────┐${NC}"
|
||||
echo -e "│ Hostname: $(hostname)"
|
||||
echo -e "│ IP: $(hostname -I | awk '{print $1}')"
|
||||
echo -e "│ Sistema: $(lsb_release -ds 2>/dev/null || cat /etc/os-release | grep PRETTY_NAME | cut -d'"' -f2)"
|
||||
echo -e "│ Kernel: $(uname -r)"
|
||||
echo -e "│ Uptime: $(uptime -p)"
|
||||
echo -e "${BLUE}└─────────────────────────────────────────────────────────────┘${NC}"
|
||||
echo ""
|
||||
|
||||
# ---------------------------------------------
|
||||
# Recursos del sistema
|
||||
# ---------------------------------------------
|
||||
echo -e "${BLUE}┌─ Recursos ─────────────────────────────────────────────────┐${NC}"
|
||||
|
||||
# CPU
|
||||
CPU_USAGE=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | cut -d'%' -f1)
|
||||
echo -e "│ CPU: ${CPU_USAGE}% usado"
|
||||
|
||||
# Memoria
|
||||
MEM_TOTAL=$(free -h | awk '/^Mem:/{print $2}')
|
||||
MEM_USED=$(free -h | awk '/^Mem:/{print $3}')
|
||||
MEM_PERCENT=$(free | awk '/^Mem:/{printf "%.1f", $3/$2*100}')
|
||||
echo -e "│ Memoria: ${MEM_USED} / ${MEM_TOTAL} (${MEM_PERCENT}%)"
|
||||
|
||||
# Disco
|
||||
DISK_USAGE=$(df -h / | awk 'NR==2{print $3 " / " $2 " (" $5 ")"}')
|
||||
echo -e "│ Disco: ${DISK_USAGE}"
|
||||
|
||||
echo -e "${BLUE}└─────────────────────────────────────────────────────────────┘${NC}"
|
||||
echo ""
|
||||
|
||||
# ---------------------------------------------
|
||||
# Servicios
|
||||
# ---------------------------------------------
|
||||
echo -e "${BLUE}┌─ Servicios ────────────────────────────────────────────────┐${NC}"
|
||||
|
||||
check_service() {
|
||||
local service="$1"
|
||||
local name="$2"
|
||||
local port="$3"
|
||||
|
||||
if systemctl is-active --quiet "$service" 2>/dev/null; then
|
||||
local status="${GREEN}ACTIVO${NC}"
|
||||
if [[ -n "$port" ]]; then
|
||||
status="$status (puerto $port)"
|
||||
fi
|
||||
else
|
||||
local status="${RED}INACTIVO${NC}"
|
||||
fi
|
||||
|
||||
printf "│ %-14s %s\n" "$name:" "$status"
|
||||
}
|
||||
|
||||
check_service "flotillas-api" "API Backend" "${API_PORT:-8000}"
|
||||
check_service "flotillas-web" "Frontend" "${FRONTEND_PORT:-3000}"
|
||||
check_service "postgresql" "PostgreSQL" "5432"
|
||||
check_service "redis-server" "Redis" "6379"
|
||||
check_service "traccar" "Traccar GPS" "${TRACCAR_PORT:-5055}"
|
||||
check_service "mediamtx" "MediaMTX" "8554"
|
||||
check_service "mosquitto" "MQTT" "1883"
|
||||
check_service "cloudflared" "Cloudflare" "-"
|
||||
|
||||
echo -e "${BLUE}└─────────────────────────────────────────────────────────────┘${NC}"
|
||||
echo ""
|
||||
|
||||
# ---------------------------------------------
|
||||
# Base de datos
|
||||
# ---------------------------------------------
|
||||
echo -e "${BLUE}┌─ Base de Datos ────────────────────────────────────────────┐${NC}"
|
||||
|
||||
if systemctl is-active --quiet postgresql; then
|
||||
# Tamanio de BD
|
||||
DB_SIZE=$(PGPASSWORD="${POSTGRES_PASSWORD}" psql -h localhost -U "${POSTGRES_USER:-flotillas}" -d "${POSTGRES_DB:-flotillas}" -t -c "SELECT pg_size_pretty(pg_database_size(current_database()));" 2>/dev/null | xargs)
|
||||
echo -e "│ Tamanio BD: ${DB_SIZE:-N/A}"
|
||||
|
||||
# Conexiones activas
|
||||
CONNECTIONS=$(PGPASSWORD="${POSTGRES_PASSWORD}" psql -h localhost -U "${POSTGRES_USER:-flotillas}" -d "${POSTGRES_DB:-flotillas}" -t -c "SELECT count(*) FROM pg_stat_activity WHERE datname = current_database();" 2>/dev/null | xargs)
|
||||
echo -e "│ Conexiones: ${CONNECTIONS:-N/A} activas"
|
||||
|
||||
# Posiciones (si existe la tabla)
|
||||
POSITIONS=$(PGPASSWORD="${POSTGRES_PASSWORD}" psql -h localhost -U "${POSTGRES_USER:-flotillas}" -d "${POSTGRES_DB:-flotillas}" -t -c "SELECT COUNT(*) FROM positions;" 2>/dev/null | xargs)
|
||||
if [[ -n "$POSITIONS" ]]; then
|
||||
echo -e "│ Posiciones: ${POSITIONS} registros"
|
||||
fi
|
||||
else
|
||||
echo -e "│ ${RED}PostgreSQL no esta activo${NC}"
|
||||
fi
|
||||
|
||||
echo -e "${BLUE}└─────────────────────────────────────────────────────────────┘${NC}"
|
||||
echo ""
|
||||
|
||||
# ---------------------------------------------
|
||||
# Redis
|
||||
# ---------------------------------------------
|
||||
echo -e "${BLUE}┌─ Redis Cache ──────────────────────────────────────────────┐${NC}"
|
||||
|
||||
if systemctl is-active --quiet redis-server; then
|
||||
REDIS_AUTH=""
|
||||
[[ -n "${REDIS_PASSWORD}" ]] && REDIS_AUTH="-a ${REDIS_PASSWORD}"
|
||||
|
||||
REDIS_KEYS=$(redis-cli $REDIS_AUTH DBSIZE 2>/dev/null | awk '{print $2}')
|
||||
REDIS_MEM=$(redis-cli $REDIS_AUTH INFO memory 2>/dev/null | grep used_memory_human | cut -d: -f2 | tr -d '\r')
|
||||
|
||||
echo -e "│ Keys: ${REDIS_KEYS:-N/A}"
|
||||
echo -e "│ Memoria: ${REDIS_MEM:-N/A}"
|
||||
else
|
||||
echo -e "│ ${RED}Redis no esta activo${NC}"
|
||||
fi
|
||||
|
||||
echo -e "${BLUE}└─────────────────────────────────────────────────────────────┘${NC}"
|
||||
echo ""
|
||||
|
||||
# ---------------------------------------------
|
||||
# Unidades GPS activas
|
||||
# ---------------------------------------------
|
||||
echo -e "${BLUE}┌─ GPS / Unidades ───────────────────────────────────────────┐${NC}"
|
||||
|
||||
if systemctl is-active --quiet postgresql; then
|
||||
# Total de unidades
|
||||
TOTAL_UNITS=$(PGPASSWORD="${POSTGRES_PASSWORD}" psql -h localhost -U "${POSTGRES_USER:-flotillas}" -d "${POSTGRES_DB:-flotillas}" -t -c "SELECT COUNT(*) FROM units;" 2>/dev/null | xargs)
|
||||
|
||||
# Unidades activas (con posicion en ultimos 5 min)
|
||||
ACTIVE_UNITS=$(PGPASSWORD="${POSTGRES_PASSWORD}" psql -h localhost -U "${POSTGRES_USER:-flotillas}" -d "${POSTGRES_DB:-flotillas}" -t -c "SELECT COUNT(DISTINCT unit_id) FROM positions WHERE device_time > NOW() - INTERVAL '5 minutes';" 2>/dev/null | xargs)
|
||||
|
||||
echo -e "│ Total: ${TOTAL_UNITS:-0} unidades"
|
||||
echo -e "│ Activas: ${ACTIVE_UNITS:-0} (ultimo 5 min)"
|
||||
else
|
||||
echo -e "│ ${YELLOW}No se puede obtener info de unidades${NC}"
|
||||
fi
|
||||
|
||||
echo -e "${BLUE}└─────────────────────────────────────────────────────────────┘${NC}"
|
||||
echo ""
|
||||
|
||||
# ---------------------------------------------
|
||||
# Streaming
|
||||
# ---------------------------------------------
|
||||
echo -e "${BLUE}┌─ Video Streaming ──────────────────────────────────────────┐${NC}"
|
||||
|
||||
if systemctl is-active --quiet mediamtx; then
|
||||
# Obtener streams activos de MediaMTX API
|
||||
STREAMS=$(curl -s http://localhost:9997/v3/paths/list 2>/dev/null | jq '.items | length' 2>/dev/null)
|
||||
echo -e "│ Streams: ${STREAMS:-0} activos"
|
||||
|
||||
# Endpoints
|
||||
echo -e "│ RTSP: rtsp://localhost:8554"
|
||||
echo -e "│ WebRTC: http://localhost:8889"
|
||||
echo -e "│ HLS: http://localhost:8888"
|
||||
else
|
||||
echo -e "│ ${RED}MediaMTX no esta activo${NC}"
|
||||
fi
|
||||
|
||||
echo -e "${BLUE}└─────────────────────────────────────────────────────────────┘${NC}"
|
||||
echo ""
|
||||
|
||||
# ---------------------------------------------
|
||||
# Ultimos errores
|
||||
# ---------------------------------------------
|
||||
echo -e "${BLUE}┌─ Ultimos Errores (API) ────────────────────────────────────┐${NC}"
|
||||
|
||||
ERRORS=$(journalctl -u flotillas-api --since "1 hour ago" -p err --no-pager -q 2>/dev/null | tail -3)
|
||||
|
||||
if [[ -z "$ERRORS" ]]; then
|
||||
echo -e "│ ${GREEN}Sin errores en la ultima hora${NC}"
|
||||
else
|
||||
echo "$ERRORS" | while read line; do
|
||||
echo "│ $line" | cut -c1-65
|
||||
done
|
||||
fi
|
||||
|
||||
echo -e "${BLUE}└─────────────────────────────────────────────────────────────┘${NC}"
|
||||
echo ""
|
||||
|
||||
# ---------------------------------------------
|
||||
# Comandos utiles
|
||||
# ---------------------------------------------
|
||||
echo -e "${YELLOW}Comandos utiles:${NC}"
|
||||
echo " ./health-check.sh - Verificar salud del sistema"
|
||||
echo " ./logs.sh api -f - Ver logs de API en tiempo real"
|
||||
echo " ./backup.sh - Crear backup"
|
||||
echo " ./update.sh - Actualizar sistema"
|
||||
echo ""
|
||||
485
deploy/scripts/update.sh
Normal file
485
deploy/scripts/update.sh
Normal file
@@ -0,0 +1,485 @@
|
||||
#!/bin/bash
|
||||
# ============================================
|
||||
# Sistema de Flotillas - Script de Actualizacion
|
||||
# ============================================
|
||||
# Actualiza la aplicacion a la ultima version
|
||||
#
|
||||
# Uso: ./update.sh [--branch BRANCH] [--force] [--no-backup]
|
||||
#
|
||||
# Opciones:
|
||||
# --branch Branch a usar (default: main)
|
||||
# --force Forzar actualizacion (descartar cambios locales)
|
||||
# --no-backup No crear backup antes de actualizar
|
||||
# --backend Solo actualizar backend
|
||||
# --frontend Solo actualizar frontend
|
||||
# ============================================
|
||||
|
||||
set -e
|
||||
set -o pipefail
|
||||
|
||||
# ---------------------------------------------
|
||||
# Colores
|
||||
# ---------------------------------------------
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
# ---------------------------------------------
|
||||
# Variables
|
||||
# ---------------------------------------------
|
||||
INSTALL_DIR="${INSTALL_DIR:-/opt/flotillas}"
|
||||
REPO_BRANCH="${REPO_BRANCH:-main}"
|
||||
BACKUP_BEFORE_UPDATE=true
|
||||
FORCE_UPDATE=false
|
||||
UPDATE_BACKEND=true
|
||||
UPDATE_FRONTEND=true
|
||||
|
||||
# Cargar variables de entorno
|
||||
if [[ -f "$INSTALL_DIR/.env" ]]; then
|
||||
export $(grep -v '^#' "$INSTALL_DIR/.env" | xargs)
|
||||
fi
|
||||
|
||||
# ---------------------------------------------
|
||||
# Funciones
|
||||
# ---------------------------------------------
|
||||
log_info() {
|
||||
echo -e "${BLUE}[$(date '+%H:%M:%S')] [INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}[$(date '+%H:%M:%S')] [OK]${NC} $1"
|
||||
}
|
||||
|
||||
log_warn() {
|
||||
echo -e "${YELLOW}[$(date '+%H:%M:%S')] [WARN]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[$(date '+%H:%M:%S')] [ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# ---------------------------------------------
|
||||
# Parsear argumentos
|
||||
# ---------------------------------------------
|
||||
parse_args() {
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--branch)
|
||||
REPO_BRANCH="$2"
|
||||
shift 2
|
||||
;;
|
||||
--force)
|
||||
FORCE_UPDATE=true
|
||||
shift
|
||||
;;
|
||||
--no-backup)
|
||||
BACKUP_BEFORE_UPDATE=false
|
||||
shift
|
||||
;;
|
||||
--backend)
|
||||
UPDATE_FRONTEND=false
|
||||
shift
|
||||
;;
|
||||
--frontend)
|
||||
UPDATE_BACKEND=false
|
||||
shift
|
||||
;;
|
||||
--help|-h)
|
||||
echo "Uso: $0 [--branch BRANCH] [--force] [--no-backup]"
|
||||
echo ""
|
||||
echo "Opciones:"
|
||||
echo " --branch Branch a usar (default: main)"
|
||||
echo " --force Forzar (descartar cambios locales)"
|
||||
echo " --no-backup No crear backup antes de actualizar"
|
||||
echo " --backend Solo actualizar backend"
|
||||
echo " --frontend Solo actualizar frontend"
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
log_error "Opcion desconocida: $1"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
# ---------------------------------------------
|
||||
# Verificar requisitos
|
||||
# ---------------------------------------------
|
||||
check_requirements() {
|
||||
log_info "Verificando requisitos..."
|
||||
|
||||
# Verificar directorio
|
||||
if [[ ! -d "$INSTALL_DIR" ]]; then
|
||||
log_error "Directorio de instalacion no encontrado: $INSTALL_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verificar git
|
||||
if [[ ! -d "$INSTALL_DIR/.git" ]]; then
|
||||
log_error "No es un repositorio git"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verificar conexion
|
||||
if ! ping -c 1 github.com &> /dev/null; then
|
||||
log_error "Sin conexion a internet"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_success "Requisitos OK"
|
||||
}
|
||||
|
||||
# ---------------------------------------------
|
||||
# Crear backup
|
||||
# ---------------------------------------------
|
||||
create_backup() {
|
||||
if [[ "$BACKUP_BEFORE_UPDATE" != "true" ]]; then
|
||||
log_warn "Saltando backup (--no-backup)"
|
||||
return
|
||||
fi
|
||||
|
||||
log_info "Creando backup antes de actualizar..."
|
||||
|
||||
if [[ -f "$INSTALL_DIR/deploy/scripts/backup.sh" ]]; then
|
||||
bash "$INSTALL_DIR/deploy/scripts/backup.sh" || log_warn "Backup fallo, continuando..."
|
||||
else
|
||||
log_warn "Script de backup no encontrado"
|
||||
fi
|
||||
}
|
||||
|
||||
# ---------------------------------------------
|
||||
# Obtener version actual
|
||||
# ---------------------------------------------
|
||||
get_current_version() {
|
||||
cd "$INSTALL_DIR"
|
||||
|
||||
# Intentar obtener version de git tag
|
||||
local version=$(git describe --tags --always 2>/dev/null || echo "unknown")
|
||||
|
||||
# O del commit
|
||||
local commit=$(git rev-parse --short HEAD 2>/dev/null || echo "unknown")
|
||||
|
||||
echo "${version} (${commit})"
|
||||
}
|
||||
|
||||
# ---------------------------------------------
|
||||
# Verificar cambios locales
|
||||
# ---------------------------------------------
|
||||
check_local_changes() {
|
||||
cd "$INSTALL_DIR"
|
||||
|
||||
if [[ -n $(git status --porcelain) ]]; then
|
||||
if [[ "$FORCE_UPDATE" == "true" ]]; then
|
||||
log_warn "Descartando cambios locales..."
|
||||
git reset --hard HEAD
|
||||
git clean -fd
|
||||
else
|
||||
log_error "Hay cambios locales sin commitear"
|
||||
log_error "Usa --force para descartarlos"
|
||||
git status --short
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# ---------------------------------------------
|
||||
# Actualizar codigo
|
||||
# ---------------------------------------------
|
||||
update_code() {
|
||||
log_info "Actualizando codigo desde repositorio..."
|
||||
|
||||
cd "$INSTALL_DIR"
|
||||
|
||||
# Guardar version actual
|
||||
local old_version=$(get_current_version)
|
||||
|
||||
# Fetch
|
||||
log_info "Obteniendo cambios..."
|
||||
git fetch origin
|
||||
|
||||
# Verificar si hay actualizaciones
|
||||
local local_hash=$(git rev-parse HEAD)
|
||||
local remote_hash=$(git rev-parse origin/${REPO_BRANCH})
|
||||
|
||||
if [[ "$local_hash" == "$remote_hash" ]]; then
|
||||
log_success "Ya estas en la version mas reciente"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Mostrar cambios pendientes
|
||||
log_info "Cambios disponibles:"
|
||||
git log --oneline HEAD..origin/${REPO_BRANCH} | head -10
|
||||
|
||||
# Actualizar
|
||||
log_info "Aplicando cambios..."
|
||||
git reset --hard origin/${REPO_BRANCH}
|
||||
|
||||
local new_version=$(get_current_version)
|
||||
|
||||
log_success "Codigo actualizado: $old_version -> $new_version"
|
||||
|
||||
return 1 # Indica que hubo cambios
|
||||
}
|
||||
|
||||
# ---------------------------------------------
|
||||
# Actualizar backend
|
||||
# ---------------------------------------------
|
||||
update_backend() {
|
||||
if [[ "$UPDATE_BACKEND" != "true" ]]; then
|
||||
log_info "Saltando actualizacion de backend"
|
||||
return
|
||||
fi
|
||||
|
||||
log_info "Actualizando backend..."
|
||||
|
||||
cd "$INSTALL_DIR/backend"
|
||||
|
||||
# Activar entorno virtual
|
||||
source venv/bin/activate
|
||||
|
||||
# Actualizar dependencias
|
||||
log_info "Instalando dependencias Python..."
|
||||
|
||||
if [[ -f "requirements.txt" ]]; then
|
||||
pip install -r requirements.txt -q --upgrade
|
||||
elif [[ -f "pyproject.toml" ]]; then
|
||||
pip install -e . -q --upgrade
|
||||
fi
|
||||
|
||||
# Ejecutar migraciones
|
||||
log_info "Ejecutando migraciones..."
|
||||
|
||||
if [[ -d "alembic" ]]; then
|
||||
alembic upgrade head 2>/dev/null || log_warn "Sin migraciones pendientes"
|
||||
fi
|
||||
|
||||
deactivate
|
||||
|
||||
log_success "Backend actualizado"
|
||||
}
|
||||
|
||||
# ---------------------------------------------
|
||||
# Actualizar frontend
|
||||
# ---------------------------------------------
|
||||
update_frontend() {
|
||||
if [[ "$UPDATE_FRONTEND" != "true" ]]; then
|
||||
log_info "Saltando actualizacion de frontend"
|
||||
return
|
||||
fi
|
||||
|
||||
log_info "Actualizando frontend..."
|
||||
|
||||
cd "$INSTALL_DIR/frontend"
|
||||
|
||||
# Verificar package.json
|
||||
if [[ ! -f "package.json" ]]; then
|
||||
log_warn "No hay package.json en frontend"
|
||||
return
|
||||
fi
|
||||
|
||||
# Instalar dependencias
|
||||
log_info "Instalando dependencias Node.js..."
|
||||
|
||||
if command -v pnpm &> /dev/null; then
|
||||
pnpm install --frozen-lockfile 2>/dev/null || pnpm install
|
||||
else
|
||||
npm ci 2>/dev/null || npm install
|
||||
fi
|
||||
|
||||
# Build
|
||||
log_info "Compilando frontend..."
|
||||
|
||||
if command -v pnpm &> /dev/null; then
|
||||
pnpm build
|
||||
else
|
||||
npm run build
|
||||
fi
|
||||
|
||||
log_success "Frontend actualizado"
|
||||
}
|
||||
|
||||
# ---------------------------------------------
|
||||
# Reiniciar servicios
|
||||
# ---------------------------------------------
|
||||
restart_services() {
|
||||
log_info "Reiniciando servicios..."
|
||||
|
||||
if [[ "$UPDATE_BACKEND" == "true" ]]; then
|
||||
systemctl restart flotillas-api 2>/dev/null && log_success "flotillas-api reiniciado" || log_warn "flotillas-api no existe"
|
||||
fi
|
||||
|
||||
if [[ "$UPDATE_FRONTEND" == "true" ]]; then
|
||||
systemctl restart flotillas-web 2>/dev/null && log_success "flotillas-web reiniciado" || log_warn "flotillas-web no existe"
|
||||
fi
|
||||
}
|
||||
|
||||
# ---------------------------------------------
|
||||
# Verificar servicios
|
||||
# ---------------------------------------------
|
||||
verify_services() {
|
||||
log_info "Verificando servicios..."
|
||||
|
||||
local all_ok=true
|
||||
|
||||
# Esperar a que inicien
|
||||
sleep 3
|
||||
|
||||
if [[ "$UPDATE_BACKEND" == "true" ]]; then
|
||||
if systemctl is-active --quiet flotillas-api; then
|
||||
log_success "flotillas-api: activo"
|
||||
else
|
||||
log_error "flotillas-api: inactivo"
|
||||
all_ok=false
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "$UPDATE_FRONTEND" == "true" ]]; then
|
||||
if systemctl is-active --quiet flotillas-web; then
|
||||
log_success "flotillas-web: activo"
|
||||
else
|
||||
log_error "flotillas-web: inactivo"
|
||||
all_ok=false
|
||||
fi
|
||||
fi
|
||||
|
||||
# Verificar API health
|
||||
if [[ "$UPDATE_BACKEND" == "true" ]]; then
|
||||
local api_port="${API_PORT:-8000}"
|
||||
if curl -s "http://localhost:${api_port}/health" > /dev/null 2>&1; then
|
||||
log_success "API health check: OK"
|
||||
else
|
||||
log_warn "API health check: no responde (puede estar iniciando)"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "$all_ok" == "false" ]]; then
|
||||
log_error "Algunos servicios fallaron. Revisa los logs:"
|
||||
echo " journalctl -u flotillas-api -n 50"
|
||||
echo " journalctl -u flotillas-web -n 50"
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# ---------------------------------------------
|
||||
# Limpiar cache
|
||||
# ---------------------------------------------
|
||||
clean_cache() {
|
||||
log_info "Limpiando cache..."
|
||||
|
||||
# Python cache
|
||||
find "$INSTALL_DIR/backend" -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null || true
|
||||
find "$INSTALL_DIR/backend" -type f -name "*.pyc" -delete 2>/dev/null || true
|
||||
|
||||
# Node cache
|
||||
rm -rf "$INSTALL_DIR/frontend/.next/cache" 2>/dev/null || true
|
||||
|
||||
log_success "Cache limpiado"
|
||||
}
|
||||
|
||||
# ---------------------------------------------
|
||||
# Rollback
|
||||
# ---------------------------------------------
|
||||
rollback() {
|
||||
local previous_commit="$1"
|
||||
|
||||
log_warn "Ejecutando rollback a: $previous_commit"
|
||||
|
||||
cd "$INSTALL_DIR"
|
||||
git reset --hard "$previous_commit"
|
||||
|
||||
update_backend
|
||||
update_frontend
|
||||
restart_services
|
||||
|
||||
log_success "Rollback completado"
|
||||
}
|
||||
|
||||
# ---------------------------------------------
|
||||
# Mostrar resumen
|
||||
# ---------------------------------------------
|
||||
show_summary() {
|
||||
echo ""
|
||||
echo -e "${GREEN}========================================${NC}"
|
||||
echo -e "${GREEN} ACTUALIZACION COMPLETADA${NC}"
|
||||
echo -e "${GREEN}========================================${NC}"
|
||||
echo ""
|
||||
echo "Version actual: $(get_current_version)"
|
||||
echo "Branch: $REPO_BRANCH"
|
||||
echo ""
|
||||
echo "Servicios:"
|
||||
systemctl is-active flotillas-api 2>/dev/null && echo " - flotillas-api: activo" || echo " - flotillas-api: inactivo"
|
||||
systemctl is-active flotillas-web 2>/dev/null && echo " - flotillas-web: activo" || echo " - flotillas-web: inactivo"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# ---------------------------------------------
|
||||
# Main
|
||||
# ---------------------------------------------
|
||||
main() {
|
||||
parse_args "$@"
|
||||
|
||||
echo ""
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo -e "${BLUE} ACTUALIZANDO SISTEMA DE FLOTILLAS${NC}"
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo ""
|
||||
echo "Branch: $REPO_BRANCH"
|
||||
echo "Force: $FORCE_UPDATE"
|
||||
echo "Backup: $BACKUP_BEFORE_UPDATE"
|
||||
echo ""
|
||||
|
||||
# Guardar commit actual para posible rollback
|
||||
cd "$INSTALL_DIR"
|
||||
PREVIOUS_COMMIT=$(git rev-parse HEAD)
|
||||
|
||||
check_requirements
|
||||
create_backup
|
||||
check_local_changes
|
||||
|
||||
# Intentar actualizar
|
||||
if update_code; then
|
||||
log_info "No hay actualizaciones disponibles"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Actualizar componentes
|
||||
update_backend || {
|
||||
log_error "Fallo en backend, haciendo rollback..."
|
||||
rollback "$PREVIOUS_COMMIT"
|
||||
exit 1
|
||||
}
|
||||
|
||||
update_frontend || {
|
||||
log_error "Fallo en frontend, haciendo rollback..."
|
||||
rollback "$PREVIOUS_COMMIT"
|
||||
exit 1
|
||||
}
|
||||
|
||||
clean_cache
|
||||
restart_services
|
||||
|
||||
# Verificar
|
||||
if ! verify_services; then
|
||||
echo ""
|
||||
read -p "Hacer rollback? (y/N): " -n 1 -r
|
||||
echo
|
||||
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||
rollback "$PREVIOUS_COMMIT"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
show_summary
|
||||
}
|
||||
|
||||
# Manejo de errores
|
||||
trap 'log_error "Error en linea $LINENO"; exit 1' ERR
|
||||
|
||||
# Ejecutar
|
||||
main "$@"
|
||||
43
deploy/services/cloudflared.service
Normal file
43
deploy/services/cloudflared.service
Normal file
@@ -0,0 +1,43 @@
|
||||
[Unit]
|
||||
Description=Cloudflare Tunnel - Sistema de Flotillas
|
||||
Documentation=https://developers.cloudflare.com/cloudflare-one/connections/connect-apps
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=exec
|
||||
User=root
|
||||
Group=root
|
||||
|
||||
# Directorio de configuracion
|
||||
WorkingDirectory=/etc/cloudflared
|
||||
|
||||
# Comando de inicio
|
||||
# Opcion 1: Usando token (recomendado)
|
||||
ExecStart=/usr/local/bin/cloudflared tunnel --no-autoupdate run --token ${CLOUDFLARE_TUNNEL_TOKEN}
|
||||
|
||||
# Opcion 2: Usando archivo de configuracion
|
||||
# ExecStart=/usr/local/bin/cloudflared tunnel --config /etc/cloudflared/config.yml run
|
||||
|
||||
# Cargar variables de entorno
|
||||
EnvironmentFile=/opt/flotillas/.env
|
||||
|
||||
# Reinicio automatico
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
|
||||
# Timeouts
|
||||
TimeoutStartSec=60
|
||||
TimeoutStopSec=30
|
||||
|
||||
# Logging
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
SyslogIdentifier=cloudflared
|
||||
|
||||
# Seguridad
|
||||
NoNewPrivileges=true
|
||||
PrivateTmp=true
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
58
deploy/services/flotillas-api.service
Normal file
58
deploy/services/flotillas-api.service
Normal file
@@ -0,0 +1,58 @@
|
||||
[Unit]
|
||||
Description=Sistema de Flotillas - API Backend
|
||||
Documentation=https://github.com/tuorganizacion/flotillas
|
||||
After=network.target postgresql.service redis.service
|
||||
Wants=postgresql.service redis.service
|
||||
|
||||
[Service]
|
||||
Type=exec
|
||||
User=root
|
||||
Group=root
|
||||
WorkingDirectory=/opt/flotillas/backend
|
||||
|
||||
# Cargar variables de entorno
|
||||
EnvironmentFile=/opt/flotillas/.env
|
||||
|
||||
# Comando de inicio
|
||||
# Uvicorn con multiples workers para produccion
|
||||
ExecStart=/opt/flotillas/backend/venv/bin/uvicorn \
|
||||
app.main:app \
|
||||
--host 0.0.0.0 \
|
||||
--port 8000 \
|
||||
--workers 4 \
|
||||
--loop uvloop \
|
||||
--http httptools \
|
||||
--proxy-headers \
|
||||
--forwarded-allow-ips='*' \
|
||||
--access-log \
|
||||
--log-level info
|
||||
|
||||
# Reinicio automatico
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
|
||||
# Timeouts
|
||||
TimeoutStartSec=30
|
||||
TimeoutStopSec=30
|
||||
|
||||
# Limites de recursos
|
||||
LimitNOFILE=65535
|
||||
LimitNPROC=4096
|
||||
|
||||
# Seguridad
|
||||
NoNewPrivileges=true
|
||||
PrivateTmp=true
|
||||
ProtectSystem=strict
|
||||
ProtectHome=true
|
||||
ReadWritePaths=/opt/flotillas /var/log/flotillas /tmp
|
||||
|
||||
# Logging
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
SyslogIdentifier=flotillas-api
|
||||
|
||||
# Health check (systemd 253+)
|
||||
# WatchdogSec=30
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
58
deploy/services/flotillas-web.service
Normal file
58
deploy/services/flotillas-web.service
Normal file
@@ -0,0 +1,58 @@
|
||||
[Unit]
|
||||
Description=Sistema de Flotillas - Frontend Web
|
||||
Documentation=https://github.com/tuorganizacion/flotillas
|
||||
After=network.target flotillas-api.service
|
||||
Wants=flotillas-api.service
|
||||
|
||||
[Service]
|
||||
Type=exec
|
||||
User=root
|
||||
Group=root
|
||||
WorkingDirectory=/opt/flotillas/frontend
|
||||
|
||||
# Cargar variables de entorno
|
||||
EnvironmentFile=/opt/flotillas/.env
|
||||
|
||||
# Comando de inicio usando 'serve' para servir archivos estaticos
|
||||
# Opcion 1: Usando serve (recomendado para SPA React/Vue)
|
||||
ExecStart=/usr/bin/serve \
|
||||
-s dist \
|
||||
-l 3000 \
|
||||
--no-clipboard \
|
||||
--single
|
||||
|
||||
# Opcion 2: Si usas Next.js en modo standalone
|
||||
# ExecStart=/usr/bin/node /opt/flotillas/frontend/.next/standalone/server.js
|
||||
|
||||
# Opcion 3: Si prefieres usar Node directamente
|
||||
# ExecStart=/usr/bin/npx serve -s dist -l 3000
|
||||
|
||||
# Reinicio automatico
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
|
||||
# Timeouts
|
||||
TimeoutStartSec=30
|
||||
TimeoutStopSec=30
|
||||
|
||||
# Variables de entorno adicionales
|
||||
Environment=NODE_ENV=production
|
||||
Environment=PORT=3000
|
||||
|
||||
# Limites
|
||||
LimitNOFILE=65535
|
||||
|
||||
# Seguridad
|
||||
NoNewPrivileges=true
|
||||
PrivateTmp=true
|
||||
ProtectSystem=strict
|
||||
ProtectHome=true
|
||||
ReadWritePaths=/opt/flotillas
|
||||
|
||||
# Logging
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
SyslogIdentifier=flotillas-web
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
41
deploy/services/mediamtx.service
Normal file
41
deploy/services/mediamtx.service
Normal file
@@ -0,0 +1,41 @@
|
||||
[Unit]
|
||||
Description=MediaMTX - Real-Time Media Server
|
||||
Documentation=https://github.com/bluenviron/mediamtx
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=exec
|
||||
User=root
|
||||
Group=root
|
||||
WorkingDirectory=/opt/mediamtx
|
||||
|
||||
# Comando de inicio
|
||||
ExecStart=/opt/mediamtx/mediamtx /opt/mediamtx/mediamtx.yml
|
||||
|
||||
# Reinicio automatico
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
|
||||
# Timeouts
|
||||
TimeoutStartSec=30
|
||||
TimeoutStopSec=30
|
||||
|
||||
# Limites de recursos
|
||||
LimitNOFILE=65535
|
||||
LimitNPROC=4096
|
||||
|
||||
# Ajustes de red para streaming
|
||||
# Permitir puertos privilegiados si es necesario
|
||||
AmbientCapabilities=CAP_NET_BIND_SERVICE
|
||||
|
||||
# Seguridad
|
||||
NoNewPrivileges=true
|
||||
PrivateTmp=true
|
||||
|
||||
# Logging
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
SyslogIdentifier=mediamtx
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
200
deploy/traccar/traccar.xml
Normal file
200
deploy/traccar/traccar.xml
Normal file
@@ -0,0 +1,200 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
|
||||
<!--
|
||||
============================================
|
||||
Traccar Server - Configuracion para Sistema de Flotillas
|
||||
============================================
|
||||
Documentacion: https://www.traccar.org/configuration-file/
|
||||
|
||||
Esta configuracion:
|
||||
- Usa PostgreSQL como base de datos
|
||||
- Forward de posiciones a API del sistema
|
||||
- Deshabilita interfaz web (usamos nuestra propia UI)
|
||||
- Solo habilita protocolos GPS comunes
|
||||
============================================
|
||||
-->
|
||||
|
||||
<!DOCTYPE properties SYSTEM 'http://java.sun.com/dtd/properties.dtd'>
|
||||
|
||||
<properties>
|
||||
|
||||
<!-- ========================================
|
||||
Configuracion General
|
||||
======================================== -->
|
||||
|
||||
<!-- Modo de operacion -->
|
||||
<entry key='config.default'>./conf/default.xml</entry>
|
||||
|
||||
<!-- Logging -->
|
||||
<entry key='logger.enable'>true</entry>
|
||||
<entry key='logger.level'>info</entry>
|
||||
<entry key='logger.file'>/opt/traccar/logs/tracker-server.log</entry>
|
||||
<entry key='logger.rotate'>true</entry>
|
||||
|
||||
<!-- ========================================
|
||||
Base de Datos PostgreSQL
|
||||
======================================== -->
|
||||
|
||||
<entry key='database.driver'>org.postgresql.Driver</entry>
|
||||
<entry key='database.url'>jdbc:postgresql://localhost:5432/traccar</entry>
|
||||
<entry key='database.user'>flotillas</entry>
|
||||
<entry key='database.password'>POSTGRES_PASSWORD</entry>
|
||||
|
||||
<!-- Pool de conexiones -->
|
||||
<entry key='database.checkConnection'>true</entry>
|
||||
<entry key='database.maxPoolSize'>10</entry>
|
||||
|
||||
<!-- ========================================
|
||||
Interfaz Web (DESHABILITADA)
|
||||
======================================== -->
|
||||
|
||||
<!-- No usamos la web UI de Traccar, usamos nuestra propia -->
|
||||
<entry key='web.enable'>false</entry>
|
||||
<!-- Si necesitas habilitarla temporalmente para debug: -->
|
||||
<!-- <entry key='web.enable'>true</entry> -->
|
||||
<!-- <entry key='web.port'>8082</entry> -->
|
||||
|
||||
<!-- ========================================
|
||||
Forward de Posiciones a API
|
||||
======================================== -->
|
||||
|
||||
<!-- Enviar cada posicion a nuestro backend -->
|
||||
<entry key='forward.enable'>true</entry>
|
||||
<entry key='forward.url'>http://localhost:8000/api/v1/traccar/position</entry>
|
||||
<entry key='forward.json'>true</entry>
|
||||
|
||||
<!-- Headers adicionales (si se necesita auth) -->
|
||||
<!-- <entry key='forward.header.Authorization'>Bearer TOKEN</entry> -->
|
||||
|
||||
<!-- Reintentos en caso de fallo -->
|
||||
<entry key='forward.retryEnable'>true</entry>
|
||||
<entry key='forward.retryDelay'>60</entry>
|
||||
<entry key='forward.retryCount'>3</entry>
|
||||
|
||||
<!-- ========================================
|
||||
Protocolos GPS Habilitados
|
||||
======================================== -->
|
||||
|
||||
<!-- Puerto principal - Protocolo Osmand (Android/iOS apps) -->
|
||||
<entry key='osmand.port'>5055</entry>
|
||||
|
||||
<!-- GT06 - Dispositivos chinos comunes -->
|
||||
<entry key='gt06.port'>5023</entry>
|
||||
|
||||
<!-- H02 - Coban y similares -->
|
||||
<entry key='h02.port'>5013</entry>
|
||||
|
||||
<!-- TK103 - Muy comun -->
|
||||
<entry key='tk103.port'>5002</entry>
|
||||
|
||||
<!-- GPS103 -->
|
||||
<entry key='gps103.port'>5001</entry>
|
||||
|
||||
<!-- Teltonika - Profesionales -->
|
||||
<entry key='teltonika.port'>5027</entry>
|
||||
|
||||
<!-- Queclink -->
|
||||
<entry key='queclink.port'>5050</entry>
|
||||
|
||||
<!-- Meitrack -->
|
||||
<entry key='meitrack.port'>5020</entry>
|
||||
|
||||
<!-- Ruptela -->
|
||||
<entry key='ruptela.port'>5046</entry>
|
||||
|
||||
<!-- Suntech -->
|
||||
<entry key='suntech.port'>5011</entry>
|
||||
|
||||
<!-- Watch (smartwatches GPS) -->
|
||||
<entry key='watch.port'>5093</entry>
|
||||
|
||||
<!-- T55 -->
|
||||
<entry key='t55.port'>5005</entry>
|
||||
|
||||
<!-- Xexun -->
|
||||
<entry key='xexun.port'>5006</entry>
|
||||
|
||||
<!-- TotemAT -->
|
||||
<entry key='totem.port'>5007</entry>
|
||||
|
||||
<!-- Enfora -->
|
||||
<entry key='enfora.port'>5008</entry>
|
||||
|
||||
<!-- Meiligao -->
|
||||
<entry key='meiligao.port'>5009</entry>
|
||||
|
||||
<!-- Protocolo Traccar (app oficial) -->
|
||||
<entry key='traccar.port'>5190</entry>
|
||||
|
||||
<!-- ========================================
|
||||
Procesamiento de Eventos
|
||||
======================================== -->
|
||||
|
||||
<!-- Eventos a procesar -->
|
||||
<entry key='event.enable'>true</entry>
|
||||
<entry key='event.ignoreDuplicateAlerts'>true</entry>
|
||||
|
||||
<!-- Geocodificacion (obtener direcciones) -->
|
||||
<entry key='geocoder.enable'>true</entry>
|
||||
<entry key='geocoder.type'>nominatim</entry>
|
||||
<entry key='geocoder.url'>https://nominatim.openstreetmap.org/reverse</entry>
|
||||
<entry key='geocoder.key'></entry>
|
||||
<entry key='geocoder.processInvalidPositions'>false</entry>
|
||||
<entry key='geocoder.reuseDistance'>50</entry>
|
||||
|
||||
<!-- Alternativamente usar Google (requiere API key) -->
|
||||
<!-- <entry key='geocoder.type'>google</entry> -->
|
||||
<!-- <entry key='geocoder.key'>TU_API_KEY</entry> -->
|
||||
|
||||
<!-- ========================================
|
||||
Filtros de Posicion
|
||||
======================================== -->
|
||||
|
||||
<!-- Filtrar posiciones invalidas -->
|
||||
<entry key='filter.enable'>true</entry>
|
||||
<entry key='filter.invalid'>true</entry>
|
||||
<entry key='filter.zero'>true</entry>
|
||||
<entry key='filter.duplicate'>true</entry>
|
||||
|
||||
<!-- Filtrar por velocidad maxima (km/h) -->
|
||||
<entry key='filter.maxSpeed'>500</entry>
|
||||
|
||||
<!-- Distancia minima entre posiciones (metros) -->
|
||||
<entry key='filter.distance'>10</entry>
|
||||
|
||||
<!-- ========================================
|
||||
Notificaciones
|
||||
======================================== -->
|
||||
|
||||
<!-- Las notificaciones las maneja nuestro backend -->
|
||||
<entry key='notificator.enable'>false</entry>
|
||||
|
||||
<!-- ========================================
|
||||
API REST de Traccar (DESHABILITADA)
|
||||
======================================== -->
|
||||
|
||||
<!-- No exponemos API de Traccar, usamos nuestra propia -->
|
||||
<entry key='api.enable'>false</entry>
|
||||
|
||||
<!-- ========================================
|
||||
Seguridad
|
||||
======================================== -->
|
||||
|
||||
<!-- Registro de nuevos dispositivos -->
|
||||
<entry key='database.registerUnknown'>true</entry>
|
||||
|
||||
<!-- Dispositivos deben existir antes de enviar datos -->
|
||||
<!-- <entry key='database.registerUnknown'>false</entry> -->
|
||||
|
||||
<!-- ========================================
|
||||
Rendimiento
|
||||
======================================== -->
|
||||
|
||||
<!-- Threads para procesamiento -->
|
||||
<entry key='processing.computedAttributes.enabled'>true</entry>
|
||||
<entry key='processing.computedAttributes.threads'>4</entry>
|
||||
|
||||
<!-- Cache -->
|
||||
<entry key='database.positionsHistoryDays'>30</entry>
|
||||
|
||||
</properties>
|
||||
Reference in New Issue
Block a user