├── app
└── .gitkeep
├── addon
└── .gitkeep
├── vendor
└── .gitkeep
├── tests
├── unit
│ └── .gitkeep
├── integration
│ └── .gitkeep
├── dummy
│ ├── app
│ │ ├── helpers
│ │ │ └── .gitkeep
│ │ ├── models
│ │ │ └── .gitkeep
│ │ ├── routes
│ │ │ └── .gitkeep
│ │ ├── styles
│ │ │ └── app.css
│ │ ├── components
│ │ │ └── .gitkeep
│ │ ├── controllers
│ │ │ └── .gitkeep
│ │ ├── templates
│ │ │ ├── components
│ │ │ │ └── .gitkeep
│ │ │ └── application.hbs
│ │ ├── resolver.js
│ │ ├── router.js
│ │ ├── app.js
│ │ └── index.html
│ ├── public
│ │ ├── robots.txt
│ │ └── crossdomain.xml
│ └── config
│ │ └── environment.js
├── test-helper.js
├── helpers
│ ├── destroy-app.js
│ ├── resolver.js
│ ├── start-app.js
│ └── module-for-acceptance.js
├── .jshintrc
└── index.html
├── .watchmanconfig
├── .bowerrc
├── bower.json
├── config
├── environment.js
└── ember-try.js
├── assets
└── lambda-package
│ ├── package.json
│ └── index.js
├── .npmignore
├── testem.js
├── .ember-cli
├── .gitignore
├── index.js
├── .editorconfig
├── ember-cli-build.js
├── .jshintrc
├── LICENSE.md
├── .travis.yml
├── package.json
├── lib
└── fastboot-api-lambda-deploy-plugin.js
└── README.md
/app/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/addon/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/vendor/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/unit/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/integration/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/dummy/app/helpers/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/dummy/app/models/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/dummy/app/routes/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/dummy/app/styles/app.css:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/dummy/app/components/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/dummy/app/controllers/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/dummy/app/templates/components/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.watchmanconfig:
--------------------------------------------------------------------------------
1 | {
2 | "ignore_dirs": ["tmp", "dist"]
3 | }
4 |
--------------------------------------------------------------------------------
/.bowerrc:
--------------------------------------------------------------------------------
1 | {
2 | "directory": "bower_components",
3 | "analytics": false
4 | }
5 |
--------------------------------------------------------------------------------
/tests/dummy/public/robots.txt:
--------------------------------------------------------------------------------
1 | # http://www.robotstxt.org
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ember-cli-deploy-fastboot-api-lambda",
3 | "dependencies": {}
4 | }
5 |
--------------------------------------------------------------------------------
/tests/dummy/app/resolver.js:
--------------------------------------------------------------------------------
1 | import Resolver from 'ember-resolver';
2 |
3 | export default Resolver;
4 |
--------------------------------------------------------------------------------
/config/environment.js:
--------------------------------------------------------------------------------
1 | /*jshint node:true*/
2 | 'use strict';
3 |
4 | module.exports = function(/* environment, appConfig */) {
5 | return { };
6 | };
7 |
--------------------------------------------------------------------------------
/tests/test-helper.js:
--------------------------------------------------------------------------------
1 | import resolver from './helpers/resolver';
2 | import {
3 | setResolver
4 | } from 'ember-qunit';
5 |
6 | setResolver(resolver);
7 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/tests/dummy/public/crossdomain.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
15 |
16 |
--------------------------------------------------------------------------------
/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/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/.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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ember-cli-deploy-fastboot-api-lambda
2 |
3 | [](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 |
--------------------------------------------------------------------------------