1
1
import { NextRequest , NextResponse } from 'next/server'
2
2
3
- import { envIdCookieName , ignoreMissingApiKeyCookieName , previewApiKeyCookieName } from './lib/constants/cookies' ;
3
+ import { envIdCookieName , previewApiKeyCookieName } from './lib/constants/cookies' ;
4
4
import { createQueryString } from './lib/routing' ;
5
5
import { defaultEnvId } from './lib/utils/env' ;
6
6
@@ -20,11 +20,16 @@ export const middleware = (request: NextRequest) => {
20
20
handleArticlesCategoryRoute ,
21
21
handleArticlesCategoryWithNoPaginationRoute ( currentEnvId ) ,
22
22
handleExplicitProjectRoute ( currentEnvId ) ,
23
+ handleEmptyApiKeyCookie ( currentEnvId ) ,
23
24
handleEmptyCookies
24
25
] ;
25
26
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 ) ;
28
33
} ;
29
34
30
35
const handleExplicitProjectRoute = ( currentEnvId : string ) => ( prevResponse : NextResponse , request : NextRequest ) => {
@@ -36,33 +41,44 @@ const handleExplicitProjectRoute = (currentEnvId: string) => (prevResponse: Next
36
41
return prevResponse ;
37
42
}
38
43
39
- if ( request . nextUrl . pathname . includes ( "/api/exit-preview" ) && request . cookies . get ( ignoreMissingApiKeyCookieName ) ) {
40
- return prevResponse ;
41
- }
42
-
43
44
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 ) ;
47
48
48
49
return res
49
50
}
50
51
51
- if ( routeEnvId !== currentEnvId || ! request . cookies . get ( previewApiKeyCookieName ) ) {
52
+ if ( routeEnvId !== currentEnvId ) {
52
53
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 ) ) ;
55
56
56
57
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 ) ;
59
59
60
60
return res ;
61
61
}
62
62
63
63
return NextResponse . redirect ( new URL ( `${ remainingUrl ?? '' } ?${ createQueryString ( Object . fromEntries ( request . nextUrl . searchParams . entries ( ) ) ) } ` , request . nextUrl . origin ) ) ;
64
64
}
65
65
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
+
66
82
const handleArticlesRoute = ( currentEnvId : string ) => ( prevResponse : NextResponse , request : NextRequest ) => request . nextUrl . pathname === '/articles'
67
83
? NextResponse . rewrite ( new URL ( `/${ currentEnvId } /articles/category/all/page/1` , request . url ) )
68
84
: prevResponse ;
@@ -78,35 +94,25 @@ const handleArticlesCategoryWithNoPaginationRoute = (currentEnvId: string) => (p
78
94
: prevResponse
79
95
80
96
const 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 ) ;
83
99
}
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
-
88
100
89
101
return prevResponse ;
90
102
}
91
103
92
- const createUrlWithQueryString = ( url : string | undefined , searchParams : any ) => {
104
+ const createUrlWithQueryString = ( url : string | undefined , searchParams : IterableIterator < [ string , string ] > ) => {
93
105
const entries = Object . fromEntries ( searchParams ) ;
94
106
95
107
return Object . entries ( entries ) . length > 0 ? `${ url ?? '' } ?${ createQueryString ( entries ) } ` : url ?? '' ;
96
108
}
97
109
98
110
export const config = {
99
111
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).*)' ,
108
113
'/'
109
114
] ,
110
115
} ;
111
116
112
117
const 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