in source/constructs/lib/back-end/back-end-construct.ts [31:173]
constructor(scope: Construct, id: string, props: BackEndProps) {
super(scope, id);
const sourceCodeBucket = Bucket.fromBucketName(this, 'ImageHandlerLambdaSource', props.sourceCodeBucketName);
const imageHandlerLambdaFunctionRole = new Role(this, 'ImageHandlerFunctionRole', {
assumedBy: new ServicePrincipal('lambda.amazonaws.com'),
path: '/'
});
props.secretsManagerPolicy.attachToRole(imageHandlerLambdaFunctionRole);
const imageHandlerLambdaFunctionRolePolicy = new Policy(this, 'ImageHandlerFunctionPolicy', {
statements: [
new PolicyStatement({
actions: ['logs:CreateLogGroup', 'logs:CreateLogStream', 'logs:PutLogEvents'],
resources: [Stack.of(this).formatArn({ service: 'logs', resource: 'log-group', resourceName: '/aws/lambda/*', arnFormat: ArnFormat.COLON_RESOURCE_NAME })]
}),
new PolicyStatement({
actions: ['s3:GetObject', 's3:PutObject', 's3:ListBucket'],
resources: [Stack.of(this).formatArn({ service: 's3', resource: '*', region: '', account: '' })]
}),
new PolicyStatement({
actions: ['rekognition:DetectFaces', 'rekognition:DetectModerationLabels'],
resources: ['*']
})
]
});
addCfnSuppressRules(imageHandlerLambdaFunctionRolePolicy, [{ id: 'W12', reason: "rekognition:DetectFaces requires '*' resources." }]);
imageHandlerLambdaFunctionRole.attachInlinePolicy(imageHandlerLambdaFunctionRolePolicy);
const imageHandlerLambdaFunction = new LambdaFunction(this, 'ImageHandlerLambdaFunction', {
description: `${props.solutionDisplayName} (${props.solutionVersion}): Performs image edits and manipulations`,
runtime: Runtime.NODEJS_14_X,
handler: 'image-handler/index.handler',
timeout: Duration.minutes(15),
memorySize: 1_024,
code: Code.fromBucket(sourceCodeBucket, [props.sourceCodeKeyPrefix, 'image-handler.zip'].join('/')),
role: imageHandlerLambdaFunctionRole,
environment: {
AUTO_WEBP: props.autoWebP,
CORS_ENABLED: props.corsEnabled,
CORS_ORIGIN: props.corsOrigin,
SOURCE_BUCKETS: props.sourceBuckets,
REWRITE_MATCH_PATTERN: '',
REWRITE_SUBSTITUTION: '',
ENABLE_SIGNATURE: props.enableSignature,
SECRETS_MANAGER: props.secretsManager,
SECRET_KEY: props.secretsManagerKey,
ENABLE_DEFAULT_FALLBACK_IMAGE: props.enableDefaultFallbackImage,
DEFAULT_FALLBACK_IMAGE_BUCKET: props.fallbackImageS3Bucket,
DEFAULT_FALLBACK_IMAGE_KEY: props.fallbackImageS3KeyBucket
}
});
const imageHandlerLogGroup = new LogGroup(this, 'ImageHandlerLogGroup', {
logGroupName: `/aws/lambda/${imageHandlerLambdaFunction.functionName}`,
retention: props.logRetentionPeriod as RetentionDays
});
addCfnSuppressRules(imageHandlerLogGroup, [{ id: 'W84', reason: 'CloudWatch log group is always encrypted by default.' }]);
const cachePolicy = new CachePolicy(this, 'CachePolicy', {
cachePolicyName: `ServerlessImageHandler-${props.uuid}`,
defaultTtl: Duration.days(1),
minTtl: Duration.seconds(1),
maxTtl: Duration.days(365),
enableAcceptEncodingGzip: true,
headerBehavior: {
behavior: 'whitelist',
headers: ['origin', 'accept']
},
queryStringBehavior: {
behavior: 'whitelist',
queryStrings: ['signature']
}
});
const originRequestPolicy = new OriginRequestPolicy(this, 'OriginRequestPolicy', {
originRequestPolicyName: `ServerlessImageHandler-${props.uuid}`,
headerBehavior: {
behavior: 'whitelist',
headers: ['origin', 'accept']
},
queryStringBehavior: {
behavior: 'whitelist',
queryStrings: ['signature']
}
});
const apiGatewayRestApi = RestApi.fromRestApiId(this, 'ApiGatewayRestApi', Lazy.string({ produce: () => imageHandlerCloudFrontApiGatewayLambda.apiGateway.restApiId }));
const origin: IOrigin = new HttpOrigin(`${apiGatewayRestApi.restApiId}.execute-api.${Aws.REGION}.amazonaws.com`, {
originPath: '/image',
originSslProtocols: [OriginSslPolicy.TLS_V1_1, OriginSslPolicy.TLS_V1_2]
});
const cloudFrontDistributionProps: DistributionProps = {
comment: 'Image Handler Distribution for Serverless Image Handler',
defaultBehavior: {
origin: origin,
allowedMethods: AllowedMethods.ALLOW_GET_HEAD,
viewerProtocolPolicy: ViewerProtocolPolicy.HTTPS_ONLY,
originRequestPolicy: originRequestPolicy,
cachePolicy: cachePolicy
},
priceClass: props.cloudFrontPriceClass as PriceClass,
enableLogging: true,
logBucket: props.logsBucket,
logFilePrefix: 'api-cloudfront/',
errorResponses: [
{ httpStatus: 500, ttl: Duration.minutes(10) },
{ httpStatus: 501, ttl: Duration.minutes(10) },
{ httpStatus: 502, ttl: Duration.minutes(10) },
{ httpStatus: 503, ttl: Duration.minutes(10) },
{ httpStatus: 504, ttl: Duration.minutes(10) }
]
};
const logGroupProps = {
retention: props.logRetentionPeriod as RetentionDays
};
const apiGatewayProps: LambdaRestApiProps = {
handler: imageHandlerLambdaFunction,
deployOptions: {
stageName: 'image'
},
binaryMediaTypes: ['*/*']
};
const imageHandlerCloudFrontApiGatewayLambda = new CloudFrontToApiGatewayToLambda(this, 'ImageHandlerCloudFrontApiGatewayLambda', {
existingLambdaObj: imageHandlerLambdaFunction,
insertHttpSecurityHeaders: false,
logGroupProps: logGroupProps,
cloudFrontDistributionProps: cloudFrontDistributionProps,
apiGatewayProps: apiGatewayProps
});
imageHandlerCloudFrontApiGatewayLambda.apiGateway.node.tryRemoveChild('Endpoint'); // we don't need the RestApi endpoint in the outputs
this.domainName = imageHandlerCloudFrontApiGatewayLambda.cloudFrontWebDistribution.distributionDomainName;
}