Scalable, dynamic, image service that utilizes Node.js and AWS Lamda
Switch branches/tags
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
js
lambda
test
.gitignore
LICENSE.txt
README.md
app.js
gulpfile.coffee
gulpfile.js
package.json

README.md

Imagineer is a Node app designed to run on Elasticbeanstalk and delegate work to Lambda. On request, it will try to determine the source file, decode transformations from the URL, and serve the requested file.

Develop

Install dependencies

npm install

Gulp

Gulp tasks are available for your pleasure. The default task, gulp should be all you need. It triggers the following:

build - create source archives for deployment

test - run tests

watch - watch for changes and run build and test

Build

Elasticbeanstalk and Lamba both accept source zips. Running these shortscuts will create versioned archives in the dist directory:

gulp build

Build the app

This will produce imagineer-0.0.0.zip.

gulp buildApp

Build the Lambda function

This will produce imagineerImageCreate-0.0.0.zip.

gulp buildLambda

Permissions

The app will require permissions to a target bucket for both the EB app and the Lambda function. The following are sample configuration to allow Imagineer to fill in images from a source bucket.

App

The app needs to list from source bucket, invoke the Lambda function, and get from the destination bucket. It may also need to list the destination bucket, if it will be different.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "s3:ListBucket",
      "Resource": [
        "arn:aws:s3:::source-bucket",
        "arn:aws:s3:::destination-bucket"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "lambda:InvokeAsync",
        "lambda:InvokeFunction"
      ],
      "Resource": "arn:aws:lambda:us-east-1:123456789012:function:someFunctionName"
    },
    {
      "Effect": "Allow",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::destination-bucket/*"
    }
  ]
}

Lambda function

The Lambda function needs to get from the source bucket and post to the destination bucket. It will occasionally need to delete if streaming the new file to S3 fails.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "s3:GetObject",
      "Resource": [
        "arn:aws:s3:::source-bucket*"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "s3:PutObject",
        "s3:DeleteObject"
      ],
      "Resource": "arn:aws:s3:::destination-bucket/*"
    }
  ]
}

Sending requests to Imagineer

Imagineer generates any specified version, whether or not it exists in the destination. It benefits from only being called when an image does not exist. You have many options for forwarding requests, but we have used an S3 bucket with forwarding for this purpose.

S3 Bucket

The S3 bucket should have static web hosting enabled. You will have to supply an index document.

Redirection

Redirection rules forward 404 errors to the Imagineer service.

<RoutingRules>
    <RoutingRule>
        <Condition>
            <KeyPrefixEquals>uploads/</KeyPrefixEquals>
            <HttpErrorCodeReturnedEquals>404</HttpErrorCodeReturnedEquals>
        </Condition>
        <Redirect>
            <HostName>your.imagineer.domain</HostName>
        </Redirect>
    </RoutingRule>
</RoutingRules>
/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ O8D8\/\/\/\/\/\/\/\/\
\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ DDDDDDDDDDD/\/\/\/\/\/
/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/  DDDDDDDDDDDDDDD/\/\/\/\
\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\  DDDDDDDDDDDDDDDDD8/\/\/\/
/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\  8DDDDDDDDDDDDDDDDDDD/\/\/\
\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\  DDDDDDDDDDDDDDDDDDDD8\/\/\/
/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\  DDDDDDDDDDDDDDDDDDDDD\/\/\
\/\/\/\/\/  8888/\/\/\/\/\/\/\/\/\  DDDDDDDDDDDDDDDDDDDDD/\/\/
/\/\/\/  8DDDDDDDDDD\/\/\/\/\/\/\/  DDDDDDDDDDDDDDDDDDDDD\/\/\
\/\/\  8DDDDDDDDDDDDDD/\/\/\/\/\/\  8DDDDDDDDDDDDDDDDDDD8/\/\/
/\/\  DDDDDDDDDDDDDDDDD/\/\/\/\/\/\  DDDDDDDDDDDDDDDDDDD/\/\/\
\/\  DDDDDDDDDDDDDDDDDDD/\/\/\/\/\/\  8DDDDDDDDDDDDDDDD/\/\/\/
/\  DDDDDDDDDDDDDDDDDDDD\/\/\/\/\ ODDDDDDDDDDDDDDDDDDD/\/\/\/\
\  8DDDDDDDDDDDDDDDDDDDDD\/\ ODDDNDDN......DDDDDDDD8\/\/\/\/\/
/  8DDDDDDDDDDDDDDDDDDDDD  DDDDDDDDD.........DDDD /\/\/\/\/\/\
\/  DDDDDDDDDDDDDDDDDDDDDDD~.....DD...........$NDDD/\/\/\/\/\/
/\  DDDDDDDDDDDDDDDDDDDDDD........I............:DDDD8\/\/\/\/\
\/\  DDDDDDDDDDDDDDDDDDDD.......................8DDDDD\/\/\/\/
/\/\  DDDDDDDDDDDDDDDDDD.........................DDDDD8\/\/\/\
\/\/\  8DDDDDDDDDDDDDDDD.........................?DDDDDO\/\/\/
/\/\/\/\  DDDDDDDD88DDDD........~D/...............DDDDDD/\/\/\
\/\/\/\/\/\/\/\   DDDDD........DDDD......$:......DDDDDD8\/\/\/
/\/\/\/\/\/\/\/\/  DDDDD........DDDDD....O........D+...+D8/\/\
\/\/\/\/\/\/\/\/\  DDDDD:.......DDDDD/..O...8DD~.........8\/\/
/\/\/\/\/\/\/\/\/  DDDDDD.........:DDD....DI..............D\/\
\/\/\/\/\/\/\/\/\  DDDDDD+.......DDDDD...~...8.....$DD,...D/\/
/\/\/\/\/\/\/\/\/  DDDDDDD.......7DDDD.,?//?........7.....D\/\
\/\/\/\/\/\/\/\/\/ 8DDDDDDDI......DDD8.=DDD+........D....O\/\/
/\/\/\/\/\/\/\/\/\  DDD+........../..DDDDDDDD8......:...=8/\/\
\/\/\/\/\/\/\/\/\/  8D+.............DDDDDDDDDD.....D....8/\/\/
/\/\/\/\/\/\/\/\/\/  8.....,O$......:DDDDDDD:.....D...~D/\/\/\
\/\/\/\/\/\/\/\/\/\/  7...,.=..........:=.......7O...+8/\/\/\/
/\/\/\/\/\/\/\/\/\/\  D......D................?DD...D\/\/\/\/\
\/\/\/\/\/\/\/\/\/\/\  D.......8/........../DDDD..,D\/\/\/\/\/
/\/\/\/\/\/\/\/\/\/\/\  D8.........DDDDDDDDD///8=$/\/\/\/\/\/\
\/\/\/\/\/\/\/\/\/\/\/\/  DD........?DD///////I.D/\/\/\/\/\/\/
/\/\/\/\/\/\/\/\/\/\/\/\/\/\  D/,..?..88///OD.I8/\/\/\/\/\/\/\
\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/      8D,.....=D /\/\/\/\/\/\/\/
/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/           /\/\/\/\/\/\/\/\
\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/