11import { NextRequest , NextResponse } from 'next/server'
22
3- import { envIdCookieName , ignoreMissingApiKeyCookieName , previewApiKeyCookieName } from './lib/constants/cookies' ;
3+ import { envIdCookieName , previewApiKeyCookieName } from './lib/constants/cookies' ;
44import { createQueryString } from './lib/routing' ;
55import { defaultEnvId } from './lib/utils/env' ;
66
@@ -20,11 +20,16 @@ export const middleware = (request: NextRequest) => {
2020 handleArticlesCategoryRoute ,
2121 handleArticlesCategoryWithNoPaginationRoute ( currentEnvId ) ,
2222 handleExplicitProjectRoute ( currentEnvId ) ,
23+ handleEmptyApiKeyCookie ( currentEnvId ) ,
2324 handleEmptyCookies
2425 ] ;
2526
26- return handlers . reduce ( ( prevResponse , handler ) => handler ( prevResponse , request ) ,
27- NextResponse . rewrite ( new URL ( `/${ currentEnvId } ${ request . nextUrl . pathname ? `${ request . nextUrl . pathname } ` : '' } ` , request . url ) ) )
27+ const initialResponse = request . nextUrl . pathname . startsWith ( "/api/" )
28+ ? NextResponse . next ( )
29+ : NextResponse . rewrite ( new URL ( `/${ currentEnvId } ${ request . nextUrl . pathname ? `${ request . nextUrl . pathname } ` : '' } ` , request . url ) ) ;
30+
31+
32+ return handlers . reduce ( ( prevResponse , handler ) => handler ( prevResponse , request ) , initialResponse ) ;
2833} ;
2934
3035const handleExplicitProjectRoute = ( currentEnvId : string ) => ( prevResponse : NextResponse , request : NextRequest ) => {
@@ -36,33 +41,44 @@ const handleExplicitProjectRoute = (currentEnvId: string) => (prevResponse: Next
3641 return prevResponse ;
3742 }
3843
39- if ( request . nextUrl . pathname . includes ( "/api/exit-preview" ) && request . cookies . get ( ignoreMissingApiKeyCookieName ) ) {
40- return prevResponse ;
41- }
42-
4344 if ( routeEnvId === defaultEnvId ) {
44- const res = NextResponse . redirect ( new URL ( createUrlWithQueryString ( remainingUrl , request . nextUrl . searchParams ) , request . nextUrl . origin ) ) ;
45- res . cookies . set ( envIdCookieName , routeEnvId , cookieOptions ) ;
46- res . cookies . set ( previewApiKeyCookieName , '' , cookieOptions ) ;
45+ const res = NextResponse . redirect ( new URL ( createUrlWithQueryString ( remainingUrl , request . nextUrl . searchParams . entries ( ) ) , request . nextUrl . origin ) ) ;
46+ res . cookies . set ( envIdCookieName , defaultEnvId , cookieOptions ) ;
47+ res . cookies . set ( previewApiKeyCookieName , "" , cookieDeleteOptions ) ;
4748
4849 return res
4950 }
5051
51- if ( routeEnvId !== currentEnvId || ! request . cookies . get ( previewApiKeyCookieName ) ) {
52+ if ( routeEnvId !== currentEnvId ) {
5253 const originalPath = encodeURIComponent ( createUrlWithQueryString ( remainingUrl , request . nextUrl . searchParams . entries ( ) ) ) ;
53- const redirectPath = `/api/exit-preview?callback=${ encodeURIComponent ( `/getPreviewApiKey?path= ${ originalPath } `) } ` ;
54- const res = NextResponse . redirect ( new URL ( redirectPath , request . url ) ) ;
54+ const redirectPath = `/api/exit-preview?callback=${ originalPath } ` ; // We need to exit preview, because the old preview API key is in preview data
55+ const res = NextResponse . redirect ( new URL ( redirectPath , request . nextUrl . origin ) ) ;
5556
5657 res . cookies . set ( envIdCookieName , routeEnvId , cookieOptions ) ;
57- res . cookies . set ( previewApiKeyCookieName , '' , cookieOptions ) ;
58- res . cookies . set ( ignoreMissingApiKeyCookieName , "true" , cookieOptions ) ;
58+ res . cookies . set ( previewApiKeyCookieName , "" , cookieDeleteOptions ) ;
5959
6060 return res ;
6161 }
6262
6363 return NextResponse . redirect ( new URL ( `${ remainingUrl ?? '' } ?${ createQueryString ( Object . fromEntries ( request . nextUrl . searchParams . entries ( ) ) ) } ` , request . nextUrl . origin ) ) ;
6464}
6565
66+ const handleEmptyApiKeyCookie = ( currentEnvId : string ) => ( prevResponse : NextResponse , request : NextRequest ) => {
67+ if ( request . cookies . get ( previewApiKeyCookieName ) ?. value || ! request . nextUrl . pathname . startsWith ( "/api/preview" ) ) {
68+ return prevResponse ;
69+ }
70+
71+ if ( currentEnvId === defaultEnvId ) {
72+ const res = NextResponse . redirect ( request . url ) ; // Workaround for this issue https://github.com/vercel/next.js/issues/49442, we cannot set cookies on NextResponse.next()
73+ res . cookies . set ( previewApiKeyCookieName , KONTENT_PREVIEW_API_KEY , cookieOptions ) ;
74+ return res ;
75+ }
76+
77+ const originalPath = encodeURIComponent ( createUrlWithQueryString ( request . nextUrl . pathname , request . nextUrl . searchParams . entries ( ) ) ) ;
78+ const redirectPath = `/getPreviewApiKey?path=${ originalPath } ` ;
79+ return NextResponse . redirect ( new URL ( redirectPath , request . nextUrl . origin ) ) ;
80+ } ;
81+
6682const handleArticlesRoute = ( currentEnvId : string ) => ( prevResponse : NextResponse , request : NextRequest ) => request . nextUrl . pathname === '/articles'
6783 ? NextResponse . rewrite ( new URL ( `/${ currentEnvId } /articles/category/all/page/1` , request . url ) )
6884 : prevResponse ;
@@ -78,35 +94,25 @@ const handleArticlesCategoryWithNoPaginationRoute = (currentEnvId: string) => (p
7894 : prevResponse
7995
8096const handleEmptyCookies = ( prevResponse : NextResponse , request : NextRequest ) => {
81- if ( ! request . cookies . get ( envIdCookieName ) ?. value ) {
82- prevResponse . cookies . set ( envIdCookieName , defaultEnvId , cookieOptions )
97+ if ( ! request . cookies . get ( envIdCookieName ) ?. value && ! prevResponse . cookies . get ( envIdCookieName ) ) {
98+ prevResponse . cookies . set ( envIdCookieName , defaultEnvId , cookieOptions ) ;
8399 }
84- if ( ! request . cookies . get ( envIdCookieName ) ?. value || request . cookies . get ( envIdCookieName ) ?. value === defaultEnvId ) {
85- prevResponse . cookies . set ( previewApiKeyCookieName , KONTENT_PREVIEW_API_KEY , cookieOptions )
86- }
87-
88100
89101 return prevResponse ;
90102}
91103
92- const createUrlWithQueryString = ( url : string | undefined , searchParams : any ) => {
104+ const createUrlWithQueryString = ( url : string | undefined , searchParams : IterableIterator < [ string , string ] > ) => {
93105 const entries = Object . fromEntries ( searchParams ) ;
94106
95107 return Object . entries ( entries ) . length > 0 ? `${ url ?? '' } ?${ createQueryString ( entries ) } ` : url ?? '' ;
96108}
97109
98110export const config = {
99111 matcher : [
100- /*
101- * Match all request paths except for the ones starting with:
102- * - api (API routes)
103- * - _next/static (static files)
104- * - _next/image (image optimization files)
105- * - favicon.png (favicon file)
106- */
107- '/((?!api|_next/static|_next/image|favicon.png|getPreviewApiKey|logo.png|callback).*)' ,
112+ '/((?!_next/static|_next/image|favicon.png|getPreviewApiKey|logo.png|callback).*)' ,
108113 '/'
109114 ] ,
110115} ;
111116
112117const cookieOptions = { path : '/' , sameSite : 'none' , secure : true } as const ;
118+ const cookieDeleteOptions = { ...cookieOptions , maxAge : - 1 } as const ; // It seems that res.cookies.delete doesn't propagate provided options (we need sameSite: none) so we use this as a workaround
0 commit comments