Changed network detection to only mark as offline on actual network errors, not on HTTP errors like 404. This fixes the issue where the app shows 'You are offline' even when connected, which happens when accessing through a reverse proxy where the /api/health endpoint might not be properly routed. Now the app will show as online as long as it can reach the server (any HTTP response), and only show offline on true connection failures. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
179 lines
4.3 KiB
JavaScript
179 lines
4.3 KiB
JavaScript
const withPWA = require('next-pwa')({
|
|
dest: 'public',
|
|
register: true,
|
|
skipWaiting: true,
|
|
disable: process.env.NODE_ENV === 'development',
|
|
runtimeCaching: [
|
|
{
|
|
urlPattern: /^https:\/\/fonts\.(?:gstatic)\.com\/.*/i,
|
|
handler: 'CacheFirst',
|
|
options: {
|
|
cacheName: 'google-fonts-webfonts',
|
|
expiration: {
|
|
maxEntries: 4,
|
|
maxAgeSeconds: 365 * 24 * 60 * 60, // 365 days
|
|
},
|
|
},
|
|
},
|
|
{
|
|
urlPattern: /^https:\/\/fonts\.(?:googleapis)\.com\/.*/i,
|
|
handler: 'StaleWhileRevalidate',
|
|
options: {
|
|
cacheName: 'google-fonts-stylesheets',
|
|
expiration: {
|
|
maxEntries: 4,
|
|
maxAgeSeconds: 7 * 24 * 60 * 60, // 7 days
|
|
},
|
|
},
|
|
},
|
|
{
|
|
urlPattern: /\.(?:eot|otf|ttc|ttf|woff|woff2|font.css)$/i,
|
|
handler: 'StaleWhileRevalidate',
|
|
options: {
|
|
cacheName: 'static-font-assets',
|
|
expiration: {
|
|
maxEntries: 4,
|
|
maxAgeSeconds: 7 * 24 * 60 * 60, // 7 days
|
|
},
|
|
},
|
|
},
|
|
{
|
|
urlPattern: /\.(?:jpg|jpeg|gif|png|svg|ico|webp)$/i,
|
|
handler: 'StaleWhileRevalidate',
|
|
options: {
|
|
cacheName: 'static-image-assets',
|
|
expiration: {
|
|
maxEntries: 64,
|
|
maxAgeSeconds: 24 * 60 * 60, // 24 hours
|
|
},
|
|
},
|
|
},
|
|
{
|
|
urlPattern: /\/_next\/image\?url=.+$/i,
|
|
handler: 'StaleWhileRevalidate',
|
|
options: {
|
|
cacheName: 'next-image',
|
|
expiration: {
|
|
maxEntries: 64,
|
|
maxAgeSeconds: 24 * 60 * 60, // 24 hours
|
|
},
|
|
},
|
|
},
|
|
{
|
|
urlPattern: /\.(?:mp3|wav|ogg)$/i,
|
|
handler: 'CacheFirst',
|
|
options: {
|
|
rangeRequests: true,
|
|
cacheName: 'static-audio-assets',
|
|
expiration: {
|
|
maxEntries: 32,
|
|
maxAgeSeconds: 24 * 60 * 60, // 24 hours
|
|
},
|
|
},
|
|
},
|
|
{
|
|
urlPattern: /\.(?:mp4)$/i,
|
|
handler: 'CacheFirst',
|
|
options: {
|
|
rangeRequests: true,
|
|
cacheName: 'static-video-assets',
|
|
expiration: {
|
|
maxEntries: 32,
|
|
maxAgeSeconds: 24 * 60 * 60, // 24 hours
|
|
},
|
|
},
|
|
},
|
|
{
|
|
urlPattern: /\.(?:js)$/i,
|
|
handler: 'StaleWhileRevalidate',
|
|
options: {
|
|
cacheName: 'static-js-assets',
|
|
expiration: {
|
|
maxEntries: 32,
|
|
maxAgeSeconds: 24 * 60 * 60, // 24 hours
|
|
},
|
|
},
|
|
},
|
|
{
|
|
urlPattern: /\.(?:css|less)$/i,
|
|
handler: 'StaleWhileRevalidate',
|
|
options: {
|
|
cacheName: 'static-style-assets',
|
|
expiration: {
|
|
maxEntries: 32,
|
|
maxAgeSeconds: 24 * 60 * 60, // 24 hours
|
|
},
|
|
},
|
|
},
|
|
{
|
|
urlPattern: /\/_next\/data\/.+\/.+\.json$/i,
|
|
handler: 'StaleWhileRevalidate',
|
|
options: {
|
|
cacheName: 'next-data',
|
|
expiration: {
|
|
maxEntries: 32,
|
|
maxAgeSeconds: 24 * 60 * 60, // 24 hours
|
|
},
|
|
},
|
|
},
|
|
{
|
|
urlPattern: /\/api\/.*$/i,
|
|
handler: 'NetworkFirst',
|
|
method: 'GET',
|
|
options: {
|
|
cacheName: 'apis',
|
|
expiration: {
|
|
maxEntries: 16,
|
|
maxAgeSeconds: 24 * 60 * 60, // 24 hours
|
|
},
|
|
networkTimeoutSeconds: 10, // fall back to cache if API doesn't respond within 10s
|
|
},
|
|
},
|
|
{
|
|
urlPattern: /.*/i,
|
|
handler: 'NetworkFirst',
|
|
options: {
|
|
cacheName: 'others',
|
|
expiration: {
|
|
maxEntries: 32,
|
|
maxAgeSeconds: 24 * 60 * 60, // 24 hours
|
|
},
|
|
networkTimeoutSeconds: 10,
|
|
},
|
|
},
|
|
],
|
|
});
|
|
|
|
/** @type {import('next').NextConfig} */
|
|
const nextConfig = {
|
|
reactStrictMode: true,
|
|
|
|
// Allow access through reverse proxy
|
|
assetPrefix: process.env.NODE_ENV === 'production' ? undefined : undefined,
|
|
|
|
// Performance optimizations
|
|
compiler: {
|
|
removeConsole: process.env.NODE_ENV === 'production',
|
|
},
|
|
|
|
// Image optimization
|
|
images: {
|
|
formats: ['image/avif', 'image/webp'],
|
|
deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
|
|
imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
|
|
},
|
|
|
|
// Production optimizations
|
|
swcMinify: true,
|
|
|
|
// Compression
|
|
compress: true,
|
|
|
|
// Enable experimental features for better performance
|
|
experimental: {
|
|
optimizePackageImports: ['@mui/material', '@mui/icons-material'],
|
|
},
|
|
}
|
|
|
|
module.exports = withPWA(nextConfig)
|