"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.StaticHost = void 0;
const aws_cdk_lib_1 = require("aws-cdk-lib");
const constructs_1 = require("constructs");
const aws_ssm_1 = require("aws-cdk-lib/aws-ssm");
class StaticHost extends constructs_1.Construct {
    constructor(scope, id, props) {
        var _a;
        super(scope, id);
        this.inProps = props;
        if (this.inProps.createDns) {
            if (!this.inProps.hostedZoneTypes) {
                aws_cdk_lib_1.Annotations.of(this).addError('hostedZoneTypes is required when createDns is true');
                throw new Error('hostedZoneTypes is required when createDns is true');
            }
            if (this.inProps.hostedZoneTypes.length == 0) {
                aws_cdk_lib_1.Annotations.of(this).addError('hostedZoneTypes is required when createDns is true, and must contain at least one hostedZoneId');
                throw new Error('hostedZoneTypes is required when createDns is true, and must contain at least one hostedZoneId');
            }
        }
        const stack = aws_cdk_lib_1.Stack.of(this);
        this.hostname = `${props.hostnamePrefix}.${props.domainName}`;
        // Create buckets for holding logs and the site contents
        this.logBucket =
            props.logBucket ||
                new aws_cdk_lib_1.aws_s3.Bucket(this, 'LogBucket', {
                    accessControl: aws_cdk_lib_1.aws_s3.BucketAccessControl.LOG_DELIVERY_WRITE,
                    versioned: false,
                    removalPolicy: aws_cdk_lib_1.RemovalPolicy.DESTROY,
                    lifecycleRules: [
                        {
                            enabled: true,
                            expiration: aws_cdk_lib_1.Duration.days(90),
                            noncurrentVersionExpiration: aws_cdk_lib_1.Duration.days(1),
                        },
                    ],
                });
        this.bucket = new aws_cdk_lib_1.aws_s3.Bucket(this, 'SiteBucket', {
            serverAccessLogsBucket: this.logBucket,
            serverAccessLogsPrefix: `s3/${this.hostname}/`,
            removalPolicy: aws_cdk_lib_1.RemovalPolicy.DESTROY,
        });
        // Create OAI so CloudFront can access bucket files
        const oai = new aws_cdk_lib_1.aws_cloudfront.OriginAccessIdentity(this, 'OriginAccessIdentity', {
            comment: `Static assets in ${stack.stackName}`,
        });
        this.bucket.addToResourcePolicy(new aws_cdk_lib_1.aws_iam.PolicyStatement({
            effect: aws_cdk_lib_1.aws_iam.Effect.ALLOW,
            actions: ['s3:GetBucket*', 's3:List*', 's3:GetObject*'],
            resources: [this.bucket.bucketArn, this.bucket.bucketArn + '/*'],
            principals: [new aws_cdk_lib_1.aws_iam.CanonicalUserPrincipal(oai.cloudFrontOriginAccessIdentityS3CanonicalUserId)],
        }));
        // Allow edge lambdas to access bucket contents
        (_a = props.edgeLambdas) === null || _a === void 0 ? void 0 : _a.forEach(edgeLambda => {
            edgeLambda.function.addToRolePolicy(new aws_cdk_lib_1.aws_iam.PolicyStatement({
                resources: [this.bucket.bucketArn + '/*'],
                actions: ['s3:GetObject*'],
            }));
        });
        // Won't work right if it starts with slash. It's an easy mistake to make so just handle it here
        let indexPath = props.indexFilename || 'index.html';
        if (indexPath.startsWith('/')) {
            indexPath = indexPath.substring(1);
        }
        this.cloudfront = new aws_cdk_lib_1.aws_cloudfront.CloudFrontWebDistribution(this, 'Distribution', {
            comment: this.hostname,
            defaultRootObject: indexPath,
            errorConfigurations: props.errorConfig || [
                {
                    errorCode: 403,
                    responseCode: 403,
                    responsePagePath: '/' + indexPath,
                    errorCachingMinTtl: 3600,
                },
                {
                    errorCode: 404,
                    responseCode: 404,
                    responsePagePath: '/' + indexPath,
                    errorCachingMinTtl: 3600,
                },
            ],
            loggingConfig: {
                bucket: this.logBucket,
                includeCookies: true,
                prefix: `web/${this.hostname}/`,
            },
            originConfigs: [
                {
                    s3OriginSource: {
                        s3BucketSource: this.bucket,
                        originAccessIdentity: oai,
                    },
                    behaviors: props.edgeLambdas && props.edgeLambdas.length
                        ? props.edgeLambdas.map(item => item.behavior)
                        : [
                            {
                                allowedMethods: aws_cdk_lib_1.aws_cloudfront.CloudFrontAllowedMethods.GET_HEAD_OPTIONS,
                                compress: true,
                                isDefaultBehavior: true,
                            },
                        ],
                },
            ],
            viewerCertificate: aws_cdk_lib_1.aws_cloudfront.ViewerCertificate.fromAcmCertificate(props.websiteCertificate, {
                aliases: [this.hostname],
                securityPolicy: aws_cdk_lib_1.aws_cloudfront.SecurityPolicyProtocol.TLS_V1_2_2021,
                sslMethod: aws_cdk_lib_1.aws_cloudfront.SSLMethod.SNI,
            }),
            viewerProtocolPolicy: aws_cdk_lib_1.aws_cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
        });
        // Create DNS record (conditionally)
        if (props.createDns && props.hostedZoneTypes && props.hostedZoneTypes.length > 0) {
            for (const hostedZoneType of ['public', 'private']) {
                if (props.hostedZoneTypes.includes(hostedZoneType)) {
                    const hostedZoneIdPath = `/all/dns/${props.domainName}/${hostedZoneType}/zoneId`;
                    const hostedZoneId = aws_ssm_1.StringParameter.valueForStringParameter(this, hostedZoneIdPath);
                    new aws_cdk_lib_1.aws_route53.CnameRecord(this, `ServiceCNAME${hostedZoneType}`, {
                        recordName: this.hostname,
                        comment: this.hostname,
                        domainName: this.cloudfront.distributionDomainName,
                        zone: aws_cdk_lib_1.aws_route53.HostedZone.fromHostedZoneAttributes(this, `ImportedHostedZone${hostedZoneType}`, {
                            hostedZoneId: hostedZoneId,
                            zoneName: props.domainName,
                        }),
                        ttl: aws_cdk_lib_1.Duration.minutes(15),
                    });
                }
            }
        }
        this.bucketNameParam = new aws_cdk_lib_1.aws_ssm.StringParameter(this, 'BucketParameter', {
            parameterName: `/all/stacks/${stack.stackName}/site-bucket-name`,
            description: 'Bucket where the stack website deploys to.',
            stringValue: this.bucket.bucketName,
        });
        this.distributionIdParam = new aws_cdk_lib_1.aws_ssm.StringParameter(this, 'DistributionParameter', {
            parameterName: `/all/stacks/${stack.stackName}/distribution-id`,
            description: 'ID of the CloudFront distribution.',
            stringValue: this.cloudfront.distributionId,
        });
        new aws_cdk_lib_1.CfnOutput(this, 'DistributionDomainName', {
            value: this.cloudfront.distributionDomainName,
            description: 'The cloudfront distribution domain name.',
        });
    }
}
exports.StaticHost = StaticHost;
