Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/build/next.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ const copyAssets = async (outputPath: string, appPath: string, appRelativePath:
recursive: true
}
)
await fs.cp(path.join(appPath, 'public'), path.join(outputPath, '.next', 'standalone', appRelativePath, 'public'), {
recursive: true
})
}

const getRewritesConfig = (manifestRules: RoutesManifest['rewrites']): NextRewrites => {
Expand Down
6 changes: 4 additions & 2 deletions src/cacheHandler/strategy/s3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ export class S3Cache implements CacheStrategy {
})
}
const input: PutObjectCommandInput = { ...baseInput }
const tagsValue = [headersTags, this.buildTagKeys(data.tags)].filter(Boolean).join('&')

const promises = [
this.#dynamoDBClient.putItem({
Expand All @@ -86,8 +87,9 @@ export class S3Cache implements CacheStrategy {
pageKey: { S: pageKey },
cacheKey: { S: cacheKey },
s3Key: { S: baseInput.Key! },
tags: { S: [headersTags, this.buildTagKeys(data.tags)].filter(Boolean).join('&') },
createdAt: { S: new Date().toISOString() }
createdAt: { S: new Date().toISOString() },
// TODO: check for empty tags
...(tagsValue && { tags: { S: tagsValue } })
}
})
]
Expand Down
55 changes: 42 additions & 13 deletions src/cdk/constructs/CloudFrontDistribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import * as cloudfront from 'aws-cdk-lib/aws-cloudfront'
import * as s3 from 'aws-cdk-lib/aws-s3'
import * as origins from 'aws-cdk-lib/aws-cloudfront-origins'
import { addOutput } from '../../common/cdk'
import { CacheConfig } from '../../types'
import { DeployConfig } from '../../types'
import { HEADER_DEVICE_TYPE } from '../../constants'

