refactor: split marketplace into homepage and storefront apps
Build & Deploy / Build & Push Docker Image (push) Failing after 44s
Build & Deploy / Deploy to VPS (push) Has been skipped
Test / API Unit Tests (push) Failing after 5m0s
Test / Marketplace Unit Tests (push) Failing after 4m51s
Test / Admin Unit Tests (push) Successful in 9m31s
Test / Dashboard Unit Tests (push) Successful in 9m37s
Test / API Integration Tests (push) Successful in 9m54s

- Remove apps/marketplace entirely
- Create apps/homepage (port 3000): landing/marketing pages (home, features, pricing, platform-ops, review, legal)
- Create apps/storefront (port 3004): vehicle browsing (explore) and renter account (dashboard, profile, notifications)
- Duplicate shared components/libs into each app
- Update homepage header nav: Home, Features, Pricing instead of Home, Explore
- Fix all /explore cross-references in homepage to point to /features
- Update docker-compose.dev.yml: new homepage and storefront services, remove marketplace
- Update docker-compose.production.yml: split into homepage (main domain) and storefront (path-based routes)
- Update Dockerfiles: EXPOSE 3004 instead of 3003
- Update root package.json scripts: homepage/storefront profiles, tests, prod scripts
- Add docker-prod-up-homepage.sh and docker-prod-up-storefront.sh, remove marketplace script
This commit is contained in:
root
2026-06-25 21:09:16 -04:00
parent c5f614b3e4
commit 256ff0814e
134 changed files with 3223 additions and 38 deletions
+77
View File
@@ -0,0 +1,77 @@
import { NextResponse } from 'next/server'
import type { NextRequest } 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 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)$).*)',
],
}