Skip to content

Commit

Permalink
many fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
revmischa committed Oct 12, 2022
1 parent 8846bbe commit 8038b88
Show file tree
Hide file tree
Showing 9 changed files with 6,044 additions and 5,978 deletions.
4 changes: 4 additions & 0 deletions .projen/deps.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions .projenrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const project = new awscdk.AwsCdkConstructLibrary({
'@types/micromatch',
'esbuild',
'aws-lambda',
'serverless-http',
] /* Runtime dependencies of this module. */,
// devDeps: ['[email protected]'] /* Build dependencies for this module. */,

Expand Down
22 changes: 22 additions & 0 deletions API.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion assets/lambda/NextJsHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,16 @@ const nextHandler = new NextNodeServer(config).getRequestHandler();
// wrap next request handler with serverless-http
// to translate from API Gateway v2 to next request/response
const server = slsHttp(
async (req: any, res: ServerResponse) => {
async (req, res: ServerResponse) => {
await nextHandler(req, res).catch((e) => {
console.error(`NextJS request failed due to:`);
console.error(e);

res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify(getErrMessage(e), null, 3));
});
console.log('req headers: ', req);
console.log('res headers: ', res.getHeaders());
},
{
binary: false,
Expand Down
4 changes: 2 additions & 2 deletions package.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

66 changes: 48 additions & 18 deletions src/Nextjs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import * as route53Targets from 'aws-cdk-lib/aws-route53-targets';
import * as s3 from 'aws-cdk-lib/aws-s3';
import { Construct } from 'constructs';
import * as fs from 'fs-extra';
import { NextJsAssetsDeployment, NextjsAssetsDeploymentProps } from './NextjsAssetsDeployment';
import { CompressionLevel, NextJsAssetsDeployment, NextjsAssetsDeploymentProps } from './NextjsAssetsDeployment';
import {
BaseSiteCdkDistributionProps,
BaseSiteDomainProps,
Expand Down Expand Up @@ -101,6 +101,13 @@ export interface NextjsProps extends NextjsBaseProps {
readonly waitForInvalidation?: boolean;

readonly stageName?: string;

/**
* 0 - no compression, fatest
* 9 - maximum compression, slowest
* @default 1
*/
readonly compressionLevel?: CompressionLevel;
}

/**
Expand Down Expand Up @@ -211,6 +218,7 @@ export class Nextjs extends Construct {
public originAccessIdentity: cloudfront.IOriginAccessIdentity;
public tempBuildDir: string;
public configBucket?: s3.Bucket;
public lambdaFunctionUrl!: lambda.FunctionUrl;

constructor(scope: Construct, id: string, props: NextjsProps) {
super(scope, id);
Expand All @@ -230,6 +238,7 @@ export class Nextjs extends Construct {
...props,
...props.cdk?.deployment,
nextBuild: this.nextBuild,
compressionLevel: props.compressionLevel,
});
this.bucket = this.assetsDeployment.bucket;

Expand Down Expand Up @@ -370,8 +379,20 @@ export class Nextjs extends Construct {
// origin request policies
const lambdaOriginRequestPolicy = cdk?.lambdaOriginRequestPolicy ?? this.createLambdaOriginRequestPolicy();

// main server function origin (lambda URL HTTP origin)
const fnUrl = this.serverFunction.addFunctionUrl({
authType: lambda.FunctionUrlAuthType.NONE,
});
this.lambdaFunctionUrl = fnUrl;
const serverFunctionOrigin = new origins.HttpOrigin(Fn.parseDomainName(fnUrl.url), {
customHeaders: {
// provide config to edge lambda function
'x-origin-url': fnUrl.url,
},
});

// lambda behavior edge function
const lambdaOriginRequestEdgeFn = this.buildDefaultOriginRequestEdgeFunction();
const lambdaOriginRequestEdgeFn = this.buildLambdaOriginRequestEdgeFunction();
const lambdaOriginRequestEdgeFnVersion = lambda.Version.fromVersionArn(
this,
'Version',
Expand All @@ -385,17 +406,6 @@ export class Nextjs extends Construct {
},
];

// main server function origin (lambda URL HTTP origin)
const fnUrl = this.serverFunction.addFunctionUrl({
authType: lambda.FunctionUrlAuthType.NONE,
});
const serverFunctionOrigin = new origins.HttpOrigin(Fn.parseDomainName(fnUrl.url), {
// TODO: useful stuff here?
// customHeaders: {
// host: fnUrl.
// },
});

// default handler for requests that don't match any other path:
// - try S3 first
// - if 403, fall back to lambda handler (mostly for /)
Expand Down Expand Up @@ -550,7 +560,16 @@ export class Nextjs extends Construct {
return domainNames;
}

private buildDefaultOriginRequestEdgeFunction() {
/**
* Create an edge function to handle requests to the lambda server handler origin.
* It overrides the host header in the request to be the lambda URL's host.
* It's needed because we forward all headers to the origin, but the origin is itself an
* HTTP server so it needs the host header to be the address of the lambda and not
* the distribution.
*
* It also sets
*/
private buildLambdaOriginRequestEdgeFunction() {
const app = App.of(this) as App;

// ridiculous error: The function execution role must be assumable with edgelambda.amazonaws.com as well as lambda.amazonaws.com principals.
Expand All @@ -574,11 +593,22 @@ export class Nextjs extends Construct {
handler: 'index.handler',
// code: lambda.Code.fromAsset(path.join(__dirname, '..', 'assets', 'lambda@edge', 'DefaultOriginRequest')),
code: lambda.Code.fromInline(`
const URL = require('url');
exports.handler = (event, context, callback) => {
console.log(JSON.stringify(event, null, 2))
const request = event.Records[0].cf.request;
console.log(JSON.stringify(request, null, 2))
// get origin url from header
const originUrlHeader = request.headers['x-origin-url']
if (!originUrlHeader || !originUrlHeader[0]) {
console.error('Origin header wasn"t set correctly, cannot get origin url')
return callback(null, request)
}
const originUrl = new URL(originUrlHeader[0].value);
request.headers['x-forwarded-host'] = [ { key: 'x-forwarded-host', value: request.headers.host[0].value } ]
request.headers['host'] = [ { key: 'host', value: 'p3t2xocqdls5e5ilvgpcar7wge0glzky.lambda-url.us-west-2.on.aws' } ]
request.headers['host'] = [ { key: 'host', value: new URL(originUrl).host } ]
callback(null, request);
};
`),
Expand All @@ -589,8 +619,8 @@ export class Nextjs extends Construct {
stackId:
`Nextjs-${this.props.stageName || app.stageName || 'default'}-EdgeFunctions-` + this.node.addr.substring(0, 5),
});
fn.grantInvoke(new ServicePrincipal('lambda.amazonaws.com'));
fn.grantInvoke(new ServicePrincipal('edgelambda.amazonaws.com'));
// fn.grantInvoke(new ServicePrincipal('lambda.amazonaws.com'));
// fn.grantInvoke(new ServicePrincipal('edgelambda.amazonaws.com'));
fn.currentVersion.grantInvoke(new ServicePrincipal('edgelambda.amazonaws.com'));
fn.currentVersion.grantInvoke(new ServicePrincipal('lambda.amazonaws.com'));

Expand Down
5 changes: 5 additions & 0 deletions src/NextjsAssetsDeployment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import * as micromatch from 'micromatch';
import { NextjsBaseProps } from './NextjsBase';
import { createArchive, makeTokenPlaceholder, NextjsBuild, replaceTokenGlobs } from './NextjsBuild';

export type CompressionLevel = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;

export interface NextjsAssetsDeploymentProps extends NextjsBaseProps {
/**
* The `NextjsBuild` instance representing the built Nextjs application.
Expand All @@ -34,6 +36,8 @@ export interface NextjsAssetsDeploymentProps extends NextjsBaseProps {
* Recommended to only set to true if you don't need the ability to roll back deployments.
*/
// readonly prune?: boolean;

readonly compressionLevel?: CompressionLevel;
}

// interface EnvReplaceValues {
Expand Down Expand Up @@ -99,6 +103,7 @@ export class NextJsAssetsDeployment extends Construct {
zipFileName: 'public.zip',
fileGlob: '*',
zipOutDir: this.props.nextBuild.tempBuildDir,
compressionLevel: this.props.compressionLevel,
});

// upload public files to root of S3 bucket
Expand Down
13 changes: 10 additions & 3 deletions src/NextjsBuild.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Token } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as spawn from 'cross-spawn';
import * as fs from 'fs-extra';
import { listDirectory } from './NextjsAssetsDeployment';
import { CompressionLevel, listDirectory } from './NextjsAssetsDeployment';
import { NextjsBaseProps } from './NextjsBase';

const NEXTJS_BUILD_DIR = '.next';
Expand Down Expand Up @@ -215,14 +215,21 @@ export class NextjsBuild extends Construct {
}

export interface CreateArchiveArgs {
readonly compressionLevel?: CompressionLevel;
readonly directory: string;
readonly zipFileName: string;
readonly zipOutDir: string;
readonly fileGlob?: string;
}

// zip up a directory and return path to zip file
export function createArchive({ directory, zipFileName, zipOutDir, fileGlob = '*' }: CreateArchiveArgs): string {
export function createArchive({
directory,
zipFileName,
zipOutDir,
fileGlob = '*',
compressionLevel = 1,
}: CreateArchiveArgs): string {
zipOutDir = path.resolve(zipOutDir);
// get output path
fs.removeSync(zipOutDir);
Expand All @@ -232,7 +239,7 @@ export function createArchive({ directory, zipFileName, zipOutDir, fileGlob = '*
// run script to create zipfile, preserving symlinks for node_modules (e.g. pnpm structure)
const result = spawn.sync(
'bash', // getting ENOENT when specifying 'node' here for some reason
['-xc', [`cd '${directory}'`, `zip -ryq2 '${zipFilePath}' ${fileGlob}`].join('&&')],
['-xc', [`cd '${directory}'`, `zip -ryq${compressionLevel} '${zipFilePath}' ${fileGlob}`].join('&&')],
{ stdio: 'inherit' }
);
if (result.status !== 0) {
Expand Down
Loading

0 comments on commit 8038b88

Please sign in to comment.