constructor()

in cdk/lib/backend-stack.ts [21:108]


  constructor(scope: cdk.Construct, id: string, props: StackProps) {
    super(scope, id, props.cdkProps);

    // Lets start by creating the S3 bucket where we will store the captured clips.
    this.bucket = new s3.Bucket(this, "clips-lambda-bucket", {
      websiteIndexDocument: 'index.html',
    })

    // For Cloudfront to access the bucket, we need to create an Origin Access Identity that CloudFront uses to identify itself
    let oai = new cloudfront.OriginAccessIdentity(this, "clips-access");

    // Instead of exposing an S3 bucket publicly (bad), this creates a CloudFront distribution with an S3 Origin Access Identity.
    this.cfDistro = new cloudfront.CloudFrontWebDistribution(this, 'clips-cdn', {
      originConfigs: [{
        behaviors: [{ isDefaultBehavior: true }],
        s3OriginSource: {
          s3BucketSource: this.bucket,
          originAccessIdentity: oai,
        },
      }]
    });

    // clips depends on another published lambda layer for FFmpeg. Lets deploy this layer as a nested stack into our application.
    // Once that is done we capture the output layer version and create a new Lambda Layer based on it for our application.
    let ffmpegLayerSamApp = new sam.CfnApplication(this, 'FFmpegLayer', {
      location: {
        applicationId: 'arn:aws:serverlessrepo:us-east-1:145266761615:applications/ffmpeg-lambda-layer',
        semanticVersion: '1.0.0'
      }
    });

    let layerVersionArn = cdk.Stack.of(this).resolve(ffmpegLayerSamApp.getAtt('Outputs.LayerVersion'))
    let ffmpegLayer = lambda.LayerVersion.fromLayerVersionArn(this, 'FFmpegLayerVersion', layerVersionArn)

    // We can now go ahead and create the Lambda that will generate clips. We pass in the bucket and url so it can return the proper clip path.
    let lambdaFunction = new lambda.Function(this, "clips-backend", {
      runtime: lambda.Runtime.GO_1_X,
      memorySize: 512,
      timeout: cdk.Duration.minutes(5),
      handler: 'bin/clips',
      layers: [ffmpegLayer],
      code: lambda.Code.fromAsset('../bin/clips.zip'),
      environment: {
        "bucket": this.bucket.bucketName,
        "url": this.cfDistro.distributionDomainName
      }
    })

    // The Lambda function needs the ability to write files to S3, so lets grant it PUT access to our bucket.
    lambdaFunction.addToRolePolicy(new iam.PolicyStatement({
      sid: "clipswriteaccess",
      effect: iam.Effect.ALLOW,
      actions: [
        "s3:PutObject"
      ],
      resources: [
        this.bucket.bucketArn,
        this.bucket.arnForObjects("*"),
      ]
    }))

    // Our frontend needs a way of calling the Lambda function so lets use a API Gateway in front of the Lambda with a proxy integration for the routes.
    let gw = new apigateway.HttpApi(this, "clips-gateway", {
      apiName: "clips-gw",
    })

    gw.addRoutes({
      path: '/clip',
      methods: [ apigateway.HttpMethod.GET ],
      integration: new apigatewayintegrations.LambdaProxyIntegration({
        handler: lambdaFunction,
      }),
    });

    // Create the IVS channel
    this.channel = new ivs.CfnChannel(this, 'clip-channel', {
      latencyMode: 'LOW',
      name: "clip-channel",
    })

    this.streamkey = new ivs.CfnStreamKey(this, "clip-streamkey", {
      channelArn: this.channel.ref,
    })

    new cdk.CfnOutput(this, 'playbackUrl', { value: this.channel.attrPlaybackUrl });
    new cdk.CfnOutput(this, 'api', { value: gw.apiEndpoint });

  }