Files
whatsapp/deploy-vps.sh
2026-02-03 21:30:55 -05:00

395 lines
11 KiB
Bash
Executable File

#!/bin/bash
#############################################################
# DEPLOY SCRIPT PARA VPS - WhatsApp Bot
#
# Uso: ./deploy-vps.sh [opciones]
#
# Opciones:
# --install Primera instalación (instala Docker, etc)
# --update Solo actualizar código y reiniciar
# --ssl Generar/renovar certificados SSL
# --logs Ver logs de la aplicación
# --status Ver estado de los contenedores
#############################################################
set -e
# ==================== CONFIGURACIÓN ====================
# Modificar estos valores según tu VPS
DOMAIN="bot.u-s.app" # Tu dominio
EMAIL="admin@u-s.app" # Email para Let's Encrypt
APP_DIR="/var/www/whatsapp" # Directorio de la app en VPS
COMPOSE_FILE="docker-compose.prod.yml"
# Colores para output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# ==================== 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"
}
# Verificar que se ejecuta como root o con sudo
check_root() {
if [ "$EUID" -ne 0 ]; then
log_error "Este script debe ejecutarse como root o con sudo"
exit 1
fi
}
# Instalar Docker y Docker Compose
install_docker() {
log_info "Instalando Docker..."
# Verificar si Docker ya está instalado
if command -v docker &> /dev/null; then
log_success "Docker ya está instalado: $(docker --version)"
else
curl -fsSL https://get.docker.com | sh
systemctl enable docker
systemctl start docker
log_success "Docker instalado correctamente"
fi
# Verificar Docker Compose
if command -v docker-compose &> /dev/null || docker compose version &> /dev/null; then
log_success "Docker Compose disponible"
else
log_info "Instalando Docker Compose plugin..."
apt-get update
apt-get install -y docker-compose-plugin
log_success "Docker Compose instalado"
fi
}
# Instalar Certbot para SSL
install_certbot() {
log_info "Instalando Certbot para SSL..."
if command -v certbot &> /dev/null; then
log_success "Certbot ya está instalado"
else
apt-get update
apt-get install -y certbot
log_success "Certbot instalado"
fi
}
# Generar certificados SSL con Let's Encrypt
generate_ssl() {
log_info "Generando certificados SSL para ${DOMAIN}..."
# Detener contenedores que usan puerto 80
cd "$APP_DIR"
docker compose -f "$COMPOSE_FILE" down 2>/dev/null || true
# Generar certificado
certbot certonly \
--standalone \
--preferred-challenges http \
--agree-tos \
--email "$EMAIL" \
-d "$DOMAIN" \
--non-interactive
if [ -f "/etc/letsencrypt/live/${DOMAIN}/fullchain.pem" ]; then
log_success "Certificado SSL generado correctamente"
# Crear directorio y copiar certificados
mkdir -p "$APP_DIR/docker/ssl"
cp "/etc/letsencrypt/live/${DOMAIN}/fullchain.pem" "$APP_DIR/docker/ssl/server.crt"
cp "/etc/letsencrypt/live/${DOMAIN}/privkey.pem" "$APP_DIR/docker/ssl/server.key"
chmod 600 "$APP_DIR/docker/ssl/server.key"
log_success "Certificados copiados a docker/ssl/"
else
log_error "Error generando certificado SSL"
exit 1
fi
}
# Renovar certificados SSL
renew_ssl() {
log_info "Renovando certificados SSL..."
cd "$APP_DIR"
docker compose -f "$COMPOSE_FILE" stop app 2>/dev/null || true
certbot renew --standalone
# Copiar certificados actualizados
if [ -f "/etc/letsencrypt/live/${DOMAIN}/fullchain.pem" ]; then
cp "/etc/letsencrypt/live/${DOMAIN}/fullchain.pem" "$APP_DIR/docker/ssl/server.crt"
cp "/etc/letsencrypt/live/${DOMAIN}/privkey.pem" "$APP_DIR/docker/ssl/server.key"
log_success "Certificados renovados"
fi
docker compose -f "$COMPOSE_FILE" start app
}
# Configurar cron para renovación automática de SSL
setup_ssl_cron() {
log_info "Configurando renovación automática de SSL..."
CRON_CMD="0 3 * * * cd $APP_DIR && docker compose -f $COMPOSE_FILE stop app && certbot renew --quiet && docker compose -f $COMPOSE_FILE start app"
# Añadir al crontab si no existe
(crontab -l 2>/dev/null | grep -v "certbot renew"; echo "$CRON_CMD") | crontab -
log_success "Cron configurado para renovar SSL cada día a las 3am"
}
# Crear archivo .env si no existe
create_env_file() {
if [ ! -f "$APP_DIR/.env" ]; then
log_info "Creando archivo .env..."
cat > "$APP_DIR/.env" << 'EOF'
# ============================================
# CONFIGURACIÓN DE PRODUCCIÓN
# ============================================
# Base de datos
DB_HOST=77.37.126.126
DB_PORT=3306
DB_NAME=usite_whatsapp_bot
DB_USER=usite_whatsapp_user
DB_PASS=6q!Vio0fn@PFmsLyHjH5
# Redis (opcional, dejar vacío si no usa password)
REDIS_PASSWORD=
# WhatsApp API (obtener de Facebook Developer)
WHATSAPP_TOKEN=
WHATSAPP_PHONE_NUMBER_ID=
WEBHOOK_VERIFY_TOKEN=
EOF
log_warn "Archivo .env creado. ¡EDITA LAS CREDENCIALES antes de continuar!"
log_warn "Ejecuta: nano $APP_DIR/.env"
else
log_success "Archivo .env ya existe"
fi
}
# Crear directorios necesarios
create_directories() {
log_info "Creando directorios..."
mkdir -p "$APP_DIR/logs"
mkdir -p "$APP_DIR/uploads"
mkdir -p "$APP_DIR/docker/ssl"
# Permisos
chown -R www-data:www-data "$APP_DIR/logs" 2>/dev/null || true
chown -R www-data:www-data "$APP_DIR/uploads" 2>/dev/null || true
chmod -R 755 "$APP_DIR/logs"
chmod -R 755 "$APP_DIR/uploads"
log_success "Directorios creados"
}
# Construir y levantar contenedores
deploy() {
log_info "Desplegando aplicación..."
cd "$APP_DIR"
# Construir imágenes
log_info "Construyendo imágenes Docker..."
docker compose -f "$COMPOSE_FILE" build --no-cache
# Detener contenedores existentes (NO borra volúmenes, los datos persisten)
log_info "Deteniendo contenedores anteriores..."
docker compose -f "$COMPOSE_FILE" down --remove-orphans 2>/dev/null || true
# Levantar nuevos contenedores
log_info "Levantando contenedores..."
docker compose -f "$COMPOSE_FILE" up -d
# Esperar a que estén listos
log_info "Esperando a que los servicios estén listos..."
sleep 10
# Verificar estado
if docker compose -f "$COMPOSE_FILE" ps | grep -q "Up"; then
log_success "Contenedores ejecutándose correctamente"
else
log_error "Error iniciando contenedores"
docker compose -f "$COMPOSE_FILE" logs --tail=50
exit 1
fi
}
# Actualizar código y reiniciar
update() {
log_info "Actualizando aplicación..."
cd "$APP_DIR"
# Si es un repo git, hacer pull
if [ -d ".git" ]; then
log_info "Actualizando desde Git..."
git pull origin main || git pull origin master
fi
# Reconstruir y reiniciar
docker compose -f "$COMPOSE_FILE" build
docker compose -f "$COMPOSE_FILE" up -d
log_success "Aplicación actualizada"
}
# Ver logs
show_logs() {
cd "$APP_DIR"
docker compose -f "$COMPOSE_FILE" logs -f --tail=100
}
# Ver estado
show_status() {
cd "$APP_DIR"
echo ""
echo "==================== ESTADO DE CONTENEDORES ===================="
docker compose -f "$COMPOSE_FILE" ps
echo ""
echo "==================== USO DE RECURSOS ===================="
docker stats --no-stream --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}"
echo ""
echo "==================== VERIFICACIÓN DE SERVICIOS ===================="
# Verificar HTTP
HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost/health.php" 2>/dev/null || echo "ERROR")
if [ "$HTTP_STATUS" = "200" ]; then
log_success "HTTP: OK (status $HTTP_STATUS)"
else
log_error "HTTP: FALLO (status $HTTP_STATUS)"
fi
# Verificar HTTPS
HTTPS_STATUS=$(curl -sk -o /dev/null -w "%{http_code}" "https://localhost/health.php" 2>/dev/null || echo "ERROR")
if [ "$HTTPS_STATUS" = "200" ]; then
log_success "HTTPS: OK (status $HTTPS_STATUS)"
else
log_warn "HTTPS: $HTTPS_STATUS (puede necesitar certificados SSL)"
fi
echo ""
echo "==================== URLS ===================="
echo " App: https://${DOMAIN}"
echo " Health: https://${DOMAIN}/health.php"
}
# Primera instalación completa
full_install() {
check_root
echo ""
echo "=========================================="
echo " INSTALACIÓN COMPLETA - WhatsApp Bot"
echo "=========================================="
echo ""
install_docker
install_certbot
create_directories
create_env_file
echo ""
log_warn "ANTES DE CONTINUAR:"
log_warn "1. Edita el archivo .env con tus credenciales"
log_warn "2. Asegúrate que el dominio ${DOMAIN} apunta a este servidor"
echo ""
read -p "¿Continuar con el deploy? (s/n): " -n 1 -r
echo ""
if [[ $REPLY =~ ^[Ss]$ ]]; then
generate_ssl
setup_ssl_cron
deploy
show_status
else
log_info "Deploy cancelado. Ejecuta './deploy-vps.sh' cuando estés listo."
fi
}
# Mostrar ayuda
show_help() {
echo ""
echo "Uso: ./deploy-vps.sh [opción]"
echo ""
echo "Opciones:"
echo " --install Primera instalación completa"
echo " --update Actualizar código y reiniciar"
echo " --ssl Generar/renovar certificados SSL"
echo " --logs Ver logs en tiempo real"
echo " --status Ver estado de servicios"
echo " --help Mostrar esta ayuda"
echo ""
echo "Ejemplos:"
echo " sudo ./deploy-vps.sh --install # Primera vez"
echo " sudo ./deploy-vps.sh --update # Actualizar"
echo " ./deploy-vps.sh --logs # Ver logs"
echo ""
}
# ==================== MAIN ====================
case "${1:-}" in
--install)
full_install
;;
--update)
update
;;
--ssl)
check_root
generate_ssl
;;
--renew-ssl)
check_root
renew_ssl
;;
--logs)
show_logs
;;
--status)
show_status
;;
--help|-h)
show_help
;;
*)
# Sin argumentos, hacer deploy normal
if [ ! -d "$APP_DIR" ] || [ ! -f "$APP_DIR/$COMPOSE_FILE" ]; then
log_error "Directorio de aplicación no encontrado: $APP_DIR"
log_info "Para primera instalación usa: sudo ./deploy-vps.sh --install"
exit 1
fi
deploy
show_status
;;
esac