Created
April 2, 2025 06:52
-
-
Save sommeeeer/288ac14bdc978348337b77f97f2e6896 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import cf from "cloudfront"; | |
| async function handler(event) { | |
| async function routeSite(kvNamespace, metadata) { | |
| const baselessUri = metadata.base | |
| ? event.request.uri.replace(metadata.base, "") | |
| : event.request.uri; | |
| // Route to S3 asset routes | |
| if (metadata.s3 && metadata.s3.routes) { | |
| for (var i=0, l=metadata.s3.routes.length; i<l; i++) { | |
| const route = metadata.s3.routes[i]; | |
| if (baselessUri.startsWith(route)) { | |
| event.request.uri = metadata.s3.dir + baselessUri; | |
| setS3Origin(metadata.s3.domain); | |
| return; | |
| } | |
| } | |
| } | |
| // Route to S3 | |
| try { | |
| // check using baselessUri b/c files are stored in the root | |
| const u = decodeURIComponent(baselessUri); | |
| const postfixes = u.endsWith("/") | |
| ? ["index.html"] | |
| : ["", ".html", "/index.html"]; | |
| const v = await Promise.any(postfixes.map(p => cf.kvs().get(kvNamespace + ":" + u + p).then(v => p))); | |
| // files are stored in a subdirectory, add it to the request uri | |
| event.request.uri = metadata.s3.dir + baselessUri + v; | |
| setS3Origin(metadata.s3.domain); | |
| return; | |
| } catch (e) {} | |
| // Route to S3 custom 404 (no servers) | |
| if (metadata.custom404) { | |
| event.request.uri = metadata.s3.dir + metadata.custom404; | |
| setS3Origin(metadata.s3.domain); | |
| return; | |
| } | |
| // Route to image optimizer | |
| if (metadata.image && baselessUri.startsWith(metadata.image.pattern)) { | |
| setUrlOrigin(metadata.image.host); | |
| return; | |
| } | |
| // Route to servers | |
| if (metadata.servers){ | |
| event.request.headers["x-forwarded-host"] = event.request.headers.host; | |
| for (var key in event.request.querystring) { | |
| if (key.includes("/")) { | |
| event.request.querystring[encodeURIComponent(key)] = event.request.querystring[key]; | |
| delete event.request.querystring[key]; | |
| } | |
| } | |
| setNextjsGeoHeaders(); | |
| setNextjsCacheKey(); | |
| setUrlOrigin(findNearestServer(metadata.servers), metadata.origin); | |
| } | |
| function setNextjsGeoHeaders() { | |
| if(event.request.headers["cloudfront-viewer-city"]) { | |
| event.request.headers["x-open-next-city"] = event.request.headers["cloudfront-viewer-city"]; | |
| } | |
| if(event.request.headers["cloudfront-viewer-country"]) { | |
| event.request.headers["x-open-next-country"] = event.request.headers["cloudfront-viewer-country"]; | |
| } | |
| if(event.request.headers["cloudfront-viewer-region"]) { | |
| event.request.headers["x-open-next-region"] = event.request.headers["cloudfront-viewer-region"]; | |
| } | |
| if(event.request.headers["cloudfront-viewer-latitude"]) { | |
| event.request.headers["x-open-next-latitude"] = event.request.headers["cloudfront-viewer-latitude"]; | |
| } | |
| if(event.request.headers["cloudfront-viewer-longitude"]) { | |
| event.request.headers["x-open-next-longitude"] = event.request.headers["cloudfront-viewer-longitude"]; | |
| } | |
| } | |
| function setNextjsCacheKey() { | |
| var cacheKey = ""; | |
| if (event.request.uri.startsWith("/_next/image")) { | |
| cacheKey = getHeader("accept"); | |
| } else { | |
| cacheKey = | |
| getHeader("rsc") + | |
| getHeader("next-router-prefetch") + | |
| getHeader("next-router-state-tree") + | |
| getHeader("next-url") + | |
| getHeader("x-prerender-revalidate"); | |
| } | |
| if (event.request.cookies["__prerender_bypass"]) { | |
| cacheKey += event.request.cookies["__prerender_bypass"] | |
| ? event.request.cookies["__prerender_bypass"].value | |
| : ""; | |
| } | |
| var crypto = require("crypto"); | |
| var hashedKey = crypto.createHash("md5").update(cacheKey).digest("hex"); | |
| event.request.headers["x-open-next-cache-key"] = { value: hashedKey }; | |
| } | |
| function getHeader(key) { | |
| var header = event.request.headers[key]; | |
| if (header) { | |
| if (header.multiValue) { | |
| return header.multiValue.map((header) => header.value).join(","); | |
| } | |
| if (header.value) { | |
| return header.value; | |
| } | |
| } | |
| return ""; | |
| } | |
| function findNearestServer(servers) { | |
| if (servers.length === 1) return servers[0][0]; | |
| const h = event.request.headers; | |
| const lat = h["cloudfront-viewer-latitude"] && h["cloudfront-viewer-latitude"].value; | |
| const lon = h["cloudfront-viewer-longitude"] && h["cloudfront-viewer-longitude"].value; | |
| if (!lat || !lon) return servers[0][0]; | |
| return servers | |
| .map((s) => ({ | |
| distance: haversineDistance(lat, lon, s[1], s[2]), | |
| host: s[0], | |
| })) | |
| .sort((a, b) => a.distance - b.distance)[0] | |
| .host; | |
| } | |
| function haversineDistance(lat1, lon1, lat2, lon2) { | |
| const toRad = angle => angle * Math.PI / 180; | |
| const radLat1 = toRad(lat1); | |
| const radLat2 = toRad(lat2); | |
| const dLat = toRad(lat2 - lat1); | |
| const dLon = toRad(lon2 - lon1); | |
| const a = Math.sin(dLat / 2) ** 2 + Math.cos(radLat1) * Math.cos(radLat2) * Math.sin(dLon / 2) ** 2; | |
| return 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); | |
| } | |
| } | |
| const kvNamespace = "4105"; | |
| // Load metadata | |
| let metadata; | |
| try { | |
| const v = await cf.kvs().get(kvNamespace + ":metadata"); | |
| metadata = JSON.parse(v); | |
| } catch (e) {} | |
| await routeSite(kvNamespace, metadata); | |
| return event.request; | |
| } | |
| function setUrlOrigin(urlHost, override) { | |
| const origin = { | |
| domainName: urlHost, | |
| customOriginConfig: { | |
| port: 443, | |
| protocol: "https", | |
| sslProtocols: ["TLSv1.2"], | |
| }, | |
| originAccessControlConfig: { | |
| enabled: false, | |
| } | |
| }; | |
| override = override ?? {}; | |
| if (override.protocol === "http") { | |
| delete origin.customOriginConfig; | |
| } | |
| if (override.connectionAttempts) { | |
| origin.connectionAttempts = override.connectionAttempts; | |
| } | |
| if (override.timeouts) { | |
| origin.timeouts = override.timeouts; | |
| } | |
| cf.updateRequestOrigin(origin); | |
| } | |
| function setS3Origin(s3Domain, override) { | |
| const origin = { | |
| domainName: s3Domain, | |
| originAccessControlConfig: { | |
| enabled: true, | |
| signingBehavior: "always", | |
| signingProtocol: "sigv4", | |
| originType: "s3", | |
| } | |
| }; | |
| override = override ?? {}; | |
| if (override.connectionAttempts) { | |
| origin.connectionAttempts = override.connectionAttempts; | |
| } | |
| if (override.timeouts) { | |
| origin.timeouts = override.timeouts; | |
| } | |
| cf.updateRequestOrigin(origin); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment