Files
root 2de00868ad
Build & Deploy / Build & Push Docker Image (push) Successful in 2m58s
Test / Type Check (all packages) (push) Successful in 54s
Build & Deploy / Deploy to VPS (push) Successful in 4s
Test / API Unit Tests (push) Successful in 47s
Test / Homepage Unit Tests (push) Successful in 46s
Test / Carplace Unit Tests (push) Successful in 40s
Test / Admin Unit Tests (push) Successful in 42s
Test / Dashboard Unit Tests (push) Successful in 39s
Test / API Integration Tests (push) Successful in 59s
fix(dashboard): proxy API requests through dashboard route
2026-07-02 22:14:34 -04:00

108 lines
3.0 KiB
JavaScript

/** @type {import('next').NextConfig} */
const { buildSecurityHeaders, normalizeAssetPrefix } = require('../../config/nextSecurityHeaders')
const toStoragePattern = (rawUrl) => {
try {
const u = new URL(rawUrl.replace(/\/api\/v1\/?$/, ''))
return {
protocol: u.protocol.replace(':', ''),
hostname: u.hostname,
...(u.port ? { port: u.port } : {}),
pathname: '/storage/**',
}
} catch {
return null
}
}
const storagePatterns = [
process.env.API_INTERNAL_URL ?? 'http://localhost:4000',
process.env.API_URL,
process.env.NEXT_PUBLIC_API_URL,
]
.filter(Boolean)
.map(toStoragePattern)
.filter(Boolean)
.filter((p, i, arr) => arr.findIndex((q) => q.hostname === p.hostname && q.port === p.port) === i)
const DASHBOARD_BASE_PATH = '/dashboard'
// basePath is required so the client-side router knows it's mounted at /dashboard.
// Without it, React cannot hydrate because the URL (/dashboard/sign-up) doesn't
// match the route (/sign-up). The Turbopack double-basePath prefetch bug is
// handled by the Carplace middleware (307 redirect /dashboard/dashboard → /dashboard).
//
// In local development the dashboard is often viewed through the Carplace
// proxy on port 3000 while the dashboard dev server runs on port 3001. Next
// rewrites do not reliably proxy HMR WebSocket upgrades, so emit absolute
// dashboard asset/HMR URLs directly to the dashboard dev server.
const dashboardAssetPrefix = process.env.DASHBOARD_ASSET_PREFIX
?? (process.env.NODE_ENV !== 'production' ? 'http://localhost:3001' : undefined)
const assetPrefix = normalizeAssetPrefix(dashboardAssetPrefix, DASHBOARD_BASE_PATH)
const securityHeaders = buildSecurityHeaders({
assetSources: [assetPrefix].filter(Boolean),
})
const nextConfig = {
basePath: DASHBOARD_BASE_PATH,
...(assetPrefix ? { assetPrefix } : {}),
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'res.cloudinary.com',
},
...storagePatterns,
],
},
transpilePackages: ['@rentaldrivego/types'],
async headers() {
return [
{
source: '/_next/static/media/:path*',
headers: [
{
key: 'Access-Control-Allow-Origin',
value: '*',
},
],
},
{
source: '/:path*',
headers: securityHeaders,
},
]
},
async redirects() {
return [
{
source: `${DASHBOARD_BASE_PATH}/:path*`,
destination: '/:path*',
permanent: false,
},
{
source: DASHBOARD_BASE_PATH,
destination: '/',
permanent: false,
},
{
source: '/',
destination: DASHBOARD_BASE_PATH,
permanent: false,
basePath: false,
},
]
},
async rewrites() {
const apiOrigin = (process.env.API_INTERNAL_URL ?? process.env.API_URL ?? 'http://localhost:4000').replace(/\/api\/v1\/?$/, '')
return [
{
source: '/storage/:path*',
destination: `${apiOrigin}/storage/:path*`,
},
]
},
}
module.exports = nextConfig