in packages/infra/lib/website-construct.ts [28:150]
constructor(scope: Construct, id: string, props: WebsiteConstructProps) {
super(scope, id);
const cloudFrontOia = new cloudfront.CfnCloudFrontOriginAccessIdentity(this, 'OIA', {
cloudFrontOriginAccessIdentityConfig: {
comment: 'OIA for website.',
},
});
const originAccessIdentity = cloudfront.OriginAccessIdentity.fromOriginAccessIdentityName(
this,
'OriginAccessIdentity',
cloudFrontOia.ref
);
const sourceBucket = new s3.Bucket(this, 'WebsiteBucket', {
websiteIndexDocument: 'index.html',
encryption: s3.BucketEncryption.S3_MANAGED,
blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
enforceSSL: true,
});
const cloudFrontLoggingBucket = new s3.Bucket(this, 'S3BucketForWebsiteLogging', {
blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
encryption: s3.BucketEncryption.S3_MANAGED,
});
const cloudFrontDistribution = new cloudfront.CloudFrontWebDistribution(this, 'WebsiteCloudFront', {
priceClass: cloudfront.PriceClass.PRICE_CLASS_ALL,
defaultRootObject: 'index.html',
viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
loggingConfig: {
bucket: cloudFrontLoggingBucket,
},
originConfigs: [
{
s3OriginSource: {
s3BucketSource: sourceBucket,
originAccessIdentity,
},
behaviors: [{ isDefaultBehavior: true }],
},
],
errorConfigurations: [
{
errorCode: 404,
responseCode: 200,
responsePagePath: `/index.html`,
errorCachingMinTtl: 0,
},
],
});
/**S3 bucket storing the dynamically generated index files.*/
const websiteIndexBucket = new s3.Bucket(this, 'WebsiteIndexBucket', {
blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
encryption: s3.BucketEncryption.S3_MANAGED,
enforceSSL: true,
});
const buildWebsiteIndexFunction = new lambdaNodeJs.NodejsFunction(this, 'BuildWebsiteIndexFunction', {
runtime: lambda.Runtime.NODEJS_14_X,
handler: 'handler',
entry: path.join(__dirname, '../../website-index-builder/src/index.ts'),
timeout: Duration.minutes(1),
reservedConcurrentExecutions: 1,
});
websiteIndexBucket.grantReadWrite(buildWebsiteIndexFunction);
const buildWebsiteIndexCustomResourceProvider = new customResource.Provider(
this,
'BuildWebsiteIndexCustomResourceProvider',
{
onEventHandler: buildWebsiteIndexFunction,
logRetention: logs.RetentionDays.ONE_DAY,
}
);
const indexFile = fs.readFileSync(path.join(props.websiteDistPath, 'index.html'), 'utf8');
const buildWebsiteIndexCustomResource = new CustomResource(this, 'BuildWebsiteIndexCustomResource', {
serviceToken: buildWebsiteIndexCustomResourceProvider.serviceToken,
properties: {
s3BucketName: websiteIndexBucket.bucketName,
template: indexFile,
apiUrl: props.api.url,
},
});
buildWebsiteIndexCustomResource.node.addDependency(websiteIndexBucket, props.api);
const cachedDeployment = new s3Deployment.BucketDeployment(this, 'CachedDeployWebsite', {
sources: [
s3Deployment.Source.asset(props.websiteDistPath, {
exclude: ['index.html', 'config.js', 'config.*.js'],
}),
],
prune: false,
destinationBucket: sourceBucket,
distribution: cloudFrontDistribution,
});
const uncachedDeployment = new s3Deployment.BucketDeployment(this, 'UncachedDeployWebsite', {
sources: [s3Deployment.Source.bucket(websiteIndexBucket, buildWebsiteIndexCustomResource.ref)],
destinationBucket: sourceBucket,
prune: false,
distribution: cloudFrontDistribution,
cacheControl: [s3Deployment.CacheControl.noCache()],
});
uncachedDeployment.node.addDependency(cachedDeployment, buildWebsiteIndexCustomResource);
new CfnOutput(this, 'WebsiteCloudfrontDistributionId', {
value: cloudFrontDistribution.distributionId,
exportName: `WebsiteCloudfrontDistributionId`,
});
new CfnOutput(this, 'CloudfrontDistributionDomainName', {
value: cloudFrontDistribution.distributionDomainName,
exportName: `WebsiteCloudfrontDistributionDomainName`,
});
}