diff --git a/web/DEVELOPMENT.md b/web/DEVELOPMENT.md new file mode 100644 index 0000000..de84209 --- /dev/null +++ b/web/DEVELOPMENT.md @@ -0,0 +1,205 @@ +# Guía de Desarrollo + +## Configuración de Puertos + +### Desarrollo Local +| Servicio | Puerto | URL | +|----------|--------|-----| +| Dashboard (Vue) | 3000 | http://localhost:3000 | +| Backend (API) | 3001 | http://localhost:3001 | +| Landing (Astro) | 3002 | http://localhost:3002 | + +### Producción (Docker) +| Servicio | Puerto Externo | Puerto Interno | +|----------|----------------|----------------| +| Nginx Proxy | 80 | - | +| Dashboard | - | 80 | +| Backend | - | 3001 | +| Landing | - | 80 | + +## Ejecutar en Desarrollo Local + +### 1. Backend (API) + +```bash +cd web/backend +npm install +npm run dev +``` + +El backend estará disponible en `http://localhost:3001` + +**Endpoints**: +- API: `http://localhost:3001/api/*` +- WebSocket: `ws://localhost:3001/ws` + +### 2. Dashboard (Vue) + +```bash +cd web/dashboard +npm install +npm run dev +``` + +El dashboard estará disponible en `http://localhost:3000` + +**Nota**: Vite está configurado con proxy automático: +- `/api/*` → `http://localhost:3001/api/*` +- `/ws` → `ws://localhost:3001/ws` + +Esto significa que puedes hacer peticiones a `/api/users` desde el dashboard y automáticamente se redirigirán al backend. + +### 3. Landing (Astro) + +```bash +cd web/landing +npm install +npm run dev +``` + +La landing estará disponible en `http://localhost:3002` + +## Ejecutar con Docker (Producción) + +```bash +# Construir imágenes +docker-compose build + +# Iniciar servicios +docker-compose up -d + +# Ver logs +docker-compose logs -f + +# Detener servicios +docker-compose down +``` + +Todo estará disponible en `http://localhost`: +- Landing: `http://localhost/` +- Dashboard: `http://localhost/dashboard/` +- API: `http://localhost/api/` + +## Reconstruir después de cambios + +### Cambios en código (desarrollo) + +No es necesario hacer nada, Vite y Node tienen hot-reload automático. + +### Cambios en configuración o Docker + +```bash +# Reconstruir servicio específico +docker-compose build dashboard +docker-compose build backend +docker-compose build landing + +# Reconstruir todo +docker-compose build + +# Reiniciar servicios +docker-compose up -d +``` + +## Diferencias entre Desarrollo y Producción + +### Dashboard +- **Desarrollo**: + - Puerto 3000 + - Vite proxy activo para `/api` y `/ws` + - Hot Module Replacement (HMR) + - Source maps + +- **Producción**: + - Puerto 80 (interno) + - Sin proxy (nginx principal maneja todo) + - Código minificado y optimizado + - Assets con hash para cache + +### Backend +- **Desarrollo**: + - Puerto 3001 + - `node --watch` para auto-reload + - Variables de entorno por defecto + +- **Producción**: + - Puerto 3001 (interno) + - `node` sin watch + - Variables de entorno de Docker + +### Landing +- **Desarrollo**: + - Puerto 3002 + - Servidor de desarrollo de Astro + - Hot reload + +- **Producción**: + - Puerto 80 (interno) + - Archivos estáticos servidos por nginx + - Pre-renderizado + +## Troubleshooting + +### Puerto ya en uso + +```bash +# Ver qué está usando el puerto +lsof -i :3000 # Dashboard +lsof -i :3001 # Backend +lsof -i :3002 # Landing + +# Matar proceso +kill -9 +``` + +### Proxy no funciona en desarrollo + +Asegúrate de que: +1. El backend está corriendo en el puerto 3001 +2. Estás ejecutando `npm run dev` (modo desarrollo) +3. La configuración de proxy en `vite.config.js` está correcta + +### Assets 404 en producción + +Ver `NGINX_CONFIG.md` para detalles de configuración de nginx y assets. + +## Variables de Entorno + +### Backend (desarrollo local) + +Crear archivo `.env` en `web/backend/`: + +```env +PORT=3001 +PROJECT_ROOT=/ruta/al/proyecto +MONGODB_HOST=localhost +MONGODB_PORT=27017 +MONGODB_DATABASE=wallabicher +MONGODB_USERNAME=admin +MONGODB_PASSWORD=adminpassword +``` + +### Backend (Docker) + +Las variables se configuran en `docker-compose.yml`: +- `PORT=3001` +- `PROJECT_ROOT=/data` +- Credenciales de MongoDB + +## Base URLs + +### Dashboard + +La configuración de `base: '/dashboard/'` en `vite.config.js` hace que: +- Todos los assets se construyan con prefijo `/dashboard/` +- Vue Router use `/dashboard` como base +- Service Worker se registre en `/dashboard/sw.js` + +**No cambiar** a menos que quieras cambiar la ruta del dashboard. + +### Landing + +Sin base (raíz por defecto). Se sirve desde `/`. + +**No cambiar** a menos que quieras mover la landing a otra ruta. + diff --git a/web/NGINX_CONFIG.md b/web/NGINX_CONFIG.md new file mode 100644 index 0000000..48926e8 --- /dev/null +++ b/web/NGINX_CONFIG.md @@ -0,0 +1,139 @@ +# Configuración de Nginx y Assets + +## Arquitectura + +``` +Usuario + ↓ +nginx (proxy principal) :80 + ├─→ /api → backend:3001 + ├─→ /ws → backend:3001 + ├─→ /dashboard → dashboard:80 + └─→ / → landing:80 +``` + +## Configuración de rutas + +### 1. Dashboard (Vue + Vite) + +**Base URL**: `/dashboard/` + +- **Vite config**: `base: '/dashboard/'` (siempre) +- **Vue Router**: `createWebHistory('/dashboard')` +- **Nginx interno**: Sirve desde `/usr/share/nginx/html/dashboard/` +- **Assets**: Se construyen con prefijo `/dashboard/` automáticamente por Vite + +**Flujo de peticiones**: +``` +Usuario → http://localhost/dashboard/ + ↓ +nginx proxy → http://dashboard:80/dashboard/ + ↓ +nginx dashboard → /usr/share/nginx/html/dashboard/index.html +``` + +**Assets**: +``` +Usuario → http://localhost/dashboard/assets/index-abc123.js + ↓ +nginx proxy → http://dashboard:80/dashboard/assets/index-abc123.js + ↓ +nginx dashboard → /usr/share/nginx/html/dashboard/assets/index-abc123.js +``` + +### 2. Landing (Astro) + +**Base URL**: `/` + +- **Astro config**: Sin base (raíz por defecto) +- **Nginx interno**: Sirve desde `/usr/share/nginx/html/` +- **Assets**: Se construyen para la raíz + +**Flujo de peticiones**: +``` +Usuario → http://localhost/ + ↓ +nginx proxy → http://landing:80/ + ↓ +nginx landing → /usr/share/nginx/html/index.html +``` + +## Puertos + +### Desarrollo local +``` +Dashboard (Vue): http://localhost:3000 +Backend (API): http://localhost:3001 +Landing (Astro): http://localhost:3002 +``` + +### Producción (Docker) +``` +nginx (proxy): :80 (externo) + ├─ dashboard: :80 (interno) + ├─ backend: :3001 (interno) + └─ landing: :80 (interno) +``` + +## Desarrollo local + +Para desarrollo local, el proxy de Vite está configurado solo para `mode === 'development'`: + +```bash +# Terminal 1: Backend +cd web/backend +npm run dev +# → API en http://localhost:3001 + +# Terminal 2: Dashboard +cd web/dashboard +npm run dev +# → Dashboard en http://localhost:3000 +# → Vite proxy activo: /api → localhost:3001, /ws → localhost:3001 + +# Terminal 3: Landing +cd web/landing +npm run dev +# → Landing en http://localhost:3002 + +# Producción (Docker) +docker-compose up -d +# → Todo en http://localhost:80 +``` + +## Reconstruir después de cambios + +Si cambias la configuración de nginx o los archivos de configuración: + +```bash +# Reconstruir solo el dashboard +docker-compose build dashboard + +# Reconstruir solo la landing +docker-compose build landing + +# Reconstruir todo +docker-compose build + +# Reiniciar servicios +docker-compose up -d +``` + +## Troubleshooting + +### Assets 404 en dashboard + +1. Verificar que Vite construyó con `base: '/dashboard/'` +2. Verificar que el Dockerfile copia a `/usr/share/nginx/html/dashboard` +3. Verificar que nginx-dashboard.conf tiene la location `/dashboard/` + +### Assets 404 en landing + +1. Verificar que Astro construyó sin base (raíz) +2. Verificar que el Dockerfile copia a `/usr/share/nginx/html/` +3. Verificar que nginx landing usa root `/usr/share/nginx/html` + +### Service Worker no se registra + +El Service Worker debe estar en `/dashboard/sw.js` y registrarse con scope `/dashboard/` + diff --git a/web/dashboard/Dockerfile b/web/dashboard/Dockerfile index 941108c..afac163 100644 --- a/web/dashboard/Dockerfile +++ b/web/dashboard/Dockerfile @@ -11,14 +11,14 @@ RUN npm ci # Copiar código fuente COPY . . -# Construir aplicación +# Construir aplicación con base /dashboard/ RUN npm run build # Stage de producción - servir con nginx FROM nginx:alpine -# Copiar archivos construidos -COPY --from=builder /app/dist /usr/share/nginx/html +# Copiar archivos construidos (ya incluyen el prefijo /dashboard en las rutas) +COPY --from=builder /app/dist /usr/share/nginx/html/dashboard # Copiar configuración de nginx (se puede sobrescribir con volumen) COPY nginx-dashboard.conf /etc/nginx/conf.d/default.conf diff --git a/web/dashboard/nginx-dashboard.conf b/web/dashboard/nginx-dashboard.conf index acd6f45..a34e830 100644 --- a/web/dashboard/nginx-dashboard.conf +++ b/web/dashboard/nginx-dashboard.conf @@ -10,15 +10,22 @@ server { gzip_min_length 1024; gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml+rss application/json application/javascript; - # SPA routing - todas las rutas del dashboard - location / { - try_files $uri $uri/ /index.html; + # Dashboard assets con prefijo /dashboard/ + location /dashboard/ { + # Cache static assets + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + try_files $uri =404; + } + + # SPA routing - todas las rutas del dashboard + try_files $uri $uri/ /dashboard/index.html; } - # Cache static assets - location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { - expires 1y; - add_header Cache-Control "public, immutable"; + # Redirigir raíz al dashboard + location = / { + return 301 /dashboard/; } } diff --git a/web/dashboard/nginx.conf b/web/dashboard/nginx.conf index 4911cf0..a34e830 100644 --- a/web/dashboard/nginx.conf +++ b/web/dashboard/nginx.conf @@ -8,42 +8,24 @@ server { gzip on; gzip_vary on; gzip_min_length 1024; - gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml+rss application/json; + gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml+rss application/json application/javascript; - # SPA routing - location / { - try_files $uri $uri/ /index.html; + # Dashboard assets con prefijo /dashboard/ + location /dashboard/ { + # Cache static assets + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + try_files $uri =404; + } + + # SPA routing - todas las rutas del dashboard + try_files $uri $uri/ /dashboard/index.html; } - # API proxy - location /api { - proxy_pass http://backend:3001; - 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; - } - - # WebSocket proxy - location /ws { - proxy_pass http://backend:3001; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "upgrade"; - proxy_set_header Host $host; - 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; - } - - # Cache static assets - location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { - expires 1y; - add_header Cache-Control "public, immutable"; + # Redirigir raíz al dashboard + location = / { + return 301 /dashboard/; } } diff --git a/web/dashboard/public/sw.js b/web/dashboard/public/sw.js index 7e62403..82f0739 100644 --- a/web/dashboard/public/sw.js +++ b/web/dashboard/public/sw.js @@ -20,8 +20,8 @@ self.addEventListener('push', (event) => { let notificationData = { title: 'Wallabicher', body: 'Tienes nuevas notificaciones', - icon: '/android-chrome-192x192.png', - badge: '/android-chrome-192x192.png', + icon: '/dashboard/android-chrome-192x192.png', + badge: '/dashboard/android-chrome-192x192.png', tag: 'wallabicher-notification', requireInteraction: false, data: {} @@ -89,7 +89,7 @@ self.addEventListener('notificationclick', (event) => { } else { // Si no hay URL, abrir la app event.waitUntil( - clients.openWindow('/') + clients.openWindow('/dashboard/') ); } }); diff --git a/web/dashboard/src/main.js b/web/dashboard/src/main.js index 1ebe498..c83cc28 100644 --- a/web/dashboard/src/main.js +++ b/web/dashboard/src/main.js @@ -105,8 +105,8 @@ app.mount('#app'); if ('serviceWorker' in navigator) { window.addEventListener('load', async () => { try { - const registration = await navigator.serviceWorker.register('/sw.js', { - scope: '/' + const registration = await navigator.serviceWorker.register('/dashboard/sw.js', { + scope: '/dashboard/' }); console.log('Service Worker registrado:', registration.scope); } catch (error) { diff --git a/web/dashboard/vite.config.js b/web/dashboard/vite.config.js index 92adf38..a641abf 100644 --- a/web/dashboard/vite.config.js +++ b/web/dashboard/vite.config.js @@ -2,7 +2,7 @@ import { defineConfig } from 'vite'; import vue from '@vitejs/plugin-vue'; import { fileURLToPath, URL } from 'url'; -export default defineConfig({ +export default defineConfig(({ mode }) => ({ plugins: [vue()], base: '/dashboard/', resolve: { @@ -12,7 +12,9 @@ export default defineConfig({ }, server: { port: 3000, - proxy: { + host: true, + // Proxy solo para desarrollo local + proxy: mode === 'development' ? { '/api': { target: 'http://localhost:3001', changeOrigin: true, @@ -21,7 +23,7 @@ export default defineConfig({ target: 'ws://localhost:3001', ws: true, }, - }, + } : undefined, }, -}); +})); diff --git a/web/landing/Dockerfile b/web/landing/Dockerfile index 8d13b8b..e2adf21 100644 --- a/web/landing/Dockerfile +++ b/web/landing/Dockerfile @@ -17,11 +17,8 @@ RUN npm run build # Stage de producción - servir con nginx FROM nginx:alpine -# Copiar archivos construidos -COPY --from=builder /app/dist /usr/share/nginx/html/landing - -#change /usr/share/nginx/html to /usr/share/nginx/html/landing -RUN sed -i 's|/usr/share/nginx/html|/usr/share/nginx/html/landing|g' /etc/nginx/conf.d/default.conf +# Copiar archivos construidos a la raíz +COPY --from=builder /app/dist /usr/share/nginx/html # Exponer puerto EXPOSE 80 diff --git a/web/landing/astro.config.mjs b/web/landing/astro.config.mjs index 5d3750b..1924114 100644 --- a/web/landing/astro.config.mjs +++ b/web/landing/astro.config.mjs @@ -5,5 +5,9 @@ import tailwind from '@astrojs/tailwind'; export default defineConfig({ integrations: [tailwind()], output: 'static', + server: { + port: 3002, + host: true, + }, }); diff --git a/web/nginx.conf b/web/nginx.conf index 498cae0..2a7d3f7 100644 --- a/web/nginx.conf +++ b/web/nginx.conf @@ -33,17 +33,7 @@ server { proxy_set_header X-Forwarded-Proto $scheme; } - # Login también va al dashboard - location /login { - proxy_pass http://dashboard:80; - proxy_http_version 1.1; - proxy_set_header Host $host; - 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; - } - - # Dashboard Vue + # Dashboard Vue - pasar petición completa con prefijo /dashboard location /dashboard { proxy_pass http://dashboard:80; proxy_http_version 1.1; @@ -51,10 +41,6 @@ server { 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; - - # Para SPA routing dentro de /dashboard - quitar el prefijo /dashboard - rewrite ^/dashboard/(.*)$ /$1 break; - rewrite ^/dashboard$ / break; } # Landing page (Astro) - raíz @@ -66,11 +52,5 @@ server { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } - - # Cache static assets - location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { - expires 1y; - add_header Cache-Control "public, immutable"; - } }