import { type NextMiddleware, NextResponse } from "next/server" import { notFound } from "@/server/errors/next" import { getUidAndContentTypeByPath } from "@/services/cms/getUidAndContentTypeByPath" import { findLang } from "@/utils/languages" import { removeTrailingSlash } from "@/utils/url" import { getDefaultRequestHeaders } from "./utils" import type { MiddlewareMatcher } from "@/types/middleware" import { PageContentTypeEnum } from "@/types/requests/contentType" export const middleware: NextMiddleware = async (request) => { const { nextUrl } = request const lang = findLang(nextUrl.pathname)! const pathWithoutTrailingSlash = removeTrailingSlash(nextUrl.pathname) const isPreview = request.nextUrl.pathname.includes("/preview") const incomingPathName = isPreview ? pathWithoutTrailingSlash.replace("/preview", "") : pathWithoutTrailingSlash let { contentType, uid } = await getUidAndContentTypeByPath(incomingPathName) const searchParams = new URLSearchParams(request.nextUrl.searchParams) if (!contentType || !uid) { // Routes to subpages we need to check if the parent of the incomingPathName exists. // Then we considered the incomingPathName to be a subpage. These subpages do not live in the CMS. const incomingPathNameParts = incomingPathName.split("/") // If the incomingPathName has 2 or more parts, it could possibly be a subpage. if (incomingPathNameParts.length >= 2) { const subpage = incomingPathNameParts.pop() if (subpage) { let { contentType: parentContentType, uid: parentUid } = await getUidAndContentTypeByPath(incomingPathNameParts.join("/")) if (parentUid) { switch (parentContentType) { case PageContentTypeEnum.hotelPage: // E.g. Dedicated pages for restaurant, parking etc. searchParams.set("subpage", subpage) contentType = parentContentType uid = parentUid break case PageContentTypeEnum.destinationCityPage: case PageContentTypeEnum.destinationCountryPage: // E.g. Active filters inside destination pages to filter hotels. searchParams.set("filterFromUrl", subpage) contentType = parentContentType uid = parentUid break default: break } } } } } if (!contentType || !uid) { const headers = getDefaultRequestHeaders(request) headers.set("x-continue", "1") return NextResponse.next({ headers }) } const headers = getDefaultRequestHeaders(request) headers.set("x-uid", uid) headers.set("x-contenttype", contentType) const isCurrent = contentType ? contentType.indexOf("current") >= 0 : false if (isPreview) { searchParams.set("isPreview", "true") return NextResponse.rewrite( new URL( `/${lang}/${contentType}/${uid}?${searchParams.toString()}`, nextUrl ), { request: { headers, }, } ) } if (isCurrent) { const contentTypePathName = pathWithoutTrailingSlash.replace(`/${lang}`, "") searchParams.set("uid", uid) searchParams.set("uri", contentTypePathName) return NextResponse.rewrite( new URL( `/${lang}/current-content-page?${searchParams.toString()}`, nextUrl ), { request: { headers, }, } ) } return NextResponse.rewrite( new URL( `/${lang}/${contentType}/${uid}?${searchParams.toString()}`, nextUrl ), { request: { headers, }, } ) } export const matcher: MiddlewareMatcher = (request) => { // Do not process paths with file extension. // Only looking for dot might be too brute force/give false positives. // It works right now but adjust accordingly when new use cases/data emerges. const lastPathnameSegment = request.nextUrl.pathname.split("/").pop() return lastPathnameSegment?.indexOf(".") === -1 }