Files
RepoDructor/nuxt.config.ts
josedario87 25aace816f
All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 40s
Agregar mejoras PWA y unificar workflow a 1 job
- Agregar id único (/?app=musica), launch_handler, handle_links y url_handlers
- Unificar workflow de 2 jobs (build + deploy) a 1 solo job
- Workaround para bug de Gitea que solo ejecuta el primer job
2025-10-17 03:26:28 -06:00

271 lines
8.7 KiB
TypeScript

// https://nuxt.com/docs/api/configuration/nuxt-config
import { defineNuxtConfig } from 'nuxt/config'
export default defineNuxtConfig({
// Helpers
hooks: {},
compatibilityDate: '2025-08-02',
// Disable SSR completely to avoid hydration issues with client-side audio APIs
ssr: false,
devtools: {
enabled: true,
vscode: {},
timeline: {
enabled: true
}
},
// Server configuration for proxy compatibility
devServer: {
host: process.env.NUXT_HOST || '0.0.0.0',
port: process.env.NUXT_PORT ? Number(process.env.NUXT_PORT) : 3000,
https: false
},
// Additional development configuration for proxy
dev: process.env.NODE_ENV !== 'production',
// Vite configuration for HMR through proxy
vite: {
server: {
// Configure HMR via env when developing behind HTTPS proxy
hmr: process.env.NODE_ENV !== 'production' ? {
host: process.env.HMR_HOST || undefined,
clientPort: process.env.HMR_CLIENT_PORT ? Number(process.env.HMR_CLIENT_PORT) : undefined,
protocol: process.env.HMR_PROTOCOL || undefined
} : undefined
}
},
modules: [
'@vueuse/nuxt',
'@pinia/nuxt',
['@vite-pwa/nuxt', {
registerType: 'autoUpdate',
includeAssets: ['favicon.ico', 'logo.png', 'logo-192.png', 'logo-512.png', 'logo-maskable-512.png', 'icon.svg'],
workbox: process.env.NODE_ENV === 'production' ? {
navigateFallback: '/',
navigateFallbackDenylist: [
// Never cache authentication redirects
/^\/outpost\.goauthentik\.io/,
/^\/akprox/,
],
cleanupOutdatedCaches: true,
globPatterns: [
'**/*.{js,css,html,ico,png,svg}',
'_nuxt/**/*.{js,css}',
'assets/**/*.{png,jpg,jpeg,svg,gif,webp}'
],
// Exclude authentication and sensitive paths from precaching
globIgnores: [
'**/_payload.json',
'_nuxt/builds/**/*.json',
],
runtimeCaching: [
// Nuxt build metadata: Network-Only with graceful fallback for offline
{
urlPattern: /\/_nuxt\/builds\/(meta|latest)\/.*\.json$/,
handler: 'NetworkOnly',
options: {
plugins: [
{
// Fail silently when offline - these are HMR/build metadata files
handlerDidError: async () => {
return new Response(JSON.stringify({}), {
status: 200,
headers: { 'Content-Type': 'application/json' }
})
}
}
]
}
},
// Static images: Cache-First (offline-ready)
{
urlPattern: /\.(png|jpg|jpeg|svg|gif|webp|ico)$/,
handler: 'CacheFirst',
options: {
cacheName: 'images-cache',
expiration: {
maxEntries: 50,
maxAgeSeconds: 60 * 60 * 24 * 7 // 7 days
},
cacheableResponse: {
statuses: [0, 200]
}
}
},
// API Music list: Network-First with offline fallback
// IMPORTANTE: Esta ruta REQUIERE autenticación via Authentik
// - Online + Autenticado: Fetches desde servidor (pasa por Authentik)
// - Online + NO autenticado: Authentik redirige a login (401/403)
// - Offline: Usa cache si existe
{
urlPattern: /\/api\/music$/,
handler: 'NetworkFirst',
options: {
cacheName: 'api-music-list',
networkTimeoutSeconds: 10,
expiration: {
maxEntries: 5,
maxAgeSeconds: 60 * 5 // 5 minutes
},
cacheableResponse: {
// Solo cachea respuestas exitosas (200)
// NO cachea 401/403 (sin autenticación)
statuses: [0, 200]
}
}
},
// API Music files: Cache-First for downloaded tracks
// IMPORTANTE: Esta ruta REQUIERE autenticación via Authentik
// - Cache hit: Sirve desde cache (offline-ready, no requiere auth)
// - Cache miss + Online + Autenticado: Descarga y cachea
// - Cache miss + Online + NO autenticado: Falla con 401/403
// - Cache miss + Offline: Falla (app debe manejar el error)
{
urlPattern: /\/api\/music\/.+/,
handler: 'CacheFirst',
options: {
cacheName: 'music-files-cache',
expiration: {
maxEntries: 100,
maxAgeSeconds: 60 * 60 * 24 * 30 // 30 days
},
cacheableResponse: {
// Solo cachea respuestas exitosas
// NO cachea 401/403/404
statuses: [0, 200, 206] // Include partial content
},
plugins: [
{
// Handle authentication errors gracefully
handlerDidError: async ({ request }) => {
console.warn('[SW] Failed to fetch (posible error de auth o red):', request.url)
// Return null para que la app maneje el error
// La app puede mostrar mensaje de "requiere login" o "sin conexión"
return null
}
}
]
}
},
// Nuxt build assets: Cache-First (immutable)
{
urlPattern: /\/_nuxt\/.+\.(js|css)$/,
handler: 'CacheFirst',
options: {
cacheName: 'nuxt-assets',
expiration: {
maxEntries: 100,
maxAgeSeconds: 60 * 60 * 24 * 365 // 1 year for hashed assets
},
cacheableResponse: {
statuses: [0, 200]
}
}
}
]
} : {
// Dev SW: configuración mínima y sin patrones problemáticos
navigateFallback: '/',
globPatterns: ['**/*.{js,css,html,ico,png,svg}'],
globIgnores: ['**/_payload.json', '_nuxt/builds/**/*.json'],
},
client: {
installPrompt: true,
periodicSyncForUpdates: 20
},
devOptions: {
// Permite instalar SW en dev cuando se habilita explícitamente
enabled: process.env.PWA_DEV === 'true',
type: 'module',
navigateFallback: '/'
},
manifest: {
id: '/?app=musica',
name: 'RepoDructor Music Player',
short_name: 'RepoDructor',
description: 'A beautiful glassmorphism music player for your local network',
theme_color: '#8b5cf6', // Purple from logo gradient
background_color: '#0f172a',
display: 'standalone',
orientation: 'portrait',
scope: '/',
start_url: '/',
categories: ['music', 'entertainment'],
lang: 'es',
dir: 'ltr',
// Control de ventanas - mantener una sola ventana
launch_handler: {
client_mode: 'navigate-existing'
},
// Capturar enlaces dentro de la app
capture_links: 'existing-client-navigate',
// URL handling - nuevo estándar para manejar links a esta PWA
handle_links: 'preferred',
url_handlers: [
{ origin: 'https://musica.nucleoriofrio.com' }
],
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'
}
]
}
}]
],
css: ['~/assets/css/main.css'],
// Nitro configuration for proxy support
nitro: {
experimental: {
wasm: true,
// Disable payload extraction in SPA mode to avoid build metadata fetches
payloadExtraction: false
},
prerender: {
crawlLinks: true,
routes: ['/', '/offline']
},
// Development configuration for proxy
devProxy: process.env.NODE_ENV === 'development' ? {} : undefined
},
// App configuration
app: {
// Disable build manifest fetching in production
buildAssetsDir: '/_nuxt/',
// Disable automatic payload extraction
keepalive: false
},
// Runtime configuration
runtimeConfig: {
public: {
musicPath: process.env.NUXT_PUBLIC_MUSIC_PATH || '/music'
}
},
})