-
-
Notifications
You must be signed in to change notification settings - Fork 455
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support Preview Mode #562
Support Preview Mode #562
Changes from all commits
b4cf02b
b0d997d
aadbc82
59af91f
9f68430
c8b12a8
938e21d
6b95135
12efe58
0cb6377
c5d662c
131913c
e477354
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,2 @@ | ||
!tests/** | ||
dist/ | ||
dist/ |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import commonjs from "@rollup/plugin-commonjs"; | ||
import typescript from "@rollup/plugin-typescript"; | ||
import { nodeResolve } from "@rollup/plugin-node-resolve"; | ||
import externals from "rollup-plugin-node-externals"; | ||
|
||
const LOCAL_EXTERNALS = [ | ||
"./manifest.json", | ||
"./routes-manifest.json", | ||
"./prerender-manifest.json" | ||
]; | ||
const NPM_EXTERNALS = ["aws-lambda", "aws-sdk/clients/s3"]; | ||
|
||
const generateConfig = (filename) => ({ | ||
input: `./src/${filename}.ts`, | ||
output: { | ||
file: `./dist/${filename}.js`, | ||
format: "cjs" | ||
}, | ||
plugins: [ | ||
commonjs(), | ||
externals({ | ||
exclude: "@sls-next/next-aws-cloudfront" | ||
}), | ||
nodeResolve(), | ||
typescript({ | ||
tsconfig: "tsconfig.bundle.json" | ||
}) | ||
], | ||
external: [...NPM_EXTERNALS, ...LOCAL_EXTERNALS] | ||
}); | ||
|
||
export default ["default-handler", "api-handler"].map(generateConfig); |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,6 +5,7 @@ import Manifest from "./manifest.json"; | |
// @ts-ignore | ||
import { basePath } from "./routes-manifest.json"; | ||
import lambdaAtEdgeCompat from "@sls-next/next-aws-cloudfront"; | ||
import cookie from "cookie"; | ||
import { | ||
CloudFrontRequest, | ||
CloudFrontS3Origin, | ||
|
@@ -21,6 +22,29 @@ import { | |
import S3 from "aws-sdk/clients/s3"; | ||
import { performance } from "perf_hooks"; | ||
import { ServerResponse } from "http"; | ||
import jsonwebtoken from "jsonwebtoken"; | ||
|
||
const NEXT_PREVIEW_DATA_COOKIE = "__next_preview_data"; | ||
const NEXT_PRERENDER_BYPASS_COOKIE = "__prerender_bypass"; | ||
const defaultPreviewCookies = { | ||
[NEXT_PRERENDER_BYPASS_COOKIE]: "", | ||
[NEXT_PREVIEW_DATA_COOKIE]: "" | ||
}; | ||
|
||
const getPreviewCookies = (request: CloudFrontRequest) => { | ||
const targetCookie = request.headers.cookie || []; | ||
return targetCookie.reduce((previewCookies, cookieObj) => { | ||
const cookieValue = cookie.parse(cookieObj.value); | ||
if ( | ||
cookieValue[NEXT_PREVIEW_DATA_COOKIE] && | ||
cookieValue[NEXT_PRERENDER_BYPASS_COOKIE] | ||
) { | ||
return cookieValue as typeof defaultPreviewCookies; | ||
} else { | ||
return previewCookies; | ||
} | ||
}, defaultPreviewCookies); | ||
}; | ||
|
||
const perfLogger = (logLambdaExecutionTimes: boolean): PerfLogger => { | ||
if (logLambdaExecutionTimes) { | ||
|
@@ -211,11 +235,34 @@ const handleOriginRequest = async ({ | |
const normalisedS3DomainName = normaliseS3OriginDomain(s3Origin); | ||
const hasFallback = hasFallbackForUri(uri, prerenderManifest); | ||
const { now, log } = perfLogger(manifest.logLambdaExecutionTimes); | ||
const previewCookies = getPreviewCookies(request); | ||
const isPreviewRequest = | ||
previewCookies[NEXT_PREVIEW_DATA_COOKIE] && | ||
previewCookies[NEXT_PRERENDER_BYPASS_COOKIE]; | ||
|
||
if (isPreviewRequest) { | ||
try { | ||
jsonwebtoken.verify( | ||
previewCookies[NEXT_PREVIEW_DATA_COOKIE], | ||
prerenderManifest.preview.previewModeSigningKey | ||
); | ||
} catch (e) { | ||
console.error("Failed preview mode verification for URI:", request.uri); | ||
return { | ||
status: "403", | ||
statusDescription: "Forbidden" | ||
}; | ||
} | ||
} | ||
|
||
s3Origin.domainName = normalisedS3DomainName; | ||
|
||
// Check if we can serve request from S3 | ||
S3Check: if (isHTMLPage || isPublicFile || hasFallback || isDataReq) { | ||
S3Check: if ( | ||
isPublicFile || | ||
(isHTMLPage && !isPreviewRequest) || | ||
(hasFallback && !isPreviewRequest) || | ||
(isDataReq && !isPreviewRequest) | ||
) { | ||
if (isHTMLPage || hasFallback) { | ||
s3Origin.path = `${basePath}/static-pages`; | ||
const pageName = uri === "/" ? "/index" : uri; | ||
|
@@ -249,7 +296,7 @@ const handleOriginRequest = async ({ | |
|
||
const pagePath = router(manifest)(uri); | ||
|
||
if (pagePath.endsWith(".html")) { | ||
if (pagePath.endsWith(".html") && !isPreviewRequest) { | ||
s3Origin.path = `${basePath}/static-pages`; | ||
request.uri = pagePath.replace("pages", ""); | ||
addS3HostHeader(request, normalisedS3DomainName); | ||
|
@@ -272,7 +319,17 @@ const handleOriginRequest = async ({ | |
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this can be moved into the else block below just before the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does that mean we know for a fact that data requests will not result in a possible error page? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, I guess unmatched data request can still go to |
||
|
||
// Render page | ||
await page.render(req, res); | ||
if (isDataReq) { | ||
const { renderOpts } = await page.renderReqToHTML( | ||
req, | ||
res, | ||
"passthrough" | ||
); | ||
res.setHeader("Content-Type", "application/json"); | ||
res.end(JSON.stringify(renderOpts.pageData)); | ||
} else { | ||
await page.render(req, res); | ||
} | ||
} catch (error) { | ||
// Set status to 500 so _error.js will render a 500 page | ||
console.error( | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe some logging (at info level?) is useful here, I added it for 500 page as well, not sure if it's needed for this though
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added a
console.error
, similar to the logging for the 500 response.