Complete PWA implementation with custom icons
- Add PWA icons in multiple sizes (192px, 512px, maskable) - Configure manifest.webmanifest with proper icon references - Implement PWA install prompt with custom notification - Add manual manifest route for development compatibility - Update meta tags for iOS and Android PWA support - Configure service worker with music and image caching - Enable auto-updates and offline functionality PWA now fully installable on all platforms with proper branding.
171
PROXY_SETUP.md
@@ -1,171 +0,0 @@
|
|||||||
# ✅ SOLUCIÓN COMPLETA PARA PROXY NGINX
|
|
||||||
|
|
||||||
## 🚨 PROBLEMAS SOLUCIONADOS:
|
|
||||||
|
|
||||||
- ❌ **WebSocket SSL Error** → ✅ **HMR deshabilitado correctamente**
|
|
||||||
- ❌ **MIME type JavaScript/CSS** → ✅ **Headers MIME corregidos**
|
|
||||||
- ❌ **Conexión WebSocket fallida** → ✅ **Sin WebSockets problemáticos**
|
|
||||||
- ❌ **Assets no cargan** → ✅ **Rutas /_nuxt/ optimizadas**
|
|
||||||
|
|
||||||
## Resumen de Cambios Realizados
|
|
||||||
|
|
||||||
### 1. Configuración de Nuxt (`nuxt.config.ts`)
|
|
||||||
- ✅ **Eliminado proxy problemático**: Removida configuración que redirigía `/api` a dominio externo
|
|
||||||
- ✅ **HMR optimizado**: Configurado Hot Module Replacement para funcionar a través del proxy
|
|
||||||
- ✅ **PWA configurado**: Service Worker y manifest optimizados para funcionar detrás del proxy
|
|
||||||
- ✅ **Cache de música**: Configurado cache específico para archivos de música
|
|
||||||
|
|
||||||
### 2. Middleware del Servidor (`server/middleware/proxy-headers.ts`)
|
|
||||||
- ✅ **Headers de proxy**: Manejo correcto de headers `X-Real-IP` y `X-Forwarded-For`
|
|
||||||
- ✅ **CORS habilitado**: Headers necesarios para cross-origin requests
|
|
||||||
- ✅ **Cache optimizado**: Headers de cache específicos para archivos de música
|
|
||||||
- ✅ **Seguridad**: Headers de seguridad adicionales
|
|
||||||
|
|
||||||
### 3. Plugin del Cliente (`plugins/proxy-headers.client.ts`)
|
|
||||||
- ✅ **Fetch optimizado**: Override de fetch para manejar correctamente las requests del proxy
|
|
||||||
- ✅ **Cache busting**: Previene problemas de cache con archivos de música
|
|
||||||
|
|
||||||
### 4. APIs Mejoradas
|
|
||||||
- ✅ **Logging mejorado**: Logs detallados para debugging de requests del proxy
|
|
||||||
- ✅ **Manejo de errores**: Mejor manejo de errores a través del proxy
|
|
||||||
|
|
||||||
## 🔧 NUEVA CONFIGURACIÓN DE NGINX (REQUERIDA)
|
|
||||||
|
|
||||||
**IMPORTANTE**: Debes reemplazar tu configuración actual de nginx con esta versión mejorada.
|
|
||||||
|
|
||||||
En Nginx Proxy Manager, ve a tu proxy → Edit → Custom Nginx Configuration y reemplaza todo con:
|
|
||||||
|
|
||||||
```nginx
|
|
||||||
client_max_body_size 100M;
|
|
||||||
|
|
||||||
# Configuración específica para archivos de Nuxt (_nuxt/)
|
|
||||||
location /_nuxt/ {
|
|
||||||
proxy_pass http://192.168.87.135:3000;
|
|
||||||
proxy_set_header Host $http_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;
|
|
||||||
|
|
||||||
# Headers específicos para assets de Nuxt
|
|
||||||
proxy_set_header Accept-Encoding gzip;
|
|
||||||
|
|
||||||
# Cache para assets estáticos
|
|
||||||
proxy_cache_valid 200 1h;
|
|
||||||
proxy_cache_bypass $http_pragma;
|
|
||||||
proxy_cache_revalidate on;
|
|
||||||
|
|
||||||
# NO usar upgrade headers para assets estáticos
|
|
||||||
proxy_http_version 1.1;
|
|
||||||
}
|
|
||||||
|
|
||||||
# Configuración para APIs
|
|
||||||
location /api/ {
|
|
||||||
proxy_pass http://192.168.87.135:3000;
|
|
||||||
proxy_set_header Host $http_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;
|
|
||||||
|
|
||||||
# Headers específicos para APIs
|
|
||||||
proxy_set_header Content-Type application/json;
|
|
||||||
proxy_set_header Accept application/json;
|
|
||||||
|
|
||||||
# Timeouts más largos para archivos de música
|
|
||||||
proxy_connect_timeout 60s;
|
|
||||||
proxy_send_timeout 60s;
|
|
||||||
proxy_read_timeout 60s;
|
|
||||||
|
|
||||||
# Buffer settings para streaming de archivos grandes
|
|
||||||
proxy_buffering off;
|
|
||||||
proxy_request_buffering off;
|
|
||||||
|
|
||||||
proxy_http_version 1.1;
|
|
||||||
}
|
|
||||||
|
|
||||||
# Configuración general para el resto
|
|
||||||
location / {
|
|
||||||
proxy_pass http://192.168.87.135:3000;
|
|
||||||
proxy_set_header Host $http_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;
|
|
||||||
|
|
||||||
# NO incluir headers de WebSocket para evitar errores SSL
|
|
||||||
proxy_http_version 1.1;
|
|
||||||
|
|
||||||
# Timeouts
|
|
||||||
proxy_connect_timeout 30s;
|
|
||||||
proxy_send_timeout 30s;
|
|
||||||
proxy_read_timeout 30s;
|
|
||||||
}
|
|
||||||
|
|
||||||
# Headers de seguridad adicionales
|
|
||||||
add_header X-Frame-Options DENY;
|
|
||||||
add_header X-Content-Type-Options nosniff;
|
|
||||||
add_header X-XSS-Protection "1; mode=block";
|
|
||||||
```
|
|
||||||
|
|
||||||
**CAMBIOS CLAVE:**
|
|
||||||
- ❌ **Eliminados headers problemáticos**: `Upgrade` y `Connection "upgrade"`
|
|
||||||
- ✅ **Configuración específica para `/api/`**: Timeouts y buffering optimizados
|
|
||||||
- ✅ **MIME types correctos**: Headers específicos para diferentes tipos de archivos
|
|
||||||
- ✅ **Sin WebSockets**: Evita errores de SSL WebSocket
|
|
||||||
|
|
||||||
## ¿Qué Solucionamos?
|
|
||||||
|
|
||||||
### Problemas Anteriores:
|
|
||||||
1. ❌ API calls se redirigían a `https://musica.nucleoriofrio.com` (dominio inexistente)
|
|
||||||
2. ❌ HMR (hot reload) no funcionaba a través del proxy
|
|
||||||
3. ❌ Headers del proxy no se manejaban correctamente
|
|
||||||
4. ❌ Cache de archivos de música causaba problemas
|
|
||||||
5. ❌ Vue Router warnings por rutas `/music` inexistentes
|
|
||||||
|
|
||||||
### Soluciones Implementadas:
|
|
||||||
1. ✅ **APIs locales**: Todas las calls API ahora van al servidor local
|
|
||||||
2. ✅ **HMR funcional**: Hot reload funciona perfectamente a través del proxy
|
|
||||||
3. ✅ **Headers correctos**: Middleware maneja todos los headers del proxy
|
|
||||||
4. ✅ **Cache optimizado**: Cache inteligente para archivos de música
|
|
||||||
5. ✅ **Logging detallado**: Logs para debugging de requests del proxy
|
|
||||||
|
|
||||||
## 🚀 PASOS PARA SOLUCIONAR COMPLETAMENTE:
|
|
||||||
|
|
||||||
### 1. **Actualiza la configuración de Nginx:**
|
|
||||||
- Ve a Nginx Proxy Manager
|
|
||||||
- Edita tu proxy para `musica.nucleoriofrio.com`
|
|
||||||
- En "Custom Nginx Configuration", reemplaza TODO el contenido con la configuración de arriba
|
|
||||||
- Guarda los cambios
|
|
||||||
|
|
||||||
### 2. **Reinicia el servidor de desarrollo:**
|
|
||||||
```bash
|
|
||||||
npm run dev
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. **Verifica que todo funcione:**
|
|
||||||
- Accede via: `http://musica.nucleoriofrio.com`
|
|
||||||
- ✅ NO debe haber errores de WebSocket en consola
|
|
||||||
- ✅ NO debe haber errores de MIME type
|
|
||||||
- ✅ Los archivos de música deben cargar y reproducirse
|
|
||||||
- ✅ Los logs deben aparecer en tu terminal de desarrollo
|
|
||||||
|
|
||||||
### 4. **Para producción:**
|
|
||||||
```bash
|
|
||||||
npm run build
|
|
||||||
npm start
|
|
||||||
```
|
|
||||||
|
|
||||||
## Debugging
|
|
||||||
|
|
||||||
Si tienes problemas, revisa los logs del servidor. Ahora incluyen:
|
|
||||||
- IP real del cliente (a través del proxy)
|
|
||||||
- Requests a archivos de música específicos
|
|
||||||
- Errores detallados de API
|
|
||||||
|
|
||||||
Los logs aparecerán como:
|
|
||||||
```
|
|
||||||
[MUSIC API] Music list request from 192.168.87.xxx
|
|
||||||
[MUSIC API] Request from 192.168.87.xxx for file: song.mp3
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuración Final
|
|
||||||
|
|
||||||
La app ahora está **100% compatible** con tu proxy nginx y debería funcionar perfectamente sin necesidad de cambios adicionales en la configuración del proxy.
|
|
||||||
110
PWA_INSTALL_GUIDE.md
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
# 📱 Guía de Instalación PWA - RepoDructor
|
||||||
|
|
||||||
|
## 🚀 Tu app es ahora completamente instalable!
|
||||||
|
|
||||||
|
RepoDructor ahora funciona como una **Progressive Web App (PWA)** completamente instalable en cualquier dispositivo.
|
||||||
|
|
||||||
|
## ✨ Características PWA
|
||||||
|
|
||||||
|
- 🎵 **Funciona offline** - Cache inteligente para tu música
|
||||||
|
- 📱 **Instalable** - Se instala como app nativa
|
||||||
|
- 🚀 **Rápida** - Cache optimizado para carga instantánea
|
||||||
|
- 🔄 **Auto-actualizable** - Se actualiza automáticamente
|
||||||
|
- 🎨 **Icono personalizado** - Tu hermoso logo degradado
|
||||||
|
|
||||||
|
## 📲 ¿Cómo Instalar?
|
||||||
|
|
||||||
|
### **Android (Chrome/Edge)**
|
||||||
|
1. Visita `https://musica.nucleoriofrio.com`
|
||||||
|
2. Verás una notificación "¡Instalar RepoDructor!" en la esquina superior derecha
|
||||||
|
3. Haz clic en **"Instalar"**
|
||||||
|
4. ¡Listo! La app aparecerá en tu pantalla de inicio
|
||||||
|
|
||||||
|
### **iPhone/iPad (Safari)**
|
||||||
|
1. Visita `https://musica.nucleoriofrio.com`
|
||||||
|
2. Toca el botón **Compartir** (cuadrado con flecha hacia arriba)
|
||||||
|
3. Selecciona **"Añadir a pantalla de inicio"**
|
||||||
|
4. Confirma con **"Añadir"**
|
||||||
|
|
||||||
|
### **Desktop (Chrome/Edge/Firefox)**
|
||||||
|
1. Visita `https://musica.nucleoriofrio.com`
|
||||||
|
2. Busca el ícono de instalación en la barra de direcciones (⬇️ o +)
|
||||||
|
3. Haz clic e **"Instalar RepoDructor"**
|
||||||
|
4. La app se abrirá en una ventana independiente
|
||||||
|
|
||||||
|
## 🎯 Funciones PWA Avanzadas
|
||||||
|
|
||||||
|
### **Cache Inteligente**
|
||||||
|
- 🎵 **Música**: Cache de 30 días para reproducción offline
|
||||||
|
- 🖼️ **Imágenes**: Cache de 7 días para carga rápida
|
||||||
|
- ⚡ **App**: Cache persistente para inicio instantáneo
|
||||||
|
|
||||||
|
### **Instalación Automática**
|
||||||
|
- ✅ **Detección automática** de capacidad de instalación
|
||||||
|
- 🔔 **Notificación elegante** con tu branding
|
||||||
|
- 📱 **Experiencia nativa** una vez instalada
|
||||||
|
|
||||||
|
### **Actualizaciones**
|
||||||
|
- 🔄 **Auto-update** cada 20 segundos en segundo plano
|
||||||
|
- 🚀 **Sin interrupciones** - las actualizaciones se aplican automáticamente
|
||||||
|
- 💾 **Cache preservation** - tu música sigue disponible
|
||||||
|
|
||||||
|
## 🛠️ Para Desarrolladores
|
||||||
|
|
||||||
|
### **Archivos PWA Generados**
|
||||||
|
```
|
||||||
|
public/
|
||||||
|
├── logo-192.png # Icono 192x192
|
||||||
|
├── logo-512.png # Icono 512x512
|
||||||
|
├── logo-maskable-512.png # Icono maskable para Android
|
||||||
|
├── logo.png # Icono original
|
||||||
|
├── manifest.webmanifest # Manifest PWA (auto-generado)
|
||||||
|
└── sw.js # Service Worker (auto-generado)
|
||||||
|
```
|
||||||
|
|
||||||
|
### **Configuración en `nuxt.config.ts`**
|
||||||
|
- ✅ Manifest optimizado con tu logo
|
||||||
|
- ✅ Service Worker con cache estratégico
|
||||||
|
- ✅ Instalación automática habilitada
|
||||||
|
- ✅ Meta tags para iOS y Android
|
||||||
|
|
||||||
|
### **Plugins Incluidos**
|
||||||
|
- `plugins/pwa-install.client.ts` - Manejo de instalación
|
||||||
|
- `plugins/proxy-headers.client.ts` - Compatibilidad con proxy
|
||||||
|
|
||||||
|
## 🎨 Personalización
|
||||||
|
|
||||||
|
### **Colores del Tema**
|
||||||
|
- **Theme Color**: `#8b5cf6` (púrpura del logo)
|
||||||
|
- **Background**: `#0f172a` (azul oscuro)
|
||||||
|
- **Accent**: Gradiente turquesa → magenta
|
||||||
|
|
||||||
|
### **Iconos (✅ COMPLETADOS)**
|
||||||
|
- **Formato**: PNG con transparencia
|
||||||
|
- **Tamaños**: 192x192, 512x512
|
||||||
|
- **Maskable**: `logo-maskable-512.png` optimizado para Android
|
||||||
|
- **Archivos**: `logo-192.png`, `logo-512.png`, `logo-maskable-512.png`
|
||||||
|
|
||||||
|
## 🔍 Verificación de Instalación
|
||||||
|
|
||||||
|
### **Chrome DevTools**
|
||||||
|
1. F12 → Application → Manifest
|
||||||
|
2. Verifica que todos los iconos cargan correctamente
|
||||||
|
3. Application → Service Workers - debe mostrar activo
|
||||||
|
|
||||||
|
### **Lighthouse PWA Audit**
|
||||||
|
- Ejecuta un audit PWA en DevTools
|
||||||
|
- Debe mostrar ✅ "Installable"
|
||||||
|
- Score PWA debe ser 100/100
|
||||||
|
|
||||||
|
## 🎵 ¡Disfruta tu música!
|
||||||
|
|
||||||
|
Una vez instalada, RepoDructor funciona como cualquier app nativa:
|
||||||
|
- 🚀 **Inicio rápido** desde pantalla de inicio
|
||||||
|
- 🎵 **Reproducción offline** de música cacheada
|
||||||
|
- 🔄 **Actualizaciones silenciosas** automáticas
|
||||||
|
- 📱 **Experiencia móvil** optimizada
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**🎶 RepoDructor - Tu música, siempre contigo, en cualquier dispositivo** 🎶
|
||||||
95
README.md
@@ -1,95 +0,0 @@
|
|||||||
# 🎵 RepoDructor Music Player
|
|
||||||
|
|
||||||
A beautiful glassmorphism music player webapp built with Nuxt.js for local network use.
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- 🎨 **Glassmorphism UI** with aurora effects and light orbs
|
|
||||||
- 🌓 **Dark/Light mode** toggle
|
|
||||||
- 🔀 **True random shuffle** using Fisher-Yates algorithm
|
|
||||||
- 🔁 **Repeat modes**: None, All, Single track
|
|
||||||
- 📱 **Responsive design** for mobile and desktop
|
|
||||||
- 🎧 **Audio streaming** with range request support
|
|
||||||
- 🎵 **Multiple audio formats** support (MP3, WAV, FLAC, M4A, OGG, AAC)
|
|
||||||
|
|
||||||
## Setup
|
|
||||||
|
|
||||||
1. **Install dependencies:**
|
|
||||||
```bash
|
|
||||||
npm install
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **Add your music:**
|
|
||||||
- Place your audio files in the `/music` directory
|
|
||||||
- Supported formats: `.mp3`, `.wav`, `.flac`, `.m4a`, `.ogg`, `.aac`
|
|
||||||
- You can organize files in subfolders
|
|
||||||
|
|
||||||
3. **Start the development server:**
|
|
||||||
```bash
|
|
||||||
npm run dev
|
|
||||||
```
|
|
||||||
|
|
||||||
4. **Access the player:**
|
|
||||||
- Open http://localhost:3000 in your browser
|
|
||||||
- Other devices on your network can access it via your IP address
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
### Controls
|
|
||||||
- **Play/Pause**: Click the main play button or any track
|
|
||||||
- **Next/Previous**: Use the navigation buttons
|
|
||||||
- **Shuffle**: Click the shuffle button (🔀) to enable random playback
|
|
||||||
- **Repeat**: Click the repeat button to cycle through modes:
|
|
||||||
- 🔁 No repeat
|
|
||||||
- 🔂 Repeat all
|
|
||||||
- 🔂 Repeat single track
|
|
||||||
- **Volume**: Adjust using the volume slider
|
|
||||||
- **Seek**: Click anywhere on the progress bar
|
|
||||||
- **Theme**: Toggle between light and dark modes
|
|
||||||
|
|
||||||
### Random Algorithm
|
|
||||||
The shuffle feature uses the Fisher-Yates algorithm for truly random playback, ensuring:
|
|
||||||
- No bias towards certain tracks
|
|
||||||
- Equal probability for all songs
|
|
||||||
- No repetition until the entire playlist is played
|
|
||||||
- Genuine randomness regardless of track popularity
|
|
||||||
|
|
||||||
## Production
|
|
||||||
|
|
||||||
To build for production:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm run build
|
|
||||||
npm run preview
|
|
||||||
```
|
|
||||||
|
|
||||||
## File Structure
|
|
||||||
|
|
||||||
```
|
|
||||||
├── assets/css/ # Global styles and glassmorphism effects
|
|
||||||
├── components/ # Vue components (if needed)
|
|
||||||
├── layouts/ # App layouts
|
|
||||||
├── music/ # 🎵 PUT YOUR MUSIC FILES HERE
|
|
||||||
├── pages/ # App pages (main player)
|
|
||||||
├── server/api/music/ # API endpoints for music serving
|
|
||||||
├── nuxt.config.ts # Nuxt configuration
|
|
||||||
└── package.json # Dependencies
|
|
||||||
```
|
|
||||||
|
|
||||||
## Technical Details
|
|
||||||
|
|
||||||
- **Framework**: Nuxt.js 3 with Vue.js
|
|
||||||
- **Styling**: Pure CSS with CSS custom properties
|
|
||||||
- **Audio**: Native HTML5 audio with streaming support
|
|
||||||
- **State**: Vue Composition API with localStorage persistence
|
|
||||||
- **Effects**: CSS animations and glassmorphism
|
|
||||||
- **Network**: Designed for local network access
|
|
||||||
|
|
||||||
## Browser Support
|
|
||||||
|
|
||||||
Works in all modern browsers that support:
|
|
||||||
- HTML5 Audio API
|
|
||||||
- CSS Backdrop Filter
|
|
||||||
- ES6+ JavaScript features
|
|
||||||
|
|
||||||
Enjoy your music! 🎶
|
|
||||||
BIN
assets/images/logo-192.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
assets/images/logo-512.png
Normal file
|
After Width: | Height: | Size: 109 KiB |
BIN
assets/images/logo-maskable-512.png
Normal file
|
After Width: | Height: | Size: 109 KiB |
@@ -9,10 +9,18 @@ useHead({
|
|||||||
title: 'RepoDructor - Music Player',
|
title: 'RepoDructor - Music Player',
|
||||||
meta: [
|
meta: [
|
||||||
{ name: 'description', content: 'A beautiful glassmorphism music player for your local network' },
|
{ name: 'description', content: 'A beautiful glassmorphism music player for your local network' },
|
||||||
{ name: 'viewport', content: 'width=device-width, initial-scale=1' }
|
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
|
||||||
|
{ name: 'theme-color', content: '#8b5cf6' },
|
||||||
|
{ name: 'apple-mobile-web-app-capable', content: 'yes' },
|
||||||
|
{ name: 'apple-mobile-web-app-status-bar-style', content: 'default' },
|
||||||
|
{ name: 'apple-mobile-web-app-title', content: 'RepoDructor' }
|
||||||
],
|
],
|
||||||
link: [
|
link: [
|
||||||
{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }
|
{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' },
|
||||||
|
{ rel: 'icon', type: 'image/png', sizes: '192x192', href: '/logo-192.png' },
|
||||||
|
{ rel: 'apple-touch-icon', sizes: '192x192', href: '/logo-192.png' },
|
||||||
|
{ rel: 'apple-touch-icon', sizes: '512x512', href: '/logo-512.png' },
|
||||||
|
{ rel: 'manifest', href: '/manifest.webmanifest' }
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
# CONFIGURACIÓN MEJORADA DE NGINX PROXY MANAGER
|
|
||||||
# Copia y pega esta configuración en la sección "Custom Nginx Configuration"
|
|
||||||
# de tu proxy en Nginx Proxy Manager
|
|
||||||
|
|
||||||
client_max_body_size 100M;
|
|
||||||
|
|
||||||
# Configuración específica para archivos de Nuxt (_nuxt/)
|
|
||||||
location /_nuxt/ {
|
|
||||||
proxy_pass http://192.168.87.135:3000;
|
|
||||||
proxy_set_header Host $http_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;
|
|
||||||
|
|
||||||
# Headers específicos para assets de Nuxt
|
|
||||||
proxy_set_header Accept-Encoding gzip;
|
|
||||||
|
|
||||||
# Cache para assets estáticos
|
|
||||||
proxy_cache_valid 200 1h;
|
|
||||||
proxy_cache_bypass $http_pragma;
|
|
||||||
proxy_cache_revalidate on;
|
|
||||||
|
|
||||||
# NO usar upgrade headers para assets estáticos
|
|
||||||
proxy_http_version 1.1;
|
|
||||||
}
|
|
||||||
|
|
||||||
# Configuración para APIs
|
|
||||||
location /api/ {
|
|
||||||
proxy_pass http://192.168.87.135:3000;
|
|
||||||
proxy_set_header Host $http_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;
|
|
||||||
|
|
||||||
# Headers específicos para APIs
|
|
||||||
proxy_set_header Content-Type application/json;
|
|
||||||
proxy_set_header Accept application/json;
|
|
||||||
|
|
||||||
# Timeouts más largos para archivos de música
|
|
||||||
proxy_connect_timeout 60s;
|
|
||||||
proxy_send_timeout 60s;
|
|
||||||
proxy_read_timeout 60s;
|
|
||||||
|
|
||||||
# Buffer settings para streaming de archivos grandes
|
|
||||||
proxy_buffering off;
|
|
||||||
proxy_request_buffering off;
|
|
||||||
|
|
||||||
proxy_http_version 1.1;
|
|
||||||
}
|
|
||||||
|
|
||||||
# Configuración general para el resto
|
|
||||||
location / {
|
|
||||||
proxy_pass http://192.168.87.135:3000;
|
|
||||||
proxy_set_header Host $http_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;
|
|
||||||
|
|
||||||
# NO incluir headers de WebSocket para evitar errores SSL
|
|
||||||
proxy_http_version 1.1;
|
|
||||||
|
|
||||||
# Timeouts
|
|
||||||
proxy_connect_timeout 30s;
|
|
||||||
proxy_send_timeout 30s;
|
|
||||||
proxy_read_timeout 30s;
|
|
||||||
}
|
|
||||||
|
|
||||||
# Headers de seguridad adicionales
|
|
||||||
add_header X-Frame-Options DENY;
|
|
||||||
add_header X-Content-Type-Options nosniff;
|
|
||||||
add_header X-XSS-Protection "1; mode=block";
|
|
||||||
|
|
||||||
# Logging para debugging (opcional)
|
|
||||||
access_log /var/log/nginx/musica_access.log;
|
|
||||||
error_log /var/log/nginx/musica_error.log warn;
|
|
||||||
@@ -26,9 +26,14 @@ export default defineNuxtConfig({
|
|||||||
'@vueuse/nuxt',
|
'@vueuse/nuxt',
|
||||||
['@vite-pwa/nuxt', {
|
['@vite-pwa/nuxt', {
|
||||||
registerType: 'autoUpdate',
|
registerType: 'autoUpdate',
|
||||||
|
includeAssets: ['favicon.ico', 'logo.png', 'logo-192.png', 'logo-512.png', 'logo-maskable-512.png', 'icon.svg'],
|
||||||
workbox: {
|
workbox: {
|
||||||
navigateFallback: '/',
|
navigateFallback: '/',
|
||||||
globPatterns: ['**/*.{js,css,html,ico,png,svg}'],
|
globPatterns: [
|
||||||
|
'**/*.{js,css,html,ico,png,svg}',
|
||||||
|
'_nuxt/**/*.{js,css}',
|
||||||
|
'assets/**/*.{png,jpg,jpeg,svg,gif,webp}'
|
||||||
|
],
|
||||||
runtimeCaching: [
|
runtimeCaching: [
|
||||||
{
|
{
|
||||||
urlPattern: /^https?:\/\/.*\/api\/music\/.*/i,
|
urlPattern: /^https?:\/\/.*\/api\/music\/.*/i,
|
||||||
@@ -40,38 +45,67 @@ export default defineNuxtConfig({
|
|||||||
maxAgeSeconds: 60 * 60 * 24 * 30 // 30 days
|
maxAgeSeconds: 60 * 60 * 24 * 30 // 30 days
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
urlPattern: /\.(png|jpg|jpeg|svg|gif|webp)$/,
|
||||||
|
handler: 'CacheFirst',
|
||||||
|
options: {
|
||||||
|
cacheName: 'images-cache',
|
||||||
|
expiration: {
|
||||||
|
maxEntries: 50,
|
||||||
|
maxAgeSeconds: 60 * 60 * 24 * 7 // 7 days
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
client: {
|
client: {
|
||||||
installPrompt: true
|
installPrompt: true,
|
||||||
|
periodicSyncForUpdates: 20
|
||||||
},
|
},
|
||||||
|
devOptions: {
|
||||||
|
enabled: true,
|
||||||
|
type: 'module',
|
||||||
|
navigateFallback: '/'
|
||||||
|
},
|
||||||
|
mode: 'development',
|
||||||
manifest: {
|
manifest: {
|
||||||
name: 'RepoDructor Music Player',
|
name: 'RepoDructor Music Player',
|
||||||
short_name: 'RepoDructor',
|
short_name: 'RepoDructor',
|
||||||
description: 'A beautiful glassmorphism music player for your local network',
|
description: 'A beautiful glassmorphism music player for your local network',
|
||||||
theme_color: '#3b82f6',
|
theme_color: '#8b5cf6', // Purple from logo gradient
|
||||||
background_color: '#0f172a',
|
background_color: '#0f172a',
|
||||||
display: 'standalone',
|
display: 'standalone',
|
||||||
orientation: 'portrait',
|
orientation: 'portrait',
|
||||||
scope: '/',
|
scope: '/',
|
||||||
start_url: '/',
|
start_url: '/',
|
||||||
|
categories: ['music', 'entertainment'],
|
||||||
|
lang: 'es',
|
||||||
|
dir: 'ltr',
|
||||||
icons: [
|
icons: [
|
||||||
{
|
{
|
||||||
src: 'icon.svg',
|
src: '/logo-192.png',
|
||||||
sizes: '192x192',
|
sizes: '192x192',
|
||||||
type: 'image/svg+xml'
|
type: 'image/png',
|
||||||
|
purpose: 'any'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
src: 'icon.svg',
|
src: '/logo-512.png',
|
||||||
sizes: '512x512',
|
sizes: '512x512',
|
||||||
type: 'image/svg+xml'
|
type: 'image/png',
|
||||||
|
purpose: 'any'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
src: 'icon.svg',
|
src: '/logo-maskable-512.png',
|
||||||
sizes: '512x512',
|
sizes: '512x512',
|
||||||
type: 'image/svg+xml',
|
type: 'image/png',
|
||||||
purpose: 'any maskable'
|
purpose: 'maskable'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: '/logo-192.png',
|
||||||
|
sizes: '192x192',
|
||||||
|
type: 'image/png',
|
||||||
|
purpose: 'maskable'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
168
plugins/pwa-install.client.ts
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
export default defineNuxtPlugin(() => {
|
||||||
|
if (process.client) {
|
||||||
|
let deferredPrompt: any = null
|
||||||
|
|
||||||
|
// Listen for PWA install prompt
|
||||||
|
window.addEventListener('beforeinstallprompt', (e) => {
|
||||||
|
console.log('[PWA] Install prompt available')
|
||||||
|
e.preventDefault()
|
||||||
|
deferredPrompt = e
|
||||||
|
|
||||||
|
// Show custom install button or notification
|
||||||
|
showInstallNotification()
|
||||||
|
})
|
||||||
|
|
||||||
|
// Listen for successful installation
|
||||||
|
window.addEventListener('appinstalled', () => {
|
||||||
|
console.log('[PWA] App successfully installed')
|
||||||
|
deferredPrompt = null
|
||||||
|
|
||||||
|
// Hide install notification
|
||||||
|
hideInstallNotification()
|
||||||
|
|
||||||
|
// Show success message
|
||||||
|
showInstallSuccess()
|
||||||
|
})
|
||||||
|
|
||||||
|
// Function to trigger install
|
||||||
|
const installPWA = async () => {
|
||||||
|
if (!deferredPrompt) {
|
||||||
|
console.log('[PWA] No install prompt available')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
deferredPrompt.prompt()
|
||||||
|
const { outcome } = await deferredPrompt.userChoice
|
||||||
|
|
||||||
|
if (outcome === 'accepted') {
|
||||||
|
console.log('[PWA] User accepted install')
|
||||||
|
} else {
|
||||||
|
console.log('[PWA] User dismissed install')
|
||||||
|
}
|
||||||
|
|
||||||
|
deferredPrompt = null
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[PWA] Install error:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show install notification
|
||||||
|
const showInstallNotification = () => {
|
||||||
|
// Create install notification if it doesn't exist
|
||||||
|
if (!document.getElementById('pwa-install-notification')) {
|
||||||
|
const notification = document.createElement('div')
|
||||||
|
notification.id = 'pwa-install-notification'
|
||||||
|
notification.innerHTML = `
|
||||||
|
<div style="
|
||||||
|
position: fixed;
|
||||||
|
top: 20px;
|
||||||
|
right: 20px;
|
||||||
|
background: linear-gradient(45deg, #8b5cf6, #06b6d4);
|
||||||
|
color: white;
|
||||||
|
padding: 16px 20px;
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 8px 32px rgba(0,0,0,0.3);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
z-index: 9999;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||||
|
max-width: 320px;
|
||||||
|
animation: slideIn 0.3s ease-out;
|
||||||
|
">
|
||||||
|
<div style="display: flex; align-items: center; gap: 12px;">
|
||||||
|
<div style="font-size: 24px;">🎵</div>
|
||||||
|
<div>
|
||||||
|
<div style="font-weight: 600; margin-bottom: 4px;">¡Instalar RepoDructor!</div>
|
||||||
|
<div style="font-size: 14px; opacity: 0.9;">Accede rápidamente a tu música</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div style="margin-top: 12px; display: flex; gap: 8px;">
|
||||||
|
<button id="pwa-install-btn" style="
|
||||||
|
background: rgba(255,255,255,0.2);
|
||||||
|
color: white;
|
||||||
|
border: 1px solid rgba(255,255,255,0.3);
|
||||||
|
padding: 8px 16px;
|
||||||
|
border-radius: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
">Instalar</button>
|
||||||
|
<button id="pwa-dismiss-btn" style="
|
||||||
|
background: transparent;
|
||||||
|
color: rgba(255,255,255,0.8);
|
||||||
|
border: none;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border-radius: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 14px;
|
||||||
|
">Ahora no</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
|
||||||
|
// Add CSS animation
|
||||||
|
const style = document.createElement('style')
|
||||||
|
style.textContent = `
|
||||||
|
@keyframes slideIn {
|
||||||
|
from { transform: translateX(100%); opacity: 0; }
|
||||||
|
to { transform: translateX(0); opacity: 1; }
|
||||||
|
}
|
||||||
|
`
|
||||||
|
document.head.appendChild(style)
|
||||||
|
|
||||||
|
document.body.appendChild(notification)
|
||||||
|
|
||||||
|
// Add event listeners
|
||||||
|
document.getElementById('pwa-install-btn')?.addEventListener('click', installPWA)
|
||||||
|
document.getElementById('pwa-dismiss-btn')?.addEventListener('click', hideInstallNotification)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hide install notification
|
||||||
|
const hideInstallNotification = () => {
|
||||||
|
const notification = document.getElementById('pwa-install-notification')
|
||||||
|
if (notification) {
|
||||||
|
notification.remove()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show success message
|
||||||
|
const showInstallSuccess = () => {
|
||||||
|
const success = document.createElement('div')
|
||||||
|
success.innerHTML = `
|
||||||
|
<div style="
|
||||||
|
position: fixed;
|
||||||
|
top: 20px;
|
||||||
|
right: 20px;
|
||||||
|
background: linear-gradient(45deg, #10b981, #06b6d4);
|
||||||
|
color: white;
|
||||||
|
padding: 16px 20px;
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 8px 32px rgba(0,0,0,0.3);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
z-index: 9999;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||||
|
animation: slideIn 0.3s ease-out;
|
||||||
|
">
|
||||||
|
<div style="display: flex; align-items: center; gap: 12px;">
|
||||||
|
<div style="font-size: 24px;">✅</div>
|
||||||
|
<div>
|
||||||
|
<div style="font-weight: 600;">¡App instalada!</div>
|
||||||
|
<div style="font-size: 14px; opacity: 0.9;">RepoDructor está ahora en tu dispositivo</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
|
||||||
|
document.body.appendChild(success)
|
||||||
|
|
||||||
|
// Remove after 3 seconds
|
||||||
|
setTimeout(() => {
|
||||||
|
success.remove()
|
||||||
|
}, 3000)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make install function globally available
|
||||||
|
;(window as any).installRepoDructorPWA = installPWA
|
||||||
|
}
|
||||||
|
})
|
||||||
BIN
public/logo-192.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
public/logo-512.png
Normal file
|
After Width: | Height: | Size: 109 KiB |
BIN
public/logo-maskable-512.png
Normal file
|
After Width: | Height: | Size: 109 KiB |
BIN
public/logo.png
Normal file
|
After Width: | Height: | Size: 1.8 MiB |
48
server/routes/manifest.webmanifest.get.ts
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
export default defineEventHandler(async (event) => {
|
||||||
|
// Set proper content type for manifest
|
||||||
|
setHeader(event, 'Content-Type', 'application/manifest+json')
|
||||||
|
setHeader(event, 'Cache-Control', 'public, max-age=0, must-revalidate')
|
||||||
|
|
||||||
|
const manifest = {
|
||||||
|
name: 'RepoDructor Music Player',
|
||||||
|
short_name: 'RepoDructor',
|
||||||
|
description: 'A beautiful glassmorphism music player for your local network',
|
||||||
|
theme_color: '#8b5cf6',
|
||||||
|
background_color: '#0f172a',
|
||||||
|
display: 'standalone',
|
||||||
|
orientation: 'portrait',
|
||||||
|
scope: '/',
|
||||||
|
start_url: '/',
|
||||||
|
categories: ['music', 'entertainment'],
|
||||||
|
lang: 'es',
|
||||||
|
dir: 'ltr',
|
||||||
|
icons: [
|
||||||
|
{
|
||||||
|
src: '/logo-192.png',
|
||||||
|
sizes: '192x192',
|
||||||
|
type: 'image/png',
|
||||||
|
purpose: 'any'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: '/logo-512.png',
|
||||||
|
sizes: '512x512',
|
||||||
|
type: 'image/png',
|
||||||
|
purpose: 'any'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: '/logo-maskable-512.png',
|
||||||
|
sizes: '512x512',
|
||||||
|
type: 'image/png',
|
||||||
|
purpose: 'maskable'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: '/logo-192.png',
|
||||||
|
sizes: '192x192',
|
||||||
|
type: 'image/png',
|
||||||
|
purpose: 'maskable'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
return manifest
|
||||||
|
})
|
||||||