diff --git a/.gitignore b/.gitignore index 986b126a3..5ad2d3dcf 100755 --- a/.gitignore +++ b/.gitignore @@ -6,12 +6,17 @@ *.pid *.gz +**/.idea +.DS_Store +.tmp + +# Runtime data pids logs results -.DS_Store npm-debug.log .env +.adminenv site/public/libs site/node_modules diff --git a/aws/data-model-cf.json b/aws/data-model-cf.json new file mode 100644 index 000000000..490de6ff2 --- /dev/null +++ b/aws/data-model-cf.json @@ -0,0 +1,147 @@ +{ + "AWSTemplateFormatVersion": "2010-09-09", + "Parameters": { + "aaaPrefix": { + "Type": "String", + "Default": "test", + "AllowedValues": [ + "me", + "test", + "prod" + ] + }, + "aaaDefaultDynamoRWThroughput": { + "Type": "String", + "Default": "1" + }, + "aaaProjectName": { + "Type": "String", + "Default": "jaws", + "AllowedValues": [ + "jaws" + ] + }, + "HostedZoneName": { + "Type": "String", + "Default": "doapps.co" + }, + "imagesReadThroughput": { + "Type": "String", + "Default": "1" + }, + "imagesWriteThroughput": { + "Type": "String", + "Default": "1" + } + }, + "Resources": { + "images": { + "Type": "AWS::DynamoDB::Table", + "Properties": { + "TableName": { + "Fn::Join": [ + "-", + [ + { + "Ref": "aaaPrefix" + }, + { + "Ref": "aaaProjectName" + }, + "images" + ] + ] + }, + "AttributeDefinitions": [ + { + "AttributeName": "bucketKey", + "AttributeType": "S" + } + ], + "KeySchema": [ + { + "AttributeName": "bucketKey", + "KeyType": "HASH" + } + ], + "ProvisionedThroughput": { + "ReadCapacityUnits": { + "Ref": "imagesReadThroughput" + }, + "WriteCapacityUnits": { + "Ref": "imagesWriteThroughput" + } + } + } + }, + "users": { + "Type": "AWS::DynamoDB::Table", + "DependsOn": "images", + "Properties": { + "TableName": { + "Fn::Join": [ + "-", + [ + { + "Ref": "aaaPrefix" + }, + { + "Ref": "aaaProjectName" + }, + "users" + ] + ] + }, + "AttributeDefinitions": [ + { + "AttributeName": "_id", + "AttributeType": "S" + }, + { + "AttributeName": "created", + "AttributeType": "N" + } + ], + "KeySchema": [ + { + "AttributeName": "_id", + "KeyType": "HASH" + }, + { + "AttributeName": "created", + "KeyType": "RANGE" + } + ], + "ProvisionedThroughput": { + "ReadCapacityUnits": { + "Ref": "aaaDefaultDynamoRWThroughput" + }, + "WriteCapacityUnits": { + "Ref": "aaaDefaultDynamoRWThroughput" + } + } + } + }, + "imagesBucket": { + "Type": "AWS::S3::Bucket", + "DeletionPolicy": "Delete", + "Properties": { + "BucketName": { + "Fn::Join": [ + "", + [ + { + "Ref": "aaaProjectName" + }, + "-images.", + { + "Ref": "HostedZoneName" + } + ] + ] + }, + "AccessControl": "PublicRead" + } + } + } +} \ No newline at end of file diff --git a/aws/perms-cf.json b/aws/perms-cf.json new file mode 100644 index 000000000..85c2b03e5 --- /dev/null +++ b/aws/perms-cf.json @@ -0,0 +1,166 @@ +{ + "AWSTemplateFormatVersion": "2010-09-09", + "Description": "JAWS", + "Parameters": { + "aaaStage": { + "Type": "String", + "Default": "test" + }, + "aaaDataModelPrefix": { + "Type": "String", + "Default": "test", + "AllowedValues": [ + "test", + "prod" + ] + }, + "aaaProjectName": { + "Type": "String", + "Default": "jaws", + "AllowedValues": [ + "jaws" + ] + } + }, + "Resources": { + "LambdaRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + }, + "Action": [ + "sts:AssumeRole" + ] + } + ] + }, + "Path": "/", + "Policies": [ + { + "PolicyName": "CloudWatch", + "PolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Resource": "arn:aws:logs:*:*:*" + } + ] + } + }, + { + "PolicyName": "s3Write", + "PolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "s3:Get*", + "s3:List*", + "s3:Put*" + ], + "Resource": [ + { + "Fn::Join": [ + "", + [ + "arn:aws:s3:::", + { + "Ref": "aaaProjectName" + }, + "-images/", + { + "Ref": "aaaDataModelPrefix" + }, + "*" + ] + ] + } + ], + "Effect": "Allow" + } + ] + } + }, + { + "PolicyName": "DynamoWrite", + "PolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "dynamodb:BatchGetItem", + "dynamodb:BatchWriteItem", + "dynamodb:DeleteItem", + "dynamodb:DescribeTable", + "dynamodb:Get*", + "dynamodb:List*", + "dynamodb:PutItem", + "dynamodb:Query", + "dynamodb:Scan", + "dynamodb:UpdateItem", + "dynamodb:UpdateTable" + ], + "Resource": [ + { + "Fn::Join": [ + "", + [ + "arn:aws:dynamodb:us-east-1:", + { + "Ref": "AWS::AccountId" + }, + ":table/", + { + "Ref": "aaaDataModelPrefix" + }, + { + "Ref": "aaaProjectName" + }, + "-users*" + ] + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:aws:dynamodb:us-east-1:", + { + "Ref": "AWS::AccountId" + }, + ":table/", + { + "Ref": "aaaDataModelPrefix" + }, + { + "Ref": "aaaProjectName" + }, + "-images*" + ] + ] + } + ] + } + ] + } + } + ] + } + } + } +} \ No newline at end of file diff --git a/cli/bin/jaws b/cli/bin/jaws index feb4db4db..5a88d342f 100755 --- a/cli/bin/jaws +++ b/cli/bin/jaws @@ -1,9 +1,9 @@ #!/usr/bin/env node -var JAWS = require('../lib/main.js'); +var JAWS = require('../lib/main.js'); var program = require('commander'); var shortid = require('shortid'); - +var Q = require('q'); /** @@ -12,12 +12,32 @@ var shortid = require('shortid'); */ program - .version(JAWS.version) - .command( 'deploy' ) - .description( 'Deploy your application to Amazon Lambda' ) - .action( function( prg ) { - JAWS.deploy( prg ); - }); + .version(JAWS.version) + .command('deploy ') + .description('Deploy your application to Amazon Lambda') + .action(function (stage, prg) { + try { + JAWS.deploy(stage) + .then(function (functionArns) { + console.log('****** JAWS: Success! - Your Lambda Function has been successfully deployed to AWS Lambda. This Lambda Function\'s ARNs are: '); + console.log("\t", functionArns.join(', ')); + process.exit(0); + }) + .fail(function (err) { + console.error(err, err.stack); + process.exit(-1); + }); + } catch (err) { + console.error(err, err.stack); + process.exit(-1); + } + }) + .on('--help', function () { + console.log(' Examples:'); + console.log("\n\tdeploy prod"); + console.log("\n\tdeploy test"); + console.log(); + }); /** @@ -26,12 +46,12 @@ program */ program - .version( JAWS.version ) - .command( 'run' ) - .description( 'Run your Amazon Lambda application locally' ) - .action( function( prg ) { - JAWS.run( prg ); - }); + .version(JAWS.version) + .command('run') + .description('Run your Amazon Lambda application locally') + .action(function (prg) { + JAWS.run(); + }); /** @@ -40,12 +60,12 @@ program */ program - .version( JAWS.version ) - .command( 'server' ) - .description( 'Start your server' ) - .action( function( prg ) { - JAWS.server( prg ); - }); + .version(JAWS.version) + .command('server') + .description('Start your server') + .action(function (prg) { + JAWS.server(); + }); -program.parse( process.argv ); +program.parse(process.argv); diff --git a/cli/jaws.yml b/cli/jaws.yml new file mode 100644 index 000000000..3ce10a520 --- /dev/null +++ b/cli/jaws.yml @@ -0,0 +1,22 @@ +version: 0.0 +jaws: + deploy: + envS3Location: lambdadeploy.doapps.com/jaws/env/ #will read stage (prod,test,developername etc) containing contents of temp.env + regions: + - us-east-1 + packageExcludes: #see man page on zip -x. Ex: -x *.DS_Store *.svn* + - "*.git/*" + - "node_modules/chai/*" + - "node_modules/mocha/*" + - "tests/*" + packageFiles: #list of files/dirs passed to zip, relative to lib dir + - controllers + - lib + - node_modules + - config.js + - index.js + - .env # created by aws-deployment + iamRoles: + test: arn:aws:iam::28933237018:role/test-jaws_lambda_role + prod: arn:aws:iam::28933237018:role/prod-jaws_lambda_role + me: arn:aws:iam::28933237018:role/me-jaws_lambda_role diff --git a/cli/lib/main.js b/cli/lib/main.js index 82ada380c..53cd42027 100755 --- a/cli/lib/main.js +++ b/cli/lib/main.js @@ -3,23 +3,22 @@ */ -var aws = require('aws-sdk'); -var exec = require('child_process').exec; -var fs = require('fs'); -var os = require('os'); -var sys = require('sys'); +var aws = require('aws-sdk'); +var exec = require('child_process').exec; +var fs = require('fs'); +var os = require('os'); var packageJson = require('./../package.json'); -var path = require('path'); -var async = require('async'); -var zip = new require('node-zip')(); -var wrench = require('wrench'); -var jsonfile = require('jsonfile'); -var del = require('del'); -var moment = require('moment'); +var path = require('path'); +var async = require('async'); +var zip = new require('node-zip')(); +var wrench = require('wrench'); +var moment = require('moment'); +var yaml = require('js-yaml'); +var Q = require('q'); - -var JAWS = function() { - this.version = packageJson.version; +var JAWS = function () { + this.version = packageJson.version; + this.configYml = yaml.safeLoad(fs.readFileSync(__dirname + '/../jaws.yml', 'utf8')).jaws; return this; }; @@ -28,7 +27,7 @@ var JAWS = function() { * JAWS: Run Lambda Function Locally */ -JAWS.prototype.run = function(program) { +JAWS.prototype.run = function () { console.log('****** JAWS: Running Lambda Function Locally...'); @@ -36,26 +35,26 @@ JAWS.prototype.run = function(program) { if (!fs.existsSync(process.cwd() + '/index.js')) return console.log('****** JAWS: Error - You must be in the root directory of your Lambda function to run it locally.'); var extension = require(process.cwd() + '/index.js'); - var event = require(process.cwd() + '/event.json'); + var event = require(process.cwd() + '/event.json'); // Run Handler this._runHandler(extension.handler, event); }; -JAWS.prototype._runHandler = function(handler, event) { +JAWS.prototype._runHandler = function (handler, event) { var context = { - succeed: function(result) { + succeed: function (result) { console.log('****** JAWS: Lambda Finished Successfully: '); console.log(result); return process.exit(0); }, - fail: function(error) { + fail: function (error) { console.log('****** JAWS: Lambda Returned An Error: '); console.log(error); return process.exit(0); }, - done: function(error, result) { + done: function (error, result) { if (error) { console.log('****** JAWS: Lambda Returned An Error: '); @@ -75,48 +74,51 @@ JAWS.prototype._runHandler = function(handler, event) { }; - - - /** * JAWS: Deploy Lambda Function */ -JAWS.prototype.deploy = function(program) { +JAWS.prototype.deploy = function (stage) { + var deferred = Q.defer(); console.log('****** JAWS: Deploying your Lambda function to AWS Lambda. This could take a few minutes...'); // Require ENV Variables require('dotenv').config({ - path: process.cwd() + '/node_modules/app-lib/.env' + path: process.cwd() + '/node_modules/app-lib/.adminenv' }); // Defaults - var _this = this; - var fs = require('fs'); + var _this = this; var previous = '/'; - var regions = process.env.AWS_LAMBDA_REGIONS.split(','); - var codeDirectory = os.tmpDir() + process.env.LAMBDA_FUNCTION_NAME + '-' + moment().unix(); + var regions = this.configYml.deploy.regions; // Get Lambda Config if (!fs.existsSync(process.cwd() + '/lambda.json')) return console.log('****** JAWS Error: lambda.json is missing in this folder'); - var lambda_config = require(process.cwd() + '/lambda.json'); + var lambda_config = require(process.cwd() + '/lambda.json'); + lambda_config.FunctionName = stage + "_" + lambda_config.FunctionName; + + var codeDirectory = os.tmpDir() + lambda_config.FunctionName + '-' + moment().unix(); // Get path to "lib" folder var lib_path = false; - for (i = 0; i < 20; i++) { + for (var i = 0; i < 20; i++) { previous = previous + '../'; if (fs.existsSync(process.cwd() + previous + 'lib/index.js')) { lib_path = previous + 'lib'; break; } } - if (!lib_path) return console.log('***** JAWS Error: Can\'t find your lib folder. Did you rename it or create folders over 20 levels deep in your api folder?'); + if (!lib_path) { + return Q.fcall(function () { + throw new Error('***** JAWS Error: Can\'t find your lib folder. Did you rename it or create folders over 20 levels deep in your api folder?') + }); + } // Copy Lambda Folder To System Temp Directory wrench.copyDirSyncRecursive(process.cwd(), codeDirectory, { forceDelete: true, - include: function(name, more) { + include: function (name, more) { if (name === '.git') return false; else return true; } @@ -129,15 +131,13 @@ JAWS.prototype.deploy = function(program) { wrench.copyDirSyncRecursive(process.cwd() + lib_path, codeDirectory + '/node_modules/app-lib'); // Zip function - _this._zip(program, codeDirectory, function(err, buffer) { + _this._zip(lambda_config.FunctionName, codeDirectory, function (err, buffer) { console.log('****** JAWS: Zipping up your Lambda Function\'s files...'); - async.map(regions, function(region, cb) { + async.map(regions, function (region, cb) { - aws.config.update({ - accessKeyId: process.env.AWS_ADMIN_ACCESS_KEY, - secretAccessKey: process.env.AWS_ADMIN_SECRET_ACCESS_KEY, + aws.config.update({ //SDK auto-loads creds from process.env.AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY region: region }); @@ -152,7 +152,7 @@ JAWS.prototype.deploy = function(program) { }, FunctionName: lambda_config.FunctionName, Handler: lambda_config.Handler ? lambda_config.Handler : 'index.handler', - Role: lambda_config.Role ? lambda_config.Role : process.env.AWS_LAMBDA_ROLE_ARN, + Role: lambda_config.Role ? lambda_config.Role : _this.configYml.iamRoles[stage], Runtime: lambda_config.Runtime, Description: lambda_config.Description ? lambda_config.Description : 'A Lambda function that was created with the JAWS framework', MemorySize: lambda_config.MemorySize, @@ -162,9 +162,11 @@ JAWS.prototype.deploy = function(program) { // Check If Lambda Function Exists Already lambda.getFunction({ FunctionName: lambda_config.FunctionName - }, function(err, data) { + }, function (err, data) { - if (err && err.code !== 'ResourceNotFoundException') return console.log(err, err.stack); + if (err && err.code !== 'ResourceNotFoundException') { + return deferred.reject(err); + } if (!data || !data.Code) { @@ -173,10 +175,10 @@ JAWS.prototype.deploy = function(program) { * Create New Lambda Function */ - console.log('****** JAWS: Uploading your Lambda Function to AWS Lambda with these parameters: '); + console.log('****** JAWS: Uploading your Lambda function to ' + stage + ' with these parameters: '); console.log(params); - lambda.createFunction(params, function(err, data) { + lambda.createFunction(params, function (err, data) { lambda_arn = data; return cb(err, data); }); @@ -192,55 +194,62 @@ JAWS.prototype.deploy = function(program) { lambda.deleteFunction({ FunctionName: lambda_config.FunctionName - }, function(err, data) { + }, function (err, data) { - if (err) return console.log(err, err.stack); // an error occurred + if (err) { + return deferred.reject(err); + } console.log('****** JAWS: Re-uploading your Lambda Function to AWS Lambda with these parameters: '); console.log(params); - lambda.createFunction(params, function(err, data) { + lambda.createFunction(params, function (err, data) { return cb(err, data); }); }); } }); - }, function(err, results) { + }, function (err, results) { - if (err) return console.log(err); + if (err) { + return deferred.reject(err); + } - // Return - console.log('****** JAWS: Success! - Your Lambda Function has been successfully deployed to AWS Lambda. This Lambda Function\'s ARNs are: '); - for (i = 0; i < results.length; i++) console.log(results[i].FunctionArn); - return; + var functionArns = []; + for (var i in results) { + functionArns.push(results[i].FunctionArn); + } + deferred.resolve(functionArns); }); }); + + return deferred.promise; }; -JAWS.prototype._zipfileTmpPath = function(program) { +JAWS.prototype._zipfileTmpPath = function (functionName) { var ms_since_epoch = +new Date; - var filename = program.functionName + '-' + ms_since_epoch + '.zip'; - var zipfile = path.join(os.tmpDir(), filename); + var filename = functionName + '-' + ms_since_epoch + '.zip'; + var zipfile = path.join(os.tmpDir(), filename); return zipfile; }; -JAWS.prototype._zip = function(program, codeDirectory, callback) { - var zipfile = this._zipfileTmpPath(program); +JAWS.prototype._zip = function (functionName, codeDirectory, callback) { + var zipfile = this._zipfileTmpPath(functionName); var options = { type: 'nodebuffer', compression: 'DEFLATE' - } + }; var files = wrench.readdirSyncRecursive(codeDirectory); - files.forEach(function(file) { + files.forEach(function (file) { var filePath = [codeDirectory, file].join('/'); - var isFile = fs.lstatSync(filePath).isFile(); + var isFile = fs.lstatSync(filePath).isFile(); if (isFile) { var content = fs.readFileSync(filePath); zip.file(file, content); @@ -253,17 +262,16 @@ JAWS.prototype._zip = function(program, codeDirectory, callback) { }; - /** * JAWS: Start "site" Server */ -JAWS.prototype.server = function(program) { +JAWS.prototype.server = function () { // Check if in server root folder if (!fs.existsSync(process.cwd() + '/server.js')) return console.log('****** JAWS: Error - You must be in the "site" directory of your JAWS application to run this command and start the server.'); - var child = exec('node server', function(error, stdout, stderr) { + var child = exec('node server', function (error, stdout, stderr) { if (error !== null) console.log('exec error: ' + error); }); @@ -272,7 +280,5 @@ JAWS.prototype.server = function(program) { }; - - // Export module.exports = new JAWS(); \ No newline at end of file diff --git a/cli/package.json b/cli/package.json index 99623a248..7ddbd019d 100644 --- a/cli/package.json +++ b/cli/package.json @@ -1,55 +1,57 @@ { - "name": "jaws-cli", - "version": "0.0.1", - "description": "Command line interface for the JAWS stack", - "main": "lib/main.js", - "directories": { - "test": "test" - }, - "scripts": { - "test": "mocha test/*.js" - }, - "bin": { - "jaws": "./bin/jaws" - }, - "repository": { - "type": "git", - "url": "git://github.com/servant-app/JAWS.git" - }, - "keywords": [ - "jaws", - "jaws stack", - "api gateway", - "lambda", - "aws", - "amazon", - "amazon-lambda", - "aws-lambda" - ], - "readmeFilename": "README.md", - "author": "ac360", - "license": "BSD", - "devDependencies": { - "adm-zip": "^0.4.7", - "chai": "^2.0.0", - "hoek": "^2.11.1", - "lodash": "^3.2.0", - "mocha": "", - "should": "" - }, - "dependencies": { - "async": "^0.9.0", - "aws-sdk": "^2.1.24", - "commander": "^2.5.0", - "del": "^1.2.0", - "dotenv": "^1.2.0", - "jsonfile": "^2.2.1", - "moment": "^2.10.6", - "node-uuid": "^1.4.2", - "node-zip": "^1.1.0", - "nodemon": "^1.3.8", - "rimraf": "^2.2.8", - "shortid": "^2.2.2", - "wrench": "^1.5.8" - } -} \ No newline at end of file + "name": "jaws-cli", + "version": "0.0.1", + "description": "Command line interface for the JAWS stack", + "main": "lib/main.js", + "directories": { + "test": "test" + }, + "scripts": { + "test": "mocha test/*.js" + }, + "bin": { + "jaws": "./bin/jaws" + }, + "repository": { + "type": "git", + "url": "git://github.com/servant-app/JAWS.git" + }, + "keywords": [ + "jaws", + "jaws stack", + "api gateway", + "lambda", + "aws", + "amazon", + "amazon-lambda", + "aws-lambda" + ], + "readmeFilename": "README.md", + "author": "ac360", + "license": "BSD", + "devDependencies": { + "adm-zip": "^0.4.7", + "chai": "^2.0.0", + "hoek": "^2.11.1", + "lodash": "^3.2.0", + "mocha": "", + "should": "" + }, + "dependencies": { + "async": "^0.9.0", + "aws-sdk": "^2.1.24", + "commander": "^2.5.0", + "del": "^1.2.0", + "dotenv": "^1.2.0", + "js-yaml": "^3.3.1", + "jsonfile": "^2.2.1", + "moment": "^2.10.6", + "node-uuid": "^1.4.2", + "node-zip": "^1.1.0", + "nodemon": "^1.3.8", + "q": "^1.4.1", + "rimraf": "^2.2.8", + "shortid": "^2.2.2", + "wrench": "^1.5.8" + } +} diff --git a/cli/temp.adminenv b/cli/temp.adminenv new file mode 100644 index 000000000..fbf0f5604 --- /dev/null +++ b/cli/temp.adminenv @@ -0,0 +1,3 @@ +### IAM Admin with perms to create/update lambdas and deploy +AWS_ACCESS_KEY_ID= +AWS_SECRET_ACCESS_KEY= \ No newline at end of file diff --git a/cli/tests/all.js b/cli/tests/all.js new file mode 100644 index 000000000..d51e75387 --- /dev/null +++ b/cli/tests/all.js @@ -0,0 +1,13 @@ +describe('AllTests', function () { + before(function (done) { + this.timeout(0); //dont timeout anything, creating tables, deleting tables etc + + done(); + }); + + after(function () { + }); + + //require('./wsclient/index'); + require('./controllers/weather'); +}); \ No newline at end of file diff --git a/cli/tests/deploy/index.js b/cli/tests/deploy/index.js new file mode 100644 index 000000000..9ed63a3b5 --- /dev/null +++ b/cli/tests/deploy/index.js @@ -0,0 +1,59 @@ +var assert = require("chai").assert, + JAWS = require('../../lib/main'); + +describe('deploy', function () { + before(function (done) { + this.timeout(0); //dont timeout anything, creating tables, deleting tables etc + done(); + }); + + describe('test env', function (ddone) { + this.timeout(0); + + it('successful', function (done) { + this.timeout(0); + + JAWS.deploy("test", {}); + + ////rochester, MN + //weather + // .closestPlaces(44.0150757, -92.4775256, 30, 50000) + // .then(function (locations) { + // assert.deepEqual(locations, ['Blooming Prairie, MN', + // 'Waltham, MN', + // 'Hayfield, MN', + // 'Claremont, MN', + // 'Dodge Center, MN', + // 'West Concord, MN', + // 'Kenyon, MN', + // 'Sargeant, MN', + // 'Racine, MN', + // 'Stewartville, MN', + // 'Chatfield, MN', + // 'Kasson, MN', + // 'Mantorville, MN', + // 'Byron, MN', + // 'Rochester, MN', + // 'Zumbrota, MN', + // 'Pine Island, MN', + // 'Oronoco, MN', + // 'Mazeppa, MN', + // 'Eyota, MN', + // 'Dover, MN', + // 'Viola, MN', + // 'Elgin, MN', + // 'Hammond, MN', + // 'Zumbro Falls, MN', + // 'Millville, MN', + // 'Plainview, MN', + // 'Theilman, MN']); + // done(); + // }) + // .catch(function (err) { + // console.error(err); + // done(err); + // }); + }); + + }); +}); \ No newline at end of file diff --git a/lib/config/config.js b/lib/config/config.js index 0324fb445..9b6efbaf1 100755 --- a/lib/config/config.js +++ b/lib/config/config.js @@ -16,9 +16,8 @@ module.exports = { // AWS aws: { - admin_access_key: process.env.AWS_ADMIN_ACCESS_KEY, - admin_secret_access_key: process.env.AWS_ADMIN_SECRET_ACCESS_KEY, - aws_region: 'us-east-1' + dynamoDbEndpoint: global.process.env.DYNAMODB_LOCAL_ENDPT || '' //"http://localhost:8000" for localdynamodb-local + //logger: process.stdout //uncomment if you want aws sdk logging }, // JSON Web Token diff --git a/lib/models/model_aws.js b/lib/models/model_aws.js index 5429f2ab0..5ac72657b 100755 --- a/lib/models/model_aws.js +++ b/lib/models/model_aws.js @@ -7,22 +7,18 @@ // Dependencies var Config = require('../config/config'); -var AWS = require('aws-sdk'); - - -// Config AWS Client -AWS.config.update({ - accessKeyId: Config.aws.admin_access_key, - secretAccessKey: Config.aws.admin_secret_access_key, - region: Config.aws.aws_region -}); +var AWS = require('aws-sdk'); +// Creds read from IAM role or +if (Config.aws.logger) { + AWS.config.update({logger: config.aws.logger}); +} /** * Export AWS Services */ -module.exports.DynamoDB = function() { +module.exports.DynamoDB = function () { var DOC = require('dynamodb-doc'); return new DOC.DynamoDB(new AWS.DynamoDB()); }; diff --git a/lib/models/model_user.js b/lib/models/model_user.js index 776dae096..95c6ee149 100755 --- a/lib/models/model_user.js +++ b/lib/models/model_user.js @@ -5,33 +5,33 @@ // Dependencies -var Config = require('../config/config'); -var Utilities = require('../utilities/utilities'); +var Config = require('../config/config'); +var Utilities = require('../utilities/utilities'); var AppDynamoDB = require('./model_aws').DynamoDB; -AppDynamoDB = new AppDynamoDB(); +AppDynamoDB = new AppDynamoDB(); -var moment = require('moment'); +var moment = require('moment'); var bcryptjs = require('bcryptjs'); -var _ = require('lodash'); -var jwt = require('jsonwebtoken'); +var _ = require('lodash'); +var jwt = require('jsonwebtoken'); // DynamoDB Table Name for this Model -var dynamodb_table = 'jaws_users'; +var dynamodb_table = process.env.JAWS_DATA_MODEL_PREFIX + '-jaws-users'; // Export module.exports = new User(); -function User() {} - +function User() { +} /** * SignUp */ -User.prototype.signUp = function(data, callback) { +User.prototype.signUp = function (data, callback) { // Defaults var _this = this; @@ -58,7 +58,7 @@ User.prototype.signUp = function(data, callback) { // Check if email is already in use - _this.showByEmail(data.email, function(error, user) { + _this.showByEmail(data.email, function (error, user) { if (error) return callback(error, null); @@ -69,7 +69,6 @@ User.prototype.signUp = function(data, callback) { }, null); - /** * Instantiate */ @@ -89,19 +88,18 @@ User.prototype.signUp = function(data, callback) { user = _this.hashPassword(user); - /** * Save */ - _this.save(user, function(error, user) { + _this.save(user, function (error, user) { if (error) return callback(error, null); /** - * Create JSON Web Token & Return + * Create JSON Web Token & Return */ var token = jwt.sign({ @@ -119,14 +117,11 @@ User.prototype.signUp = function(data, callback) { } - - - /** * SignIn */ -User.prototype.signIn = function(data, callback) { +User.prototype.signIn = function (data, callback) { // Defaults var _this = this; @@ -147,9 +142,8 @@ User.prototype.signIn = function(data, callback) { }, null); - // Check if email is already in use - _this.showByEmail(data.email, function(error, user) { + _this.showByEmail(data.email, function (error, user) { if (error) return callback(error, null); @@ -166,24 +160,21 @@ User.prototype.signIn = function(data, callback) { }, null); - // Update User user.sign_in_count++; - /** * Save */ - _this.save(user, function(error, user) { + _this.save(user, function (error, user) { if (error) return (error, null); - /** - * Create JSON Web Token & Return + * Create JSON Web Token & Return */ var token = jwt.sign({ @@ -202,14 +193,11 @@ User.prototype.signIn = function(data, callback) { } - - - /** * ShowByEmail */ -User.prototype.showByEmail = function(email, callback) { +User.prototype.showByEmail = function (email, callback) { /** @@ -222,19 +210,18 @@ User.prototype.showByEmail = function(email, callback) { }, null); - /** * Find User */ - var params = {}; - params.TableName = dynamodb_table; - params.IndexName = "email-index"; + var params = {}; + params.TableName = dynamodb_table; + params.IndexName = "email-index"; params.KeyConditions = [ AppDynamoDB.Condition("email", "EQ", email) ]; - AppDynamoDB.query(params, function(error, response) { + AppDynamoDB.query(params, function (error, response) { if (error || !response) return callback({ status: 500, @@ -248,16 +235,11 @@ User.prototype.showByEmail = function(email, callback) { } - - - - - /** * ShowByID */ -User.prototype.showByID = function(user_id, callback) { +User.prototype.showByID = function (user_id, callback) { /** @@ -270,18 +252,17 @@ User.prototype.showByID = function(user_id, callback) { }, null); - /** * Find User */ - var params = {}; - params.TableName = dynamodb_table; + var params = {}; + params.TableName = dynamodb_table; params.KeyConditions = [ AppDynamoDB.Condition("_id", "EQ", user_id) ]; - AppDynamoDB.query(params, function(error, response) { + AppDynamoDB.query(params, function (error, response) { if (error || !response) return callback({ status: 500, @@ -295,14 +276,12 @@ User.prototype.showByID = function(user_id, callback) { } - - /** * Save * - Updates existing record or creates a record if one does not already exist */ -User.prototype.save = function(user, callback) { +User.prototype.save = function (user, callback) { /** @@ -341,48 +320,47 @@ User.prototype.save = function(user, callback) { * Basic Information */ - // email - params.UpdateExpression = params.UpdateExpression + '#a0 = :email_val, '; - params.ExpressionAttributeNames['#a0'] = 'email'; + // email + params.UpdateExpression = params.UpdateExpression + '#a0 = :email_val, '; + params.ExpressionAttributeNames['#a0'] = 'email'; params.ExpressionAttributeValues[':email_val'] = user.email; // password - params.UpdateExpression = params.UpdateExpression + '#a1 = :password_val, '; - params.ExpressionAttributeNames['#a1'] = 'password'; + params.UpdateExpression = params.UpdateExpression + '#a1 = :password_val, '; + params.ExpressionAttributeNames['#a1'] = 'password'; params.ExpressionAttributeValues[':password_val'] = user.password; // salt - params.UpdateExpression = params.UpdateExpression + '#a2 = :salt_val, '; - params.ExpressionAttributeNames['#a2'] = 'salt'; + params.UpdateExpression = params.UpdateExpression + '#a2 = :salt_val, '; + params.ExpressionAttributeNames['#a2'] = 'salt'; params.ExpressionAttributeValues[':salt_val'] = user.salt; // plan - params.UpdateExpression = params.UpdateExpression + '#a3 = :plan_val, '; - params.ExpressionAttributeNames['#a3'] = 'plan'; + params.UpdateExpression = params.UpdateExpression + '#a3 = :plan_val, '; + params.ExpressionAttributeNames['#a3'] = 'plan'; params.ExpressionAttributeValues[':plan_val'] = user.plan; // sign_in_count - params.UpdateExpression = params.UpdateExpression + '#a4 = :sign_in_count_val, '; - params.ExpressionAttributeNames['#a4'] = 'sign_in_count'; + params.UpdateExpression = params.UpdateExpression + '#a4 = :sign_in_count_val, '; + params.ExpressionAttributeNames['#a4'] = 'sign_in_count'; params.ExpressionAttributeValues[':sign_in_count_val'] = user.sign_in_count; // updated - params.UpdateExpression = params.UpdateExpression + '#b0 = :updated_val, '; - params.ExpressionAttributeNames['#b0'] = 'updated'; + params.UpdateExpression = params.UpdateExpression + '#b0 = :updated_val, '; + params.ExpressionAttributeNames['#b0'] = 'updated'; params.ExpressionAttributeValues[':updated_val'] = moment().unix(); - /** * Save */ - // Remove Any Trailing Commas & Space In Update Expression + // Remove Any Trailing Commas & Space In Update Expression params.UpdateExpression = params.UpdateExpression.trim(); if (params.UpdateExpression[params.UpdateExpression.length - 1] === ',') params.UpdateExpression = params.UpdateExpression.substring(0, params.UpdateExpression.length - 1); - AppDynamoDB.updateItem(params, function(error, response) { + AppDynamoDB.updateItem(params, function (error, response) { if (error || !response) return callback({ status: 500, @@ -395,15 +373,13 @@ User.prototype.save = function(user, callback) { } - - /** * Hash Password */ -User.prototype.hashPassword = function(user) { +User.prototype.hashPassword = function (user) { - user.salt = bcryptjs.genSaltSync(10); + user.salt = bcryptjs.genSaltSync(10); user.password = bcryptjs.hashSync(user.password, user.salt); return user; diff --git a/lib/package.json b/lib/package.json index 0f854aa2f..6d90d3c34 100755 --- a/lib/package.json +++ b/lib/package.json @@ -9,8 +9,8 @@ "test": "test" }, "engines": { - "node": "0.12.x", - "npm": "2.1.x" + "node": "0.10.33", + "npm": "1.4.28" }, "dependencies": { "async": "^1.2.1", diff --git a/lib/temp.env b/lib/temp.env index 9b2d0a05b..46ff104b8 100644 --- a/lib/temp.env +++ b/lib/temp.env @@ -2,12 +2,13 @@ JWT_ISSUER=JAWS JWT_SECRET=JAWSJAWSJAWS123 -#### AWS Variables -AWS_ADMIN_ACCESS_KEY=123123 -AWS_ADMIN_SECRET_ACCESS_KEY=456456 -AWS_LAMBDA_ROLE_ARN=arn:aws:iam::28933237018:role/jaws_lambda_role -AWS_LAMBDA_REGIONS=us-east-1 - - +### AWS SDK Variables. Key/secret are auto populated by IAM Role +AWS_REGION=us-east-1 + +### JAWS Variables +JAWS_DATA_MODEL_PREFIX=test + +### Custom Variables +GOOGLE_SERVER_API_KEY= diff --git a/package.json b/package.json index 1deb8841c..f2296c925 100644 --- a/package.json +++ b/package.json @@ -1,30 +1,29 @@ { - "name": "jaws-stack", - "version": "0.0.1", - "description": "The Javascript + AWS Web Application Boilerplate", - "author": "Austen Collins", - "repository": { - "type": "git", - "url": "https://github.com/servant-app/JAWS.git" - }, - "keywords": [ - "jaws", - "api gateway", - "lambda", - "aws", - "boilerplate", - "command", - "dynamodb", - "s3", - "template", - "framework" - ], - "engines": { - "node": "0.12.x", - "npm": "2.1.x" - }, - "bugs": { - "url": "https://github.com/servant-app/JAWS/issues" - }, - "dependencies": {} -} \ No newline at end of file + "name": "jaws-stack", + "version": "0.0.1", + "description": "The Javascript + AWS Web Application Boilerplate", + "author": "Austen Collins", + "repository": { + "type": "git", + "url": "https://github.com/servant-app/JAWS.git" + }, + "keywords": [ + "jaws", + "api gateway", + "lambda", + "aws", + "boilerplate", + "command", + "dynamodb", + "s3", + "template", + "framework" + ], + "engines": { + "node": "0.10.33", + "npm": "1.4.28" + }, + "bugs": { + "url": "https://github.com/servant-app/JAWS/issues" + } +}