cdk/lib/gudocs.ts (176 lines of code) (raw):

import { GuApiGatewayWithLambdaByPath, GuScheduledLambda } from '@guardian/cdk'; import { GuCertificate } from '@guardian/cdk/lib/constructs/acm'; import type { GuStackProps } from '@guardian/cdk/lib/constructs/core'; import { GuStack, GuStringParameter } from '@guardian/cdk/lib/constructs/core'; import { GuCname } from '@guardian/cdk/lib/constructs/dns/dns-records'; import { GuVpc } from '@guardian/cdk/lib/constructs/ec2/vpc' import { GuLambdaFunction } from '@guardian/cdk/lib/constructs/lambda'; import { type App, Duration } from 'aws-cdk-lib'; import { CfnBasePathMapping, CfnDomainName } from 'aws-cdk-lib/aws-apigateway'; import {AttributeType, Table} from "aws-cdk-lib/aws-dynamodb"; import { Schedule } from 'aws-cdk-lib/aws-events'; import { PolicyStatement } from 'aws-cdk-lib/aws-iam'; import { Runtime } from 'aws-cdk-lib/aws-lambda'; const APP_NAME = 'gudocs'; interface GuDocsStackProps extends GuStackProps { domainName: string; } interface GuDocsCertificateStackProps extends GuStackProps { domainName: string; } export class GuDocsCertificate extends GuStack { constructor(scope: App, id: string, props: GuDocsCertificateStackProps) { super(scope, id, props); new GuCertificate(this, { domainName: props.domainName, app: "gudocs-cloudfront", }); } } export class GuDocs extends GuStack { constructor( scope: App, id: string, props: GuDocsStackProps ) { super(scope, id, props); const app = "gudocs"; const vpc = GuVpc.fromIdParameter(this, "vpc"); const runtime = Runtime.NODEJS_18_X; const fileName = "gudocs2.zip"; const environment = { "Stage": this.stage, }; const vpcSubnets = { subnets: GuVpc.subnetsFromParameter(this), }; const sharedLambdaProps = { runtime, fileName, vpc, vpcSubnets, environment, }; const s3BucketName = new GuStringParameter(this, 'S3BucketName', { fromSSM: true, default: `/${this.stage}/interactives/gudocs/s3bucket`, description: "The bucket used to store docs" }); const s3BucketPolicy = new PolicyStatement({ actions: [ "s3:PutObject", "s3:PutObjectAcl", ], resources: [ `arn:aws:s3:::${s3BucketName.valueAsString}/*`, ], }) const permissionsBucketPolicy = new PolicyStatement({ actions: [ "s3:GetObject", ], resources: [ `arn:aws:s3:::permissions-cache/${this.stage}/*`, ], }) const sharedParametersPolicy = new PolicyStatement({ actions: [ "ssm:GetParametersByPath", "ssm:GetParameters", "ssm:GetParameter" ], resources: [ `arn:aws:ssm:${this.region}:${this.account}:parameter/${this.stage}/${this.stack}/${app}/*`, ], }) const serverlessExpressLambda = new GuLambdaFunction(this, "serverless-express", { handler: "index.handler", functionName: `gudocs-serverless-express-${this.stage}`, app: `${app}-serverless-express`, ...sharedLambdaProps, }); serverlessExpressLambda.addToRolePolicy(s3BucketPolicy) serverlessExpressLambda.addToRolePolicy(sharedParametersPolicy) serverlessExpressLambda.addToRolePolicy(permissionsBucketPolicy) const gateway = new GuApiGatewayWithLambdaByPath(this, { app: "testing", monitoringConfiguration: { noMonitoring: true }, targets: [ { path: "/", httpMethod: "GET", lambda: serverlessExpressLambda, }, { path: "/publish", httpMethod: "POST", lambda: serverlessExpressLambda, }, { path: "/legacy", httpMethod: "POST", lambda: serverlessExpressLambda, }, ], }); const cloudFrontCertificateArn = new GuStringParameter(this, 'CloudFrontCertificateArn', { fromSSM: true, default: `/INFRA/${props.domainName}/cloudFrontCertificateArn`, description: "The ARN of the certificate for the Cloudfront distribution. Must be created in us-east-1." }); const cfnDomainName = new CfnDomainName(this, 'DomainName', { domainName: props.domainName, certificateArn: cloudFrontCertificateArn.valueAsString, endpointConfiguration: { types: ['EDGE'], }, }); new CfnBasePathMapping(this, 'BasePathMapping', { domainName: cfnDomainName.ref, restApiId: gateway.api.restApiId, stage: gateway.api.deploymentStage.stageName, }); new GuCname(this, 'CnameApiRecord', { domainName: props.domainName, app, resourceRecord: cfnDomainName.attrDistributionDomainName, ttl: Duration.minutes(1), }); const scheduledLambda = new GuScheduledLambda(this, APP_NAME, { handler: 'index.scheduleHandler', functionName: `gudocs-schedule-${this.stage}`, rules: [ { schedule: Schedule.cron({ }), }, ], monitoringConfiguration: { noMonitoring: true, // todo }, app: `${app}-schedule`, ...sharedLambdaProps, }); scheduledLambda.addToRolePolicy(s3BucketPolicy) scheduledLambda.addToRolePolicy(sharedParametersPolicy) const table = new Table(this, 'Table', { tableName: `${this.stack}-${this.stage}-${app}`, partitionKey: { name: 'key', type: AttributeType.STRING, }, }); table.addGlobalSecondaryIndex({ indexName: 'last-modified', partitionKey: { name: 'type', type: AttributeType.STRING, }, sortKey: { name: 'lastModified', type: AttributeType.NUMBER, } }); table.grantReadWriteData(scheduledLambda); table.grantReadWriteData(serverlessExpressLambda); } }