8aab968e09
Build & Deploy / Build & Push Docker Image (push) Successful in 1m2s
Test / Type Check (all packages) (push) Failing after 28s
Test / API Unit Tests (push) Has been skipped
Test / Homepage Unit Tests (push) Has been skipped
Test / Storefront Unit Tests (push) Has been skipped
Test / Admin Unit Tests (push) Has been skipped
Test / Dashboard Unit Tests (push) Has been skipped
Test / API Integration Tests (push) Has been skipped
Build & Deploy / Deploy to VPS (push) Successful in 3s
Comprehensive rename of all marketplace references to storefront: - API module: apps/api/src/modules/marketplace/ → storefront/ - Components: MarketplaceHeader → StorefrontHeader, MarketplaceShell → StorefrontShell, MarketplaceFooter → StorefrontFooter - Types: marketplace-homepage.ts → storefront-homepage.ts - Test files: employee-marketplace-* → employee-storefront-* - All source code identifiers, imports, route paths, and strings - Documentation (docs/), CI config (.gitlab-ci.yml), scripts - Dashboard, admin, storefront workspace references - Prisma field names preserved (isListedOnMarketplace, marketplaceRating, marketplaceFunnelEvent) as they map to database schema Validation: - API type-check: 0 errors - Storefront type-check: 0 errors - Dashboard type-check: 0 errors - Full monorepo type-check: only pre-existing admin TS18046
78 lines
2.6 KiB
TypeScript
78 lines
2.6 KiB
TypeScript
import { NextResponse } from 'next/server'
|
|
import type { NextRequest } from 'next/server'
|
|
|
|
const SHARED_LANGUAGE_COOKIE = 'rentaldrivego-language'
|
|
const MARKETPLACE_LANGUAGE_COOKIE = 'storefront-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 isValidLanguage(val: string | null | undefined): val is 'en' | 'fr' | 'ar' {
|
|
return val === 'en' || val === 'fr' || val === 'ar'
|
|
}
|
|
|
|
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)
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
export const config = {
|
|
matcher: [
|
|
'/dashboard/dashboard/:path*',
|
|
'/((?!_next/static|_next/image|_next/webpack-hmr|favicon\\.ico|.*\\.(?:png|ico|jpg|jpeg|svg|webp)$).*)',
|
|
],
|
|
}
|