-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
15 changed files
with
1,200 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,6 +16,7 @@ | |
/lib | ||
/test-reports/ | ||
build/Release | ||
cdk.out | ||
coverage | ||
jspm_packages/ | ||
junit.xml | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
# API Reference | ||
|
||
**Classes** | ||
|
||
Name|Description | ||
----|----------- | ||
[ImageEnhancer](#aws-cdk-image-enhancer-imageenhancer)|*No description* | ||
|
||
|
||
**Structs** | ||
|
||
Name|Description | ||
----|----------- | ||
[DistributionProps](#aws-cdk-image-enhancer-distributionprops)|Properties for a Distribution. | ||
[FunctionProps](#aws-cdk-image-enhancer-functionprops)|*No description* | ||
|
||
|
||
**Interfaces** | ||
|
||
Name|Description | ||
----|----------- | ||
[IImageEnhancerProps](#aws-cdk-image-enhancer-iimageenhancerprops)|*No description* | ||
|
||
|
||
|
||
## class ImageEnhancer <a id="aws-cdk-image-enhancer-imageenhancer"></a> | ||
|
||
|
||
|
||
__Implements__: [IConstruct](#constructs-iconstruct), [IConstruct](#aws-cdk-core-iconstruct), [IConstruct](#constructs-iconstruct), [IDependable](#aws-cdk-core-idependable) | ||
__Extends__: [Construct](#aws-cdk-core-construct) | ||
|
||
### Initializer | ||
|
||
|
||
|
||
|
||
```ts | ||
new ImageEnhancer(scope: Construct, id: string, props?: IImageEnhancerProps) | ||
``` | ||
|
||
* **scope** (<code>[Construct](#aws-cdk-core-construct)</code>) *No description* | ||
* **id** (<code>string</code>) *No description* | ||
* **props** (<code>[IImageEnhancerProps](#aws-cdk-image-enhancer-iimageenhancerprops)</code>) *No description* | ||
|
||
|
||
|
||
|
||
## struct DistributionProps <a id="aws-cdk-image-enhancer-distributionprops"></a> | ||
|
||
|
||
Properties for a Distribution. | ||
|
||
|
||
|
||
Name | Type | Description | ||
-----|------|------------- | ||
**additionalBehaviors**? | <code>Map<string, [BehaviorOptions](#aws-cdk-aws-cloudfront-behavioroptions)></code> | Additional behaviors for the distribution, mapped by the pathPattern that specifies which requests to apply the behavior to.<br/>__*Default*__: no additional behaviors are added. | ||
**certificate**? | <code>[ICertificate](#aws-cdk-aws-certificatemanager-icertificate)</code> | A certificate to associate with the distribution.<br/>__*Default*__: the CloudFront wildcard certificate (*.cloudfront.net) will be used. | ||
**comment**? | <code>string</code> | Any comments you want to include about the distribution.<br/>__*Default*__: no comment | ||
**defaultBehavior**? | <code>[BehaviorOptions](#aws-cdk-aws-cloudfront-behavioroptions)</code> | The default behavior for the distribution.<br/>__*Optional*__ | ||
**defaultRootObject**? | <code>string</code> | The object that you want CloudFront to request from your origin (for example, index.html) when a viewer requests the root URL for your distribution. If no default object is set, the request goes to the origin's root (e.g., example.com/).<br/>__*Default*__: no default root object | ||
**domainNames**? | <code>Array<string></code> | Alternative domain names for this distribution.<br/>__*Default*__: The distribution will only support the default generated name (e.g., d111111abcdef8.cloudfront.net) | ||
**enableIpv6**? | <code>boolean</code> | Whether CloudFront will respond to IPv6 DNS requests with an IPv6 address.<br/>__*Default*__: true | ||
**enableLogging**? | <code>boolean</code> | Enable access logging for the distribution.<br/>__*Default*__: false, unless `logBucket` is specified. | ||
**enabled**? | <code>boolean</code> | Enable or disable the distribution.<br/>__*Default*__: true | ||
**errorResponses**? | <code>Array<[ErrorResponse](#aws-cdk-aws-cloudfront-errorresponse)></code> | How CloudFront should handle requests that are not successful (e.g., PageNotFound).<br/>__*Default*__: No custom error responses. | ||
**geoRestriction**? | <code>[GeoRestriction](#aws-cdk-aws-cloudfront-georestriction)</code> | Controls the countries in which your content is distributed.<br/>__*Default*__: No geographic restrictions | ||
**httpVersion**? | <code>[HttpVersion](#aws-cdk-aws-cloudfront-httpversion)</code> | Specify the maximum HTTP version that you want viewers to use to communicate with CloudFront.<br/>__*Default*__: HttpVersion.HTTP2 | ||
**logBucket**? | <code>[IBucket](#aws-cdk-aws-s3-ibucket)</code> | The Amazon S3 bucket to store the access logs in.<br/>__*Default*__: A bucket is created if `enableLogging` is true | ||
**logFilePrefix**? | <code>string</code> | An optional string that you want CloudFront to prefix to the access log filenames for this distribution.<br/>__*Default*__: no prefix | ||
**logIncludesCookies**? | <code>boolean</code> | Specifies whether you want CloudFront to include cookies in access logs.<br/>__*Default*__: false | ||
**minimumProtocolVersion**? | <code>[SecurityPolicyProtocol](#aws-cdk-aws-cloudfront-securitypolicyprotocol)</code> | The minimum version of the SSL protocol that you want CloudFront to use for HTTPS connections.<br/>__*Default*__: SecurityPolicyProtocol.TLS_V1_2_2019 | ||
**priceClass**? | <code>[PriceClass](#aws-cdk-aws-cloudfront-priceclass)</code> | The price class that corresponds with the maximum price that you want to pay for CloudFront service.<br/>__*Default*__: PriceClass.PRICE_CLASS_ALL | ||
**webAclId**? | <code>string</code> | Unique identifier that specifies the AWS WAF web ACL to associate with this CloudFront distribution.<br/>__*Default*__: No AWS Web Application Firewall web access control list (web ACL). | ||
|
||
|
||
|
||
## struct FunctionProps <a id="aws-cdk-image-enhancer-functionprops"></a> | ||
|
||
|
||
|
||
|
||
|
||
|
||
Name | Type | Description | ||
-----|------|------------- | ||
**allowAllOutbound**? | <code>boolean</code> | Whether to allow the Lambda to send all network traffic.<br/>__*Default*__: true | ||
**allowPublicSubnet**? | <code>boolean</code> | Lambda Functions in a public subnet can NOT access the internet.<br/>__*Default*__: false | ||
**code**? | <code>[Code](#aws-cdk-aws-lambda-code)</code> | The source code of your Lambda function.<br/>__*Optional*__ | ||
**currentVersionOptions**? | <code>[VersionOptions](#aws-cdk-aws-lambda-versionoptions)</code> | Options for the `lambda.Version` resource automatically created by the `fn.currentVersion` method.<br/>__*Default*__: default options as described in `VersionOptions` | ||
**deadLetterQueue**? | <code>[IQueue](#aws-cdk-aws-sqs-iqueue)</code> | The SQS queue to use if DLQ is enabled.<br/>__*Default*__: SQS queue with 14 day retention period if `deadLetterQueueEnabled` is `true` | ||
**deadLetterQueueEnabled**? | <code>boolean</code> | Enabled DLQ.<br/>__*Default*__: false unless `deadLetterQueue` is set, which implies DLQ is enabled. | ||
**description**? | <code>string</code> | A description of the function.<br/>__*Default*__: No description. | ||
**environment**? | <code>Map<string, string></code> | Key-value pairs that Lambda caches and makes available for your Lambda functions.<br/>__*Default*__: No environment variables. | ||
**environmentEncryption**? | <code>[IKey](#aws-cdk-aws-kms-ikey)</code> | The AWS KMS key that's used to encrypt your function's environment variables.<br/>__*Default*__: AWS Lambda creates and uses an AWS managed customer master key (CMK). | ||
**events**? | <code>Array<[IEventSource](#aws-cdk-aws-lambda-ieventsource)></code> | Event sources for this function.<br/>__*Default*__: No event sources. | ||
**filesystem**? | <code>[FileSystem](#aws-cdk-aws-lambda-filesystem)</code> | The filesystem configuration for the lambda function.<br/>__*Default*__: will not mount any filesystem | ||
**functionName**? | <code>string</code> | A name for the function.<br/>__*Default*__: AWS CloudFormation generates a unique physical ID and uses that ID for the function's name. For more information, see Name Type. | ||
**handler**? | <code>string</code> | The name of the method within your code that Lambda calls to execute your function.<br/>__*Optional*__ | ||
**initialPolicy**? | <code>Array<[PolicyStatement](#aws-cdk-aws-iam-policystatement)></code> | Initial policy statements to add to the created Lambda Role.<br/>__*Default*__: No policy statements are added to the created Lambda role. | ||
**layers**? | <code>Array<[ILayerVersion](#aws-cdk-aws-lambda-ilayerversion)></code> | A list of layers to add to the function's execution environment.<br/>__*Default*__: No layers. | ||
**logRetention**? | <code>[RetentionDays](#aws-cdk-aws-logs-retentiondays)</code> | The number of days log events are kept in CloudWatch Logs.<br/>__*Default*__: logs.RetentionDays.INFINITE | ||
**logRetentionRetryOptions**? | <code>[LogRetentionRetryOptions](#aws-cdk-aws-lambda-logretentionretryoptions)</code> | When log retention is specified, a custom resource attempts to create the CloudWatch log group.<br/>__*Default*__: Default AWS SDK retry options. | ||
**logRetentionRole**? | <code>[IRole](#aws-cdk-aws-iam-irole)</code> | The IAM role for the Lambda function associated with the custom resource that sets the retention policy.<br/>__*Default*__: A new role is created. | ||
**maxEventAge**? | <code>[Duration](#aws-cdk-core-duration)</code> | The maximum age of a request that Lambda sends to a function for processing.<br/>__*Default*__: Duration.hours(6) | ||
**memorySize**? | <code>number</code> | The amount of memory, in MB, that is allocated to your Lambda function.<br/>__*Default*__: 128 | ||
**onFailure**? | <code>[IDestination](#aws-cdk-aws-lambda-idestination)</code> | The destination for failed invocations.<br/>__*Default*__: no destination | ||
**onSuccess**? | <code>[IDestination](#aws-cdk-aws-lambda-idestination)</code> | The destination for successful invocations.<br/>__*Default*__: no destination | ||
**profiling**? | <code>boolean</code> | Enable profiling.<br/>__*Default*__: No profiling. | ||
**profilingGroup**? | <code>[IProfilingGroup](#aws-cdk-aws-codeguruprofiler-iprofilinggroup)</code> | Profiling Group.<br/>__*Default*__: A new profiling group will be created if `profiling` is set. | ||
**reservedConcurrentExecutions**? | <code>number</code> | The maximum of concurrent executions you want to reserve for the function.<br/>__*Default*__: No specific limit - account limit. | ||
**retryAttempts**? | <code>number</code> | The maximum number of times to retry when the function returns an error.<br/>__*Default*__: 2 | ||
**role**? | <code>[IRole](#aws-cdk-aws-iam-irole)</code> | Lambda execution role.<br/>__*Default*__: A unique role will be generated for this lambda function. Both supplied and generated roles can always be changed by calling `addToRolePolicy`. | ||
**runtime**? | <code>[Runtime](#aws-cdk-aws-lambda-runtime)</code> | The runtime environment for the Lambda function that you are uploading.<br/>__*Optional*__ | ||
**securityGroup**?⚠️ | <code>[ISecurityGroup](#aws-cdk-aws-ec2-isecuritygroup)</code> | What security group to associate with the Lambda's network interfaces. This property is being deprecated, consider using securityGroups instead.<br/>__*Default*__: If the function is placed within a VPC and a security group is not specified, either by this or securityGroups prop, a dedicated security group will be created for this function. | ||
**securityGroups**? | <code>Array<[ISecurityGroup](#aws-cdk-aws-ec2-isecuritygroup)></code> | The list of security groups to associate with the Lambda's network interfaces.<br/>__*Default*__: If the function is placed within a VPC and a security group is not specified, either by this or securityGroup prop, a dedicated security group will be created for this function. | ||
**timeout**? | <code>[Duration](#aws-cdk-core-duration)</code> | The function execution time (in seconds) after which Lambda terminates the function.<br/>__*Default*__: Duration.seconds(3) | ||
**tracing**? | <code>[Tracing](#aws-cdk-aws-lambda-tracing)</code> | Enable AWS X-Ray Tracing for Lambda Function.<br/>__*Default*__: Tracing.Disabled | ||
**vpc**? | <code>[IVpc](#aws-cdk-aws-ec2-ivpc)</code> | VPC network to place Lambda network interfaces.<br/>__*Default*__: Function is not placed within a VPC. | ||
**vpcSubnets**? | <code>[SubnetSelection](#aws-cdk-aws-ec2-subnetselection)</code> | Where to place the network interfaces within the VPC.<br/>__*Default*__: the Vpc default strategy if not specified | ||
|
||
|
||
|
||
## interface IImageEnhancerProps <a id="aws-cdk-image-enhancer-iimageenhancerprops"></a> | ||
|
||
|
||
|
||
|
||
### Properties | ||
|
||
|
||
Name | Type | Description | ||
-----|------|------------- | ||
**cloudfrontDistributionProps**? | <code>[DistributionProps](#aws-cdk-image-enhancer-distributionprops)</code> | __*Optional*__ | ||
**originResponseLambdaProps**? | <code>[FunctionProps](#aws-cdk-image-enhancer-functionprops)</code> | __*Optional*__ | ||
**s3BucketProps**? | <code>[BucketProps](#aws-cdk-aws-s3-bucketprops)</code> | __*Optional*__ | ||
**viewerRequestLambdaProps**? | <code>[FunctionProps](#aws-cdk-image-enhancer-functionprops)</code> | __*Optional*__ | ||
|
||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
/* eslint-disable @typescript-eslint/no-var-requires */ | ||
const sharp = require('sharp'); | ||
const AWS = require('aws-sdk'); | ||
const s3 = new AWS.S3({ signatureVersion: 'v4' }); | ||
|
||
/** | ||
* @param {{uri: String}} request | ||
* @returns {{key: String, prefix: String, extension: String, width?: Number, height?: Number}} | ||
*/ | ||
exports.extractDataFromUri = request => { | ||
const uri = request.uri; | ||
// AWS key is the URI without the initial '/' | ||
const key = uri.substring(1); | ||
|
||
// Try to match dimensions first | ||
// e.g.: /path/to/file-100wx100h.webp | ||
const dimensionMatch = uri.match(/\/(.*)-([0-9]+)wx([0-9]+)h\.([^.]*)$/); | ||
if (dimensionMatch) | ||
return { | ||
key, | ||
prefix: dimensionMatch[1], | ||
width: parseInt(dimensionMatch[2]), | ||
height: parseInt(dimensionMatch[3]), | ||
extension: dimensionMatch[4], | ||
}; | ||
|
||
// If no dimensions included, we just care about the prefix and the extension | ||
const simpleMatch = uri.match(/\/(.*)\.([^.]*)$/); | ||
|
||
return { key, prefix: simpleMatch[1], extension: simpleMatch[2] }; | ||
}; | ||
|
||
exports.handler = async (event, _context, callback) => { | ||
const response = event.Records[0].cf.response; | ||
|
||
const request = event.Records[0].cf.request; | ||
|
||
// Extracting bucket name. domainName looks like this: bucket-name.s3.region.amazonaws.com" | ||
const [, Bucket] = request.origin.s3.domainName.match(/(.*).s3./); | ||
|
||
if (Number(response.status) !== 404) { | ||
if (Number(response.status) !== 200) response.status = 400; | ||
callback(null, response); | ||
return; | ||
} | ||
|
||
// Image not found in bucket | ||
let params; | ||
try { | ||
params = this.extractDataFromUri(request); | ||
} catch (e) { | ||
callback(null, response); | ||
return; | ||
} | ||
|
||
const { Contents } = await s3 | ||
.listObjects({ | ||
Bucket, | ||
// List all keys starting with path/to/file. | ||
Prefix: params.prefix + '.', | ||
}) | ||
.promise(); | ||
|
||
if (!Contents.length) { | ||
callback(null, response); | ||
return; | ||
} | ||
|
||
const baseImageKey = (() => { | ||
/** | ||
* Try to find an existent image for the requested extension. | ||
* If there isn't one, the use as base image the first from the Contents array | ||
*/ | ||
const found = Contents.find(({ Key }) => Key.split(`${params.prefix}.`)[1] === params.extension); | ||
if (found) return found.Key; | ||
return Contents[0].Key; | ||
})(); | ||
|
||
// Use the found key to get the image from the s3 bucket | ||
const { Body, ContentType } = await s3.getObject({ Key: baseImageKey, Bucket }).promise(); | ||
|
||
const sharpPromise = sharp(Body); | ||
|
||
// If dimensions passed, resize base image | ||
if (params.width || params.height) { | ||
// Allow to pass only one of width or height | ||
sharpPromise.resize(params.width || undefined, params.height || undefined); | ||
} | ||
// If the requested extension is different than the base image extension, then | ||
// format it to the new extension | ||
if (ContentType !== `image/${params.extension}`) sharpPromise.toFormat(params.extension); | ||
|
||
const buffer = await sharpPromise.toBuffer(); | ||
|
||
// Save the new image to s3 bucket. Don't await for this to finish. | ||
// Even if the upload fails we return the converted image | ||
s3.putObject({ | ||
Body: buffer, | ||
Bucket, | ||
ContentType: 'image/' + params.extension, | ||
CacheControl: 'max-age=31536000', | ||
Key: params.key, | ||
StorageClass: 'STANDARD', | ||
}).promise(); | ||
|
||
response.status = 200; | ||
response.body = buffer.toString('base64'); | ||
response.bodyEncoding = 'base64'; | ||
response.headers['content-type'] = [{ key: 'Content-Type', value: 'image/' + params.extension }]; | ||
callback(null, response); | ||
}; |
Oops, something went wrong.