Files
carmanagement/apps/dashboard/src/middleware.ts
T
root 149806a773
Build & Push / Pipeline Tests (push) Successful in 1m53s
Test / Type Check (all packages) (push) Successful in 56s
Build & Push / Build & Push Docker Image (push) Failing after 4m53s
Test / API Unit Tests (push) Successful in 1m15s
Test / Homepage Unit Tests (push) Successful in 46s
Test / Carplace Unit Tests (push) Successful in 41s
Test / Admin Unit Tests (push) Successful in 40s
Test / Dashboard Unit Tests (push) Successful in 42s
Test / API Integration Tests (push) Successful in 1m6s
fix bug 16 and 17
2026-08-04 00:52:58 -04:00

133 lines
4.8 KiB
TypeScript

import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
const WEBSITE_URL = process.env.NEXT_PUBLIC_WEBSITE_URL ?? 'http://localhost:3000'
const DASHBOARD_PUBLIC_URL = process.env.NEXT_PUBLIC_DASHBOARD_URL ?? `${WEBSITE_URL.replace(/\/$/, '')}/dashboard`
const DASHBOARD_BASE_PATH = '/dashboard'
const DEFAULT_SIGN_IN_LOCALE = 'en'
const DEFAULT_SIGN_IN_THEME = 'dark'
function toDashboardAppPath(pathname: string): string {
let normalized = pathname || '/'
while (normalized === DASHBOARD_BASE_PATH || normalized.startsWith(`${DASHBOARD_BASE_PATH}/`)) {
normalized = normalized.slice(DASHBOARD_BASE_PATH.length) || '/'
}
return normalized
}
function toPublicDashboardPath(pathname: string): string {
const appPath = toDashboardAppPath(pathname)
return appPath === '/' ? DASHBOARD_BASE_PATH : `${DASHBOARD_BASE_PATH}${appPath}`
}
function resolveSignInPreference(value: string | undefined, allowed: string[], fallback: string) {
return value && allowed.includes(value) ? value : fallback
}
function resolveHomepageSignInUrl(req: NextRequest, redirectPath?: string): URL {
const locale = resolveSignInPreference(
req.nextUrl.searchParams.get('lang') ?? req.cookies.get('rentaldrivego-language')?.value,
['en', 'fr', 'ar'],
DEFAULT_SIGN_IN_LOCALE,
)
const theme = resolveSignInPreference(
req.nextUrl.searchParams.get('theme') ?? req.cookies.get('rentaldrivego-theme')?.value ?? req.cookies.get('hpc-theme')?.value,
['light', 'dark'],
DEFAULT_SIGN_IN_THEME,
)
const signInUrl = new URL(`/${locale}/${theme}/sign-in`, new URL(WEBSITE_URL).origin)
if (redirectPath) signInUrl.searchParams.set('redirect', toPublicDashboardPath(redirectPath))
for (const key of ['portal', 'embedded']) {
const value = req.nextUrl.searchParams.get(key)
if (value) signInUrl.searchParams.set(key, value)
}
return signInUrl
}
function deduplicatePublicDashboardPath(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
}
function resolveProxyUrl(_req: NextRequest, pathname: string): URL {
const canonicalDashboard = new URL(DASHBOARD_PUBLIC_URL)
const publicPath = pathname.startsWith(DASHBOARD_BASE_PATH)
? pathname
: toPublicDashboardPath(pathname)
return new URL(publicPath, canonicalDashboard.origin)
}
function hasExplicitPort(host: string): boolean {
return /^[^:]+:\d+$/.test(host)
}
function isInternalAppPort(port: string): boolean {
return ['3000', '3001', '3002', '3004', '4000'].includes(port)
}
function isInternalHost(host: string | null): boolean {
if (!host) return false
const hostname = host.split(':')[0]?.toLowerCase()
return ['host.docker.internal', 'dashboard', 'admin', 'api', 'homepage', 'carplace'].includes(hostname)
}
function isProtectedRoute(req: NextRequest) {
const pathname = toDashboardAppPath(req.nextUrl.pathname)
if (pathname === '/api' || pathname.startsWith('/api/') || pathname === '/trpc' || pathname.startsWith('/trpc/')) return false
if (['/sign-in', '/sign-up', '/forgot-password', '/reset-password', '/onboarding', '/verify-email'].some((p) => pathname === p || pathname.startsWith(p + '/'))) return false
return pathname === '/' || (pathname.startsWith('/') && !pathname.startsWith('/_next'))
}
function localJwtMiddleware(req: NextRequest): NextResponse {
const token = req.cookies.get('employee_session')?.value
const pathname = toDashboardAppPath(req.nextUrl.pathname)
if (pathname === '/sign-in') {
if (token) {
const dashboardUrl = resolveProxyUrl(req, DASHBOARD_BASE_PATH)
return NextResponse.redirect(dashboardUrl)
}
return NextResponse.redirect(resolveHomepageSignInUrl(req, req.nextUrl.searchParams.get('redirect') ?? undefined))
}
if (!isProtectedRoute(req)) return NextResponse.next()
if (!token) {
return NextResponse.redirect(resolveHomepageSignInUrl(req, req.nextUrl.pathname))
}
return NextResponse.next()
}
export default function middleware(req: NextRequest) {
if (req.headers.has('x-middleware-subrequest')) {
return new NextResponse('Unsupported internal request header', { status: 400 })
}
const dedupedPath = deduplicatePublicDashboardPath(req.nextUrl.pathname)
if (dedupedPath) {
const redirectUrl = req.nextUrl.clone()
redirectUrl.pathname = dedupedPath
return NextResponse.redirect(redirectUrl)
}
return localJwtMiddleware(req)
}
export const config = {
matcher: [
'/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)',
'/(api|trpc)(.*)',
],
}