Skip to content

Commit e383fd2

Browse files
Merge pull request #40 from DBB-Software/feat/PLATFORM-1377-next-image
feat: added support of next image route
2 parents 4ec562c + 7561483 commit e383fd2

File tree

5 files changed

+44
-11
lines changed

5 files changed

+44
-11
lines changed

src/build/next.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import childProcess from 'node:child_process'
22
import fs from 'node:fs'
33
import path from 'node:path'
4-
import { type ProjectPackager, type ProjectSettings } from '../common/project'
4+
import { type ProjectPackager, type ProjectSettings, loadFile } from '../common/project'
55
import loadConfig from '../commands/helpers/loadConfig'
66

77
interface BuildOptions {
@@ -18,12 +18,12 @@ interface BuildAppOptions {
1818

1919
export const OUTPUT_FOLDER = 'serverless-next'
2020

21-
const setNextOptions = async (nextConfig: string, s3BucketName: string) => {
21+
const setNextOptions = async (nextConfigPath: string, s3BucketName: string) => {
2222
// set s3 bucket name for cache handler during build time
2323
process.env.STATIC_BUCKET_NAME = s3BucketName
2424

2525
const cacheConfig = await loadConfig()
26-
const currentConfig = await import(nextConfig).then((r) => r.default)
26+
const currentConfig = await loadFile(nextConfigPath)
2727
const updatedConfig = {
2828
...currentConfig,
2929
output: 'standalone',
@@ -34,20 +34,20 @@ const setNextOptions = async (nextConfig: string, s3BucketName: string) => {
3434
cacheHandler: require.resolve(path.join('..', 'cacheHandler', 'index.js'))
3535
}
3636

37-
const currentContent = fs.readFileSync(nextConfig, 'utf-8')
37+
const currentContent = fs.readFileSync(nextConfigPath, 'utf-8')
3838

3939
let updatedContent = `module.exports = ${JSON.stringify(updatedConfig, null, 4)};\n`
4040

4141
// Check if the file has .mjs extension
42-
if (nextConfig.endsWith('.mjs')) {
42+
if (nextConfigPath.endsWith('.mjs')) {
4343
updatedContent = `export default ${JSON.stringify(updatedConfig, null, 4)};\n`
4444
}
4545

46-
fs.writeFileSync(nextConfig, updatedContent, 'utf-8')
46+
fs.writeFileSync(nextConfigPath, updatedContent, 'utf-8')
4747

4848
// Function to revert back to original content of file
4949
return () => {
50-
fs.writeFileSync(nextConfig, currentContent, 'utf-8')
50+
fs.writeFileSync(nextConfigPath, currentContent, 'utf-8')
5151
}
5252
}
5353

src/cdk/constructs/CloudFrontDistribution.ts

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,23 @@ interface CloudFrontPropsDistribution {
1313
requestEdgeFunction: cloudfront.experimental.EdgeFunction
1414
responseEdgeFunction: cloudfront.experimental.EdgeFunction
1515
cacheConfig: CacheConfig
16+
imageTTL?: number
1617
}
1718

19+
const OneDayCache = Duration.days(1)
1820
const OneMonthCache = Duration.days(30)
1921
const NoCache = Duration.seconds(0)
22+
2023
const defaultNextQueries = ['_rsc']
2124
const defaultNextHeaders = ['Cache-Control']
25+
const imageQueries = ['w', 'h', 'url', 'q']
2226
export class CloudFrontDistribution extends Construct {
2327
public readonly cf: cloudfront.Distribution
2428

2529
constructor(scope: Construct, id: string, props: CloudFrontPropsDistribution) {
2630
super(scope, id)
2731

28-
const { staticBucket, requestEdgeFunction, responseEdgeFunction, cacheConfig } = props
32+
const { staticBucket, requestEdgeFunction, responseEdgeFunction, cacheConfig, ebAppDomain, imageTTL } = props
2933

3034
const splitCachePolicy = new cloudfront.CachePolicy(this, 'SplitCachePolicy', {
3135
cachePolicyName: `${id}-SplitCachePolicy`,
@@ -53,7 +57,23 @@ export class CloudFrontDistribution extends Construct {
5357
minTtl: OneMonthCache
5458
})
5559

60+
const imageTTLValue = imageTTL ? Duration.seconds(imageTTL) : OneDayCache
61+
62+
const imageCachePolicy = new cloudfront.CachePolicy(this, 'ImageCachePolicy', {
63+
cachePolicyName: `${id}-ImageCachePolicy`,
64+
queryStringBehavior: cloudfront.CacheQueryStringBehavior.allowList(...imageQueries),
65+
cookieBehavior: cloudfront.CacheCookieBehavior.none(),
66+
headerBehavior: cloudfront.CacheHeaderBehavior.allowList(...defaultNextHeaders),
67+
defaultTtl: imageTTLValue,
68+
maxTtl: imageTTLValue,
69+
minTtl: imageTTLValue
70+
})
71+
5672
const s3Origin = new origins.S3Origin(staticBucket)
73+
const nextServerOrigin = new origins.HttpOrigin(ebAppDomain, {
74+
protocolPolicy: cloudfront.OriginProtocolPolicy.HTTP_ONLY,
75+
httpPort: 80
76+
})
5777

5878
this.cf = new cloudfront.Distribution(this, id, {
5979
defaultBehavior: {
@@ -82,6 +102,10 @@ export class CloudFrontDistribution extends Construct {
82102
],
83103
cachePolicy: splitCachePolicy
84104
},
105+
'/_next/image*': {
106+
origin: nextServerOrigin,
107+
cachePolicy: imageCachePolicy
108+
},
85109
'/_next/*': {
86110
origin: s3Origin,
87111
cachePolicy: longCachePolicy

src/cdk/stacks/NextCloudfrontStack.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export interface NextCloudfrontStackProps extends StackProps {
1313
ebAppDomain: string
1414
buildOutputPath: string
1515
cacheConfig: CacheConfig
16+
imageTTL?: number
1617
}
1718

1819
export class NextCloudfrontStack extends Stack {
@@ -22,7 +23,7 @@ export class NextCloudfrontStack extends Stack {
2223

2324
constructor(scope: Construct, id: string, props: NextCloudfrontStackProps) {
2425
super(scope, id, props)
25-
const { nodejs, buildOutputPath, staticBucketName, ebAppDomain, region, cacheConfig } = props
26+
const { nodejs, buildOutputPath, staticBucketName, ebAppDomain, region, cacheConfig, imageTTL } = props
2627

2728
this.routingLambdaEdge = new RoutingLambdaEdge(this, `${id}-RoutingLambdaEdge`, {
2829
nodejs,
@@ -52,7 +53,8 @@ export class NextCloudfrontStack extends Stack {
5253
ebAppDomain,
5354
requestEdgeFunction: this.routingLambdaEdge.lambdaEdge,
5455
responseEdgeFunction: this.checkExpLambdaEdge.lambdaEdge,
55-
cacheConfig
56+
cacheConfig,
57+
imageTTL
5658
})
5759

5860
staticBucket.grantRead(this.routingLambdaEdge.lambdaEdge)

src/commands/deploy.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { NextRenderServerStack, type NextRenderServerStackProps } from '../cdk/s
99
import { NextCloudfrontStack, type NextCloudfrontStackProps } from '../cdk/stacks/NextCloudfrontStack'
1010
import { getAWSCredentials, uploadFolderToS3, uploadFileToS3, AWS_EDGE_REGION, emptyBucket } from '../common/aws'
1111
import { AppStack } from '../common/cdk'
12-
import { getProjectSettings } from '../common/project'
12+
import { getProjectSettings, loadFile } from '../common/project'
1313
import loadConfig from './helpers/loadConfig'
1414

1515
export interface DeployConfig {
@@ -73,6 +73,8 @@ export const deploy = async (config: DeployConfig) => {
7373

7474
const cacheConfig = await loadConfig()
7575

76+
const nextConfig = await loadFile(projectSettings.nextConfigPath)
77+
7678
const outputPath = createOutputFolder()
7779

7880
const clientAWSCredentials = {
@@ -130,6 +132,7 @@ export const deploy = async (config: DeployConfig) => {
130132
crossRegionReferences: true,
131133
region,
132134
cacheConfig,
135+
imageTTL: nextConfig.imageTTL,
133136
env: {
134137
region: AWS_EDGE_REGION // required since Edge can be deployed only here.
135138
}

src/common/project.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,7 @@ export const getProjectSettings = (projectPath: string): ProjectSettings | undef
5353
currentPath = path.dirname(currentPath)
5454
}
5555
}
56+
57+
export const loadFile = async (filePath: string) => {
58+
return import(filePath).then((r) => r.default)
59+
}

0 commit comments

Comments
 (0)