chore: bulk commit of pre-existing changes
Build & Deploy / Build & Push Docker Image (push) Successful in 48s
Test / API Unit Tests (push) Successful in 9m48s
Test / Marketplace Unit Tests (push) Successful in 9m38s
Test / Admin Unit Tests (push) Successful in 9m33s
Build & Deploy / Deploy to VPS (push) Has been cancelled
Test / Dashboard Unit Tests (push) Has been cancelled
Test / API Integration Tests (push) Has been cancelled
Build & Deploy / Build & Push Docker Image (push) Successful in 48s
Test / API Unit Tests (push) Successful in 9m48s
Test / Marketplace Unit Tests (push) Successful in 9m38s
Test / Admin Unit Tests (push) Successful in 9m33s
Build & Deploy / Deploy to VPS (push) Has been cancelled
Test / Dashboard Unit Tests (push) Has been cancelled
Test / API Integration Tests (push) Has been cancelled
Includes: - Admin dashboard layout and page updates - API account auth module (routes, schemas, service) - Dashboard sign-up form extraction, middleware refactor - Marketplace proxy and middleware updates - CORS origins expansion for dev environment - Seed email domain correction (.com → .ma) - Docs: create-account guide, fleet page, signup plan - Various UI component and layout refinements
This commit is contained in:
@@ -1,17 +1,51 @@
|
||||
import { NextResponse } from 'next/server'
|
||||
import type { NextRequest } from 'next/server'
|
||||
|
||||
const PUBLIC_DASHBOARD_PREFIXES = ['/dashboard/sign-in', '/dashboard/forgot-password']
|
||||
const MARKETPLACE_URL = process.env.NEXT_PUBLIC_MARKETPLACE_URL ?? 'http://localhost:3000'
|
||||
const DASHBOARD_BASE_PATH = '/dashboard'
|
||||
|
||||
function toDashboardAppPath(pathname: string): string {
|
||||
let normalized = pathname || '/'
|
||||
|
||||
function rejectInternalSubrequest(req: NextRequest): NextResponse | null {
|
||||
if (!req.headers.has('x-middleware-subrequest')) return null
|
||||
return new NextResponse('Unsupported internal request header', { status: 400 })
|
||||
while (normalized === DASHBOARD_BASE_PATH || normalized.startsWith(`${DASHBOARD_BASE_PATH}/`)) {
|
||||
normalized = normalized.slice(DASHBOARD_BASE_PATH.length) || '/'
|
||||
}
|
||||
|
||||
return normalized
|
||||
}
|
||||
|
||||
function dedupeDashboardPath(pathname: string): string {
|
||||
return pathname.replace(/^\/dashboard\/dashboard(?=\/|$)/, '/dashboard')
|
||||
function toPublicDashboardPath(pathname: string): string {
|
||||
const appPath = toDashboardAppPath(pathname)
|
||||
return appPath === '/' ? DASHBOARD_BASE_PATH : `${DASHBOARD_BASE_PATH}${appPath}`
|
||||
}
|
||||
|
||||
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 forwardedHost = req.headers.get('x-forwarded-host')
|
||||
const forwardedProto = req.headers.get('x-forwarded-proto')
|
||||
const marketplaceOrigin = new URL(MARKETPLACE_URL)
|
||||
|
||||
const url = new URL(pathname, req.nextUrl.origin)
|
||||
if (forwardedHost && !isInternalHost(forwardedHost)) {
|
||||
url.host = forwardedHost
|
||||
url.protocol = (forwardedProto ?? 'http') + ':'
|
||||
} else if (!isInternalHost(req.nextUrl.host)) {
|
||||
url.host = req.nextUrl.host
|
||||
url.protocol = req.nextUrl.protocol
|
||||
} else {
|
||||
url.host = marketplaceOrigin.host
|
||||
url.protocol = marketplaceOrigin.protocol
|
||||
}
|
||||
return url
|
||||
}
|
||||
|
||||
function isInternalHost(host: string | null): boolean {
|
||||
@@ -21,38 +55,25 @@ function isInternalHost(host: string | null): boolean {
|
||||
}
|
||||
|
||||
function isProtectedRoute(req: NextRequest) {
|
||||
const { pathname } = req.nextUrl
|
||||
if (PUBLIC_DASHBOARD_PREFIXES.some((p) => pathname === p || pathname.startsWith(p + '/'))) return false
|
||||
return pathname === '/dashboard' || pathname.startsWith('/dashboard/')
|
||||
const pathname = toDashboardAppPath(req.nextUrl.pathname)
|
||||
if (['/sign-in', '/sign-up', '/forgot-password', '/reset-password', '/onboarding'].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
|
||||
if (token && req.nextUrl.pathname === '/dashboard/sign-in') {
|
||||
const dashboardUrl = req.nextUrl.clone()
|
||||
dashboardUrl.pathname = '/dashboard'
|
||||
dashboardUrl.search = ''
|
||||
const pathname = toDashboardAppPath(req.nextUrl.pathname)
|
||||
|
||||
// Redirect signed-in users from sign-in to dashboard (through marketplace proxy)
|
||||
if (token && pathname === '/sign-in') {
|
||||
const dashboardUrl = resolveProxyUrl(req, DASHBOARD_BASE_PATH)
|
||||
return NextResponse.redirect(dashboardUrl)
|
||||
}
|
||||
|
||||
if (!isProtectedRoute(req)) return NextResponse.next()
|
||||
|
||||
if (!token) {
|
||||
const forwardedHost = req.headers.get('x-forwarded-host')
|
||||
const forwardedProto = req.headers.get('x-forwarded-proto')
|
||||
const marketplaceOrigin = new URL(MARKETPLACE_URL)
|
||||
|
||||
const signInUrl = req.nextUrl.clone()
|
||||
if (forwardedHost && !isInternalHost(forwardedHost)) {
|
||||
signInUrl.host = forwardedHost
|
||||
signInUrl.protocol = (forwardedProto ?? 'http') + ':'
|
||||
} else if (isInternalHost(signInUrl.host)) {
|
||||
signInUrl.host = marketplaceOrigin.host
|
||||
signInUrl.protocol = marketplaceOrigin.protocol
|
||||
}
|
||||
|
||||
const isMarketplaceHost = signInUrl.host === marketplaceOrigin.host
|
||||
signInUrl.pathname = isMarketplaceHost ? '/' : '/dashboard/sign-in'
|
||||
const signInUrl = resolveProxyUrl(req, toPublicDashboardPath('/sign-in'))
|
||||
signInUrl.searchParams.set('redirect', req.nextUrl.pathname)
|
||||
return NextResponse.redirect(signInUrl)
|
||||
}
|
||||
@@ -60,11 +81,12 @@ function localJwtMiddleware(req: NextRequest): NextResponse {
|
||||
}
|
||||
|
||||
export default function middleware(req: NextRequest) {
|
||||
const rejected = rejectInternalSubrequest(req)
|
||||
if (rejected) return rejected
|
||||
if (req.headers.has('x-middleware-subrequest')) {
|
||||
return new NextResponse('Unsupported internal request header', { status: 400 })
|
||||
}
|
||||
|
||||
const dedupedPath = dedupeDashboardPath(req.nextUrl.pathname)
|
||||
if (dedupedPath !== req.nextUrl.pathname) {
|
||||
const dedupedPath = deduplicatePublicDashboardPath(req.nextUrl.pathname)
|
||||
if (dedupedPath) {
|
||||
const redirectUrl = req.nextUrl.clone()
|
||||
redirectUrl.pathname = dedupedPath
|
||||
return NextResponse.redirect(redirectUrl)
|
||||
|
||||
Reference in New Issue
Block a user