interface CloudFrontPropsDistribution {
Expand All @@ -13,7 +13,7 @@ interface CloudFrontPropsDistribution {
requestEdgeFunction: cloudfront.experimental.EdgeFunction
viewerResponseEdgeFunction: cloudfront.experimental.EdgeFunction
viewerRequestLambdaEdge: cloudfront.experimental.EdgeFunction
cacheConfig: CacheConfig
deployConfig: DeployConfig
imageTTL?: number
}

Expand All @@ -35,25 +35,27 @@ export class CloudFrontDistribution extends Construct {
requestEdgeFunction,
viewerResponseEdgeFunction,
viewerRequestLambdaEdge,
cacheConfig,
deployConfig,
renderServerDomain,
imageTTL
} = props

const splitCachePolicy = new cloudfront.CachePolicy(this, 'SplitCachePolicy', {
cachePolicyName: `${id}-SplitCachePolicy`,
queryStringBehavior: cloudfront.CacheQueryStringBehavior.allowList(
...defaultNextQueries.concat(cacheConfig.cacheQueries ?? [])
...defaultNextQueries.concat(deployConfig.cache.cacheQueries ?? [])
),
cookieBehavior: cacheConfig.cacheCookies?.length
? cloudfront.CacheCookieBehavior.allowList(...cacheConfig.cacheCookies)
cookieBehavior: deployConfig.cache.cacheCookies?.length
? cloudfront.CacheCookieBehavior.allowList(...deployConfig.cache.cacheCookies)
: cloudfront.CacheCookieBehavior.none(),
headerBehavior: cloudfront.CacheHeaderBehavior.allowList(
...defaultNextHeaders,
...Object.values(HEADER_DEVICE_TYPE)
),
minTtl: NoCache,
defaultTtl: NoCache // no caching by default, cache value is going to be used from Cache-Control header.
defaultTtl: NoCache, // no caching by default, cache value is going to be used from Cache-Control header.
enableAcceptEncodingBrotli: true,
enableAcceptEncodingGzip: true
})

const longCachePolicy = new cloudfront.CachePolicy(this, 'LongCachePolicy', {
Expand All @@ -63,7 +65,9 @@ export class CloudFrontDistribution extends Construct {
headerBehavior: cloudfront.CacheHeaderBehavior.none(),
defaultTtl: OneMonthCache,
maxTtl: OneMonthCache,
minTtl: OneMonthCache
minTtl: OneMonthCache,
enableAcceptEncodingBrotli: true,
enableAcceptEncodingGzip: true
})

const imageTTLValue = imageTTL ? Duration.seconds(imageTTL) : OneDayCache
Expand All @@ -75,10 +79,24 @@ export class CloudFrontDistribution extends Construct {
headerBehavior: cloudfront.CacheHeaderBehavior.allowList(...defaultNextHeaders),
defaultTtl: imageTTLValue,
maxTtl: imageTTLValue,
minTtl: imageTTLValue
minTtl: imageTTLValue,
enableAcceptEncodingBrotli: true,
enableAcceptEncodingGzip: true
})

const publicAssetsCachePolicy = new cloudfront.CachePolicy(this, 'PublicAssetsCachePolicy', {
cachePolicyName: `${id}-PublicAssetsCachePolicy`,
defaultTtl: deployConfig.publicAssets?.ttl ? Duration.seconds(deployConfig.publicAssets.ttl) : NoCache,
maxTtl: deployConfig.publicAssets?.ttl ? Duration.seconds(deployConfig.publicAssets.ttl) : NoCache,
minTtl: deployConfig.publicAssets?.ttl ? Duration.seconds(deployConfig.publicAssets.ttl) : NoCache,
enableAcceptEncodingBrotli: true,
enableAcceptEncodingGzip: true
})

const s3Origin = new origins.S3Origin(staticBucket)
const publicFolderS3Origin = new origins.S3Origin(staticBucket, {
originPath: '/public'
})
const nextServerOrigin = new origins.HttpOrigin(renderServerDomain, {
protocolPolicy: cloudfront.OriginProtocolPolicy.HTTP_ONLY,
httpPort: 80
Expand All @@ -101,7 +119,8 @@ export class CloudFrontDistribution extends Construct {
eventType: cloudfront.LambdaEdgeEventType.VIEWER_REQUEST
}
],
cachePolicy: splitCachePolicy
cachePolicy: splitCachePolicy,
compress: true
},
defaultRootObject: '',
additionalBehaviors: {
Expand All @@ -113,16 +132,26 @@ export class CloudFrontDistribution extends Construct {
eventType: cloudfront.LambdaEdgeEventType.ORIGIN_REQUEST
}
],
cachePolicy: splitCachePolicy
cachePolicy: splitCachePolicy,
compress: true
},
'/_next/image*': {
origin: nextServerOrigin,
cachePolicy: imageCachePolicy
},
'/_next/*': {
origin: s3Origin,
cachePolicy: longCachePolicy
}
cachePolicy: longCachePolicy,
compress: true
},
...(deployConfig.publicAssets
? {
[`${deployConfig.publicAssets.prefix}/*`]: {
origin: publicFolderS3Origin,
cachePolicy: publicAssetsCachePolicy
}
}
: {})
}
})

Expand Down
2 changes: 1 addition & 1 deletion src/cdk/stacks/NextCloudfrontStack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ export class NextCloudfrontStack extends Stack {
requestEdgeFunction: this.originRequestLambdaEdge.lambdaEdge,
viewerResponseEdgeFunction: this.viewerResponseLambdaEdge.lambdaEdge,
viewerRequestLambdaEdge: this.viewerRequestLambdaEdge.lambdaEdge,
cacheConfig: deployConfig.cache,
deployConfig: deployConfig,
imageTTL
})

Expand Down
12 changes: 12 additions & 0 deletions src/commands/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,18 @@ export const deploy = async (config: DeployConfig) => {
folderRootPath: path.join(outputPath, '.next', 'static')
})

await uploadFolderToS3(s3Client, {
Bucket: nextRenderServerStackOutput.StaticBucketName,
Key: 'public',
folderRootPath: path.join(
outputPath,
'.next',
'standalone',
path.relative(projectSettings.root, projectSettings.projectPath),
'public'
)
})

// upload code version to bucket.
await uploadFileToS3(s3Client, {
Bucket: nextRenderServerStackOutput.RenderServerVersionsBucketName,
Expand Down
4 changes: 4 additions & 0 deletions src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ export interface CacheConfig {

export interface DeployConfig {
cache: CacheConfig
publicAssets?: {
prefix: string
ttl?: number
}
}

export type NextRedirects = Awaited<ReturnType<Required<NextConfig>['redirects']>>
Expand Down
Loading