#!/bin/bash # ============================================ # Sistema de ATLAS - Script de Instalacion # ============================================ # Este script instala y configura todo el sistema # Ejecutar como root en Ubuntu 22.04 LTS # # Uso: sudo ./install.sh [--skip-db] [--skip-traccar] [--dev] # # Opciones: # --skip-db No instalar PostgreSQL (usar DB externa) # --skip-traccar No instalar Traccar # --dev Modo desarrollo (sin optimizaciones) # ============================================ set -e # Salir en caso de error set -o pipefail # Capturar errores en pipes # --------------------------------------------- # Colores para output # --------------------------------------------- RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # --------------------------------------------- # Variables de Configuracion # --------------------------------------------- INSTALL_DIR="${INSTALL_DIR:-/opt/atlas}" REPO_URL="${REPO_URL:-https://github.com/tuorganizacion/atlas.git}" REPO_BRANCH="${REPO_BRANCH:-main}" BACKUP_DIR="${BACKUP_DIR:-/var/backups/atlas}" # Versiones POSTGRES_VERSION="15" NODE_VERSION="20" PYTHON_VERSION="3.11" TRACCAR_VERSION="5.12" MEDIAMTX_VERSION="1.5.1" # Puertos API_PORT="${API_PORT:-8000}" FRONTEND_PORT="${FRONTEND_PORT:-3000}" TRACCAR_PORT="${TRACCAR_PORT:-5055}" # Flags SKIP_DB=false SKIP_TRACCAR=false DEV_MODE=false # Archivo de credenciales generadas CREDENTIALS_FILE="/root/atlas-credentials.txt" # --------------------------------------------- # Funciones de utilidad # --------------------------------------------- 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" } log_section() { echo "" echo -e "${GREEN}========================================${NC}" echo -e "${GREEN} $1${NC}" echo -e "${GREEN}========================================${NC}" echo "" } # Generar password aleatorio generate_password() { openssl rand -base64 32 | tr -dc 'a-zA-Z0-9' | head -c "${1:-24}" } # Generar secret key generate_secret_key() { openssl rand -hex 32 } # Verificar si comando existe command_exists() { command -v "$1" &> /dev/null } # Verificar si servicio esta activo service_active() { systemctl is-active --quiet "$1" 2>/dev/null } # --------------------------------------------- # Parsear argumentos # --------------------------------------------- parse_args() { while [[ $# -gt 0 ]]; do case $1 in --skip-db) SKIP_DB=true shift ;; --skip-traccar) SKIP_TRACCAR=true shift ;; --dev) DEV_MODE=true shift ;; --help|-h) echo "Uso: $0 [--skip-db] [--skip-traccar] [--dev]" echo "" echo "Opciones:" echo " --skip-db No instalar PostgreSQL (usar DB externa)" echo " --skip-traccar No instalar Traccar" echo " --dev Modo desarrollo" exit 0 ;; *) log_error "Opcion desconocida: $1" exit 1 ;; esac done } # --------------------------------------------- # Verificar requisitos # --------------------------------------------- check_requirements() { log_section "Verificando Requisitos" # Verificar root if [[ $EUID -ne 0 ]]; then log_error "Este script debe ejecutarse como root" exit 1 fi # Verificar Ubuntu 22.04 o 24.04 if [[ -f /etc/os-release ]]; then . /etc/os-release if [[ "$ID" != "ubuntu" ]] || [[ ! "$VERSION_ID" =~ ^(22|24) ]]; then log_warn "Este script esta optimizado para Ubuntu 22.04/24.04" log_warn "Sistema detectado: $ID $VERSION_ID" read -p "Continuar de todos modos? (y/N) " -n 1 -r echo if [[ ! $REPLY =~ ^[Yy]$ ]]; then exit 1 fi else log_success "Sistema operativo: $ID $VERSION_ID" fi fi # Verificar memoria minima (2GB) TOTAL_MEM=$(free -m | awk '/^Mem:/{print $2}') if [[ $TOTAL_MEM -lt 2048 ]]; then log_warn "Memoria RAM: ${TOTAL_MEM}MB (recomendado: 4GB+)" else log_success "Memoria RAM: ${TOTAL_MEM}MB" fi # Verificar espacio en disco (10GB minimo) FREE_SPACE=$(df -BG / | awk 'NR==2 {print $4}' | tr -d 'G') if [[ $FREE_SPACE -lt 10 ]]; then log_error "Espacio insuficiente: ${FREE_SPACE}GB (minimo: 10GB)" exit 1 else log_success "Espacio en disco: ${FREE_SPACE}GB" fi # Verificar conexion a internet if ! ping -c 1 google.com &> /dev/null; then log_error "Sin conexion a internet" exit 1 fi log_success "Conexion a internet OK" } # --------------------------------------------- # Actualizar sistema # --------------------------------------------- update_system() { log_section "Actualizando Sistema" export DEBIAN_FRONTEND=noninteractive log_info "Actualizando lista de paquetes..." apt-get update -qq log_info "Actualizando paquetes instalados..." apt-get upgrade -y -qq log_info "Instalando paquetes base..." apt-get install -y -qq \ curl \ wget \ gnupg \ lsb-release \ ca-certificates \ apt-transport-https \ software-properties-common \ build-essential \ git \ unzip \ jq \ htop \ net-tools \ ufw \ fail2ban \ logrotate \ cron log_success "Sistema actualizado" } # --------------------------------------------- # Instalar PostgreSQL + TimescaleDB + PostGIS # --------------------------------------------- install_postgresql() { if [[ "$SKIP_DB" == "true" ]]; then log_warn "Saltando instalacion de PostgreSQL (--skip-db)" return fi log_section "Instalando PostgreSQL ${POSTGRES_VERSION} + TimescaleDB + PostGIS" # Verificar si ya esta instalado if command_exists psql && service_active postgresql; then log_warn "PostgreSQL ya esta instalado" POSTGRES_EXISTING=true else POSTGRES_EXISTING=false # Agregar repositorio PostgreSQL log_info "Agregando repositorio PostgreSQL..." curl -fsSL https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor -o /usr/share/keyrings/postgresql-keyring.gpg echo "deb [signed-by=/usr/share/keyrings/postgresql-keyring.gpg] http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list # Agregar repositorio TimescaleDB log_info "Agregando repositorio TimescaleDB..." curl -fsSL https://packagecloud.io/timescale/timescaledb/gpgkey | gpg --dearmor -o /usr/share/keyrings/timescaledb-keyring.gpg echo "deb [signed-by=/usr/share/keyrings/timescaledb-keyring.gpg] https://packagecloud.io/timescale/timescaledb/ubuntu/ $(lsb_release -cs) main" > /etc/apt/sources.list.d/timescaledb.list apt-get update -qq # Instalar PostgreSQL log_info "Instalando PostgreSQL ${POSTGRES_VERSION}..." apt-get install -y -qq postgresql-${POSTGRES_VERSION} postgresql-contrib-${POSTGRES_VERSION} # Instalar PostGIS log_info "Instalando PostGIS..." apt-get install -y -qq postgresql-${POSTGRES_VERSION}-postgis-3 # Instalar TimescaleDB log_info "Instalando TimescaleDB..." apt-get install -y -qq timescaledb-2-postgresql-${POSTGRES_VERSION} # Configurar TimescaleDB log_info "Configurando TimescaleDB..." timescaledb-tune -yes -quiet fi # Iniciar PostgreSQL systemctl enable postgresql systemctl start postgresql log_success "PostgreSQL instalado y configurado" } # --------------------------------------------- # Instalar Redis # --------------------------------------------- install_redis() { log_section "Instalando Redis" if command_exists redis-server && service_active redis-server; then log_warn "Redis ya esta instalado" else log_info "Instalando Redis..." apt-get install -y -qq redis-server # Configurar Redis para produccion log_info "Configurando Redis..." # Backup de config original cp /etc/redis/redis.conf /etc/redis/redis.conf.bak # Configuraciones de seguridad y rendimiento sed -i 's/^bind 127.0.0.1 ::1/bind 127.0.0.1/' /etc/redis/redis.conf sed -i 's/^# maxmemory /maxmemory 256mb/' /etc/redis/redis.conf sed -i 's/^# maxmemory-policy noeviction/maxmemory-policy allkeys-lru/' /etc/redis/redis.conf fi systemctl enable redis-server systemctl restart redis-server log_success "Redis instalado y configurado" } # --------------------------------------------- # Instalar Python 3.11 # --------------------------------------------- install_python() { log_section "Instalando Python ${PYTHON_VERSION}" if python3.11 --version &> /dev/null; then log_warn "Python 3.11 ya esta instalado" else log_info "Agregando repositorio deadsnakes..." add-apt-repository -y ppa:deadsnakes/ppa apt-get update -qq log_info "Instalando Python ${PYTHON_VERSION}..." apt-get install -y -qq \ python${PYTHON_VERSION} \ python${PYTHON_VERSION}-venv \ python${PYTHON_VERSION}-dev \ python${PYTHON_VERSION}-distutils fi # Instalar pip if ! python3.11 -m pip --version &> /dev/null; then log_info "Instalando pip..." curl -sS https://bootstrap.pypa.io/get-pip.py | python3.11 --user fi # Actualizar pip (ignorar paquetes del sistema) python3.11 -m pip install --upgrade pip setuptools wheel --ignore-installed -q 2>/dev/null || true log_success "Python ${PYTHON_VERSION} instalado" } # --------------------------------------------- # Instalar Node.js 20 # --------------------------------------------- install_nodejs() { log_section "Instalando Node.js ${NODE_VERSION}" if node --version 2>/dev/null | grep -q "v${NODE_VERSION}"; then log_warn "Node.js ${NODE_VERSION} ya esta instalado" else log_info "Instalando Node.js ${NODE_VERSION}..." curl -fsSL https://deb.nodesource.com/setup_${NODE_VERSION}.x | bash - apt-get install -y -qq nodejs fi # Instalar pnpm (mas rapido que npm) if ! command_exists pnpm; then log_info "Instalando pnpm..." npm install -g pnpm -q fi # Instalar serve para frontend if ! command_exists serve; then log_info "Instalando serve..." npm install -g serve -q fi log_success "Node.js ${NODE_VERSION} instalado" } # --------------------------------------------- # Instalar Traccar # --------------------------------------------- install_traccar() { if [[ "$SKIP_TRACCAR" == "true" ]]; then log_warn "Saltando instalacion de Traccar (--skip-traccar)" return fi log_section "Instalando Traccar ${TRACCAR_VERSION}" TRACCAR_DIR="/opt/traccar" if [[ -d "$TRACCAR_DIR" ]] && service_active traccar; then log_warn "Traccar ya esta instalado" else # Instalar Java (requerido por Traccar) log_info "Instalando OpenJDK 17..." apt-get install -y -qq openjdk-17-jre-headless # Descargar Traccar log_info "Descargando Traccar ${TRACCAR_VERSION}..." TRACCAR_URL="https://github.com/traccar/traccar/releases/download/v${TRACCAR_VERSION}/traccar-linux-64-${TRACCAR_VERSION}.zip" cd /tmp wget -q "$TRACCAR_URL" -O traccar.zip # Instalar Traccar log_info "Instalando Traccar..." unzip -q -o traccar.zip ./traccar.run rm -f traccar.zip traccar.run fi log_success "Traccar instalado" } # --------------------------------------------- # Instalar MediaMTX # --------------------------------------------- install_mediamtx() { log_section "Instalando MediaMTX ${MEDIAMTX_VERSION}" MEDIAMTX_DIR="/opt/mediamtx" if [[ -d "$MEDIAMTX_DIR" ]] && [[ -f "$MEDIAMTX_DIR/mediamtx" ]]; then log_warn "MediaMTX ya esta instalado" else log_info "Descargando MediaMTX ${MEDIAMTX_VERSION}..." mkdir -p "$MEDIAMTX_DIR" cd "$MEDIAMTX_DIR" ARCH=$(uname -m) case $ARCH in x86_64) ARCH="amd64" ;; aarch64) ARCH="arm64v8" ;; armv7l) ARCH="armv7" ;; esac MEDIAMTX_URL="https://github.com/bluenviron/mediamtx/releases/download/v${MEDIAMTX_VERSION}/mediamtx_v${MEDIAMTX_VERSION}_linux_${ARCH}.tar.gz" wget -q "$MEDIAMTX_URL" -O mediamtx.tar.gz tar -xzf mediamtx.tar.gz rm -f mediamtx.tar.gz chmod +x mediamtx fi log_success "MediaMTX instalado" } # --------------------------------------------- # Instalar Mosquitto MQTT # --------------------------------------------- install_mosquitto() { log_section "Instalando Mosquitto MQTT" if command_exists mosquitto && service_active mosquitto; then log_warn "Mosquitto ya esta instalado" else log_info "Instalando Mosquitto..." apt-get install -y -qq mosquitto mosquitto-clients # Configuracion basica se hara despues con las credenciales fi systemctl enable mosquitto log_success "Mosquitto instalado" } # --------------------------------------------- # Configurar base de datos # --------------------------------------------- configure_database() { if [[ "$SKIP_DB" == "true" ]]; then log_warn "Saltando configuracion de base de datos" return fi log_section "Configurando Base de Datos" # Generar credenciales si no existen if [[ -z "$DB_PASSWORD" ]]; then DB_PASSWORD=$(generate_password 24) fi DB_NAME="${POSTGRES_DB:-atlas}" DB_USER="${POSTGRES_USER:-atlas}" log_info "Creando usuario y base de datos..." # Crear usuario y base de datos sudo -u postgres psql -v ON_ERROR_STOP=1 </dev/null; then log_info "Copiando desde directorio local..." cp -r /root/Atlas/* "$INSTALL_DIR/" else git clone --branch "$REPO_BRANCH" "$REPO_URL" "$INSTALL_DIR" fi fi cd "$INSTALL_DIR" # Configurar backend log_info "Configurando backend..." cd "$INSTALL_DIR/backend" # Crear entorno virtual python3.11 -m venv venv source venv/bin/activate # Instalar dependencias if [[ -f "requirements.txt" ]]; then pip install -r requirements.txt -q elif [[ -f "pyproject.toml" ]]; then pip install -e . -q fi # Instalar dependencias adicionales pip install uvicorn[standard] gunicorn -q deactivate # Configurar frontend log_info "Configurando frontend..." cd "$INSTALL_DIR/frontend" if [[ -f "package.json" ]]; then pnpm install --frozen-lockfile 2>/dev/null || npm install # Build para produccion if [[ "$DEV_MODE" != "true" ]]; then pnpm build 2>/dev/null || npm run build fi fi log_success "Aplicacion configurada" } # --------------------------------------------- # Generar credenciales # --------------------------------------------- generate_credentials() { log_section "Generando Credenciales" # Generar todas las credenciales [[ -z "$DB_PASSWORD" ]] && DB_PASSWORD=$(generate_password 24) REDIS_PASSWORD=$(generate_password 24) SECRET_KEY=$(generate_secret_key) MQTT_PASSWORD=$(generate_password 16) ADMIN_PASSWORD=$(generate_password 16) # Guardar en archivo seguro cat > "$CREDENTIALS_FILE" < "$ENV_FILE" </dev/null || true # Reiniciar Traccar systemctl restart traccar 2>/dev/null || true log_success "Traccar configurado" } # --------------------------------------------- # Configurar MediaMTX # --------------------------------------------- configure_mediamtx() { log_section "Configurando MediaMTX" MEDIAMTX_CONFIG="/opt/mediamtx/mediamtx.yml" # Copiar configuracion personalizada if [[ -f "$INSTALL_DIR/deploy/mediamtx/mediamtx.yml" ]]; then cp "$INSTALL_DIR/deploy/mediamtx/mediamtx.yml" "$MEDIAMTX_CONFIG" fi log_success "MediaMTX configurado" } # --------------------------------------------- # Configurar Mosquitto # --------------------------------------------- configure_mosquitto() { log_section "Configurando Mosquitto" # Crear archivo de passwords touch /etc/mosquitto/passwd mosquitto_passwd -b /etc/mosquitto/passwd atlas "${MQTT_PASSWORD}" # Configuracion cat > /etc/mosquitto/conf.d/atlas.conf </dev/null || true systemctl enable atlas-web 2>/dev/null || true systemctl enable mediamtx 2>/dev/null || true # Iniciar servicios log_info "Iniciando servicios..." systemctl start atlas-api 2>/dev/null || log_warn "atlas-api no pudo iniciar (puede requerir configuracion adicional)" systemctl start atlas-web 2>/dev/null || log_warn "atlas-web no pudo iniciar" systemctl start mediamtx 2>/dev/null || log_warn "mediamtx no pudo iniciar" log_success "Servicios instalados" } # --------------------------------------------- # Ejecutar migraciones de BD # --------------------------------------------- run_migrations() { log_section "Ejecutando Migraciones" cd "$INSTALL_DIR/backend" source venv/bin/activate # Cargar variables de entorno export $(grep -v '^#' "$INSTALL_DIR/.env" | xargs) # Ejecutar migraciones con Alembic si existe if [[ -d "alembic" ]]; then log_info "Ejecutando migraciones Alembic..." alembic upgrade head 2>/dev/null || log_warn "Migraciones fallaron o no hay migraciones pendientes" fi # Ejecutar init.sql si existe y no hay migraciones if [[ ! -d "alembic" ]] && [[ -f "$INSTALL_DIR/deploy/postgres/init.sql" ]]; then log_info "Ejecutando script init.sql..." PGPASSWORD="${DB_PASSWORD}" psql -h localhost -U atlas -d atlas -f "$INSTALL_DIR/deploy/postgres/init.sql" || true fi deactivate log_success "Migraciones completadas" } # --------------------------------------------- # Configurar firewall # --------------------------------------------- configure_firewall() { log_section "Configurando Firewall" # Resetear UFW ufw --force reset # Politica por defecto ufw default deny incoming ufw default allow outgoing # SSH (siempre permitir) ufw allow ssh # Puerto GPS (Traccar) - UNICO PUERTO PUBLICO ufw allow ${TRACCAR_PORT}/tcp comment 'Traccar GPS' # Puertos internos (solo localhost para desarrollo) # En produccion, todo va por Cloudflare Tunnel if [[ "$DEV_MODE" == "true" ]]; then ufw allow ${API_PORT}/tcp comment 'API (dev)' ufw allow ${FRONTEND_PORT}/tcp comment 'Frontend (dev)' fi # Habilitar firewall ufw --force enable log_success "Firewall configurado - Solo puerto ${TRACCAR_PORT} publico" } # --------------------------------------------- # Configurar fail2ban # --------------------------------------------- configure_fail2ban() { log_section "Configurando Fail2ban" cat > /etc/fail2ban/jail.local < /etc/logrotate.d/atlas < /dev/null 2>&1 || true endscript } EOF mkdir -p /var/log/atlas log_success "Logrotate configurado" } # --------------------------------------------- # Configurar cron para backups # --------------------------------------------- configure_cron() { log_section "Configurando Backups Automaticos" # Crear cron para backup diario a las 3 AM CRON_JOB="0 3 * * * $INSTALL_DIR/deploy/scripts/backup.sh >> /var/log/atlas/backup.log 2>&1" # Agregar si no existe (crontab -l 2>/dev/null | grep -v "backup.sh"; echo "$CRON_JOB") | crontab - log_success "Backup diario configurado (3 AM)" } # --------------------------------------------- # Mostrar resumen final # --------------------------------------------- show_summary() { log_section "Instalacion Completada" echo "" echo -e "${GREEN}============================================${NC}" echo -e "${GREEN} SISTEMA DE ATLAS INSTALADO ${NC}" echo -e "${GREEN}============================================${NC}" echo "" echo -e "${BLUE}Servicios:${NC}" echo " - API Backend: http://localhost:${API_PORT}" echo " - Frontend: http://localhost:${FRONTEND_PORT}" echo " - Traccar GPS: puerto ${TRACCAR_PORT}" echo " - MediaMTX RTSP: rtsp://localhost:8554" echo " - MediaMTX WebRTC: http://localhost:8889" echo "" echo -e "${BLUE}Base de Datos:${NC}" echo " - PostgreSQL: localhost:5432" echo " - Database: atlas" echo " - Usuario: atlas" echo "" echo -e "${BLUE}Credenciales:${NC}" echo " - Guardadas en: ${CREDENTIALS_FILE}" echo "" echo -e "${YELLOW}IMPORTANTE:${NC}" echo " 1. Guarda las credenciales en un lugar seguro" echo " 2. Configura Cloudflare Tunnel para acceso externo" echo " 3. El unico puerto publico es ${TRACCAR_PORT} (GPS)" echo "" echo -e "${BLUE}Comandos utiles:${NC}" echo " - Ver logs API: journalctl -u atlas-api -f" echo " - Reiniciar API: systemctl restart atlas-api" echo " - Backup manual: ${INSTALL_DIR}/deploy/scripts/backup.sh" echo " - Actualizar: ${INSTALL_DIR}/deploy/scripts/update.sh" echo "" echo -e "${GREEN}============================================${NC}" # Mostrar credenciales en pantalla echo "" echo -e "${YELLOW}=== CREDENCIALES (guardar y eliminar archivo) ===${NC}" cat "$CREDENTIALS_FILE" echo "" } # --------------------------------------------- # Main # --------------------------------------------- main() { parse_args "$@" echo "" echo -e "${GREEN}============================================${NC}" echo -e "${GREEN} INSTALADOR SISTEMA DE ATLAS ${NC}" echo -e "${GREEN}============================================${NC}" echo "" echo "Directorio instalacion: $INSTALL_DIR" echo "Modo desarrollo: $DEV_MODE" echo "Saltar DB: $SKIP_DB" echo "Saltar Traccar: $SKIP_TRACCAR" echo "" # Confirmar instalacion read -p "Continuar con la instalacion? (Y/n) " -n 1 -r echo if [[ $REPLY =~ ^[Nn]$ ]]; then echo "Instalacion cancelada" exit 0 fi # Ejecutar pasos de instalacion check_requirements update_system install_postgresql install_redis install_python install_nodejs install_traccar install_mediamtx install_mosquitto generate_credentials configure_database setup_application create_env_file configure_traccar configure_mediamtx configure_mosquitto configure_redis run_migrations install_services configure_firewall configure_fail2ban configure_logrotate configure_cron show_summary } # Ejecutar main "$@"