redesign the homepage
Build & Deploy / Build & Push Docker Image (push) Failing after 47s
Build & Deploy / Deploy to VPS (push) Has been skipped
Test / API Unit Tests (push) Failing after 5m4s
Test / Marketplace Unit Tests (push) Failing after 4m55s
Test / Admin Unit Tests (push) Successful in 9m37s
Test / Dashboard Unit Tests (push) Successful in 9m37s
Test / API Integration Tests (push) Successful in 9m54s

This commit is contained in:
root
2026-06-26 16:27:21 -04:00
parent 256ff0814e
commit d7fb7b7a7b
1030 changed files with 107374 additions and 2657 deletions
+60 -66
View File
@@ -1,77 +1,71 @@
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
import {
defaultLocale,
isLocale,
localeCookie,
localeCookieMaxAgeSeconds,
localeFromAcceptLanguage,
} from '@/lib/localization/config';
import { buildContentSecurityPolicy, createNonce } from '@/lib/security/csp';
import type { NextRequest } from 'next/server';
import { NextResponse } from 'next/server';
const SHARED_LANGUAGE_COOKIE = 'rentaldrivego-language'
const MARKETPLACE_LANGUAGE_COOKIE = 'marketplace-language'
const DASHBOARD_BASE_PATH = '/dashboard'
function rejectInternalSubrequest(request: NextRequest): NextResponse | null {
if (!request.headers.has('x-middleware-subrequest')) return null
return new NextResponse('Unsupported internal request header', { status: 400 })
function applySecurityHeaders(response: NextResponse, contentSecurityPolicy: string): NextResponse {
response.headers.set('Content-Security-Policy', contentSecurityPolicy);
response.headers.set('Referrer-Policy', 'strict-origin-when-cross-origin');
response.headers.set('X-Content-Type-Options', 'nosniff');
response.headers.set('X-Frame-Options', 'DENY');
response.headers.set('Cross-Origin-Opener-Policy', 'same-origin');
response.headers.set('Permissions-Policy', 'camera=(), microphone=(), geolocation=()');
return response;
}
function isValidLanguage(val: string | null | undefined): val is 'en' | 'fr' | 'ar' {
return val === 'en' || val === 'fr' || val === 'ar'
}
export function proxy(request: NextRequest): NextResponse {
const nonce = createNonce();
const csp = buildContentSecurityPolicy(nonce, process.env.NODE_ENV !== 'production');
const requestHeaders = new Headers(request.headers);
requestHeaders.set('x-nonce', nonce);
requestHeaders.set('Content-Security-Policy', csp);
function detectFromAcceptLanguage(header: string | null): 'en' | 'fr' | 'ar' {
if (!header) return 'en'
for (const entry of header.split(',')) {
const code = entry.split(';')[0].trim().split('-')[0].toLowerCase()
if (code === 'ar' || code === 'fr' || code === 'en') return code as 'en' | 'fr' | 'ar'
}
return 'en'
}
function deduplicateDashboardPath(pathname: string): string | null {
if (!pathname.startsWith(`${DASHBOARD_BASE_PATH}${DASHBOARD_BASE_PATH}`)) return null
let normalized = pathname
while (normalized.startsWith(`${DASHBOARD_BASE_PATH}${DASHBOARD_BASE_PATH}`)) {
normalized = normalized.slice(DASHBOARD_BASE_PATH.length)
}
return normalized || DASHBOARD_BASE_PATH
}
export function proxy(request: NextRequest) {
const rejected = rejectInternalSubrequest(request)
if (rejected) return rejected
const deduped = deduplicateDashboardPath(request.nextUrl.pathname)
if (deduped) {
const url = request.nextUrl.clone()
url.pathname = deduped
return NextResponse.redirect(url, 307)
if (request.nextUrl.pathname === '/') {
const cookieLocale = request.cookies.get(localeCookie)?.value;
const locale = isLocale(cookieLocale)
? cookieLocale
: localeFromAcceptLanguage(request.headers.get('accept-language')) || defaultLocale;
const destination = request.nextUrl.clone();
destination.pathname = `/${locale}`;
const response = NextResponse.redirect(destination);
response.cookies.set(localeCookie, locale, {
path: '/',
sameSite: 'lax',
maxAge: localeCookieMaxAgeSeconds,
secure: process.env.NODE_ENV === 'production',
httpOnly: false,
});
return applySecurityHeaders(response, csp);
}
const sharedLang = request.cookies.get(SHARED_LANGUAGE_COOKIE)?.value
if (isValidLanguage(sharedLang)) return NextResponse.next()
const legacyLang = request.cookies.get(MARKETPLACE_LANGUAGE_COOKIE)?.value
const resolved: 'en' | 'fr' | 'ar' = isValidLanguage(legacyLang)
? legacyLang
: detectFromAcceptLanguage(request.headers.get('accept-language'))
const requestHeaders = new Headers(request.headers)
const existing = request.headers.get('cookie') ?? ''
const injected = `${SHARED_LANGUAGE_COOKIE}=${resolved}`
requestHeaders.set('cookie', existing ? `${existing}; ${injected}` : injected)
const response = NextResponse.next({ request: { headers: requestHeaders } })
response.cookies.set(SHARED_LANGUAGE_COOKIE, resolved, {
maxAge: 365 * 24 * 60 * 60,
path: '/',
sameSite: 'lax',
})
return response
const response = NextResponse.next({ request: { headers: requestHeaders } });
const explicitLocale = request.nextUrl.pathname.split('/')[1];
if (isLocale(explicitLocale)) {
response.cookies.set(localeCookie, explicitLocale, {
path: '/',
sameSite: 'lax',
maxAge: localeCookieMaxAgeSeconds,
secure: process.env.NODE_ENV === 'production',
httpOnly: false,
});
}
return applySecurityHeaders(response, csp);
}
export const config = {
matcher: [
'/dashboard/dashboard/:path*',
'/((?!_next/static|_next/image|_next/webpack-hmr|favicon\\.ico|.*\\.(?:png|ico|jpg|jpeg|svg|webp)$).*)',
{
source: '/((?!api|_next/static|_next/image|assets|favicon.ico|robots.txt|sitemap.xml).*)',
missing: [
{ type: 'header', key: 'next-router-prefetch' },
{ type: 'header', key: 'purpose', value: 'prefetch' },
],
},
],
}
};