Shumiland/next.config.ts
Vadym Samoilenko 3a44b986bc
Some checks failed
CI / Type Check (push) Has been cancelled
CI / Lint (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
Deploy / Build & Push Image (push) Has been cancelled
Deploy / Deploy to VPS (push) Has been cancelled
fix(images): add dynamic remotePattern from NEXT_PUBLIC_SITE_URL
Payload media served from the deployment host (e.g. http://147.135.209.100)
was blocked by Next.js image optimiser with 400 because the host was absent
from remotePatterns. Now parsed at build time from NEXT_PUBLIC_SITE_URL so
it works for any environment (localhost, IP, or domain) without hardcoding.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-06 21:18:40 +01:00

71 lines
2.3 KiB
TypeScript

import { withPayload } from '@payloadcms/next/withPayload'
import type { NextConfig } from 'next'
const ONE_YEAR = 'public, max-age=31536000, immutable'
const ONE_DAY = 'public, max-age=86400, must-revalidate'
// Dynamically allow the deployment origin so /_next/image can optimise CMS media
// regardless of whether it's localhost, an IP, or a domain.
function buildRemotePatterns(): NextConfig['images']['remotePatterns'] {
const patterns: NextConfig['images']['remotePatterns'] = [
{ protocol: 'http', hostname: 'localhost', port: '3000', pathname: '/media/**' },
{ protocol: 'http', hostname: 'localhost', port: '3000', pathname: '/api/media/**' },
{ protocol: 'https', hostname: 'shumiland.com.ua', pathname: '/api/media/**' },
{ protocol: 'https', hostname: 'shumi.ai-impress.com', pathname: '/api/media/**' },
]
const siteUrl = process.env.NEXT_PUBLIC_SITE_URL
if (siteUrl) {
try {
const { protocol, hostname, port } = new URL(siteUrl)
const proto = protocol.replace(':', '') as 'http' | 'https'
const entry: (typeof patterns)[number] = {
protocol: proto,
hostname,
pathname: '/api/media/**',
}
if (port) entry.port = port
patterns.push(entry)
} catch {
// invalid URL — skip
}
}
return patterns
}
const nextConfig: NextConfig = {
output: 'standalone',
reactStrictMode: true,
typescript: { ignoreBuildErrors: true },
async headers() {
return [
{
// public/ images are mutable (replaced in-place) — allow daily revalidation
source: '/images/:path*',
headers: [{ key: 'Cache-Control', value: ONE_DAY }],
},
{
// _next/static/ assets have content-hashed filenames — safe to cache forever
source: '/_next/static/:path*',
headers: [{ key: 'Cache-Control', value: ONE_YEAR }],
},
]
},
webpack: (config) => {
config.watchOptions = {
...config.watchOptions,
ignored: /node_modules|importMap\.js|\.next/,
}
return config
},
images: {
formats: ['image/avif', 'image/webp'],
deviceSizes: [375, 640, 768, 1024, 1280, 1536, 1920],
imageSizes: [16, 32, 64, 96, 128, 256, 384, 512, 694],
minimumCacheTTL: 31536000,
remotePatterns: buildRemotePatterns(),
},
}
export default withPayload(nextConfig)