├── .bowerrc ├── .editorconfig ├── .ember-cli ├── .gitignore ├── .jshintrc ├── .npmignore ├── .travis.yml ├── .watchmanconfig ├── LICENSE.md ├── README.md ├── addon └── .gitkeep ├── app └── .gitkeep ├── assets └── lambda-package │ ├── index.js │ └── package.json ├── bower.json ├── config ├── ember-try.js └── environment.js ├── ember-cli-build.js ├── index.js ├── lib └── fastboot-api-lambda-deploy-plugin.js ├── package.json ├── testem.js ├── tests ├── .jshintrc ├── dummy │ ├── app │ │ ├── app.js │ │ ├── components │ │ │ └── .gitkeep │ │ ├── controllers │ │ │ └── .gitkeep │ │ ├── helpers │ │ │ └── .gitkeep │ │ ├── index.html │ │ ├── models │ │ │ └── .gitkeep │ │ ├── resolver.js │ │ ├── router.js │ │ ├── routes │ │ │ └── .gitkeep │ │ ├── styles │ │ │ └── app.css │ │ └── templates │ │ │ ├── application.hbs │ │ │ └── components │ │ │ └── .gitkeep │ ├── config │ │ └── environment.js │ └── public │ │ ├── crossdomain.xml │ │ └── robots.txt ├── helpers │ ├── destroy-app.js │ ├── module-for-acceptance.js │ ├── resolver.js │ └── start-app.js ├── index.html ├── integration │ └── .gitkeep ├── test-helper.js └── unit │ └── .gitkeep └── vendor └── .gitkeep /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "bower_components", 3 | "analytics": false 4 | } 5 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | 8 | [*] 9 | end_of_line = lf 10 | charset = utf-8 11 | trim_trailing_whitespace = true 12 | insert_final_newline = true 13 | indent_style = space 14 | indent_size = 2 15 | 16 | [*.hbs] 17 | insert_final_newline = false 18 | 19 | [*.{diff,md}] 20 | trim_trailing_whitespace = false 21 | -------------------------------------------------------------------------------- /.ember-cli: -------------------------------------------------------------------------------- 1 | { 2 | /** 3 | Ember CLI sends analytics information by default. The data is completely 4 | anonymous, but there are times when you might want to disable this behavior. 5 | 6 | Setting `disableAnalytics` to true will prevent any data from being sent. 7 | */ 8 | "disableAnalytics": false 9 | } 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | 7 | # dependencies 8 | /node_modules 9 | /bower_components 10 | 11 | # misc 12 | /.sass-cache 13 | /connect.lock 14 | /coverage/* 15 | /libpeerconnection.log 16 | npm-debug.log* 17 | testem.log 18 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "predef": [ 3 | "document", 4 | "window", 5 | "-Promise" 6 | ], 7 | "browser": true, 8 | "boss": true, 9 | "curly": true, 10 | "debug": false, 11 | "devel": true, 12 | "eqeqeq": true, 13 | "evil": true, 14 | "forin": false, 15 | "immed": false, 16 | "laxbreak": false, 17 | "newcap": true, 18 | "noarg": true, 19 | "noempty": false, 20 | "nonew": false, 21 | "nomen": false, 22 | "onevar": false, 23 | "plusplus": false, 24 | "regexp": false, 25 | "undef": true, 26 | "sub": true, 27 | "strict": false, 28 | "white": false, 29 | "eqnull": true, 30 | "esversion": 6, 31 | "unused": true 32 | } 33 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | /bower_components 2 | /config/ember-try.js 3 | /dist 4 | /tests 5 | /tmp 6 | **/.gitkeep 7 | .bowerrc 8 | .editorconfig 9 | .ember-cli 10 | .gitignore 11 | .jshintrc 12 | .watchmanconfig 13 | .travis.yml 14 | bower.json 15 | ember-cli-build.js 16 | testem.js 17 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | language: node_js 3 | node_js: 4 | - "4" 5 | 6 | sudo: false 7 | 8 | cache: 9 | directories: 10 | - $HOME/.npm 11 | - $HOME/.cache # includes bowers cache 12 | 13 | env: 14 | # we recommend testing LTS's and latest stable release (bonus points to beta/canary) 15 | - EMBER_TRY_SCENARIO=ember-lts-2.4 16 | - EMBER_TRY_SCENARIO=ember-lts-2.8 17 | - EMBER_TRY_SCENARIO=ember-release 18 | - EMBER_TRY_SCENARIO=ember-beta 19 | - EMBER_TRY_SCENARIO=ember-canary 20 | - EMBER_TRY_SCENARIO=ember-default 21 | 22 | matrix: 23 | fast_finish: true 24 | allow_failures: 25 | - env: EMBER_TRY_SCENARIO=ember-canary 26 | 27 | before_install: 28 | - npm config set spin false 29 | - npm install -g bower phantomjs-prebuilt 30 | - bower --version 31 | - phantomjs --version 32 | 33 | install: 34 | - npm install 35 | - bower install 36 | 37 | script: 38 | # Usually, it's ok to finish the test scenario without reverting 39 | # to the addon's original dependency state, skipping "cleanup". 40 | - node_modules/.bin/ember try:one $EMBER_TRY_SCENARIO test --skip-cleanup 41 | -------------------------------------------------------------------------------- /.watchmanconfig: -------------------------------------------------------------------------------- 1 | { 2 | "ignore_dirs": ["tmp", "dist"] 3 | } 4 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ember-cli-deploy-fastboot-api-lambda 2 | 3 | [![npm version](https://badge.fury.io/js/ember-cli-deploy-fastboot-api-lambda.svg)](https://badge.fury.io/js/ember-cli-deploy-fastboot-api-lambda) 4 | 5 | An ambitious ember-cli-deploy plugin for serving Ember FastBoot Applications entirely from within AWS Lambda/API Gateway (assets and all!). 6 | 7 | 8 | ## Background 9 | API Gateway [now supports the handling binary payloads](https://aws.amazon.com/about-aws/whats-new/2016/11/binary-data-now-supported-by-api-gateway/), which means an end-to-end fastboot hosting solution can now be achieved through API gateway and Lambda without the use of S3 for serving static files. This is what this addon aims to achieve. 10 | 11 | ## Prerequisites 12 | - You have [ember-fastboot](https://ember-fastboot.com) installed and configured within your Ember app. 13 | - You have an [AWS account](https://aws.amazon.com/free) setup. 14 | - You have the [AWS CLI](https://aws.amazon.com/cli) installed and configured. 15 | 16 | ## Installation 17 | 18 | * Install the ember-cli-deploy addons 19 | ``` 20 | ember install ember-cli-deploy 21 | ember install ember-cli-deploy-build 22 | ember install ember-cli-deploy-fastboot-api-lambda 23 | ``` 24 | 25 | ## Configuration 26 | 27 | * Configure the deployment variables 28 | ``` 29 | // config/deploy.js 30 | ENV['fastboot-api-lambda'] = { 31 | accessKeyId: process.env.AWS_ACCESS_KEY_ID, // (Required) AWS accessKeyId (must have permission to deploy lambda functions) 32 | secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY, // (Required) AWS secretAccessKey 33 | lambdaFunction: 'my-ember-app', // (Required) Lambda functions name 34 | region: 'us-east-1', // (Required) Region where lambda is deployed 35 | 36 | fallbackPath: '/' // (optional) The route that will be attempted if the current route fails. i.e. doesn't exist, fails etc. 37 | stringyExtensions: [ // (optional) The file extensions that will be treated as text and not binary. Defaults are shown. Any additional items will be concatenated to this list. 38 | 'html', 39 | 'css', 40 | 'js', 41 | 'json', 42 | 'xml', 43 | 'ico', 44 | 'txt', 45 | 'map' 46 | ], 47 | validAssetPaths: [ // (optional) The assets paths that the lambda is explicitly allow serve. Defaults are shown. Any additional items will be concatenated to this list. 48 | '/assets/', 49 | '/robots.txt', 50 | '/humans.txt', 51 | '/crossdomain.xml', 52 | '/sitemap.xml' 53 | ] 54 | }; 55 | ``` 56 | 57 | * Create the lambda function 58 | 59 | * Open the [AWS Lambda console](https://console.aws.amazon.com/lambda). 60 | * Select the region that you defined in your deploy variables above. 61 | * Create a blank lambda, with the name you defined in your deploy variables above. 62 | * Handler => `index.handler`. 63 | * Role => `Create a custom role`. Give it a name and use the default policy document. 64 | * Memory => `128`. 65 | * Timeout => `30 seconds`. 66 | * Select `Next` and then select `Create function`. 67 | 68 | * Create the API Gateway Proxy 69 | 70 | * Open the [AWS API Gateway console](https://console.aws.amazon.com/apigateway). 71 | * Select the region that you defined in your deploy variables above. 72 | * Select `New API` and give it a name 73 | * Select Binary Support. Click `Edit`. Add `*/*` and click `Save`. 74 | * Create proxy method: 75 | * Under resources, click `/`, then click `Actions => Create Method`. Select `Any`. 76 | * Click the `Any label`, choose Integration type `lambda`, check the `Use Lambda Proxy integration` checkbox, and finally select your lambda function's region and name. 77 | * Create proxy resource: 78 | * Under resources, click `/`, then click `Actions => Create Resource`. Select `Any`. 79 | * Select `Configure as proxy resource`, and select `Enable API Gateway CORS`. 80 | * Select Integration type `Lambda Function Proxy`, and finally select your lambda function's region and name. 81 | * Under resources, click `Actions => Deploy API`. Select a new stage and give it the name `fastboot`. Hit `Deploy`. You will now see the `Invoke URL`. This is where you site will be hosted. 82 | 83 | * Ember Application 84 | * The `rootURL` must match the stage name you selected when creating the api gateway. Otherwise the `link-to` helper wont work. 85 | ``` 86 | // config/environment.js 87 | var ENV = { 88 | rootURL: '/fastboot/' 89 | } 90 | ``` 91 | 92 | * Configuration is done! 🎉 93 | 94 | ## Deployment 95 | 96 | Is as simple as going: 97 | 98 | `ember deploy production --activate --verbose=true` 99 | 100 | 101 | ## Caveats 102 | 103 | Just a word of warning.. just because this architecture is possible, doesn't make it the optimal for all use-cases. 104 | Lambda functions suffer from a cold start delay, which can make response times unpredictable. 105 | 106 | 107 | ## Sites using this addon 108 | 109 | * [nzsupps.co.nz](https://nzsupps.co.nz) 110 | 111 | *Feel free to make a pull request if you would like your site added to the list!* 112 | 113 | 114 | 115 | ## Credit 116 | [ember-cli-deploy-fastboot-lambda](https://github.com/bustlelabs/ember-cli-deploy-fastboot-lambda) for providing the base upload logic. 117 | 118 | ## Information 119 | For more information on using ember-cli, visit [http://www.ember-cli.com/](http://www.ember-cli.com/). 120 | 121 | For more information on using ember-cli-deploy, visit [https://github.com/ember-cli-deploy/ember-cli-deploy](https://github.com/ember-cli-deploy/ember-cli-deploy). 122 | -------------------------------------------------------------------------------- /addon/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kiwi-josh/ember-cli-deploy-fastboot-api-lambda/31ab85c924f72ccc3398ca11bd3f404508c7fbfe/addon/.gitkeep -------------------------------------------------------------------------------- /app/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kiwi-josh/ember-cli-deploy-fastboot-api-lambda/31ab85c924f72ccc3398ca11bd3f404508c7fbfe/app/.gitkeep -------------------------------------------------------------------------------- /assets/lambda-package/index.js: -------------------------------------------------------------------------------- 1 | // ember-cli-deploy-fastboot-api-lambda 2 | 3 | var config = require('./config.json'); 4 | var mime = require('mime'); 5 | var nodePath = require('path'); 6 | var fs = require('fs-promise'); 7 | var FastBoot = require('fastboot'); 8 | 9 | var fancyACacheYeh = { 10 | yes: 'max-age=63072000, public', 11 | no: 'max-age=0, public' 12 | }; 13 | 14 | var defaults = { 15 | distPath: 'dist', 16 | fallbackPath: '/', 17 | protocol: 'https', 18 | host: 'localhost:4200', 19 | assetsPath: '/assets/', 20 | stringyExtensions: [ 21 | 'html', 22 | 'css', 23 | 'js', 24 | 'json', 25 | 'xml', 26 | 'ico', 27 | 'txt', 28 | 'map' 29 | ], 30 | validAssetPaths: [ 31 | '/assets/', 32 | '/robots.txt', 33 | '/humans.txt', 34 | '/crossdomain.xml', 35 | '/sitemap.xml', 36 | '/sw-registration.js', 37 | '/sw.js' 38 | ], 39 | headers: { 40 | 'Content-Type': 'text/html;charset=UTF-8', 41 | 'Cache-Control': fancyACacheYeh.no 42 | }, 43 | fastBootOptions: { 44 | request: { 45 | headers: {}, 46 | get: function() {} 47 | }, 48 | response: {} 49 | } 50 | }; 51 | 52 | // Merge config: start 53 | var distPath = config.distPath || defaults.distPath; 54 | var fallbackPath = config.fallbackPath || defaults.fallbackPath; 55 | var protocol = config.protocol || defaults.protocol; 56 | var host = config.host || defaults.host; 57 | var validAssetPaths = defaults.validAssetPaths.concat(config.validAssetPaths || []); 58 | var stringyExtensions = defaults.stringyExtensions.concat(config.stringyExtensions || []); 59 | // Merge config: end 60 | 61 | // Instantiate Fastboot server 62 | var app = new FastBoot({ distPath: distPath }); 63 | 64 | var serveACheekyFile = (path, staticPath, fileBuffer) => { 65 | // 1. Early exit bail 66 | var isAssetValidPath = validAssetPaths.find(p => path.includes(p)); 67 | console.log('INFO validAssetPaths:', validAssetPaths); 68 | console.log('INFO isAssetValidPath:', isAssetValidPath); 69 | if (!isAssetValidPath) { throw true; } 70 | 71 | // 2. Look up files content type. 72 | var contentType = mime.lookup(staticPath); 73 | 74 | //3. Get file extension. 75 | var extension = mime.extension(contentType); 76 | 77 | //4. If it isn't a standard file, then base64 encode it. 78 | var shouldEncode = stringyExtensions.indexOf(extension) < 0; 79 | 80 | //5. Determine if the item is fingerprinted/cacheable 81 | var shouldCache = staticPath.includes(defaults.assetsPath); 82 | 83 | //6. Set encoding value 84 | var encoding = shouldEncode ? 'base64' : 'utf8'; 85 | 86 | //7. Create headers 87 | var headers = { 88 | 'Content-Type': contentType, 89 | 'Cache-Control': shouldCache ? fancyACacheYeh.yes : fancyACacheYeh.no 90 | }; 91 | 92 | //8. Create body 93 | var body = fileBuffer.toString(encoding); 94 | 95 | //9. Create final output 96 | var payload = { 97 | statusCode: 200, 98 | headers: headers, 99 | body: body, 100 | isBase64Encoded: shouldEncode 101 | }; 102 | 103 | console.log('INFO: contentType:', contentType); 104 | console.log('INFO: extension:', extension); 105 | console.log('INFO: stringyExtensions:', stringyExtensions); 106 | console.log('INFO: shouldEncode:', shouldEncode); 107 | console.log('INFO: shouldCache:', shouldCache); 108 | console.log('INFO: encoding:', encoding); 109 | 110 | return payload; 111 | }; 112 | 113 | 114 | var doSomeFastBoot = (event, path) => { 115 | 116 | // 1. Create options 117 | var options = defaults.fastBootOptions; 118 | options.request.headers = event.headers || {}; 119 | options.request.protocol = (event.headers || {})['X-Forwarded-Proto'] || protocol; 120 | options.request.headers.host = (event.headers || {}).Host || host; 121 | if (event.cookie) { 122 | options.request.headers.cookie = event.cookie; 123 | } 124 | 125 | console.log('INFO: options:', options); 126 | 127 | // 2. Fire up fastboot server 128 | return app.visit(path, options) 129 | .then(function(result) { 130 | 131 | var statusCode = result.statusCode; 132 | 133 | // Not interested yo. Wheres that sweet 200's at? 134 | if (statusCode !== 200) { throw true; } 135 | 136 | return result.html() 137 | .then(function(html) { 138 | 139 | // 3. Create headers object 140 | var headers = Object.assign(result.headers.headers, defaults.headers); 141 | 142 | console.log('INFO: headers:', headers); 143 | 144 | // 4. Create payload 145 | var payload = { 146 | statusCode: statusCode, 147 | headers: headers, 148 | body: html 149 | }; 150 | 151 | return payload; 152 | }); 153 | }); 154 | 155 | }; 156 | 157 | exports.handler = function(event, context, callback) { 158 | console.log('INFO event:', event); 159 | 160 | var path = event.path || fallbackPath; 161 | var staticPath = nodePath.join(distPath, path); 162 | 163 | console.log('INFO path:', path); 164 | console.log('INFO staticPath:', staticPath); 165 | 166 | return fs.readFile(staticPath) 167 | .then(fileBuffer => serveACheekyFile(path, staticPath, fileBuffer), () => doSomeFastBoot(event, path)) 168 | .then(r => callback(null, r), () => doSomeFastBoot(event, fallbackPath)) 169 | .then(r => callback(null, r)) 170 | .catch(error => { 171 | console.log('INFO: ERROR:', error); 172 | return callback(error); 173 | }); 174 | }; 175 | -------------------------------------------------------------------------------- /assets/lambda-package/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "fastboot": "1.0.0-rc.6", 4 | "fs-promise": "0.5.0", 5 | "mine": "0.1.0" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ember-cli-deploy-fastboot-api-lambda", 3 | "dependencies": {} 4 | } 5 | -------------------------------------------------------------------------------- /config/ember-try.js: -------------------------------------------------------------------------------- 1 | /*jshint node:true*/ 2 | module.exports = { 3 | scenarios: [ 4 | { 5 | name: 'ember-lts-2.4', 6 | bower: { 7 | dependencies: { 8 | 'ember': 'components/ember#lts-2-4' 9 | }, 10 | resolutions: { 11 | 'ember': 'lts-2-4' 12 | } 13 | }, 14 | npm: { 15 | devDependencies: { 16 | 'ember-source': null 17 | } 18 | } 19 | }, 20 | { 21 | name: 'ember-lts-2.8', 22 | bower: { 23 | dependencies: { 24 | 'ember': 'components/ember#lts-2-8' 25 | }, 26 | resolutions: { 27 | 'ember': 'lts-2-8' 28 | } 29 | }, 30 | npm: { 31 | devDependencies: { 32 | 'ember-source': null 33 | } 34 | } 35 | }, 36 | { 37 | name: 'ember-release', 38 | bower: { 39 | dependencies: { 40 | 'ember': 'components/ember#release' 41 | }, 42 | resolutions: { 43 | 'ember': 'release' 44 | } 45 | }, 46 | npm: { 47 | devDependencies: { 48 | 'ember-source': null 49 | } 50 | } 51 | }, 52 | { 53 | name: 'ember-beta', 54 | bower: { 55 | dependencies: { 56 | 'ember': 'components/ember#beta' 57 | }, 58 | resolutions: { 59 | 'ember': 'beta' 60 | } 61 | }, 62 | npm: { 63 | devDependencies: { 64 | 'ember-source': null 65 | } 66 | } 67 | }, 68 | { 69 | name: 'ember-canary', 70 | bower: { 71 | dependencies: { 72 | 'ember': 'components/ember#canary' 73 | }, 74 | resolutions: { 75 | 'ember': 'canary' 76 | } 77 | }, 78 | npm: { 79 | devDependencies: { 80 | 'ember-source': null 81 | } 82 | } 83 | }, 84 | { 85 | name: 'ember-default', 86 | npm: { 87 | devDependencies: {} 88 | } 89 | } 90 | ] 91 | }; 92 | -------------------------------------------------------------------------------- /config/environment.js: -------------------------------------------------------------------------------- 1 | /*jshint node:true*/ 2 | 'use strict'; 3 | 4 | module.exports = function(/* environment, appConfig */) { 5 | return { }; 6 | }; 7 | -------------------------------------------------------------------------------- /ember-cli-build.js: -------------------------------------------------------------------------------- 1 | /*jshint node:true*/ 2 | /* global require, module */ 3 | var EmberAddon = require('ember-cli/lib/broccoli/ember-addon'); 4 | 5 | module.exports = function(defaults) { 6 | var app = new EmberAddon(defaults, { 7 | // Add options here 8 | }); 9 | 10 | /* 11 | This build file specifies the options for the dummy test app of this 12 | addon, located in `/tests/dummy` 13 | This build file does *not* influence how the addon or the app using it 14 | behave. You most likely want to be modifying `./index.js` or app's build file 15 | */ 16 | 17 | return app.toTree(); 18 | }; 19 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /* jshint node: true */ 2 | 'use strict'; 3 | 4 | var FastbootAPILambdaDeployPlugin = require('./lib/fastboot-api-lambda-deploy-plugin'); 5 | 6 | module.exports = { 7 | name: 'ember-cli-deploy-fastboot-api-lambda', 8 | 9 | createDeployPlugin: function(options) { 10 | return new FastbootAPILambdaDeployPlugin({ 11 | name: options.name 12 | }); 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /lib/fastboot-api-lambda-deploy-plugin.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const DEFAULT_REGION = 'us-west-2'; 4 | 5 | const DeployPlugin = require('ember-cli-deploy-plugin'); 6 | const fs = require('fs-promise'); 7 | const path = require('path'); 8 | const AWS = require('aws-sdk'); 9 | const RSVP = require('rsvp'); 10 | const exec = RSVP.denodeify(require('child_process').exec); 11 | 12 | module.exports = DeployPlugin.extend({ 13 | requiredConfig: ['lambdaFunction'], 14 | 15 | _getConfig(context) { 16 | const config = Object.assign({}, context.config['fastboot-api-lambda']); 17 | return config; 18 | }, 19 | 20 | _getSafeConfig(context) { 21 | const config = this._getConfig(context); 22 | delete config.accessKeyId; 23 | delete config.secretAccessKey; 24 | return config; 25 | }, 26 | 27 | _getPaths(context) { 28 | const addonRootPath = path.join(__dirname, '..'); 29 | const projectRootPath = context.project.root; 30 | 31 | const skeletonPath = path.join(addonRootPath, 'assets/lambda-package'); 32 | const tempPath = path.join(projectRootPath, 'tmp/lambda-package'); 33 | 34 | return { 35 | addonRootPath, 36 | projectRootPath, 37 | skeletonPath, 38 | tempPath 39 | }; 40 | }, 41 | 42 | didBuild: function(context) { 43 | const config = this._getSafeConfig(context); 44 | const paths = this._getPaths(context); 45 | 46 | const addonRootPath = paths.addonRootPath; 47 | const projectRootPath = paths.projectRootPath 48 | const skeletonPath = paths.skeletonPath; 49 | const tempPath = paths.tempPath; 50 | 51 | return RSVP.resolve() 52 | .then(() => this.log(`1/6. Cleaning up any existing temp files`)) 53 | .then(() => fs.remove(tempPath)) 54 | .then(() => fs.remove(`${tempPath}.zip`)) 55 | 56 | .then(() => this.log(`2/6. Cloning skeleton FastBoot server`)) 57 | .then(() => fs.copy(skeletonPath, tempPath)) 58 | 59 | .then(() => this.log(`3/6. Installing FastBoot server dependencies`)) 60 | .then(() => exec("npm install --production", { cwd: tempPath })) 61 | 62 | .then(() => this.log(`4/6. Cloning config into FastBoot server directory`)) 63 | .then(() => { 64 | const json = JSON.stringify(config); 65 | return fs.writeFile(`${tempPath}/config.json`, json, 'utf8'); 66 | }) 67 | 68 | .then(() => this.log(`5/6. Cloning FastBoot build into FastBoot server directory`)) 69 | .then(() => fs.copy(context.distDir, `${tempPath}/dist`)) 70 | 71 | .then(() => this.log(`6/6. Installing dependencies of the FastBoot build`)) 72 | .then(() => exec('npm install --production', { cwd: `${tempPath}/dist` })) 73 | 74 | .then(() => this.log(`API FastBoot lambda production bundle successfully built`)); 75 | }, 76 | 77 | activate: function(context) { 78 | const tempPath = this._getPaths(context).tempPath; 79 | const config = this._getConfig(context); 80 | 81 | const lambdaFunction = config.lambdaFunction; 82 | 83 | const lambdaConfig = { 84 | region: config.region || DEFAULT_REGION 85 | }; 86 | 87 | if (config.accessKeyId) { 88 | lambdaConfig.accessKeyId = config.accessKeyId; 89 | } 90 | 91 | if (config.secretAccessKey) { 92 | lambdaConfig.secretAccessKey = config.secretAccessKey; 93 | } 94 | 95 | const Lambda = new AWS.Lambda(lambdaConfig); 96 | const UpdateLambdaFunc = RSVP.denodeify(Lambda.updateFunctionCode.bind(Lambda)); 97 | 98 | return RSVP.resolve() 99 | .then(() => this.log('1/3. zipping up API FastBoot lambda bundle')) 100 | .then(() => exec("zip -qr lambda-package.zip *", { cwd: tempPath })) 101 | .then(() => exec("mv lambda-package.zip ../", { cwd: tempPath })) 102 | 103 | .then(() => this.log('2/3. Reading zip file into file buffer')) 104 | .then(() => fs.readFile(`${tempPath}.zip`)) 105 | 106 | .then(fileBuf => { 107 | this.log(`3/3. Uploading zip to ${lambdaFunction} lambda to ${lambdaConfig.region} region`); 108 | return UpdateLambdaFunc({ 109 | FunctionName: lambdaFunction, 110 | ZipFile: fileBuf 111 | }); 112 | }) 113 | 114 | .then(() => this.log(`API FastBoot lambda production bundle successfully uploaded to "${lambdaFunction}" lambda in region "${lambdaConfig.region}" 🚀`)); 115 | } 116 | }); 117 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ember-cli-deploy-fastboot-api-lambda", 3 | "version": "0.0.7", 4 | "description": "An ambitious ember-cli-deploy plugin for serving Ember FastBoot Applications entirely from within AWS Lambda/API Gateway (assets and all!).", 5 | "keywords": [ 6 | "ember-addon", 7 | "ember-cli-deploy-plugin" 8 | ], 9 | "license": "MIT", 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/wytlytningNZ/ember-cli-deploy-fastboot-api-lambda" 13 | }, 14 | "author": "Joshua Hollinshead ", 15 | "directories": { 16 | "doc": "doc", 17 | "test": "tests" 18 | }, 19 | "scripts": { 20 | "build": "ember build", 21 | "start": "ember server", 22 | "test": "ember try:each" 23 | }, 24 | "dependencies": { 25 | "ember-cli-babel": "^5.1.7", 26 | "aws-sdk": "^2.2.39", 27 | "ember-cli-babel": "^5.1.5", 28 | "ember-cli-deploy-plugin": "^0.2.2", 29 | "fs-promise": "^0.5.0", 30 | "rsvp": "^3.2.1" 31 | }, 32 | "devDependencies": { 33 | "broccoli-asset-rev": "^2.4.5", 34 | "ember-ajax": "^2.4.1", 35 | "ember-cli": "2.11.0", 36 | "ember-cli-app-version": "^2.0.0", 37 | "ember-cli-dependency-checker": "^1.3.0", 38 | "ember-cli-htmlbars": "^1.1.1", 39 | "ember-cli-htmlbars-inline-precompile": "^0.3.3", 40 | "ember-cli-inject-live-reload": "^1.4.1", 41 | "ember-cli-jshint": "^2.0.1", 42 | "ember-cli-qunit": "^3.0.1", 43 | "ember-cli-release": "^0.2.9", 44 | "ember-cli-shims": "^1.0.2", 45 | "ember-cli-sri": "^2.1.0", 46 | "ember-cli-test-loader": "^1.1.0", 47 | "ember-cli-uglify": "^1.2.0", 48 | "ember-data": "^2.11.0", 49 | "ember-disable-prototype-extensions": "^1.1.0", 50 | "ember-export-application-global": "^1.0.5", 51 | "ember-load-initializers": "^0.6.0", 52 | "ember-resolver": "^2.0.3", 53 | "ember-source": "^2.11.0", 54 | "ember-welcome-page": "^2.0.2", 55 | "loader.js": "^4.0.10" 56 | }, 57 | "engines": { 58 | "node": ">= 0.12.0" 59 | }, 60 | "ember-addon": { 61 | "configPath": "tests/dummy/config", 62 | "after": [ 63 | "ember-cli-deploy-lightning-pack", 64 | "ember-cli-deploy-build" 65 | ] 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /testem.js: -------------------------------------------------------------------------------- 1 | /*jshint node:true*/ 2 | module.exports = { 3 | "framework": "qunit", 4 | "test_page": "tests/index.html?hidepassed", 5 | "disable_watching": true, 6 | "launch_in_ci": [ 7 | "PhantomJS" 8 | ], 9 | "launch_in_dev": [ 10 | "PhantomJS", 11 | "Chrome" 12 | ] 13 | }; 14 | -------------------------------------------------------------------------------- /tests/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "predef": [ 3 | "document", 4 | "window", 5 | "location", 6 | "setTimeout", 7 | "$", 8 | "-Promise", 9 | "define", 10 | "console", 11 | "visit", 12 | "exists", 13 | "fillIn", 14 | "click", 15 | "keyEvent", 16 | "triggerEvent", 17 | "find", 18 | "findWithAssert", 19 | "wait", 20 | "DS", 21 | "andThen", 22 | "currentURL", 23 | "currentPath", 24 | "currentRouteName" 25 | ], 26 | "node": false, 27 | "browser": false, 28 | "boss": true, 29 | "curly": true, 30 | "debug": false, 31 | "devel": false, 32 | "eqeqeq": true, 33 | "evil": true, 34 | "forin": false, 35 | "immed": false, 36 | "laxbreak": false, 37 | "newcap": true, 38 | "noarg": true, 39 | "noempty": false, 40 | "nonew": false, 41 | "nomen": false, 42 | "onevar": false, 43 | "plusplus": false, 44 | "regexp": false, 45 | "undef": true, 46 | "sub": true, 47 | "strict": false, 48 | "white": false, 49 | "eqnull": true, 50 | "esversion": 6, 51 | "unused": true 52 | } 53 | -------------------------------------------------------------------------------- /tests/dummy/app/app.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import Resolver from './resolver'; 3 | import loadInitializers from 'ember-load-initializers'; 4 | import config from './config/environment'; 5 | 6 | let App; 7 | 8 | Ember.MODEL_FACTORY_INJECTIONS = true; 9 | 10 | App = Ember.Application.extend({ 11 | modulePrefix: config.modulePrefix, 12 | podModulePrefix: config.podModulePrefix, 13 | Resolver 14 | }); 15 | 16 | loadInitializers(App, config.modulePrefix); 17 | 18 | export default App; 19 | -------------------------------------------------------------------------------- /tests/dummy/app/components/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kiwi-josh/ember-cli-deploy-fastboot-api-lambda/31ab85c924f72ccc3398ca11bd3f404508c7fbfe/tests/dummy/app/components/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/controllers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kiwi-josh/ember-cli-deploy-fastboot-api-lambda/31ab85c924f72ccc3398ca11bd3f404508c7fbfe/tests/dummy/app/controllers/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/helpers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kiwi-josh/ember-cli-deploy-fastboot-api-lambda/31ab85c924f72ccc3398ca11bd3f404508c7fbfe/tests/dummy/app/helpers/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Dummy 7 | 8 | 9 | 10 | {{content-for "head"}} 11 | 12 | 13 | 14 | 15 | {{content-for "head-footer"}} 16 | 17 | 18 | {{content-for "body"}} 19 | 20 | 21 | 22 | 23 | {{content-for "body-footer"}} 24 | 25 | 26 | -------------------------------------------------------------------------------- /tests/dummy/app/models/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kiwi-josh/ember-cli-deploy-fastboot-api-lambda/31ab85c924f72ccc3398ca11bd3f404508c7fbfe/tests/dummy/app/models/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/resolver.js: -------------------------------------------------------------------------------- 1 | import Resolver from 'ember-resolver'; 2 | 3 | export default Resolver; 4 | -------------------------------------------------------------------------------- /tests/dummy/app/router.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import config from './config/environment'; 3 | 4 | const Router = Ember.Router.extend({ 5 | location: config.locationType, 6 | rootURL: config.rootURL 7 | }); 8 | 9 | Router.map(function() { 10 | }); 11 | 12 | export default Router; 13 | -------------------------------------------------------------------------------- /tests/dummy/app/routes/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kiwi-josh/ember-cli-deploy-fastboot-api-lambda/31ab85c924f72ccc3398ca11bd3f404508c7fbfe/tests/dummy/app/routes/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/styles/app.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kiwi-josh/ember-cli-deploy-fastboot-api-lambda/31ab85c924f72ccc3398ca11bd3f404508c7fbfe/tests/dummy/app/styles/app.css -------------------------------------------------------------------------------- /tests/dummy/app/templates/application.hbs: -------------------------------------------------------------------------------- 1 | {{!-- The following component displays Ember's default welcome message. --}} 2 | {{welcome-page}} 3 | {{!-- Feel free to remove this! --}} 4 | 5 | {{outlet}} 6 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/components/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kiwi-josh/ember-cli-deploy-fastboot-api-lambda/31ab85c924f72ccc3398ca11bd3f404508c7fbfe/tests/dummy/app/templates/components/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/config/environment.js: -------------------------------------------------------------------------------- 1 | /* jshint node: true */ 2 | 3 | module.exports = function(environment) { 4 | var ENV = { 5 | modulePrefix: 'dummy', 6 | environment: environment, 7 | rootURL: '/', 8 | locationType: 'auto', 9 | EmberENV: { 10 | FEATURES: { 11 | // Here you can enable experimental features on an ember canary build 12 | // e.g. 'with-controller': true 13 | }, 14 | EXTEND_PROTOTYPES: { 15 | // Prevent Ember Data from overriding Date.parse. 16 | Date: false 17 | } 18 | }, 19 | 20 | APP: { 21 | // Here you can pass flags/options to your application instance 22 | // when it is created 23 | } 24 | }; 25 | 26 | if (environment === 'development') { 27 | // ENV.APP.LOG_RESOLVER = true; 28 | // ENV.APP.LOG_ACTIVE_GENERATION = true; 29 | // ENV.APP.LOG_TRANSITIONS = true; 30 | // ENV.APP.LOG_TRANSITIONS_INTERNAL = true; 31 | // ENV.APP.LOG_VIEW_LOOKUPS = true; 32 | } 33 | 34 | if (environment === 'test') { 35 | // Testem prefers this... 36 | ENV.locationType = 'none'; 37 | 38 | // keep test console output quieter 39 | ENV.APP.LOG_ACTIVE_GENERATION = false; 40 | ENV.APP.LOG_VIEW_LOOKUPS = false; 41 | 42 | ENV.APP.rootElement = '#ember-testing'; 43 | } 44 | 45 | if (environment === 'production') { 46 | 47 | } 48 | 49 | return ENV; 50 | }; 51 | -------------------------------------------------------------------------------- /tests/dummy/public/crossdomain.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 15 | 16 | -------------------------------------------------------------------------------- /tests/dummy/public/robots.txt: -------------------------------------------------------------------------------- 1 | # http://www.robotstxt.org 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /tests/helpers/destroy-app.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | export default function destroyApp(application) { 4 | Ember.run(application, 'destroy'); 5 | } 6 | -------------------------------------------------------------------------------- /tests/helpers/module-for-acceptance.js: -------------------------------------------------------------------------------- 1 | import { module } from 'qunit'; 2 | import Ember from 'ember'; 3 | import startApp from '../helpers/start-app'; 4 | import destroyApp from '../helpers/destroy-app'; 5 | 6 | const { RSVP: { Promise } } = Ember; 7 | 8 | export default function(name, options = {}) { 9 | module(name, { 10 | beforeEach() { 11 | this.application = startApp(); 12 | 13 | if (options.beforeEach) { 14 | return options.beforeEach.apply(this, arguments); 15 | } 16 | }, 17 | 18 | afterEach() { 19 | let afterEach = options.afterEach && options.afterEach.apply(this, arguments); 20 | return Promise.resolve(afterEach).then(() => destroyApp(this.application)); 21 | } 22 | }); 23 | } 24 | -------------------------------------------------------------------------------- /tests/helpers/resolver.js: -------------------------------------------------------------------------------- 1 | import Resolver from '../../resolver'; 2 | import config from '../../config/environment'; 3 | 4 | const resolver = Resolver.create(); 5 | 6 | resolver.namespace = { 7 | modulePrefix: config.modulePrefix, 8 | podModulePrefix: config.podModulePrefix 9 | }; 10 | 11 | export default resolver; 12 | -------------------------------------------------------------------------------- /tests/helpers/start-app.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import Application from '../../app'; 3 | import config from '../../config/environment'; 4 | 5 | export default function startApp(attrs) { 6 | let application; 7 | 8 | let attributes = Ember.merge({}, config.APP); 9 | attributes = Ember.merge(attributes, attrs); // use defaults, but you can override; 10 | 11 | Ember.run(() => { 12 | application = Application.create(attributes); 13 | application.setupForTesting(); 14 | application.injectTestHelpers(); 15 | }); 16 | 17 | return application; 18 | } 19 | -------------------------------------------------------------------------------- /tests/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Dummy Tests 7 | 8 | 9 | 10 | {{content-for "head"}} 11 | {{content-for "test-head"}} 12 | 13 | 14 | 15 | 16 | 17 | {{content-for "head-footer"}} 18 | {{content-for "test-head-footer"}} 19 | 20 | 21 | {{content-for "body"}} 22 | {{content-for "test-body"}} 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | {{content-for "body-footer"}} 31 | {{content-for "test-body-footer"}} 32 | 33 | 34 | -------------------------------------------------------------------------------- /tests/integration/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kiwi-josh/ember-cli-deploy-fastboot-api-lambda/31ab85c924f72ccc3398ca11bd3f404508c7fbfe/tests/integration/.gitkeep -------------------------------------------------------------------------------- /tests/test-helper.js: -------------------------------------------------------------------------------- 1 | import resolver from './helpers/resolver'; 2 | import { 3 | setResolver 4 | } from 'ember-qunit'; 5 | 6 | setResolver(resolver); 7 | -------------------------------------------------------------------------------- /tests/unit/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kiwi-josh/ember-cli-deploy-fastboot-api-lambda/31ab85c924f72ccc3398ca11bd3f404508c7fbfe/tests/unit/.gitkeep -------------------------------------------------------------------------------- /vendor/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kiwi-josh/ember-cli-deploy-fastboot-api-lambda/31ab85c924f72ccc3398ca11bd3f404508c7fbfe/vendor/.gitkeep --------------------------------------------------------------------------------