learnlytics-go/templ/examples/counter/cdk/stack.go
2025-03-20 12:35:13 +01:00

137 lines
5.3 KiB
Go

package main
import (
"fmt"
"github.com/aws/aws-cdk-go/awscdk/v2"
"github.com/aws/aws-cdk-go/awscdk/v2/awscloudfront"
"github.com/aws/aws-cdk-go/awscdk/v2/awscloudfrontorigins"
"github.com/aws/aws-cdk-go/awscdk/v2/awsdynamodb"
"github.com/aws/aws-cdk-go/awscdk/v2/awsiam"
"github.com/aws/aws-cdk-go/awscdk/v2/awslambda"
"github.com/aws/aws-cdk-go/awscdk/v2/awss3"
"github.com/aws/aws-cdk-go/awscdk/v2/awss3deployment"
awslambdago "github.com/aws/aws-cdk-go/awscdklambdagoalpha/v2"
"github.com/aws/constructs-go/constructs/v10"
"github.com/aws/jsii-runtime-go"
)
type CounterStackProps struct {
awscdk.StackProps
}
func NewCounterStack(scope constructs.Construct, id string, props *CounterStackProps) awscdk.Stack {
var sprops awscdk.StackProps
if props != nil {
sprops = props.StackProps
}
stack := awscdk.NewStack(scope, &id, &sprops)
// Create a global count database.
db := awsdynamodb.NewTable(stack, jsii.String("count"), &awsdynamodb.TableProps{
PartitionKey: &awsdynamodb.Attribute{
Name: jsii.String("_pk"),
Type: awsdynamodb.AttributeType_STRING,
},
BillingMode: awsdynamodb.BillingMode_PAY_PER_REQUEST,
// Change this for production systems.
RemovalPolicy: awscdk.RemovalPolicy_DESTROY,
TimeToLiveAttribute: jsii.String("_ttl"),
})
// Strip the binary, and remove the deprecated Lambda SDK RPC code for performance.
// These options are not required, but make cold start faster.
bundlingOptions := &awslambdago.BundlingOptions{
GoBuildFlags: &[]*string{jsii.String(`-ldflags "-s -w" -tags lambda.norpc`)},
}
f := awslambdago.NewGoFunction(stack, jsii.String("handler"), &awslambdago.GoFunctionProps{
Runtime: awslambda.Runtime_PROVIDED_AL2(),
MemorySize: jsii.Number(1024),
Architecture: awslambda.Architecture_ARM_64(),
Entry: jsii.String("../lambda"),
Bundling: bundlingOptions,
Environment: &map[string]*string{
"TABLE_NAME": db.TableName(),
},
})
// Grant DB access.
db.GrantReadWriteData(f)
// Add a Function URL.
lambdaURL := f.AddFunctionUrl(&awslambda.FunctionUrlOptions{
AuthType: awslambda.FunctionUrlAuthType_NONE,
})
awscdk.NewCfnOutput(stack, jsii.String("lambdaFunctionUrl"), &awscdk.CfnOutputProps{
ExportName: jsii.String("lambdaFunctionUrl"),
Value: lambdaURL.Url(),
})
assetsBucket := awss3.NewBucket(stack, jsii.String("assets"), &awss3.BucketProps{
BlockPublicAccess: awss3.BlockPublicAccess_BLOCK_ALL(),
Encryption: awss3.BucketEncryption_S3_MANAGED,
EnforceSSL: jsii.Bool(true),
RemovalPolicy: awscdk.RemovalPolicy_DESTROY,
Versioned: jsii.Bool(false),
})
// Allow CloudFront to read from the bucket.
cfOAI := awscloudfront.NewOriginAccessIdentity(stack, jsii.String("cfnOriginAccessIdentity"), &awscloudfront.OriginAccessIdentityProps{})
cfs := awsiam.NewPolicyStatement(&awsiam.PolicyStatementProps{})
cfs.AddActions(jsii.String("s3:GetBucket*"))
cfs.AddActions(jsii.String("s3:GetObject*"))
cfs.AddActions(jsii.String("s3:List*"))
cfs.AddResources(assetsBucket.BucketArn())
cfs.AddResources(jsii.String(fmt.Sprintf("%v/*", *assetsBucket.BucketArn())))
cfs.AddCanonicalUserPrincipal(cfOAI.CloudFrontOriginAccessIdentityS3CanonicalUserId())
assetsBucket.AddToResourcePolicy(cfs)
// Add a CloudFront distribution to route between the public directory and the Lambda function URL.
lambdaURLDomain := awscdk.Fn_Select(jsii.Number(2), awscdk.Fn_Split(jsii.String("/"), lambdaURL.Url(), nil))
lambdaOrigin := awscloudfrontorigins.NewHttpOrigin(lambdaURLDomain, &awscloudfrontorigins.HttpOriginProps{
ProtocolPolicy: awscloudfront.OriginProtocolPolicy_HTTPS_ONLY,
})
cf := awscloudfront.NewDistribution(stack, jsii.String("customerFacing"), &awscloudfront.DistributionProps{
DefaultBehavior: &awscloudfront.BehaviorOptions{
AllowedMethods: awscloudfront.AllowedMethods_ALLOW_ALL(),
Origin: lambdaOrigin,
CachedMethods: awscloudfront.CachedMethods_CACHE_GET_HEAD(),
OriginRequestPolicy: awscloudfront.OriginRequestPolicy_ALL_VIEWER_EXCEPT_HOST_HEADER(),
CachePolicy: awscloudfront.CachePolicy_CACHING_DISABLED(),
ViewerProtocolPolicy: awscloudfront.ViewerProtocolPolicy_REDIRECT_TO_HTTPS,
},
PriceClass: awscloudfront.PriceClass_PRICE_CLASS_100,
})
// Add /assets* to the distribution backed by S3.
assetsOrigin := awscloudfrontorigins.NewS3Origin(assetsBucket, &awscloudfrontorigins.S3OriginProps{
// Get content from the / directory in the bucket.
OriginPath: jsii.String("/"),
OriginAccessIdentity: cfOAI,
})
cf.AddBehavior(jsii.String("/assets*"), assetsOrigin, nil)
// Export the domain.
awscdk.NewCfnOutput(stack, jsii.String("cloudFrontDomain"), &awscdk.CfnOutputProps{
ExportName: jsii.String("cloudfrontDomain"),
Value: cf.DomainName(),
})
// Deploy the contents of the ./assets directory to the S3 bucket.
awss3deployment.NewBucketDeployment(stack, jsii.String("assetsDeployment"), &awss3deployment.BucketDeploymentProps{
DestinationBucket: assetsBucket,
Sources: &[]awss3deployment.ISource{
awss3deployment.Source_Asset(jsii.String("../assets"), nil),
},
DestinationKeyPrefix: jsii.String("assets"),
Distribution: cf,
DistributionPaths: jsii.Strings("/assets*"),
})
return stack
}
func main() {
defer jsii.Close()
app := awscdk.NewApp(nil)
NewCounterStack(app, "CounterStack", &CounterStackProps{})
app.Synth(nil)
}