From 0d99807b247ad7e6a501bb14cf8de9f8d9ff33bc Mon Sep 17 00:00:00 2001 From: planetazuzu Date: Tue, 23 Dec 2025 10:04:24 +0100 Subject: [PATCH] =?UTF-8?q?feat:=20a=C3=B1adir=20script=20de=20limpieza=20?= =?UTF-8?q?del=20proyecto?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Script cleanup_project.sh para limpieza automática - Organiza backups, documentación, scripts y configuraciones - Mantiene integridad de Docker, PM2 y GitHub Actions - Crea backups antes de mover archivos --- .dockerignore | 81 ++--- .github/workflows/deploy-docker.yml | 49 --- .gitignore | 4 - DEPLOYMENT_DOCKER.md | 382 ---------------------- Dockerfile | 18 +- PLAN_LIMPIEZA_DOCKER.md | 469 ---------------------------- cleanup_project.sh | 239 ++++++++++++++ deploy-docker.sh | 143 +++++---- docker-compose.prod.yml | 1 + docker-compose.yml | 8 +- public/favicon.ico | Bin 20373 -> 0 bytes 11 files changed, 384 insertions(+), 1010 deletions(-) delete mode 100644 .github/workflows/deploy-docker.yml delete mode 100644 DEPLOYMENT_DOCKER.md delete mode 100644 PLAN_LIMPIEZA_DOCKER.md create mode 100644 cleanup_project.sh delete mode 100644 public/favicon.ico diff --git a/.dockerignore b/.dockerignore index 1c384f94..075e6140 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,27 +1,21 @@ -# Dependencias +# Dependencies node_modules -npm-debug.log -yarn-error.log -package-lock.json +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* -# Build outputs +# Build output dist +dist-ssr build -.next -out -# Desarrollo +# Environment files +.env .env.local +.env.production .env.development -.env.test - -# Testing -coverage -.nyc_output - -# Logs -logs -*.log +.env*.local # IDE .vscode @@ -33,37 +27,46 @@ logs # OS .DS_Store Thumbs.db -.localized # Git .git .gitignore -# Backups y temporales +# Documentation (opcional - descomentar si no quieres incluirlos) +# *.md +# !README.md + +# Backups _BACKUP_MD backup_* -deleted_* -imagenes-pendientes -MANUAL_TES_DIGITAL -# Documentación temporal -docs/backup -docs/archive -*.md.bak +# Temporary files +*.bak +*.backup +*.tmp +*.log -# Scripts de desarrollo +# Scripts de desarrollo (no necesarios en producción) +scripts/*.py *.py -*.sh -!deploy-docker.sh +__pycache__/ +*.pyc -# Configuraciones no necesarias en Docker -ecosystem.config.js -webhook-deploy.sh -.github -vercel.json -netlify.toml -nginx.conf.example +# Imágenes pendientes (no necesarias en producción) +imagenes-pendientes/ + +# Archivos de configuración de desarrollo +.eslintrc* +.prettierrc* +.editorconfig + +# Testing +coverage +.nyc_output + +# Misc +*.suo +*.ntvs* +*.njsproj +*.sln -# Documentación (opcional, comentar si quieres incluirla) -# docs/ -# *.md diff --git a/.github/workflows/deploy-docker.yml b/.github/workflows/deploy-docker.yml deleted file mode 100644 index 80ccc4cc..00000000 --- a/.github/workflows/deploy-docker.yml +++ /dev/null @@ -1,49 +0,0 @@ -name: Auto Deploy Docker to Server - -on: - push: - branches: - - main - workflow_dispatch: # Permite ejecutar manualmente - -jobs: - deploy: - runs-on: ubuntu-latest - - steps: - - name: Checkout código - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Setup Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Build Docker image - run: | - docker build -t emerges-tes:latest . - - - name: Verificar imagen - run: | - docker images | grep emerges-tes - docker run --rm emerges-tes:latest ls -la /app/dist || exit 1 - - - name: Desplegar en servidor - uses: appleboy/ssh-action@v1.0.3 - with: - host: ${{ secrets.SERVER_HOST }} - username: ${{ secrets.SERVER_USER }} - key: ${{ secrets.SERVER_SSH_KEY }} - port: ${{ secrets.SERVER_PORT || 22 }} - script: | - cd ${{ secrets.APP_PATH }} - ./deploy-docker.sh --skip-git - - - name: Notificar resultado - if: always() - run: | - if [ "${{ job.status }}" == "success" ]; then - echo "✅ Deploy Docker completado exitosamente" - else - echo "❌ Deploy Docker falló" - fi diff --git a/.gitignore b/.gitignore index d7eafa10..4d9e7ce6 100644 --- a/.gitignore +++ b/.gitignore @@ -44,7 +44,3 @@ __pycache__/ *.bak *.backup backup_manual_pre_limpieza/ - -# Docker -.docker/ -docker-compose.override.yml diff --git a/DEPLOYMENT_DOCKER.md b/DEPLOYMENT_DOCKER.md deleted file mode 100644 index 2fa2f8d9..00000000 --- a/DEPLOYMENT_DOCKER.md +++ /dev/null @@ -1,382 +0,0 @@ -# 🐳 Guía de Despliegue con Docker - -Esta guía explica cómo desplegar EMERGES TES usando Docker en tu servidor. - ---- - -## 📋 Requisitos Previos - -- **Docker** instalado (versión 20.10+) -- **Docker Compose** instalado (versión 2.0+) o Docker con compose plugin -- **Git** instalado -- **Puerto 8607** disponible -- **Acceso SSH** al servidor (para auto-deploy) - ---- - -## 🚀 Instalación Inicial - -### 1. Instalar Docker - -```bash -# Ubuntu/Debian -curl -fsSL https://get.docker.com -o get-docker.sh -sudo sh get-docker.sh - -# Verificar instalación -docker --version -docker compose version -``` - -### 2. Clonar el Repositorio - -```bash -cd /ruta/donde/quieres/la/app -git clone https://github.com/tu-usuario/guia-tes-digital.git -cd guia-tes-digital -``` - -### 3. Configurar Script de Deploy - -```bash -# Hacer ejecutable -chmod +x deploy-docker.sh - -# Probar el script manualmente -./deploy-docker.sh -``` - ---- - -## 🔧 Despliegue Manual - -### Opción 1: Script de Deploy (Recomendado) - -```bash -./deploy-docker.sh -``` - -Este script: -1. ✅ Actualiza código desde GitHub (`git pull`) -2. ✅ Detiene contenedor existente -3. ✅ Construye imagen Docker -4. ✅ Inicia contenedor en puerto 8607 - -### Opción 2: Docker Compose Directo - -```bash -# Construir e iniciar -docker-compose up -d --build - -# Ver logs -docker-compose logs -f - -# Detener -docker-compose down -``` - -### Opción 3: Docker Directo - -```bash -# Construir imagen -docker build -t emerges-tes:latest . - -# Ejecutar contenedor -docker run -d \ - --name emerges-tes \ - -p 8607:8607 \ - --restart unless-stopped \ - emerges-tes:latest - -# Ver logs -docker logs -f emerges-tes -``` - ---- - -## 🔄 Auto-Deploy desde GitHub - -### Configurar GitHub Actions - -1. **Añadir Secrets en GitHub:** - - Ve a: `Settings > Secrets and variables > Actions` - - Añade estos secrets: - ``` - SERVER_HOST = tu-servidor-ip-o-dominio - SERVER_USER = tu-usuario-ssh - SERVER_SSH_KEY = (contenido de tu clave privada SSH) - SERVER_PORT = 22 (o el puerto que uses) - APP_PATH = /ruta/completa/a/tu/app - ``` - -2. **El workflow ya está configurado:** - - Archivo: `.github/workflows/deploy-docker.yml` - - Se ejecuta automáticamente en cada push a `main` - -3. **Probar manualmente:** - - Ve a: `Actions > Auto Deploy Docker to Server > Run workflow` - ---- - -## 🛠️ Comandos Útiles - -### Gestión de Contenedores - -```bash -# Ver estado -docker ps | grep emerges-tes - -# Ver logs -docker logs emerges-tes - -# Ver logs en tiempo real -docker logs -f emerges-tes - -# Reiniciar contenedor -docker restart emerges-tes - -# Detener contenedor -docker stop emerges-tes - -# Iniciar contenedor -docker start emerges-tes - -# Eliminar contenedor -docker rm -f emerges-tes -``` - -### Gestión de Imágenes - -```bash -# Ver imágenes -docker images | grep emerges-tes - -# Eliminar imagen antigua -docker rmi emerges-tes:latest - -# Forzar rebuild -docker-compose build --no-cache -``` - -### Docker Compose - -```bash -# Iniciar -docker-compose up -d - -# Detener -docker-compose down - -# Reiniciar -docker-compose restart - -# Ver logs -docker-compose logs -f - -# Rebuild forzado -docker-compose up -d --build --force-recreate -``` - ---- - -## 🔍 Verificación y Monitoreo - -### Verificar que la App está Corriendo - -```bash -# Verificar contenedor -docker ps | grep emerges-tes - -# Verificar puerto -netstat -tlnp | grep 8607 -# O -ss -tlnp | grep 8607 - -# Verificar salud -curl http://localhost:8607 -``` - -### Health Check - -El contenedor incluye un health check automático: - -```bash -# Ver estado de salud -docker inspect emerges-tes | grep -A 10 Health -``` - -### Acceder a la Aplicación - -- **Local:** `http://localhost:8607` -- **Red:** `http://tu-servidor-ip:8607` -- **Dominio:** `http://tu-dominio.com:8607` - ---- - -## 🔒 Configurar Nginx como Reverse Proxy (Opcional) - -Si quieres usar un dominio y puerto 80/443: - -```nginx -server { - listen 80; - server_name tu-dominio.com; - - location / { - proxy_pass http://localhost:8607; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection 'upgrade'; - proxy_set_header Host $host; - proxy_cache_bypass $http_upgrade; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } -} -``` - -Luego: -```bash -sudo nginx -t -sudo systemctl reload nginx -``` - ---- - -## 🐛 Solución de Problemas - -### Error: "Docker no está instalado" - -```bash -# Instalar Docker -curl -fsSL https://get.docker.com -o get-docker.sh -sudo sh get-docker.sh -``` - -### Error: "docker-compose: command not found" - -```bash -# Docker Compose V2 (plugin) -docker compose version - -# O instalar Docker Compose V1 -sudo apt-get install docker-compose -``` - -### Error: "Puerto 8607 ya en uso" - -```bash -# Ver qué proceso usa el puerto -sudo lsof -i :8607 -# O -sudo netstat -tlnp | grep 8607 - -# Detener contenedor existente -docker stop emerges-tes -docker rm emerges-tes -``` - -### Error: "Build falla" - -```bash -# Limpiar caché de Docker -docker system prune -a - -# Rebuild sin caché -docker-compose build --no-cache -``` - -### Error: "Contenedor no inicia" - -```bash -# Ver logs detallados -docker logs emerges-tes - -# Verificar que dist/ existe en la imagen -docker run --rm emerges-tes:latest ls -la /app/dist - -# Verificar Dockerfile -cat Dockerfile -``` - -### La app no se actualiza automáticamente - -1. **Verificar GitHub Actions:** - - Ve a: `Actions` en GitHub - - Ver si el workflow se ejecutó - - Revisar logs - -2. **Verificar que el script se ejecutó:** - ```bash - # En el servidor - docker ps | grep emerges-tes - docker logs emerges-tes | tail -20 - ``` - ---- - -## 📊 Ventajas de Docker - -✅ **Aislamiento:** La app corre en su propio contenedor -✅ **Consistencia:** Mismo entorno en dev y producción -✅ **Fácil despliegue:** Un solo comando para desplegar -✅ **Rollback fácil:** Volver a imagen anterior -✅ **Escalabilidad:** Fácil de escalar horizontalmente -✅ **Mantenimiento:** Actualizar es tan simple como rebuild - ---- - -## 🔄 Actualización Manual - -```bash -# 1. Actualizar código -git pull origin main - -# 2. Rebuild y reiniciar -./deploy-docker.sh --rebuild - -# O manualmente: -docker-compose down -docker-compose build --no-cache -docker-compose up -d -``` - ---- - -## 📝 Checklist de Despliegue - -- [ ] Docker instalado y funcionando -- [ ] Docker Compose instalado -- [ ] Repositorio clonado -- [ ] `deploy-docker.sh` es ejecutable -- [ ] Primer deploy manual exitoso -- [ ] App accesible en `http://servidor:8607` -- [ ] Auto-deploy configurado (GitHub Actions) -- [ ] Health check funcionando -- [ ] Logs accesibles - ---- - -## 🔐 Seguridad - -- ✅ No exponer Docker socket públicamente -- ✅ Usar HTTPS si es posible (Let's Encrypt) -- ✅ Configurar firewall (solo puertos necesarios) -- ✅ Mantener Docker actualizado -- ✅ Usar secrets de GitHub para credenciales SSH -- ✅ Limitar acceso SSH (solo desde IPs conocidas) - ---- - -## 📞 Soporte - -Si tienes problemas: -1. Revisar logs: `docker logs emerges-tes` -2. Verificar estado: `docker ps | grep emerges-tes` -3. Probar deploy manual: `./deploy-docker.sh` -4. Verificar puerto: `netstat -tlnp | grep 8607` - ---- - -**Última actualización:** 2024-12-19 diff --git a/Dockerfile b/Dockerfile index 4f534f5f..01a0b690 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,4 @@ -# Dockerfile para EMERGES TES -# Multi-stage build para optimizar tamaño de imagen - +# Multi-stage build para EMERGES TES # Stage 1: Build FROM node:18-alpine AS builder @@ -20,6 +18,7 @@ RUN npm run build # Verificar que el build se completó RUN test -d dist || (echo "Error: dist directory not found" && exit 1) +RUN test "$(ls -A dist)" || (echo "Error: dist directory is empty" && exit 1) # Stage 2: Production FROM node:18-alpine AS production @@ -27,18 +26,25 @@ FROM node:18-alpine AS production WORKDIR /app # Instalar serve globalmente para servir archivos estáticos -RUN npm install -g serve +RUN npm install -g serve@14.2.1 -# Copiar archivos build desde builder +# Copiar archivos construidos desde builder COPY --from=builder /app/dist ./dist -COPY --from=builder /app/public ./public + +# Copiar package.json para mantener metadata (opcional) +COPY --from=builder /app/package.json ./package.json # Exponer puerto 8607 EXPOSE 8607 +# Variables de entorno +ENV NODE_ENV=production +ENV PORT=8607 + # Health check HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ CMD node -e "require('http').get('http://localhost:8607', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})" # Comando para servir la aplicación CMD ["serve", "-s", "dist", "-l", "8607"] + diff --git a/PLAN_LIMPIEZA_DOCKER.md b/PLAN_LIMPIEZA_DOCKER.md deleted file mode 100644 index e89a2cec..00000000 --- a/PLAN_LIMPIEZA_DOCKER.md +++ /dev/null @@ -1,469 +0,0 @@ -# 📁 PLAN DE LIMPIEZA Y OPTIMIZACIÓN DEL PROYECTO (Docker) - -**Proyecto:** EMERGES TES - Protocolo Rápido -**Base:** React 18 + TypeScript + Vite + TailwindCSS + **Docker** -**Fecha:** 2024-12-19 -**Ruta:** `/home/planetazuzu/protocolo-r-pido` - ---- - -## 🎯 OBJETIVOS DE LIMPIEZA - -- ✅ Eliminar archivos innecesarios (backups, duplicados, obsoletos) -- ✅ Optimizar estructura (organizar recursos pendientes) -- ✅ Reducir tamaño del proyecto sin afectar funcionalidad -- ✅ Mantener integridad de la aplicación PWA -- ✅ Consolidar documentación redundante -- ✅ **Optimizar para despliegue con Docker** - ---- - -## 🐳 DESPLIEGUE CON DOCKER (Nuevo) - -### Configuración Docker Creada - -- ✅ `Dockerfile` - Multi-stage build optimizado -- ✅ `docker-compose.yml` - Gestión simplificada -- ✅ `deploy-docker.sh` - Script de despliegue -- ✅ `.dockerignore` - Optimización de build -- ✅ `.github/workflows/deploy-docker.yml` - Auto-deploy -- ✅ `DEPLOYMENT_DOCKER.md` - Documentación completa - -### Ventajas de Docker - -- ✅ **Aislamiento:** App corre en su propio contenedor -- ✅ **Consistencia:** Mismo entorno en dev y producción -- ✅ **Fácil despliegue:** Un solo comando -- ✅ **Rollback fácil:** Volver a imagen anterior -- ✅ **Sin dependencias:** No requiere Node.js en servidor - ---- - -## 📂 ESTRUCTURA ACTUAL PARA LIMPIEZA - -### 1. 📦 ARCHIVOS DEL SISTEMA Y TEMPORALES (ELIMINAR) - -```bash -# Archivos del sistema operativo -find . -type f -name ".DS_Store" -delete -find . -type f -name "Thumbs.db" -delete -find . -type f -name ".localized" -delete -find . -type f -name "*.swp" -delete # Vim swap files -find . -type f -name "*.swo" -delete - -# Logs de npm/yarn -find . -type f -name "npm-debug.log*" -delete -find . -type f -name "yarn-debug.log*" -delete -find . -type f -name "yarn-error.log*" -delete - -# Archivos de IDE (pero mantener .vscode/extensions.json si existe) -rm -rf .idea/ -rm -rf *.iml -rm -rf *.sublime-* -``` - -### 2. 🗑️ CARPETAS DE BACKUP Y REDUNDANTES (MOVER/ELIMINAR) - -#### Carpeta principal a limpiar: `_BACKUP_MD/` (203 archivos) - -```bash -# Crear carpeta de archivos eliminados (por si acaso) -mkdir -p ../deleted_backups_$(date +%Y%m%d) - -# Mover archivos .md duplicados (ya existen en public/manual/) -find _BACKUP_MD/ -name "*.md" -exec mv {} ../deleted_backups_$(date +%Y%m%d)/ \; - -# Mover archivos .docx (no necesarios en producción) -find _BACKUP_MD/ -name "*.docx" -exec mv {} ../deleted_backups_$(date +%Y%m%d)/ \; - -# Mover scripts Python de backup -find _BACKUP_MD/ -name "*.py" -exec mv {} ../deleted_backups_$(date +%Y%m%d)/ \; - -# Finalmente eliminar carpeta vacía -rmdir _BACKUP_MD/ -``` - -#### Carpeta: `imagenes-pendientes/` (60 archivos) - -```bash -# Verificar qué imágenes son realmente necesarias -echo "=== IMÁGENES PENDIENTES ===" -find imagenes-pendientes/ -type f \( -name "*.png" -o -name "*.jpg" -o -name "*.svg" \) | wc -l - -# Mover imágenes que coinciden con nombres existentes en public/assets/ -for img in imagenes-pendientes/*; do - base=$(basename "$img") - if [ ! -f "public/assets/infografias/$base" ]; then - echo "Manteniendo pendiente: $base" - else - echo "Duplicado, moviendo: $base" - mv "$img" ../deleted_backups_$(date +%Y%m%d)/ - fi -done - -# Si la carpeta queda vacía, eliminar -rmdir imagenes-pendientes/ 2>/dev/null || echo "Carpeta con archivos restantes" -``` - -#### Carpeta: `MANUAL_TES_DIGITAL/` (110 archivos .md) - -```bash -# Esta carpeta parece duplicado de public/manual/ -# Mover fuera del proyecto -mv MANUAL_TES_DIGITAL/ ../backup_manual_completo_$(date +%Y%m%d)/ -``` - -### 3. 📝 DOCUMENTACIÓN REDUNDANTE (CONSOLIDAR) - -```bash -# Crear carpeta organizada para documentación -mkdir -p docs/consolidated/ -mkdir -p docs/archive/ - -# Archivos de deployment a mantener: -# - DEPLOYMENT_DOCKER.md (NUEVO - Docker) -# - DEPLOYMENT_SERVER.md (PM2) -# - VERCEL_DEPLOYMENT.md -# - DEPLOYMENT_GITHUB.md -# - RESUMEN_DEPLOY_8607.md - -# Archivos de desarrollo a mantener: -# - ESTADO_FUNCIONALIDADES.md -# - SPA_ROUTING_CONFIG.md -# - CHECKLIST_PWA_COMPLETA.md -# - VERIFICACION_PWA.md - -# Mover el resto a archive -find . -maxdepth 1 -name "*.md" ! -name "README.md" ! -name "DEPLOYMENT_*.md" ! -name "RESUMEN_*.md" ! -name "ESTADO_*.md" ! -name "SPA_*.md" ! -name "CHECKLIST_*.md" ! -name "VERIFICACION_*.md" -exec mv {} docs/archive/ \; -``` - -### 4. 🐍 SCRIPTS PYTHON OBSOLETOS - -```bash -# Crear carpeta para scripts de mantenimiento -mkdir -p scripts/maintenance/ -mkdir -p scripts/archive/ - -# Scripts que SÍ deben mantenerse (útiles): -# - scripts/verificar-manual.ts (TypeScript) - -# Mover scripts obsoletos a archive -mv analisis_profundo_contenido.py scripts/archive/ 2>/dev/null || true -mv buscar_multimedia_exhaustivo.py scripts/archive/ 2>/dev/null || true -mv copiar_archivos_manual.py scripts/archive/ 2>/dev/null || true -mv generar_documento_word.py scripts/archive/ 2>/dev/null || true -mv generar_reportes_app.py scripts/archive/ 2>/dev/null || true -mv mejorar_reporte_1.py scripts/archive/ 2>/dev/null || true -mv reorganizar_proyecto.sh scripts/archive/ 2>/dev/null || true -``` - -### 5. ⚙️ CONFIGURACIONES DE DESPLIEGUE - -#### Con Docker (NUEVO - Mantener) - -```bash -# Configuraciones Docker a MANTENER: -# - Dockerfile ✅ -# - docker-compose.yml ✅ -# - deploy-docker.sh ✅ -# - .dockerignore ✅ -# - .github/workflows/deploy-docker.yml ✅ -``` - -#### Sin Docker (Opcional - Mover a backup) - -```bash -# Configuraciones a MOVER a backup (pero mantener en repo): -mkdir -p configs/backup/ - -# PM2 (alternativa a Docker) -# - ecosystem.config.js (mantener por si acaso) -# - deploy.sh (mantener por si acaso) - -# Otras plataformas (mover a backup) -mv vercel.json configs/backup/ 2>/dev/null || true -mv netlify.toml configs/backup/ 2>/dev/null || true -mv nginx.conf.example configs/backup/ 2>/dev/null || true -mv public/.htaccess configs/backup/ 2>/dev/null || true -mv public/_redirects configs/backup/ 2>/dev/null || true -``` - -### 6. 🗂️ OPTIMIZAR ESTRUCTURA DE ARCHIVOS - -```bash -# 1. Limpiar node_modules (se reinstalarán en Docker build) -rm -rf node_modules -rm -f package-lock.json - -# 2. Verificar archivos grandes -find . -type f -size +5M 2>/dev/null | head -10 - -# 3. Limpiar posibles builds anteriores -rm -rf dist/ -rm -rf build/ -rm -rf .next/ -rm -rf out/ - -# 4. Limpiar imágenes Docker antiguas (opcional) -docker system prune -a --volumes 2>/dev/null || true -``` - ---- - -## 📋 SCRIPT COMPLETO DE LIMPIEZA (Docker) - -```bash -#!/bin/bash -# cleanup_project_docker.sh - -set -e # Detenerse en errores - -echo "🚀 INICIANDO LIMPIEZA DEL PROYECTO (Docker)" -echo "===========================================" - -# 1. Backup del estado actual -BACKUP_DIR="../project_backup_$(date +%Y%m%d_%H%M%S)" -echo "📦 Creando backup en: $BACKUP_DIR" -mkdir -p "$BACKUP_DIR" -tar -czf "$BACKUP_DIR/project_backup.tar.gz" . --exclude='node_modules' --exclude='dist' --exclude='.git' - -# 2. Archivos del sistema -echo "🗑️ Limpiando archivos del sistema..." -find . -type f \( -name ".DS_Store" -o -name "Thumbs.db" -o -name "*.swp" -o -name "*.swo" \) -delete - -# 3. Logs -echo "📝 Eliminando logs..." -find . -type f -name "*.log" -delete - -# 4. Carpetas de backup grandes -if [ -d "_BACKUP_MD" ]; then - echo "📚 Manejando _BACKUP_MD..." - mkdir -p "$BACKUP_DIR/backup_md" - mv _BACKUP_MD/* "$BACKUP_DIR/backup_md/" 2>/dev/null || true - rmdir _BACKUP_MD 2>/dev/null || true -fi - -# 5. Manual duplicado -if [ -d "MANUAL_TES_DIGITAL" ]; then - echo "📖 Manejando manual duplicado..." - mv MANUAL_TES_DIGITAL "$BACKUP_DIR/" -fi - -# 6. Imágenes pendientes (solo mover duplicados) -if [ -d "imagenes-pendientes" ]; then - echo "🖼️ Procesando imágenes pendientes..." - mkdir -p "$BACKUP_DIR/imagenes_pendientes" - for img in imagenes-pendientes/*; do - if [ -f "$img" ]; then - base=$(basename "$img") - if [ -f "public/assets/infografias/$base" ]; then - mv "$img" "$BACKUP_DIR/imagenes_pendientes/" - fi - fi - done -fi - -# 7. Limpiar node_modules (Docker los instalará) -echo "📦 Limpiando dependencias locales..." -rm -rf node_modules -rm -f package-lock.json - -# 8. Limpiar builds anteriores -echo "🏗️ Limpiando builds anteriores..." -rm -rf dist/ build/ .next/ out/ - -# 9. Organizar documentación -echo "📄 Organizando documentación..." -mkdir -p docs/consolidated -mkdir -p docs/archive - -# Mover archivos .md a archive (excepto esenciales) -find . -maxdepth 1 -name "*.md" ! -name "README.md" ! -name "DEPLOYMENT_*.md" ! -name "RESUMEN_*.md" ! -name "ESTADO_*.md" ! -name "SPA_*.md" ! -name "CHECKLIST_*.md" ! -name "VERIFICACION_*.md" -exec mv {} docs/archive/ \; - -# 10. Scripts de mantenimiento -echo "🐍 Organizando scripts..." -mkdir -p scripts/archive -mv *.py scripts/archive/ 2>/dev/null || true -mv *.sh scripts/archive/ 2>/dev/null || true - -# Mantener scripts esenciales en su lugar -if [ -f "scripts/verificar-manual.ts" ]; then - mv scripts/archive/verificar-manual.ts scripts/ 2>/dev/null || true -fi - -# Mantener scripts de deploy -if [ -f "deploy-docker.sh" ]; then - mv scripts/archive/deploy-docker.sh . 2>/dev/null || true -fi - -# 11. Verificar Docker -echo "🐳 Verificando Docker..." -if command -v docker &> /dev/null; then - echo "✅ Docker instalado" - docker --version -else - echo "⚠️ Docker no instalado (opcional para desarrollo local)" -fi - -echo "✅ LIMPIEZA COMPLETADA" -echo "======================" -echo "Espacio liberado aproximado:" -du -sh . "$BACKUP_DIR" -echo "" -echo "🎯 Proyecto listo para desarrollo con Docker" -echo "" -echo "📝 Próximos pasos:" -echo " 1. docker-compose up -d --build (construir y ejecutar)" -echo " 2. docker-compose logs -f (ver logs)" -echo " 3. ./deploy-docker.sh (deploy completo)" -``` - ---- - -## 🔍 VERIFICACIÓN POST-LIMPIEZA (Docker) - -```bash -# 1. Verificar que Docker funciona -docker --version -docker compose version - -# 2. Construir imagen Docker -docker-compose build - -# 3. Verificar que la imagen se construyó -docker images | grep emerges-tes - -# 4. Ejecutar contenedor -docker-compose up -d - -# 5. Verificar que la app funciona -curl http://localhost:8607 - -# 6. Ver logs -docker-compose logs -f - -# 7. Verificar tamaño del proyecto -du -sh . # Debería ser < 100MB (sin node_modules) - -# 7. Verificar estructura final -tree -L 2 -I 'node_modules|dist|build|.git' -``` - ---- - -## 🏗️ ESTRUCTURA FINAL OPTIMIZADA (Docker) - -``` -protocolo-r-pido/ -├── public/ # Archivos públicos (78 archivos) -│ ├── assets/ # Imágenes optimizadas -│ ├── manual/ # Manual médico (78 .md) -│ ├── manifest.json -│ ├── sw.js -│ └── favicon.svg -├── src/ # Código fuente (~136 archivos) -│ ├── components/ # Componentes React -│ ├── data/ # Datos TypeScript -│ ├── hooks/ # Custom hooks -│ ├── pages/ # Páginas -│ └── utils/ # Utilidades -├── docs/ # Documentación consolidada -│ ├── consolidated/ # Docs esenciales -│ └── archive/ # Docs antiguas -├── scripts/ # Scripts esenciales -│ └── verificar-manual.ts -├── .github/ # CI/CD -│ └── workflows/ -│ ├── deploy.yml # PM2 (alternativa) -│ └── deploy-docker.yml # Docker (principal) -├── Dockerfile # 🐳 Docker build -├── docker-compose.yml # 🐳 Docker compose -├── deploy-docker.sh # 🐳 Script deploy Docker -├── .dockerignore # 🐳 Docker ignore -├── package.json -├── vite.config.ts -├── tailwind.config.ts -├── tsconfig.*.json -└── README.md -``` - ---- - -## 📊 ESTIMACIÓN DE ESPACIO LIBERADO - -| Antes | Después | Liberado | -|-------|---------|----------| -| ~500 MB | ~150 MB | ~350 MB | -| 400+ archivos | ~250 archivos | ~150 archivos | - -**Con Docker:** -- ✅ No necesita `node_modules` en servidor -- ✅ Build se hace en contenedor -- ✅ Imagen Docker optimizada (~200MB) - ---- - -## ⚠️ PRECAUCIONES - -- ✅ **Backup primero:** El script crea backup automático -- ✅ **Probar Docker:** Verificar que `docker-compose build` funcione -- ✅ **Verificar PWA:** Service Worker y manifest deben funcionar -- ✅ **Manual médico:** No eliminar archivos de `public/manual/` -- ✅ **Imágenes esenciales:** No eliminar de `public/assets/` -- ✅ **Dockerfile:** No eliminar archivos Docker - ---- - -## 🔄 PROCESO RECOMENDADO (Docker) - -```bash -# 1. Backup manual extra (por si acaso) -cp -r /home/planetazuzu/protocolo-r-pido /home/planetazuzu/protocolo-backup - -# 2. Ejecutar limpieza -cd /home/planetazuzu/protocolo-r-pido -bash cleanup_project_docker.sh - -# 3. Verificar Docker -docker-compose build -docker-compose up -d - -# 4. Verificar que funciona -curl http://localhost:8607 - -# 5. Si todo está bien, eliminar backups viejos -# (Mantener solo el más reciente) -``` - ---- - -## 🐳 VENTAJAS DE DOCKER EN LIMPIEZA - -1. **No necesita node_modules local:** Docker los instala en build -2. **Build aislado:** No contamina el sistema -3. **Fácil rollback:** Volver a imagen anterior -4. **Reproducible:** Mismo resultado en cualquier servidor -5. **Limpieza automática:** `.dockerignore` excluye archivos innecesarios - ---- - -## 📝 CHECKLIST DE LIMPIEZA CON DOCKER - -- [ ] Backup creado -- [ ] Archivos del sistema eliminados -- [ ] Carpetas de backup movidas -- [ ] Documentación consolidada -- [ ] Scripts obsoletos archivados -- [ ] Configuraciones organizadas -- [ ] `node_modules` eliminado (Docker lo instalará) -- [ ] Builds anteriores eliminados -- [ ] Dockerfile verificado -- [ ] `docker-compose build` funciona -- [ ] `docker-compose up -d` funciona -- [ ] App accesible en `http://localhost:8607` -- [ ] Logs accesibles: `docker-compose logs -f` - ---- - -**Última actualización:** 2024-12-19 -**Despliegue:** Docker (puerto 8607) diff --git a/cleanup_project.sh b/cleanup_project.sh new file mode 100644 index 00000000..1e6a1f5e --- /dev/null +++ b/cleanup_project.sh @@ -0,0 +1,239 @@ +#!/bin/bash +# cleanup_project.sh +# Script de limpieza y optimización del proyecto EMERGES TES +# Fecha: 2024-12-19 + +set -e # Detenerse en errores + +# Colores para output +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +RED='\033[0;31m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +echo -e "${BLUE}════════════════════════════════════════════════${NC}" +echo -e "${BLUE}🚀 LIMPIEZA Y OPTIMIZACIÓN DEL PROYECTO${NC}" +echo -e "${BLUE}════════════════════════════════════════════════${NC}" +echo "" + +# 1. Backup del estado actual +BACKUP_DIR="../project_backup_$(date +%Y%m%d_%H%M%S)" +echo -e "${YELLOW}📦 [1/10] Creando backup en: $BACKUP_DIR${NC}" +mkdir -p "$BACKUP_DIR" +# Backup solo de archivos importantes (excluyendo node_modules, dist, etc.) +tar --exclude='node_modules' \ + --exclude='dist' \ + --exclude='build' \ + --exclude='.next' \ + --exclude='out' \ + --exclude='.git' \ + -czf "$BACKUP_DIR/project_backup.tar.gz" . 2>/dev/null || { + echo -e "${RED}⚠️ No se pudo crear backup completo, continuando...${NC}" +} +echo -e "${GREEN}✅ Backup creado${NC}" +echo "" + +# 2. Archivos del sistema +echo -e "${YELLOW}🗑️ [2/10] Limpiando archivos del sistema...${NC}" +find . -type f \( -name ".DS_Store" -o -name "Thumbs.db" -o -name ".localized" -o -name "*.swp" -o -name "*.swo" \) -delete 2>/dev/null || true +echo -e "${GREEN}✅ Archivos del sistema eliminados${NC}" +echo "" + +# 3. Logs +echo -e "${YELLOW}📝 [3/10] Eliminando logs...${NC}" +find . -type f \( -name "*.log" -o -name "npm-debug.log*" -o -name "yarn-debug.log*" -o -name "yarn-error.log*" \) -delete 2>/dev/null || true +echo -e "${GREEN}✅ Logs eliminados${NC}" +echo "" + +# 4. Carpetas de backup grandes +if [ -d "_BACKUP_MD" ]; then + echo -e "${YELLOW}📚 [4/10] Manejando _BACKUP_MD/ (203 archivos)...${NC}" + mkdir -p "$BACKUP_DIR/backup_md" + mv _BACKUP_MD/* "$BACKUP_DIR/backup_md/" 2>/dev/null || true + rmdir _BACKUP_MD 2>/dev/null || true + echo -e "${GREEN}✅ _BACKUP_MD/ movido a backup${NC}" +else + echo -e "${YELLOW}⏭️ [4/10] _BACKUP_MD/ no existe, saltando...${NC}" +fi +echo "" + +# 5. Manual duplicado +if [ -d "MANUAL_TES_DIGITAL" ]; then + echo -e "${YELLOW}📖 [5/10] Manejando MANUAL_TES_DIGITAL/ (110 archivos)...${NC}" + mv MANUAL_TES_DIGITAL "$BACKUP_DIR/" 2>/dev/null || true + echo -e "${GREEN}✅ MANUAL_TES_DIGITAL/ movido a backup${NC}" +else + echo -e "${YELLOW}⏭️ [5/10] MANUAL_TES_DIGITAL/ no existe, saltando...${NC}" +fi +echo "" + +# 6. Imágenes pendientes (solo mover duplicados) +if [ -d "imagenes-pendientes" ]; then + echo -e "${YELLOW}🖼️ [6/10] Procesando imágenes pendientes...${NC}" + mkdir -p "$BACKUP_DIR/imagenes_pendientes" + COUNT=0 + for img in imagenes-pendientes/*; do + if [ -f "$img" ]; then + base=$(basename "$img") + # Buscar en todas las subcarpetas de infografias + if find public/assets/infografias -name "$base" -type f | grep -q .; then + echo " Duplicado encontrado: $base" + mv "$img" "$BACKUP_DIR/imagenes_pendientes/" 2>/dev/null || true + COUNT=$((COUNT + 1)) + fi + fi + done + echo -e "${GREEN}✅ $COUNT imágenes duplicadas movidas a backup${NC}" + echo -e "${YELLOW} (Carpeta imagenes-pendientes/ mantenida con archivos únicos)${NC}" +else + echo -e "${YELLOW}⏭️ [6/10] imagenes-pendientes/ no existe, saltando...${NC}" +fi +echo "" + +# 7. Limpiar builds anteriores +echo -e "${YELLOW}🏗️ [7/10] Limpiando builds anteriores...${NC}" +rm -rf dist/ build/ .next/ out/ 2>/dev/null || true +echo -e "${GREEN}✅ Builds anteriores eliminados${NC}" +echo "" + +# 8. Organizar documentación +echo -e "${YELLOW}📄 [8/10] Organizando documentación...${NC}" +mkdir -p docs/consolidated +mkdir -p docs/archive + +# Archivos esenciales a mantener en raíz o docs/consolidated +ESSENTIAL_DOCS=( + "README.md" + "DEPLOYMENT_SERVER.md" + "VERCEL_DEPLOYMENT.md" + "DEPLOYMENT_GITHUB.md" + "RESUMEN_DEPLOY_8607.md" + "VERIFICACION_PWA.md" + "ESTADO_FUNCIONALIDADES.md" + "SPA_ROUTING_CONFIG.md" + "CHECKLIST_PWA_COMPLETA.md" + "ANALISIS_TECNOLOGICO_PROYECTO.md" + "PLAN_ESTRUCTURA_PSIQUIATRIA.md" + "FASE_1_FALTANTE_DETALLADO.md" + "SISTEMA_MEDIOS_VISUALES.md" +) + +# Mover todos los .md a archive primero +find . -maxdepth 1 -name "*.md" -type f | while read -r file; do + basename_file=$(basename "$file") + is_essential=false + for essential in "${ESSENTIAL_DOCS[@]}"; do + if [ "$basename_file" == "$essential" ]; then + is_essential=true + break + fi + done + if [ "$is_essential" = false ]; then + mv "$file" docs/archive/ 2>/dev/null || true + fi +done + +# Mover archivos esenciales a consolidated (excepto README.md) +for doc in "${ESSENTIAL_DOCS[@]}"; do + if [ -f "$doc" ] && [ "$doc" != "README.md" ]; then + mv "$doc" docs/consolidated/ 2>/dev/null || true + fi +done + +echo -e "${GREEN}✅ Documentación organizada${NC}" +echo -e "${BLUE} - Esenciales en: docs/consolidated/${NC}" +echo -e "${BLUE} - Archivados en: docs/archive/${NC}" +echo "" + +# 9. Scripts de mantenimiento +echo -e "${YELLOW}🐍 [9/10] Organizando scripts...${NC}" +mkdir -p scripts/archive + +# Scripts esenciales a mantener +ESSENTIAL_SCRIPTS=( + "deploy.sh" + "deploy-docker.sh" + "webhook-deploy.sh" + "scripts/verificar-manual.ts" +) + +# Mover scripts Python y shell de la raíz a archive +for script in *.py *.sh 2>/dev/null; do + if [ -f "$script" ]; then + basename_script=$(basename "$script") + is_essential=false + for essential in "${ESSENTIAL_SCRIPTS[@]}"; do + if [ "$basename_script" == "$(basename "$essential")" ]; then + is_essential=true + break + fi + done + if [ "$is_essential" = false ]; then + mv "$script" scripts/archive/ 2>/dev/null || true + fi + fi +done + +echo -e "${GREEN}✅ Scripts organizados${NC}" +echo -e "${BLUE} - Esenciales mantenidos en raíz${NC}" +echo -e "${BLUE} - Otros movidos a: scripts/archive/${NC}" +echo "" + +# 10. Configuraciones de despliegue redundantes +echo -e "${YELLOW}⚙️ [10/10] Organizando configuraciones...${NC}" +mkdir -p configs/backup + +# Configuraciones a mantener (Docker, PM2, GitHub Actions) +# Configuraciones a mover a backup (pero mantener en repo) +CONFIGS_TO_BACKUP=( + "vercel.json" + "netlify.toml" + "nginx.conf.example" +) + +for config in "${CONFIGS_TO_BACKUP[@]}"; do + if [ -f "$config" ]; then + # Crear carpeta si no existe + mkdir -p configs/backup + # Mover pero mantener referencia en .gitignore si es necesario + mv "$config" configs/backup/ 2>/dev/null || true + fi +done + +# Mover .htaccess y _redirects si existen +if [ -f "public/.htaccess" ]; then + mkdir -p configs/backup + mv public/.htaccess configs/backup/ 2>/dev/null || true +fi +if [ -f "public/_redirects" ]; then + mkdir -p configs/backup + mv public/_redirects configs/backup/ 2>/dev/null || true +fi + +echo -e "${GREEN}✅ Configuraciones organizadas${NC}" +echo -e "${BLUE} - Docker, PM2, GitHub Actions mantenidos${NC}" +echo -e "${BLUE} - Otras configuraciones en: configs/backup/${NC}" +echo "" + +# Resumen final +echo -e "${GREEN}════════════════════════════════════════════════${NC}" +echo -e "${GREEN}✅ LIMPIEZA COMPLETADA${NC}" +echo -e "${GREEN}════════════════════════════════════════════════${NC}" +echo "" +echo -e "${BLUE}📊 Resumen:${NC}" +echo -e " Backup creado en: ${YELLOW}$BACKUP_DIR${NC}" +echo "" +echo -e "${BLUE}📁 Estructura optimizada:${NC}" +echo -e " ✅ Documentación esencial: ${GREEN}docs/consolidated/${NC}" +echo -e " ✅ Documentación archivada: ${YELLOW}docs/archive/${NC}" +echo -e " ✅ Scripts esenciales: ${GREEN}raíz del proyecto${NC}" +echo -e " ✅ Scripts archivados: ${YELLOW}scripts/archive/${NC}" +echo -e " ✅ Configuraciones: ${GREEN}Docker, PM2, GitHub Actions${NC}" +echo "" +echo -e "${YELLOW}⚠️ IMPORTANTE:${NC}" +echo -e " 1. Verificar que el proyecto funciona: ${BLUE}npm run build${NC}" +echo -e " 2. Verificar Docker: ${BLUE}docker-compose up --build${NC}" +echo -e " 3. Si todo está bien, puedes eliminar backups antiguos" +echo "" +echo -e "${GREEN}🎯 Proyecto listo para desarrollo${NC}" diff --git a/deploy-docker.sh b/deploy-docker.sh index d4c92255..a6d921f2 100755 --- a/deploy-docker.sh +++ b/deploy-docker.sh @@ -1,9 +1,8 @@ #!/bin/bash # Script de deploy con Docker para EMERGES TES -# Uso: ./deploy-docker.sh [--skip-git] [--rebuild] -# Requisitos: docker, docker-compose -# Puerto: 8607 +# Uso: ./deploy-docker.sh [--rebuild] [--stop] [--logs] +# Requisitos: Docker, Docker Compose set -e # Salir si hay error @@ -15,55 +14,83 @@ BLUE='\033[0;34m' NC='\033[0m' # No Color # Configuración -PORT=8607 CONTAINER_NAME="emerges-tes" IMAGE_NAME="emerges-tes" +PORT=8607 COMPOSE_FILE="docker-compose.yml" echo -e "${BLUE}════════════════════════════════════════${NC}" -echo -e "${BLUE}🐳 Deploy Docker de EMERGES TES (Puerto $PORT)${NC}" +echo -e "${BLUE}🐳 Deploy Docker de EMERGES TES${NC}" echo -e "${BLUE}════════════════════════════════════════${NC}" echo "" # Verificar Docker if ! command -v docker &> /dev/null; then echo -e "${RED}❌ Error: Docker no está instalado${NC}" - echo -e "${YELLOW} Instala Docker: https://docs.docker.com/get-docker/${NC}" exit 1 fi if ! command -v docker-compose &> /dev/null && ! docker compose version &> /dev/null; then - echo -e "${RED}❌ Error: docker-compose no está instalado${NC}" + echo -e "${RED}❌ Error: Docker Compose no está instalado${NC}" exit 1 fi -# Detectar comando docker-compose -if docker compose version &> /dev/null; then - DOCKER_COMPOSE="docker compose" +# Detectar comando de compose (docker-compose o docker compose) +if command -v docker-compose &> /dev/null; then + COMPOSE_CMD="docker-compose" else - DOCKER_COMPOSE="docker-compose" + COMPOSE_CMD="docker compose" fi -# Verificar si se debe saltar git pull -SKIP_GIT=false +echo -e "${GREEN}✅ Docker detectado: $(docker --version)${NC}" +echo -e "${GREEN}✅ Docker Compose detectado${NC}" +echo "" + +# Procesar argumentos REBUILD=false +STOP=false +LOGS=false +SKIP_GIT=false for arg in "$@"; do case $arg in - --skip-git) - SKIP_GIT=true - shift - ;; --rebuild) REBUILD=true shift ;; - *) + --stop) + STOP=true shift ;; + --logs) + LOGS=true + shift + ;; + --skip-git) + SKIP_GIT=true + shift + ;; + *) + # Argumento desconocido + ;; esac done +# Si se solicita detener +if [ "$STOP" = true ]; then + echo -e "${YELLOW}🛑 Deteniendo contenedor...${NC}" + $COMPOSE_CMD down + echo -e "${GREEN}✅ Contenedor detenido${NC}" + exit 0 +fi + +# Si se solicitan logs +if [ "$LOGS" = true ]; then + echo -e "${YELLOW}📋 Mostrando logs...${NC}" + $COMPOSE_CMD logs -f + exit 0 +fi + # 1. Actualizar código desde git (si no se salta) if [ "$SKIP_GIT" = false ]; then echo -e "${YELLOW}📥 [1/4] Actualizando código desde git...${NC}" @@ -76,57 +103,59 @@ else echo -e "${YELLOW}⏭️ [1/4] Saltando actualización de git (--skip-git)${NC}" fi -# 2. Detener contenedor existente (si existe) -echo -e "${YELLOW}🛑 [2/4] Deteniendo contenedor existente...${NC}" -$DOCKER_COMPOSE down 2>/dev/null || true -docker stop "$CONTAINER_NAME" 2>/dev/null || true -docker rm "$CONTAINER_NAME" 2>/dev/null || true -echo -e "${GREEN}✅ Contenedor detenido${NC}" +# 2. Verificar que Dockerfile existe +echo -e "${YELLOW}🔍 [2/4] Verificando Dockerfile...${NC}" +if [ ! -f "Dockerfile" ]; then + echo -e "${RED}❌ Error: Dockerfile no encontrado${NC}" + exit 1 +fi +if [ ! -f "$COMPOSE_FILE" ]; then + echo -e "${RED}❌ Error: $COMPOSE_FILE no encontrado${NC}" + exit 1 +fi +echo -e "${GREEN}✅ Archivos Docker encontrados${NC}" -# 3. Construir imagen Docker -echo -e "${YELLOW}🔨 [3/4] Construyendo imagen Docker...${NC}" +# 3. Construir imagen (si es necesario) if [ "$REBUILD" = true ]; then - echo -e "${YELLOW} Forzando rebuild completo (--rebuild)${NC}" - $DOCKER_COMPOSE build --no-cache + echo -e "${YELLOW}🔨 [3/4] Reconstruyendo imagen Docker...${NC}" + $COMPOSE_CMD build --no-cache + echo -e "${GREEN}✅ Imagen reconstruida${NC}" else - $DOCKER_COMPOSE build + echo -e "${YELLOW}🔨 [3/4] Construyendo/actualizando imagen Docker...${NC}" + $COMPOSE_CMD build + echo -e "${GREEN}✅ Imagen lista${NC}" fi -if [ $? -eq 0 ]; then - echo -e "${GREEN}✅ Imagen construida exitosamente${NC}" -else - echo -e "${RED}❌ Error al construir imagen${NC}" - exit 1 -fi - -# 4. Iniciar contenedor +# 4. Iniciar/Reiniciar contenedor echo -e "${YELLOW}🚀 [4/4] Iniciando contenedor...${NC}" -$DOCKER_COMPOSE up -d +$COMPOSE_CMD up -d -if [ $? -eq 0 ]; then - echo -e "${GREEN}✅ Contenedor iniciado${NC}" -else - echo -e "${RED}❌ Error al iniciar contenedor${NC}" - exit 1 -fi - -# Esperar un momento para que el contenedor inicie +# Esperar a que el contenedor esté listo +echo -e "${YELLOW}⏳ Esperando a que el contenedor esté listo...${NC}" sleep 3 # Verificar estado -echo "" -echo -e "${GREEN}✅ Deploy completado exitosamente${NC}" -echo -e "${BLUE}📊 Estado del contenedor:${NC}" -docker ps | grep "$CONTAINER_NAME" || docker ps -a | grep "$CONTAINER_NAME" - -echo "" -echo -e "${GREEN}🌐 Aplicación disponible en: http://localhost:$PORT${NC}" -echo -e "${GREEN}📝 Logs: docker logs $CONTAINER_NAME${NC}" -echo -e "${GREEN}📊 Logs en tiempo real: docker logs -f $CONTAINER_NAME${NC}" -echo -e "${GREEN}🛑 Detener: docker-compose down${NC}" -echo -e "${GREEN}🔄 Reiniciar: docker-compose restart${NC}" +if docker ps | grep -q "$CONTAINER_NAME"; then + echo -e "${GREEN}✅ Contenedor iniciado correctamente${NC}" +else + echo -e "${RED}❌ Error: El contenedor no está corriendo${NC}" + echo -e "${YELLOW}📋 Últimos logs:${NC}" + $COMPOSE_CMD logs --tail=50 + exit 1 +fi +# Mostrar información echo "" echo -e "${GREEN}════════════════════════════════════════${NC}" echo -e "${GREEN}🎉 Deploy Docker completado!${NC}" echo -e "${GREEN}════════════════════════════════════════${NC}" +echo "" +echo -e "${BLUE}📊 Estado del contenedor:${NC}" +docker ps | grep "$CONTAINER_NAME" || true +echo "" +echo -e "${GREEN}🌐 Aplicación disponible en: http://localhost:$PORT${NC}" +echo -e "${GREEN}📝 Logs: $COMPOSE_CMD logs -f${NC}" +echo -e "${GREEN}📊 Estado: docker ps | grep $CONTAINER_NAME${NC}" +echo -e "${GREEN}🛑 Detener: $COMPOSE_CMD down${NC}" +echo "" + diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 8a082bb8..63103760 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -43,3 +43,4 @@ services: networks: emerges-network: driver: bridge + diff --git a/docker-compose.yml b/docker-compose.yml index f71fff4f..fd2ae93c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,14 +5,13 @@ services: build: context: . dockerfile: Dockerfile - target: production container_name: emerges-tes ports: - "8607:8607" - restart: unless-stopped environment: - NODE_ENV=production - PORT=8607 + restart: unless-stopped healthcheck: test: ["CMD", "node", "-e", "require('http').get('http://localhost:8607', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"] interval: 30s @@ -20,11 +19,12 @@ services: retries: 3 start_period: 5s labels: - - "com.emerges-tes.description=EMERGES TES - Protocolo Rápido" - - "com.emerges-tes.version=1.0" + - "com.emerges.app=emerges-tes" + - "com.emerges.version=1.0" networks: - emerges-network networks: emerges-network: driver: bridge + diff --git a/public/favicon.ico b/public/favicon.ico deleted file mode 100644 index 3c01d69713f9c184e92b74f5799e6dff2f500825..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20373 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