From 49dc0fbe0d90febe04bcab840352b33b4494cc90 Mon Sep 17 00:00:00 2001 From: David Luecke Date: Sun, 8 Nov 2015 15:19:34 -0800 Subject: [PATCH 01/31] Some more layout for app architecture and generator --- app/index.js | 73 +++++++++++++++++-- app/templates/src/authentication.js | 3 + app/templates/src/config/default.json | 4 + app/templates/src/config/production.json | 4 + app/templates/src/hooks/index.js | 4 +- app/templates/src/index.js | 28 ++++++- app/templates/src/middleware/error.js | 24 ++++++ app/templates/src/middleware/home.js | 30 ++++++++ app/templates/src/middleware/index.js | 9 +++ app/templates/src/services/index.js | 6 +- hook/templates/hook.js | 4 + hook/templates/hook.test.js | 0 package.json | 7 +- .../todos.js => service/templates/service.js | 0 service/templates/service.test.js | 0 15 files changed, 177 insertions(+), 19 deletions(-) create mode 100644 app/templates/src/authentication.js create mode 100644 app/templates/src/config/default.json create mode 100644 app/templates/src/config/production.json create mode 100644 app/templates/src/middleware/error.js create mode 100644 app/templates/src/middleware/home.js create mode 100644 app/templates/src/middleware/index.js create mode 100644 hook/templates/hook.js create mode 100644 hook/templates/hook.test.js rename app/templates/src/services/todos.js => service/templates/service.js (100%) create mode 100644 service/templates/service.test.js diff --git a/app/index.js b/app/index.js index 94160457..4eed8965 100644 --- a/app/index.js +++ b/app/index.js @@ -11,7 +11,13 @@ module.exports = generators.Base.extend({ name: process.cwd().split(path.sep).pop() }; this.dotfiles = ['editorconfig', 'gitignore', 'jshintrc', 'npmignore', 'travis.yml']; - this.files = ['package.json', 'src/index.js', 'test/index.test.js', 'LICENSE', 'README.md']; + this.files = [ + 'package.json', + 'src/index.js', + 'test/index.test.js', + 'LICENSE', + 'README.md' + ]; }, prompting: function () { @@ -21,18 +27,67 @@ module.exports = generators.Base.extend({ message: 'Project name', when: !this.pkg.name, default: this.props.name - }, { - name: 'repository', - message: 'The GitHub repository URL (e.g. feathersjs/feathers-myplugin)', - default: 'feathersjs/' + this.props.name }, { name: 'description', message: 'Description', when: !this.pkg.description + }, { + type: 'checkbox', + name: 'providers', + message: 'What providers should your API support?', + choices: [{ + name: 'REST', + checked: true + }, { + name: 'Socket.io', + checked: true + }, { + name: 'Primus' + }] + }, { + type: 'list', + name: 'database', + message: 'What database do you primarily want to use?', + choices: [{ + name: 'I will choose my own', + checked: true + }, { + name: 'memory' + }, { + name: 'mongodb' + }, { + name: 'mongoose' + }, { + name: 'nedb' + }, { + name: 'mysql' + }, { + name: 'postgresql' + }, { + name: 'sqlite' + }] + }, { + type: 'checkbox', + name: 'authentication', + message: 'What authentication methods would you like to support?', + choices: [{ + name: 'basic' + }, { + name: 'local' + }, { + name: 'google' + }, { + name: 'facebook' + }, { + name: 'twitter' + }, { + name: 'github' + }] }]; this.prompt(prompts, function (props) { this.props = _.extend(this.props, props); + this.config.set('database', this.props.database); done(); }.bind(this)); @@ -55,11 +110,15 @@ module.exports = generators.Base.extend({ ); }.bind(this)); + this.npmInstall([ + 'feathers', + 'feathers-hooks' + ], { save: true }); + this.npmInstall([ 'babel', 'jshint', - 'mocha', - 'feathers' + 'mocha' ], { saveDev: true}); } }); diff --git a/app/templates/src/authentication.js b/app/templates/src/authentication.js new file mode 100644 index 00000000..4706ae1e --- /dev/null +++ b/app/templates/src/authentication.js @@ -0,0 +1,3 @@ +export default function() { + const app = this; +} diff --git a/app/templates/src/config/default.json b/app/templates/src/config/default.json new file mode 100644 index 00000000..726312f3 --- /dev/null +++ b/app/templates/src/config/default.json @@ -0,0 +1,4 @@ +{ + "host": "localhost", + "port": 3030 +} diff --git a/app/templates/src/config/production.json b/app/templates/src/config/production.json new file mode 100644 index 00000000..584f5b69 --- /dev/null +++ b/app/templates/src/config/production.json @@ -0,0 +1,4 @@ +{ + "host": "myapp.com", + "port": 80, +} diff --git a/app/templates/src/hooks/index.js b/app/templates/src/hooks/index.js index 10324356..4706ae1e 100644 --- a/app/templates/src/hooks/index.js +++ b/app/templates/src/hooks/index.js @@ -1,5 +1,3 @@ export default function() { - return function() { - // TODO - }; + const app = this; } diff --git a/app/templates/src/index.js b/app/templates/src/index.js index 2098dc24..7fca1cda 100644 --- a/app/templates/src/index.js +++ b/app/templates/src/index.js @@ -1,5 +1,29 @@ import feathers from 'feathers'; +<% if(providers.indexOf('REST') !== -1) { %> +import bodyParser from 'body-parser'; +<% } %> +import middleware from './middleware'; +import services from './services'; +import hooks from './hooks'; +import authentication from './authentication'; const app = feathers() - .configure(services()) - .configura(hooks()); + <% if(providers.indexOf('REST') !== -1) { %> + .configure(feathers.rest()) + .use(bodyParser.json()) + .use(bodyParser.urlencoded({ extended: true })) + <% } %> + <% if(providers.indexOf('Socket.io') !== -1) { %> + .configure(feathers.socketio()) + <% } %> + <% if(providers.indexOf('Primus') !== -1) { %> + .configure(feathers.primus({ + transformer: 'sockjs' + })) + <% } %> + .configure(authentication) + .configure(services) + .configure(hooks) + .configure(middleware); + +export default app; diff --git a/app/templates/src/middleware/error.js b/app/templates/src/middleware/error.js new file mode 100644 index 00000000..2c0a3a9a --- /dev/null +++ b/app/templates/src/middleware/error.js @@ -0,0 +1,24 @@ +export default function(error, req, res, next) { + res.status(error.code || 500); + + res.format({ + 'text/html': function() { + res.end(` + + + + Feathers error + + +

Ooop, something went wrong

+

${error.message}

+ + + `); + }, + + 'application/json': function () { + res.json(error); + } + }); +} diff --git a/app/templates/src/middleware/home.js b/app/templates/src/middleware/home.js new file mode 100644 index 00000000..58d2b732 --- /dev/null +++ b/app/templates/src/middleware/home.js @@ -0,0 +1,30 @@ +export default function(req, res) { + const services = req.app.services; + + res.format({ + 'text/html': function() { + const list = Object.keys(services) + .map(name => `
  • ${name}
  • `); + + res.end(` + + + + Feathers API + + +

    Welcome to your Feathers API

    +

    The following services are available:

    + + + + `); + }, + + 'application/json': function () { + res.json({ + services: Object.keys(services) + }); + } + }); +} diff --git a/app/templates/src/middleware/index.js b/app/templates/src/middleware/index.js new file mode 100644 index 00000000..a8861eb8 --- /dev/null +++ b/app/templates/src/middleware/index.js @@ -0,0 +1,9 @@ +import home from './home'; +import errorHandler from './error'; + +export default function() { + const app = this; + + app.get('/', home) + .use(errorHandler); +} diff --git a/app/templates/src/services/index.js b/app/templates/src/services/index.js index 7a884fdd..4706ae1e 100644 --- a/app/templates/src/services/index.js +++ b/app/templates/src/services/index.js @@ -1,7 +1,3 @@ -import todos from './todos'; - export default function() { - return function() { - this.use('/todos', todos); - }; + const app = this; } diff --git a/hook/templates/hook.js b/hook/templates/hook.js new file mode 100644 index 00000000..8e824dc9 --- /dev/null +++ b/hook/templates/hook.js @@ -0,0 +1,4 @@ +export default function(hook, next) { + hook.data.ran = true; + next(); +} diff --git a/hook/templates/hook.test.js b/hook/templates/hook.test.js new file mode 100644 index 00000000..e69de29b diff --git a/package.json b/package.json index 7cfa8a32..200efa95 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "generator-feathers", - "description": "A Yeoman generator for a FEathers application", + "description": "A Yeoman generator for a Feathers application", "version": "0.0.0", "homepage": "https://github.com/feathersjs/generator-feathers", "main": "lib/", @@ -44,7 +44,10 @@ "directories": { "lib": "lib" }, - "dependencies": {}, + "dependencies": { + "lodash": "^3.10.1", + "yeoman-generator": "^0.21.1" + }, "devDependencies": { "babel": "^5.8.29", "feathers": "^1.1.1", diff --git a/app/templates/src/services/todos.js b/service/templates/service.js similarity index 100% rename from app/templates/src/services/todos.js rename to service/templates/service.js diff --git a/service/templates/service.test.js b/service/templates/service.test.js new file mode 100644 index 00000000..e69de29b From 163df8631ef2aad6b1739a6cf3e3f1983f7d6b6d Mon Sep 17 00:00:00 2001 From: David Luecke Date: Mon, 16 Nov 2015 10:47:45 -0800 Subject: [PATCH 02/31] Improving infrastructure --- app/templates/config/default.json | 5 +++ .../{src => }/config/production.json | 0 app/templates/src/{index.js => app.js} | 0 app/templates/src/config.js | 32 +++++++++++++++++ app/templates/src/config/default.json | 4 --- app/templates/src/middleware/error.js | 34 +++++++++++-------- app/templates/src/middleware/home.js | 30 ---------------- app/templates/src/middleware/index.js | 4 +-- app/templates/src/public/404.html | 10 ++++++ app/templates/src/public/error.html | 10 ++++++ app/templates/src/public/index.html | 10 ++++++ app/templates/test/app.test.js | 26 ++++++++++++++ app/templates/test/index.test.js | 9 ----- 13 files changed, 115 insertions(+), 59 deletions(-) create mode 100644 app/templates/config/default.json rename app/templates/{src => }/config/production.json (100%) rename app/templates/src/{index.js => app.js} (100%) create mode 100644 app/templates/src/config.js delete mode 100644 app/templates/src/config/default.json delete mode 100644 app/templates/src/middleware/home.js create mode 100644 app/templates/src/public/404.html create mode 100644 app/templates/src/public/error.html create mode 100644 app/templates/src/public/index.html create mode 100644 app/templates/test/app.test.js delete mode 100644 app/templates/test/index.test.js diff --git a/app/templates/config/default.json b/app/templates/config/default.json new file mode 100644 index 00000000..8a6b5011 --- /dev/null +++ b/app/templates/config/default.json @@ -0,0 +1,5 @@ +{ + "host": "localhost", + "port": 3030, + "public": "public/" +} diff --git a/app/templates/src/config/production.json b/app/templates/config/production.json similarity index 100% rename from app/templates/src/config/production.json rename to app/templates/config/production.json diff --git a/app/templates/src/index.js b/app/templates/src/app.js similarity index 100% rename from app/templates/src/index.js rename to app/templates/src/app.js diff --git a/app/templates/src/config.js b/app/templates/src/config.js new file mode 100644 index 00000000..ba18d365 --- /dev/null +++ b/app/templates/src/config.js @@ -0,0 +1,32 @@ +import _ from 'lodash'; +import fs from 'fs'; +import { resolve } from 'path'; +import config from '../config/config.json'; + +module.exports = function () { + return function() { + const app = this; + const env = app.settings.env; + const config = `../config/${env}.json`; + + app.info(`Initializing configuration for ${env} environment`); + + // Dev is our default development. For everything else extend the default + if(env !== 'development' && fs.existsSync(config)) { + _.extend(config, require(config)); + } + + _.each(config, (value, name) => { + if(process.env[value]) { + value = process.env[value]; + } + + // Make relative paths absolute + if(typeof value === 'string' && (value.indexOf('./') === 0 || value.indexOf('../') === 0)) { + value = resolve(__dirname, '..', value); + } + + app.set(name, value); + }); + }; +}; diff --git a/app/templates/src/config/default.json b/app/templates/src/config/default.json deleted file mode 100644 index 726312f3..00000000 --- a/app/templates/src/config/default.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "host": "localhost", - "port": 3030 -} diff --git a/app/templates/src/middleware/error.js b/app/templates/src/middleware/error.js index 2c0a3a9a..84ebfc3b 100644 --- a/app/templates/src/middleware/error.js +++ b/app/templates/src/middleware/error.js @@ -1,24 +1,30 @@ +// jshint unused:false +import path from 'path'; + export default function(error, req, res, next) { - res.status(error.code || 500); + const app = req.app; + const code = error.code || 500; + + res.status(code); res.format({ 'text/html': function() { - res.end(` - - - - Feathers error - - -

    Ooop, something went wrong

    -

    ${error.message}

    - - - `); + const file = code === 404 ? '404.html' : 'error.html'; + res.sendFile(path.join(app.get('public'), file)); }, 'application/json': function () { - res.json(error); + let output = { + code, + message: error.message, + name: error.name || 'General error' + }; + + if(app.settings.env !== 'production') { + output.stack = error.stack; + } + + res.json(output); } }); } diff --git a/app/templates/src/middleware/home.js b/app/templates/src/middleware/home.js deleted file mode 100644 index 58d2b732..00000000 --- a/app/templates/src/middleware/home.js +++ /dev/null @@ -1,30 +0,0 @@ -export default function(req, res) { - const services = req.app.services; - - res.format({ - 'text/html': function() { - const list = Object.keys(services) - .map(name => `
  • ${name}
  • `); - - res.end(` - - - - Feathers API - - -

    Welcome to your Feathers API

    -

    The following services are available:

    - - - - `); - }, - - 'application/json': function () { - res.json({ - services: Object.keys(services) - }); - } - }); -} diff --git a/app/templates/src/middleware/index.js b/app/templates/src/middleware/index.js index a8861eb8..a48565eb 100644 --- a/app/templates/src/middleware/index.js +++ b/app/templates/src/middleware/index.js @@ -1,9 +1,9 @@ -import home from './home'; import errorHandler from './error'; +import { static as staticFiles } from 'feathers'; export default function() { const app = this; - app.get('/', home) + app.use('/', staticFiles(app.get('public'))) .use(errorHandler); } diff --git a/app/templates/src/public/404.html b/app/templates/src/public/404.html new file mode 100644 index 00000000..f540bc68 --- /dev/null +++ b/app/templates/src/public/404.html @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/app/templates/src/public/error.html b/app/templates/src/public/error.html new file mode 100644 index 00000000..f540bc68 --- /dev/null +++ b/app/templates/src/public/error.html @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/app/templates/src/public/index.html b/app/templates/src/public/index.html new file mode 100644 index 00000000..f540bc68 --- /dev/null +++ b/app/templates/src/public/index.html @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/app/templates/test/app.test.js b/app/templates/test/app.test.js new file mode 100644 index 00000000..fddfb85b --- /dev/null +++ b/app/templates/test/app.test.js @@ -0,0 +1,26 @@ +import assert from 'assert'; +import app from '../src/app'; + +describe('<%= name %>', () => { + before(done => { + + }); + + after(done => { + + }); + + it('starts and shows the index page', done => { + + }); + + describe('404', () => { + it('shows a 404 HTML page', done => { + + }); + + it('shows a 404 JSON error with stack trace', done => { + + }); + }); +}); diff --git a/app/templates/test/index.test.js b/app/templates/test/index.test.js deleted file mode 100644 index 5ad17b58..00000000 --- a/app/templates/test/index.test.js +++ /dev/null @@ -1,9 +0,0 @@ -import assert from 'assert'; -import plugin from '../src'; - -describe('<%= name %>', () => { - it('basic functionality', done => { - assert.equal(typeof plugin, 'function', 'It worked'); - done(); - }); -}); From df2cc6d8fff95841f8cc30ab26c438752e13e2bc Mon Sep 17 00:00:00 2001 From: David Luecke Date: Sat, 21 Nov 2015 22:57:10 -0800 Subject: [PATCH 03/31] First working draft --- app/index.js | 53 +++++++++---------- app/templates/{src => }/app.js | 19 +++---- app/templates/npmignore | 4 -- app/templates/package.json | 41 +++----------- app/templates/src/config.js | 32 ----------- app/templates/static/.babelrc | 3 ++ .../{editorconfig => static/.editorconfig} | 0 .../{gitignore => static/.gitignore} | 0 app/templates/{jshintrc => static/.jshintrc} | 0 app/templates/{ => static}/LICENSE | 0 app/templates/{ => static}/README.md | 0 .../{ => static}/config/default.json | 0 .../{ => static}/config/production.json | 0 .../{ => static}/src/authentication.js | 0 app/templates/{ => static}/src/hooks/index.js | 0 app/templates/static/src/index.js | 9 ++++ .../{ => static}/src/middleware/error.js | 0 .../{ => static}/src/middleware/index.js | 0 .../{ => static}/src/public/404.html | 0 .../{ => static}/src/public/error.html | 0 .../{ => static}/src/public/index.html | 0 .../{ => static}/src/services/index.js | 1 + app/templates/{ => static}/test/app.test.js | 0 app/templates/travis.yml | 4 -- 24 files changed, 54 insertions(+), 112 deletions(-) rename app/templates/{src => }/app.js (50%) delete mode 100644 app/templates/npmignore delete mode 100644 app/templates/src/config.js create mode 100644 app/templates/static/.babelrc rename app/templates/{editorconfig => static/.editorconfig} (100%) rename app/templates/{gitignore => static/.gitignore} (100%) rename app/templates/{jshintrc => static/.jshintrc} (100%) rename app/templates/{ => static}/LICENSE (100%) rename app/templates/{ => static}/README.md (100%) rename app/templates/{ => static}/config/default.json (100%) rename app/templates/{ => static}/config/production.json (100%) rename app/templates/{ => static}/src/authentication.js (100%) rename app/templates/{ => static}/src/hooks/index.js (100%) create mode 100644 app/templates/static/src/index.js rename app/templates/{ => static}/src/middleware/error.js (100%) rename app/templates/{ => static}/src/middleware/index.js (100%) rename app/templates/{ => static}/src/public/404.html (100%) rename app/templates/{ => static}/src/public/error.html (100%) rename app/templates/{ => static}/src/public/index.html (100%) rename app/templates/{ => static}/src/services/index.js (94%) rename app/templates/{ => static}/test/app.test.js (100%) delete mode 100644 app/templates/travis.yml diff --git a/app/index.js b/app/index.js index 4eed8965..d1d49dc5 100644 --- a/app/index.js +++ b/app/index.js @@ -10,14 +10,6 @@ module.exports = generators.Base.extend({ this.props = { name: process.cwd().split(path.sep).pop() }; - this.dotfiles = ['editorconfig', 'gitignore', 'jshintrc', 'npmignore', 'travis.yml']; - this.files = [ - 'package.json', - 'src/index.js', - 'test/index.test.js', - 'LICENSE', - 'README.md' - ]; }, prompting: function () { @@ -94,29 +86,36 @@ module.exports = generators.Base.extend({ }, writing: function () { - this.dotfiles.forEach(function(file) { - this.fs.copyTpl( - this.templatePath(file), - this.destinationPath('.' + file), - this.props - ); - }.bind(this)); + var dependencies = [ + 'feathers', + 'feathers-hooks', + 'feathers-configuration', + 'babel-core', + 'babel-preset-es2015' + ]; - this.files.forEach(function(file) { - this.fs.copyTpl( - this.templatePath(file), - this.destinationPath(file), - this.props - ); - }.bind(this)); + if(this.props.providers.indexOf('REST') !== -1) { + dependencies.push('body-parser'); + } - this.npmInstall([ - 'feathers', - 'feathers-hooks' - ], { save: true }); + this.fs.copy(this.templatePath('static'), this.destinationPath()); + this.fs.copy(this.templatePath('static/.*'), this.destinationPath()); + + this.fs.copyTpl( + this.templatePath('app.js'), + this.destinationPath('src', 'app.js'), + this.props + ); + + this.fs.copyTpl( + this.templatePath('package.json'), + this.destinationPath('package.json'), + this.props + ); + + this.npmInstall(dependencies, { save: true }); this.npmInstall([ - 'babel', 'jshint', 'mocha' ], { saveDev: true}); diff --git a/app/templates/src/app.js b/app/templates/app.js similarity index 50% rename from app/templates/src/app.js rename to app/templates/app.js index 7fca1cda..e87289a0 100644 --- a/app/templates/src/app.js +++ b/app/templates/app.js @@ -1,26 +1,21 @@ +import { join } from 'path'; import feathers from 'feathers'; -<% if(providers.indexOf('REST') !== -1) { %> -import bodyParser from 'body-parser'; -<% } %> +import configuration from 'feathers-configuration'; +<% if(providers.indexOf('REST') !== -1) { %>import bodyParser from 'body-parser';<% } %> import middleware from './middleware'; import services from './services'; import hooks from './hooks'; import authentication from './authentication'; const app = feathers() - <% if(providers.indexOf('REST') !== -1) { %> - .configure(feathers.rest()) + .configure(configuration(path.join(__dirname, '..'))) + <% if(providers.indexOf('REST') !== -1) { %>.configure(feathers.rest()) .use(bodyParser.json()) .use(bodyParser.urlencoded({ extended: true })) - <% } %> - <% if(providers.indexOf('Socket.io') !== -1) { %> - .configure(feathers.socketio()) - <% } %> - <% if(providers.indexOf('Primus') !== -1) { %> + <% } if(providers.indexOf('Socket.io') !== -1) { %>.configure(feathers.socketio())<% } if(providers.indexOf('Primus') !== -1) { %> .configure(feathers.primus({ transformer: 'sockjs' - })) - <% } %> + }))<% } %> .configure(authentication) .configure(services) .configure(hooks) diff --git a/app/templates/npmignore b/app/templates/npmignore deleted file mode 100644 index 3b851a1f..00000000 --- a/app/templates/npmignore +++ /dev/null @@ -1,4 +0,0 @@ -.idea/ -src/ -test/ -!lib/ \ No newline at end of file diff --git a/app/templates/package.json b/app/templates/package.json index 03ca0481..6229b52f 100644 --- a/app/templates/package.json +++ b/app/templates/package.json @@ -2,48 +2,23 @@ "name": "<%= name %>", "description": "<%= description %>", "version": "0.0.0", - "homepage": "https://github.com/<%= repository %>", + "homepage": "", "main": "lib/", "keywords": [ - "feathers", - "feathers-plugin" + "feathers" ], - "licenses": [ - { - "type": "MIT", - "url": "https://github.com/<%= repository %>/blob/master/LICENSE" - } - ], - "repository": { - "type": "git", - "url": "git://github.com/<%= repository %>.git" - }, - "author": { - "name": "Feathers contributors", - "email": "hello@feathersjs.com", - "url": "https://feathersjs.com" - }, + "license": "MIT", + "repository": {}, + "author": {}, "contributors": [], - "bugs": { - "url": "https://github.com/<%= repository %>/issues" - }, + "bugs": {}, "engines": { "node": ">= 0.12.0" }, "scripts": { - "prepublish": "npm run compile", - "publish": "git push origin && git push origin --tags", - "release:patch": "npm version patch && npm publish", - "release:minor": "npm version minor && npm publish", - "release:major": "npm version major && npm publish", - "compile": "rm -rf lib/ && babel -d lib/ src/", + "start": "node src/", "jshint": "jshint src/. test/. --config", "mocha": "mocha test/ --compilers js:babel/register", "test": "npm run jshint && npm run mocha" - }, - "directories": { - "lib": "lib" - }, - "dependencies": {}, - "devDependencies": {} + } } diff --git a/app/templates/src/config.js b/app/templates/src/config.js deleted file mode 100644 index ba18d365..00000000 --- a/app/templates/src/config.js +++ /dev/null @@ -1,32 +0,0 @@ -import _ from 'lodash'; -import fs from 'fs'; -import { resolve } from 'path'; -import config from '../config/config.json'; - -module.exports = function () { - return function() { - const app = this; - const env = app.settings.env; - const config = `../config/${env}.json`; - - app.info(`Initializing configuration for ${env} environment`); - - // Dev is our default development. For everything else extend the default - if(env !== 'development' && fs.existsSync(config)) { - _.extend(config, require(config)); - } - - _.each(config, (value, name) => { - if(process.env[value]) { - value = process.env[value]; - } - - // Make relative paths absolute - if(typeof value === 'string' && (value.indexOf('./') === 0 || value.indexOf('../') === 0)) { - value = resolve(__dirname, '..', value); - } - - app.set(name, value); - }); - }; -}; diff --git a/app/templates/static/.babelrc b/app/templates/static/.babelrc new file mode 100644 index 00000000..59cc8579 --- /dev/null +++ b/app/templates/static/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": [ "es2015" ] +} diff --git a/app/templates/editorconfig b/app/templates/static/.editorconfig similarity index 100% rename from app/templates/editorconfig rename to app/templates/static/.editorconfig diff --git a/app/templates/gitignore b/app/templates/static/.gitignore similarity index 100% rename from app/templates/gitignore rename to app/templates/static/.gitignore diff --git a/app/templates/jshintrc b/app/templates/static/.jshintrc similarity index 100% rename from app/templates/jshintrc rename to app/templates/static/.jshintrc diff --git a/app/templates/LICENSE b/app/templates/static/LICENSE similarity index 100% rename from app/templates/LICENSE rename to app/templates/static/LICENSE diff --git a/app/templates/README.md b/app/templates/static/README.md similarity index 100% rename from app/templates/README.md rename to app/templates/static/README.md diff --git a/app/templates/config/default.json b/app/templates/static/config/default.json similarity index 100% rename from app/templates/config/default.json rename to app/templates/static/config/default.json diff --git a/app/templates/config/production.json b/app/templates/static/config/production.json similarity index 100% rename from app/templates/config/production.json rename to app/templates/static/config/production.json diff --git a/app/templates/src/authentication.js b/app/templates/static/src/authentication.js similarity index 100% rename from app/templates/src/authentication.js rename to app/templates/static/src/authentication.js diff --git a/app/templates/src/hooks/index.js b/app/templates/static/src/hooks/index.js similarity index 100% rename from app/templates/src/hooks/index.js rename to app/templates/static/src/hooks/index.js diff --git a/app/templates/static/src/index.js b/app/templates/static/src/index.js new file mode 100644 index 00000000..0b0bf134 --- /dev/null +++ b/app/templates/static/src/index.js @@ -0,0 +1,9 @@ +require('babel-core/register'); + +var app = require('./app'); +var port = app.get('port'); +var server = app.listen(port); + +server.on('listening', function() { + console.log('Feathers application started on port ' + port); +}); diff --git a/app/templates/src/middleware/error.js b/app/templates/static/src/middleware/error.js similarity index 100% rename from app/templates/src/middleware/error.js rename to app/templates/static/src/middleware/error.js diff --git a/app/templates/src/middleware/index.js b/app/templates/static/src/middleware/index.js similarity index 100% rename from app/templates/src/middleware/index.js rename to app/templates/static/src/middleware/index.js diff --git a/app/templates/src/public/404.html b/app/templates/static/src/public/404.html similarity index 100% rename from app/templates/src/public/404.html rename to app/templates/static/src/public/404.html diff --git a/app/templates/src/public/error.html b/app/templates/static/src/public/error.html similarity index 100% rename from app/templates/src/public/error.html rename to app/templates/static/src/public/error.html diff --git a/app/templates/src/public/index.html b/app/templates/static/src/public/index.html similarity index 100% rename from app/templates/src/public/index.html rename to app/templates/static/src/public/index.html diff --git a/app/templates/src/services/index.js b/app/templates/static/src/services/index.js similarity index 94% rename from app/templates/src/services/index.js rename to app/templates/static/src/services/index.js index 4706ae1e..4dcdbf0d 100644 --- a/app/templates/src/services/index.js +++ b/app/templates/static/src/services/index.js @@ -1,3 +1,4 @@ export default function() { const app = this; + } diff --git a/app/templates/test/app.test.js b/app/templates/static/test/app.test.js similarity index 100% rename from app/templates/test/app.test.js rename to app/templates/static/test/app.test.js diff --git a/app/templates/travis.yml b/app/templates/travis.yml deleted file mode 100644 index b7b1e281..00000000 --- a/app/templates/travis.yml +++ /dev/null @@ -1,4 +0,0 @@ -language: node_js -node_js: - - 'node' - - 'iojs' From 8a546faa74a51d84e4685263dde8cf59e676e425 Mon Sep 17 00:00:00 2001 From: David Luecke Date: Sat, 21 Nov 2015 23:34:06 -0800 Subject: [PATCH 04/31] First draft for app generator with everything but authentication --- app/index.js | 3 +- app/templates/app.js | 2 +- app/templates/package.json | 2 +- app/templates/static/config/default.json | 2 +- app/templates/static/src/hooks/index.js | 1 + app/templates/static/src/middleware/index.js | 10 ++++-- app/templates/static/src/public/404.html | 10 ------ app/templates/static/src/public/error.html | 10 ------ app/templates/static/src/public/index.html | 10 ------ app/templates/static/src/services/index.js | 3 +- app/templates/static/test/app.test.js | 37 +++++++++++++++----- 11 files changed, 43 insertions(+), 47 deletions(-) delete mode 100644 app/templates/static/src/public/404.html delete mode 100644 app/templates/static/src/public/error.html delete mode 100644 app/templates/static/src/public/index.html diff --git a/app/index.js b/app/index.js index d1d49dc5..4211b5e0 100644 --- a/app/index.js +++ b/app/index.js @@ -117,7 +117,8 @@ module.exports = generators.Base.extend({ this.npmInstall([ 'jshint', - 'mocha' + 'mocha', + 'request' ], { saveDev: true}); } }); diff --git a/app/templates/app.js b/app/templates/app.js index e87289a0..da9cea20 100644 --- a/app/templates/app.js +++ b/app/templates/app.js @@ -8,7 +8,7 @@ import hooks from './hooks'; import authentication from './authentication'; const app = feathers() - .configure(configuration(path.join(__dirname, '..'))) + .configure(configuration(join(__dirname, '..'))) <% if(providers.indexOf('REST') !== -1) { %>.configure(feathers.rest()) .use(bodyParser.json()) .use(bodyParser.urlencoded({ extended: true })) diff --git a/app/templates/package.json b/app/templates/package.json index 6229b52f..ebb4b3d2 100644 --- a/app/templates/package.json +++ b/app/templates/package.json @@ -18,7 +18,7 @@ "scripts": { "start": "node src/", "jshint": "jshint src/. test/. --config", - "mocha": "mocha test/ --compilers js:babel/register", + "mocha": "mocha test/ --compilers js:babel-core/register", "test": "npm run jshint && npm run mocha" } } diff --git a/app/templates/static/config/default.json b/app/templates/static/config/default.json index 8a6b5011..ff674166 100644 --- a/app/templates/static/config/default.json +++ b/app/templates/static/config/default.json @@ -1,5 +1,5 @@ { "host": "localhost", "port": 3030, - "public": "public/" + "public": "../public/" } diff --git a/app/templates/static/src/hooks/index.js b/app/templates/static/src/hooks/index.js index 4706ae1e..19f19f11 100644 --- a/app/templates/static/src/hooks/index.js +++ b/app/templates/static/src/hooks/index.js @@ -1,3 +1,4 @@ +// jshint unused:false export default function() { const app = this; } diff --git a/app/templates/static/src/middleware/index.js b/app/templates/static/src/middleware/index.js index a48565eb..b233309f 100644 --- a/app/templates/static/src/middleware/index.js +++ b/app/templates/static/src/middleware/index.js @@ -1,9 +1,13 @@ import errorHandler from './error'; -import { static as staticFiles } from 'feathers'; +import { errors } from 'feathers'; +import { static as host } from 'feathers'; export default function() { const app = this; - app.use('/', staticFiles(app.get('public'))) - .use(errorHandler); + app.use('/', host(app.get('public'))); + app.use(function(req, res, next) { + next(new errors.types.NotFound('Page not found')); + }); + app.use(errorHandler); } diff --git a/app/templates/static/src/public/404.html b/app/templates/static/src/public/404.html deleted file mode 100644 index f540bc68..00000000 --- a/app/templates/static/src/public/404.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/app/templates/static/src/public/error.html b/app/templates/static/src/public/error.html deleted file mode 100644 index f540bc68..00000000 --- a/app/templates/static/src/public/error.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/app/templates/static/src/public/index.html b/app/templates/static/src/public/index.html deleted file mode 100644 index f540bc68..00000000 --- a/app/templates/static/src/public/index.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/app/templates/static/src/services/index.js b/app/templates/static/src/services/index.js index 4dcdbf0d..da63c075 100644 --- a/app/templates/static/src/services/index.js +++ b/app/templates/static/src/services/index.js @@ -1,4 +1,5 @@ +// jshint unused:false export default function() { const app = this; - + } diff --git a/app/templates/static/test/app.test.js b/app/templates/static/test/app.test.js index fddfb85b..cc49d62e 100644 --- a/app/templates/static/test/app.test.js +++ b/app/templates/static/test/app.test.js @@ -1,26 +1,45 @@ import assert from 'assert'; +import request from 'request'; import app from '../src/app'; -describe('<%= name %>', () => { - before(done => { - +describe('Feathers application tests', () => { + before(function(done) { + this.server = app.listen(8787); + this.server.once('listening', () => done()); }); - after(done => { - + after(function(done) { + this.server.close(done); }); - - it('starts and shows the index page', done => { + it('starts and shows the index page', done => { + request('http://localhost:8787', (err, res, body) => { + assert.ok(body.indexOf('') !== -1); + done(err); + }); }); describe('404', () => { it('shows a 404 HTML page', done => { - + request('http://localhost:8787/path/to/nowhere', (err, res, body) => { + assert.equal(res.statusCode, 404); + assert.ok(body.indexOf('') !== -1); + done(err); + }); }); it('shows a 404 JSON error with stack trace', done => { - + request({ + url: 'http://localhost:8787/path/to/nowhere', + json: true + }, (err, res, body) => { + assert.equal(res.statusCode, 404); + assert.equal(body.code, 404); + assert.equal(body.message, 'Page not found'); + assert.equal(body.name, 'NotFound'); + assert.equal(typeof body.stack, 'string'); + done(err); + }); }); }); }); From 45e48417682abde3fb4e6becbdb9d6440a7c2374 Mon Sep 17 00:00:00 2001 From: Marshall Thompson Date: Sat, 26 Dec 2015 21:22:17 -0700 Subject: [PATCH 05/31] Require using .default. See http://stackoverflow.com/questions/33505992/babel-6-changes-how-it-exports-default --- app/templates/static/src/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/templates/static/src/index.js b/app/templates/static/src/index.js index 0b0bf134..d812b23e 100644 --- a/app/templates/static/src/index.js +++ b/app/templates/static/src/index.js @@ -1,6 +1,6 @@ require('babel-core/register'); -var app = require('./app'); +var app = require('./app').default; var port = app.get('port'); var server = app.listen(port); From 0e55341a14ef5c2fe7d5ff358627c86e3b28ba01 Mon Sep 17 00:00:00 2001 From: Marshall Thompson Date: Sun, 27 Dec 2015 21:25:16 -0700 Subject: [PATCH 06/31] Add basic local auth. --- app/authentication.js | 8 ++++++++ app/index.js | 1 + app/templates/static/src/authentication.js | 3 --- 3 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 app/authentication.js delete mode 100644 app/templates/static/src/authentication.js diff --git a/app/authentication.js b/app/authentication.js new file mode 100644 index 00000000..8f277b89 --- /dev/null +++ b/app/authentication.js @@ -0,0 +1,8 @@ +var feathersAuth = require('feathers-authentication').default; + +export default feathersAuth({ + secret: 'TODO-generate-random-key', + loginEndpoint: '/login', + userEndpoint: '/users', + usernameField: 'email' +}); diff --git a/app/index.js b/app/index.js index 4211b5e0..4224a08b 100644 --- a/app/index.js +++ b/app/index.js @@ -89,6 +89,7 @@ module.exports = generators.Base.extend({ var dependencies = [ 'feathers', 'feathers-hooks', + 'feathers-authentication', 'feathers-configuration', 'babel-core', 'babel-preset-es2015' diff --git a/app/templates/static/src/authentication.js b/app/templates/static/src/authentication.js deleted file mode 100644 index 4706ae1e..00000000 --- a/app/templates/static/src/authentication.js +++ /dev/null @@ -1,3 +0,0 @@ -export default function() { - const app = this; -} From 1e22877b12304b23d9bc333025cd6daf63143aa1 Mon Sep 17 00:00:00 2001 From: Marshall Thompson Date: Mon, 28 Dec 2015 19:44:07 -0700 Subject: [PATCH 07/31] Remove duplicate key from jshintrc --- app/templates/static/.jshintrc | 1 - 1 file changed, 1 deletion(-) diff --git a/app/templates/static/.jshintrc b/app/templates/static/.jshintrc index 741f1e44..c39bb0cb 100644 --- a/app/templates/static/.jshintrc +++ b/app/templates/static/.jshintrc @@ -18,7 +18,6 @@ "trailing": true, "smarttabs": true, "white": false, - "node": true, "globals": { "it": true, "describe": true, From adc9b2704b357e61539d4cb5f765be6c65295e35 Mon Sep 17 00:00:00 2001 From: Marshall Thompson Date: Tue, 29 Dec 2015 23:17:36 -0700 Subject: [PATCH 08/31] Added local auth support. I've removed the other options for now, since they're not yet supported by feathers-authentication. The auth config now gets built into the config.json files. Production uses a node environment variable. --- app/authentication.js | 8 ---- app/index.js | 41 +++++++++++++++------ app/templates/app.js | 17 +++++---- app/templates/authentication.js | 3 ++ app/templates/config.default.json | 13 +++++++ app/templates/config.production.json | 13 +++++++ app/templates/static/config/default.json | 5 --- app/templates/static/config/production.json | 4 -- 8 files changed, 67 insertions(+), 37 deletions(-) delete mode 100644 app/authentication.js create mode 100644 app/templates/authentication.js create mode 100644 app/templates/config.default.json create mode 100644 app/templates/config.production.json delete mode 100644 app/templates/static/config/default.json delete mode 100644 app/templates/static/config/production.json diff --git a/app/authentication.js b/app/authentication.js deleted file mode 100644 index 8f277b89..00000000 --- a/app/authentication.js +++ /dev/null @@ -1,8 +0,0 @@ -var feathersAuth = require('feathers-authentication').default; - -export default feathersAuth({ - secret: 'TODO-generate-random-key', - loginEndpoint: '/login', - userEndpoint: '/users', - usernameField: 'email' -}); diff --git a/app/index.js b/app/index.js index 4224a08b..7c001920 100644 --- a/app/index.js +++ b/app/index.js @@ -2,6 +2,7 @@ var generators = require('yeoman-generator'); var path = require('path'); +var crypto = require('crypto'); var _ = require('lodash'); module.exports = generators.Base.extend({ @@ -63,17 +64,17 @@ module.exports = generators.Base.extend({ name: 'authentication', message: 'What authentication methods would you like to support?', choices: [{ - name: 'basic' - }, { + // name: 'basic' + // }, { name: 'local' - }, { - name: 'google' - }, { - name: 'facebook' - }, { - name: 'twitter' - }, { - name: 'github' + // }, { + // name: 'google' + // }, { + // name: 'facebook' + // }, { + // name: 'twitter' + // }, { + // name: 'github' }] }]; @@ -86,10 +87,10 @@ module.exports = generators.Base.extend({ }, writing: function () { + this.props.secret = crypto.randomBytes(64).toString('base64'); var dependencies = [ 'feathers', 'feathers-hooks', - 'feathers-authentication', 'feathers-configuration', 'babel-core', 'babel-preset-es2015' @@ -99,6 +100,10 @@ module.exports = generators.Base.extend({ dependencies.push('body-parser'); } + if(this.props.authentication.length) { + dependencies.push('feathers-authentication'); + } + this.fs.copy(this.templatePath('static'), this.destinationPath()); this.fs.copy(this.templatePath('static/.*'), this.destinationPath()); @@ -107,7 +112,19 @@ module.exports = generators.Base.extend({ this.destinationPath('src', 'app.js'), this.props ); - + + this.fs.copyTpl( + this.templatePath('config.default.json'), + this.destinationPath('config', 'default.json'), + this.props + ); + + this.fs.copyTpl( + this.templatePath('config.production.json'), + this.destinationPath('config', 'production.json'), + this.props + ); + this.fs.copyTpl( this.templatePath('package.json'), this.destinationPath('package.json'), diff --git a/app/templates/app.js b/app/templates/app.js index da9cea20..9a86e209 100644 --- a/app/templates/app.js +++ b/app/templates/app.js @@ -4,20 +4,21 @@ import configuration from 'feathers-configuration'; <% if(providers.indexOf('REST') !== -1) { %>import bodyParser from 'body-parser';<% } %> import middleware from './middleware'; import services from './services'; -import hooks from './hooks'; -import authentication from './authentication'; +import hooks from './hooks';<% if(authentication.length) { %> +import feathersAuth from 'feathers-authentication';<% } %> -const app = feathers() - .configure(configuration(join(__dirname, '..'))) +const app = feathers(); + +app.configure(configuration(join(__dirname, '..'))) <% if(providers.indexOf('REST') !== -1) { %>.configure(feathers.rest()) .use(bodyParser.json()) .use(bodyParser.urlencoded({ extended: true })) - <% } if(providers.indexOf('Socket.io') !== -1) { %>.configure(feathers.socketio())<% } if(providers.indexOf('Primus') !== -1) { %> + <% } if(providers.indexOf('Socket.io') !== -1) { %>.configure(feathers.socketio())<% } if(providers.indexOf('Primus') !== -1) { %> .configure(feathers.primus({ transformer: 'sockjs' - }))<% } %> - .configure(authentication) - .configure(services) + }))<% } %><% if(authentication.length) { %> + .configure(feathersAuth(app.get('auth').local)) + <% } %>.configure(services) .configure(hooks) .configure(middleware); diff --git a/app/templates/authentication.js b/app/templates/authentication.js new file mode 100644 index 00000000..a6db8131 --- /dev/null +++ b/app/templates/authentication.js @@ -0,0 +1,3 @@ +var feathersAuth = require('feathers-authentication').default; + +export default feathersAuth(); diff --git a/app/templates/config.default.json b/app/templates/config.default.json new file mode 100644 index 00000000..8efdaefd --- /dev/null +++ b/app/templates/config.default.json @@ -0,0 +1,13 @@ +{ + "host": "localhost", + "port": 3030, + "public": "../public/"<% if(authentication.length){ %>, + "auth": {<% if(authentication.indexOf('local' > -1)){ %> + "local": { + "secret": "<%= secret %>", + "loginEndpoint": "/login", + "userEndpoint": "/users", + "usernameField": "email" + }<% } %> + }<% } %> +} diff --git a/app/templates/config.production.json b/app/templates/config.production.json new file mode 100644 index 00000000..b6b3199c --- /dev/null +++ b/app/templates/config.production.json @@ -0,0 +1,13 @@ +{ + "host": "myapp.com", + "port": 80, + "public": "../public/"<% if(authentication.length){ %>, + "auth": {<% if(authentication.indexOf('local' > -1)){ %> + "local": { + "secret": "FEATHERS_AUTH_SECRET", + "loginEndpoint": "/login", + "userEndpoint": "/users", + "usernameField": "email" + }<% } %> + }<% } %> +} diff --git a/app/templates/static/config/default.json b/app/templates/static/config/default.json deleted file mode 100644 index ff674166..00000000 --- a/app/templates/static/config/default.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "host": "localhost", - "port": 3030, - "public": "../public/" -} diff --git a/app/templates/static/config/production.json b/app/templates/static/config/production.json deleted file mode 100644 index 584f5b69..00000000 --- a/app/templates/static/config/production.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "host": "myapp.com", - "port": 80, -} From c1db026c1aad2b6e52cec689fdd3fd11b9533f6b Mon Sep 17 00:00:00 2001 From: Marshall Thompson Date: Wed, 30 Dec 2015 00:38:18 -0700 Subject: [PATCH 09/31] Added CORS config Fixes #3. --- app/index.js | 31 +++++++++++++++++++++++++++- app/templates/app.js | 16 +++++++++++--- app/templates/config.default.json | 5 ++++- app/templates/config.production.json | 5 ++++- 4 files changed, 51 insertions(+), 6 deletions(-) diff --git a/app/index.js b/app/index.js index 7c001920..4f206d21 100644 --- a/app/index.js +++ b/app/index.js @@ -37,6 +37,28 @@ module.exports = generators.Base.extend({ }, { name: 'Primus' }] + }, { + type: 'list', + name: 'cors', + message: 'CORS configuration', + choices: [{ + name: 'Enabled for all domains', + value: 'enabled', + checked: true + },{ + name: 'Enabled for whitelisted domains', + value: 'whitelisted' + },{ + name: 'Disabled', + value: false + }] + }, { + type: 'input', + name: 'corsWhitelist', + message: 'Comma-separated domains for CORS whitelist. Include http(s)', + when: function(props){ + return props.cors === 'whitelisted'; + } }, { type: 'list', name: 'database', @@ -87,7 +109,8 @@ module.exports = generators.Base.extend({ }, writing: function () { - this.props.secret = crypto.randomBytes(64).toString('base64'); + this.props.secret = crypto.randomBytes(64).toString('base64'); + this.props.corsWhitelist = this.props.corsWhitelist && this.props.corsWhitelist.split(','); var dependencies = [ 'feathers', 'feathers-hooks', @@ -103,6 +126,10 @@ module.exports = generators.Base.extend({ if(this.props.authentication.length) { dependencies.push('feathers-authentication'); } + + if(this.props.cors) { + dependencies.push('cors'); + } this.fs.copy(this.templatePath('static'), this.destinationPath()); this.fs.copy(this.templatePath('static/.*'), this.destinationPath()); @@ -130,6 +157,8 @@ module.exports = generators.Base.extend({ this.destinationPath('package.json'), this.props ); + + this.log(this.props); this.npmInstall(dependencies, { save: true }); diff --git a/app/templates/app.js b/app/templates/app.js index 9a86e209..ed1cb1dd 100644 --- a/app/templates/app.js +++ b/app/templates/app.js @@ -5,14 +5,24 @@ import configuration from 'feathers-configuration'; import middleware from './middleware'; import services from './services'; import hooks from './hooks';<% if(authentication.length) { %> -import feathersAuth from 'feathers-authentication';<% } %> +import feathersAuth from 'feathers-authentication';<% } %><% if(cors) { %> +import cors from 'cors';<% } %> -const app = feathers(); +let app = feathers();<% if (cors === 'whitelisted') { %> +let whitelist = app.get('corsWhitelist'); +let corsOptions = { + origin: function(origin, callback){ + var originIsWhitelisted = whitelist.indexOf(origin) !== -1; + callback(null, originIsWhitelisted); + } +};<% } %> app.configure(configuration(join(__dirname, '..'))) <% if(providers.indexOf('REST') !== -1) { %>.configure(feathers.rest()) .use(bodyParser.json()) - .use(bodyParser.urlencoded({ extended: true })) + .use(bodyParser.urlencoded({ extended: true }))<% } if (cors) { %> + .options('*', cors(<% if (cors === 'whitelisted') { %>corsOptions<% } %>)) + .use(cors(<% if (cors === 'whitelisted') { %>corsOptions<% } %>)) <% } if(providers.indexOf('Socket.io') !== -1) { %>.configure(feathers.socketio())<% } if(providers.indexOf('Primus') !== -1) { %> .configure(feathers.primus({ transformer: 'sockjs' diff --git a/app/templates/config.default.json b/app/templates/config.default.json index 8efdaefd..815cd1f0 100644 --- a/app/templates/config.default.json +++ b/app/templates/config.default.json @@ -9,5 +9,8 @@ "userEndpoint": "/users", "usernameField": "email" }<% } %> - }<% } %> + }<% } %><% if (cors === 'whitelisted') { %>, + "corsWhitelist": [<% corsWhitelist.forEach(function(domain, index, list){ %> + "<%= domain.trim() %>"<% if(index + 1 !== list.length){ %>,<% } %><% }); %> + ]<% } %> } diff --git a/app/templates/config.production.json b/app/templates/config.production.json index b6b3199c..0580adbd 100644 --- a/app/templates/config.production.json +++ b/app/templates/config.production.json @@ -9,5 +9,8 @@ "userEndpoint": "/users", "usernameField": "email" }<% } %> - }<% } %> + }<% } %><% if (cors === 'whitelisted') { %>, + "corsWhitelist": [<% corsWhitelist.forEach(function(domain, index, list){ %> + "<%= domain.trim() %>"<% if(index + 1 !== list.length){ %>,<% } %><% }); %> + ]<% } %> } From 9a74e5b8884901f457a5281689c58b5ca91e47dc Mon Sep 17 00:00:00 2001 From: Eric Kryski Date: Tue, 12 Jan 2016 01:41:52 -0700 Subject: [PATCH 10/31] Making the yeoman app generator file a bit more readable. --- app/index.js | 290 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 200 insertions(+), 90 deletions(-) diff --git a/app/index.js b/app/index.js index 4f206d21..260d8e43 100644 --- a/app/index.js +++ b/app/index.js @@ -15,94 +15,128 @@ module.exports = generators.Base.extend({ prompting: function () { var done = this.async(); - var prompts = [{ - name: 'name', - message: 'Project name', - when: !this.pkg.name, - default: this.props.name - }, { - name: 'description', - message: 'Description', - when: !this.pkg.description - }, { - type: 'checkbox', - name: 'providers', - message: 'What providers should your API support?', - choices: [{ - name: 'REST', - checked: true - }, { - name: 'Socket.io', - checked: true - }, { - name: 'Primus' - }] - }, { - type: 'list', - name: 'cors', - message: 'CORS configuration', - choices: [{ - name: 'Enabled for all domains', - value: 'enabled', - checked: true - },{ - name: 'Enabled for whitelisted domains', - value: 'whitelisted' - },{ - name: 'Disabled', - value: false - }] - }, { - type: 'input', - name: 'corsWhitelist', - message: 'Comma-separated domains for CORS whitelist. Include http(s)', - when: function(props){ - return props.cors === 'whitelisted'; + var prompts = [ + { + name: 'name', + message: 'Project name', + when: !this.pkg.name, + default: this.props.name + }, + { + name: 'description', + message: 'Description', + when: !this.pkg.description + }, + { + type: 'checkbox', + name: 'providers', + message: 'What type of API are you making?', + choices: [ + { + name: 'REST', + value: 'rest', + checked: true + }, + { + name: 'Realtime via Socket.io', + value: 'socket.io', + checked: true + }, + { + name: 'Realtime via Primus', + value: 'primus', + } + ] + }, + { + type: 'list', + name: 'cors', + message: 'CORS configuration', + choices: [ + { + name: 'Enabled for all domains', + value: 'enabled', + checked: true + }, + { + name: 'Enabled for whitelisted domains', + value: 'whitelisted' + }, + { + name: 'Disabled', + value: false + } + ] + }, + { + type: 'input', + name: 'corsWhitelist', + message: 'Comma-separated domains for CORS whitelist. Include http(s)', + when: function(props){ + return props.cors === 'whitelisted'; + } + }, + { + type: 'list', + name: 'database', + message: 'What database do you primarily want to use?', + choices: [ + { + name: 'I will choose my own', + checked: true + }, + { + name: 'Memory' + }, + { + name: 'MongoDB' + }, + { + name: 'MySQL' + }, + { + name: 'MariaDB' + }, + { + name: 'NeDB' + }, + { + name: 'PostgreSQL' + }, + { + name: 'SQLite' + }, + { + name: 'SQL Server' + } + ] + }, + { + type: 'checkbox', + name: 'authentication', + message: 'What authentication methods would you like to support?', + choices: [ + { + name: 'local', + checked: true + } + // name: 'basic' + // }, { + + // }, { + // name: 'google' + // }, { + // name: 'facebook' + // }, { + // name: 'twitter' + // }, { + // name: 'github' + ] } - }, { - type: 'list', - name: 'database', - message: 'What database do you primarily want to use?', - choices: [{ - name: 'I will choose my own', - checked: true - }, { - name: 'memory' - }, { - name: 'mongodb' - }, { - name: 'mongoose' - }, { - name: 'nedb' - }, { - name: 'mysql' - }, { - name: 'postgresql' - }, { - name: 'sqlite' - }] - }, { - type: 'checkbox', - name: 'authentication', - message: 'What authentication methods would you like to support?', - choices: [{ - // name: 'basic' - // }, { - name: 'local' - // }, { - // name: 'google' - // }, { - // name: 'facebook' - // }, { - // name: 'twitter' - // }, { - // name: 'github' - }] - }]; + ]; this.prompt(prompts, function (props) { this.props = _.extend(this.props, props); - this.config.set('database', this.props.database); done(); }.bind(this)); @@ -112,31 +146,107 @@ module.exports = generators.Base.extend({ this.props.secret = crypto.randomBytes(64).toString('base64'); this.props.corsWhitelist = this.props.corsWhitelist && this.props.corsWhitelist.split(','); var dependencies = [ - 'feathers', - 'feathers-hooks', + 'feathers@2.0.0-pre.2', + 'feathers-hooks@1.0.0-pre.1', 'feathers-configuration', 'babel-core', 'babel-preset-es2015' ]; - if(this.props.providers.indexOf('REST') !== -1) { + if (this.props.providers.indexOf('rest') !== -1) { dependencies.push('body-parser'); + dependencies.push('feathers-rest'); + } + + if (this.props.providers.indexOf('socket.io') !== -1) { + dependencies.push('feathers-socketio'); } - if(this.props.authentication.length) { + if (this.props.providers.indexOf('primus') !== -1) { + dependencies.push('feathers-primus'); + } + + if (this.props.authentication.length) { dependencies.push('feathers-authentication'); } - if(this.props.cors) { + if (this.props.cors) { dependencies.push('cors'); } + switch(this.props.database) { + case 'Memory': + dependencies.push('feathers-memory'); + break; + case 'MongoDB': + dependencies.push('mongoose'); + dependencies.push('feathers-mongoose'); + this.fs.copyTpl( + this.templatePath('models/mongoose-user.js'), + this.destinationPath('server/models', 'user.js'), + this.props + ); + break; + case 'MySQL': + case 'MariaDB': + dependencies.push('mysql'); + dependencies.push('sequelize'); + dependencies.push('feathers-sequelize'); + this.fs.copyTpl( + this.templatePath('models/sequelize-user.js'), + this.destinationPath('server/models', 'user.js'), + this.props + ); + break; + case 'NeDB': + dependencies.push('nedb'); + dependencies.push('feathers-nedb'); + break; + case 'PostgreSQL': + dependencies.push('pg'); + dependencies.push('pg-hstore'); + dependencies.push('sequelize'); + dependencies.push('feathers-sequelize'); + this.fs.copyTpl( + this.templatePath('models/sequelize-user.js'), + this.destinationPath('server/models', 'user.js'), + this.props + ); + break; + case 'SQLite': + dependencies.push('sqlite3'); + dependencies.push('sequelize'); + dependencies.push('feathers-sequelize'); + this.fs.copyTpl( + this.templatePath('models/sequelize-user.js'), + this.destinationPath('server/models', 'user.js'), + this.props + ); + break; + case 'SQL Server': + dependencies.push('tedious'); + dependencies.push('sequelize'); + dependencies.push('feathers-sequelize'); + this.fs.copyTpl( + this.templatePath('models/sequelize-user.js'), + this.destinationPath('server/models', 'user.js'), + this.props + ); + break; + } + this.fs.copy(this.templatePath('static'), this.destinationPath()); this.fs.copy(this.templatePath('static/.*'), this.destinationPath()); this.fs.copyTpl( this.templatePath('app.js'), - this.destinationPath('src', 'app.js'), + this.destinationPath('server', 'app.js'), + this.props + ); + + this.fs.copyTpl( + this.templatePath('user-service.js'), + this.destinationPath('server/services', 'user.js'), this.props ); From cc28b588366a4471ce4bf1a4aaa9c6dcf2df8c58 Mon Sep 17 00:00:00 2001 From: Eric Kryski Date: Tue, 12 Jan 2016 01:43:39 -0700 Subject: [PATCH 11/31] renaming src to server. We might also generate quick client now that feathers is isomorphic --- app/templates/package.json | 2 +- app/templates/static/src/hooks/index.js | 4 --- app/templates/static/src/index.js | 9 ------ app/templates/static/src/middleware/error.js | 30 -------------------- app/templates/static/src/middleware/index.js | 13 --------- app/templates/static/src/services/index.js | 5 ---- app/templates/static/test/app.test.js | 10 +++---- 7 files changed, 6 insertions(+), 67 deletions(-) delete mode 100644 app/templates/static/src/hooks/index.js delete mode 100644 app/templates/static/src/index.js delete mode 100644 app/templates/static/src/middleware/error.js delete mode 100644 app/templates/static/src/middleware/index.js delete mode 100644 app/templates/static/src/services/index.js diff --git a/app/templates/package.json b/app/templates/package.json index ebb4b3d2..4c75a80d 100644 --- a/app/templates/package.json +++ b/app/templates/package.json @@ -16,7 +16,7 @@ "node": ">= 0.12.0" }, "scripts": { - "start": "node src/", + "start": "node server/", "jshint": "jshint src/. test/. --config", "mocha": "mocha test/ --compilers js:babel-core/register", "test": "npm run jshint && npm run mocha" diff --git a/app/templates/static/src/hooks/index.js b/app/templates/static/src/hooks/index.js deleted file mode 100644 index 19f19f11..00000000 --- a/app/templates/static/src/hooks/index.js +++ /dev/null @@ -1,4 +0,0 @@ -// jshint unused:false -export default function() { - const app = this; -} diff --git a/app/templates/static/src/index.js b/app/templates/static/src/index.js deleted file mode 100644 index d812b23e..00000000 --- a/app/templates/static/src/index.js +++ /dev/null @@ -1,9 +0,0 @@ -require('babel-core/register'); - -var app = require('./app').default; -var port = app.get('port'); -var server = app.listen(port); - -server.on('listening', function() { - console.log('Feathers application started on port ' + port); -}); diff --git a/app/templates/static/src/middleware/error.js b/app/templates/static/src/middleware/error.js deleted file mode 100644 index 84ebfc3b..00000000 --- a/app/templates/static/src/middleware/error.js +++ /dev/null @@ -1,30 +0,0 @@ -// jshint unused:false -import path from 'path'; - -export default function(error, req, res, next) { - const app = req.app; - const code = error.code || 500; - - res.status(code); - - res.format({ - 'text/html': function() { - const file = code === 404 ? '404.html' : 'error.html'; - res.sendFile(path.join(app.get('public'), file)); - }, - - 'application/json': function () { - let output = { - code, - message: error.message, - name: error.name || 'General error' - }; - - if(app.settings.env !== 'production') { - output.stack = error.stack; - } - - res.json(output); - } - }); -} diff --git a/app/templates/static/src/middleware/index.js b/app/templates/static/src/middleware/index.js deleted file mode 100644 index b233309f..00000000 --- a/app/templates/static/src/middleware/index.js +++ /dev/null @@ -1,13 +0,0 @@ -import errorHandler from './error'; -import { errors } from 'feathers'; -import { static as host } from 'feathers'; - -export default function() { - const app = this; - - app.use('/', host(app.get('public'))); - app.use(function(req, res, next) { - next(new errors.types.NotFound('Page not found')); - }); - app.use(errorHandler); -} diff --git a/app/templates/static/src/services/index.js b/app/templates/static/src/services/index.js deleted file mode 100644 index da63c075..00000000 --- a/app/templates/static/src/services/index.js +++ /dev/null @@ -1,5 +0,0 @@ -// jshint unused:false -export default function() { - const app = this; - -} diff --git a/app/templates/static/test/app.test.js b/app/templates/static/test/app.test.js index cc49d62e..047f4053 100644 --- a/app/templates/static/test/app.test.js +++ b/app/templates/static/test/app.test.js @@ -1,10 +1,10 @@ import assert from 'assert'; import request from 'request'; -import app from '../src/app'; +import app from '../server/app'; describe('Feathers application tests', () => { before(function(done) { - this.server = app.listen(8787); + this.server = app.listen(3030); this.server.once('listening', () => done()); }); @@ -13,7 +13,7 @@ describe('Feathers application tests', () => { }); it('starts and shows the index page', done => { - request('http://localhost:8787', (err, res, body) => { + request('http://localhost:3030', (err, res, body) => { assert.ok(body.indexOf('') !== -1); done(err); }); @@ -21,7 +21,7 @@ describe('Feathers application tests', () => { describe('404', () => { it('shows a 404 HTML page', done => { - request('http://localhost:8787/path/to/nowhere', (err, res, body) => { + request('http://localhost:3030/path/to/nowhere', (err, res, body) => { assert.equal(res.statusCode, 404); assert.ok(body.indexOf('') !== -1); done(err); @@ -30,7 +30,7 @@ describe('Feathers application tests', () => { it('shows a 404 JSON error with stack trace', done => { request({ - url: 'http://localhost:8787/path/to/nowhere', + url: 'http://localhost:3030/path/to/nowhere', json: true }, (err, res, body) => { assert.equal(res.statusCode, 404); From 617119daebf47f38af624591976c7a5e0420dafe Mon Sep 17 00:00:00 2001 From: Eric Kryski Date: Tue, 12 Jan 2016 01:44:03 -0700 Subject: [PATCH 12/31] cleaning up the app file. Adding support for rest --- app/templates/app.js | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/app/templates/app.js b/app/templates/app.js index ed1cb1dd..1b99f6cd 100644 --- a/app/templates/app.js +++ b/app/templates/app.js @@ -1,12 +1,16 @@ import { join } from 'path'; import feathers from 'feathers'; import configuration from 'feathers-configuration'; -<% if(providers.indexOf('REST') !== -1) { %>import bodyParser from 'body-parser';<% } %> +import hooks from 'feathers-hooks';<% if (providers.indexOf('rest') !== -1) { %> +import rest from 'feathers-rest'; +import bodyParser from 'body-parser'; +<% } %><% if (providers.indexOf('socket.io') !== -1) { %>import socketio from 'feathers-socketio';<% } %> +<% if (providers.indexOf('primus') !== -1) { %>import primus from 'feathers-primus';<% } %> +<% if (authentication.length) { %>import feathersAuth from 'feathers-authentication';<% } %> +<% if (cors) { %>import cors from 'cors';<% } %> import middleware from './middleware'; import services from './services'; -import hooks from './hooks';<% if(authentication.length) { %> -import feathersAuth from 'feathers-authentication';<% } %><% if(cors) { %> -import cors from 'cors';<% } %> +import myHooks from './hooks'; let app = feathers();<% if (cors === 'whitelisted') { %> let whitelist = app.get('corsWhitelist'); @@ -18,18 +22,19 @@ let corsOptions = { };<% } %> app.configure(configuration(join(__dirname, '..'))) - <% if(providers.indexOf('REST') !== -1) { %>.configure(feathers.rest()) + .configure(hooks())<% if(providers.indexOf('rest') !== -1) { %> + .configure(rest()) .use(bodyParser.json()) - .use(bodyParser.urlencoded({ extended: true }))<% } if (cors) { %> + .use(bodyParser.urlencoded({ extended: true }))<% } %><% if (cors) { %> .options('*', cors(<% if (cors === 'whitelisted') { %>corsOptions<% } %>)) - .use(cors(<% if (cors === 'whitelisted') { %>corsOptions<% } %>)) - <% } if(providers.indexOf('Socket.io') !== -1) { %>.configure(feathers.socketio())<% } if(providers.indexOf('Primus') !== -1) { %> - .configure(feathers.primus({ + .use(cors(<% if (cors === 'whitelisted') { %>corsOptions<% } %>))<% } %><% if (providers.indexOf('socket.io') !== -1) { %> + .configure(socketio())<% } %><% if(providers.indexOf('primus') !== -1) { %> + .configure(primus({ transformer: 'sockjs' - }))<% } %><% if(authentication.length) { %> - .configure(feathersAuth(app.get('auth').local)) - <% } %>.configure(services) - .configure(hooks) + }))<% } %><% if (authentication.length) { %> + .configure(feathersAuth(app.get('auth').local))<% } %> + .configure(services) + .configure(myHooks) .configure(middleware); export default app; From 5e388dbe8433a5d3d445606e3d4232df3f7a9341 Mon Sep 17 00:00:00 2001 From: Eric Kryski Date: Tue, 12 Jan 2016 01:44:33 -0700 Subject: [PATCH 13/31] adding templates for db specific user models --- app/templates/models/mongoose-user.js | 11 +++++++++++ app/templates/models/sequelize-user.js | 0 2 files changed, 11 insertions(+) create mode 100644 app/templates/models/mongoose-user.js create mode 100644 app/templates/models/sequelize-user.js diff --git a/app/templates/models/mongoose-user.js b/app/templates/models/mongoose-user.js new file mode 100644 index 00000000..c50abbb1 --- /dev/null +++ b/app/templates/models/mongoose-user.js @@ -0,0 +1,11 @@ +import mongoose from 'mongoose'; +const Schema = mongoose.Schema; + +let UserSchema = new Schema({ + email: {type: String, required: true, index: true}, + password: {type: String, required: true} +}); + +let UserModel = mongoose.model('User', UserSchema); + +export default UserModel; \ No newline at end of file diff --git a/app/templates/models/sequelize-user.js b/app/templates/models/sequelize-user.js new file mode 100644 index 00000000..e69de29b From 1e2b98afb804962cd16a16bc24805818e168dc93 Mon Sep 17 00:00:00 2001 From: Eric Kryski Date: Tue, 12 Jan 2016 01:44:50 -0700 Subject: [PATCH 14/31] adding a quick user service template --- app/templates/user-service.js | 51 +++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 app/templates/user-service.js diff --git a/app/templates/user-service.js b/app/templates/user-service.js new file mode 100644 index 00000000..5340a850 --- /dev/null +++ b/app/templates/user-service.js @@ -0,0 +1,51 @@ +import hooks from '../hooks'; +<% if (database && (database !== 'Memory' && database !== 'NeDB')) { %>import User from '../models/user';<% } %> +<% if (database === 'Memory') { %> +import service from 'feathers-memory'; +const User = {}; +<% } %> +<% if (database === 'NeDB') { %> +import NeDB from 'feathers-nedb'; +import service from 'feathers-nedb'; +let User = new NeDB({ + filename: './data/users.db', + autoload: true +}); +<% } %><% if (database === 'MongoDB') { %> +import mongoose from 'mongoose'; +import service from 'feathers-mongoose'; +mongoose.connect('mongodb://localhost:27017/feathers'); +mongoose.Promise = global.Promise; +<% } %> + +export default function(){ + const app = this; + /* * * Do any connection stuff here, if needed * * */ + + /* * * Service Type * * */ + let options = { + Model: User, + paginate: { + default: 5, + max: 25 + } + }; + + app.use('/v1/users', service(options)); + + // const service = this.service('v1/users'); + + /* * * Before hooks * * */ + // service.before({ + // all: [hooks.requireAuthForPrivate()], + // before: [hooks.setUserID()] + // }); + + // /* * * After hooks * * */ + // service.after({ + // all: [hooks.removeSomeField()] + // }); + + // /* * * Set up event filters * * */ + // service.created = service.updated = service.patched = service.removed = events.requireAuthForPrivate; +} \ No newline at end of file From 51f46d235dadb3717c1a20182596cf4ca0fe0735 Mon Sep 17 00:00:00 2001 From: Eric Kryski Date: Tue, 12 Jan 2016 01:45:10 -0700 Subject: [PATCH 15/31] adding public directory and basic error and index pages. --- app/templates/static/public/404.html | 8 ++++++++ app/templates/static/public/500.html | 8 ++++++++ app/templates/static/public/index.html | 8 ++++++++ 3 files changed, 24 insertions(+) create mode 100644 app/templates/static/public/404.html create mode 100644 app/templates/static/public/500.html create mode 100644 app/templates/static/public/index.html diff --git a/app/templates/static/public/404.html b/app/templates/static/public/404.html new file mode 100644 index 00000000..e72b7669 --- /dev/null +++ b/app/templates/static/public/404.html @@ -0,0 +1,8 @@ + + + Page Not Found + + + 404: Page Not Found + + \ No newline at end of file diff --git a/app/templates/static/public/500.html b/app/templates/static/public/500.html new file mode 100644 index 00000000..8387d06b --- /dev/null +++ b/app/templates/static/public/500.html @@ -0,0 +1,8 @@ + + + Internal Server Error + + + 500: Internal Server Error + + \ No newline at end of file diff --git a/app/templates/static/public/index.html b/app/templates/static/public/index.html new file mode 100644 index 00000000..d8009256 --- /dev/null +++ b/app/templates/static/public/index.html @@ -0,0 +1,8 @@ + + + Welcome to Feathers + + + Welcome to Feathers + + \ No newline at end of file From 4847151f0ef41a4a3b71787b7e852eff6832ab18 Mon Sep 17 00:00:00 2001 From: Eric Kryski Date: Tue, 12 Jan 2016 01:45:39 -0700 Subject: [PATCH 16/31] adding a more robust error handler --- .../static/server/middleware/error-handler.js | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 app/templates/static/server/middleware/error-handler.js diff --git a/app/templates/static/server/middleware/error-handler.js b/app/templates/static/server/middleware/error-handler.js new file mode 100644 index 00000000..7636a20d --- /dev/null +++ b/app/templates/static/server/middleware/error-handler.js @@ -0,0 +1,48 @@ +// jshint unused:false +import path from 'path'; +import errors from 'feathers-errors'; + +export default function(error, req, res, next) { + const app = req.app; + const code = parseInt(error.code, 10) !== NaN ? parseInt(error.code, 10) : 500; + + if (typeof error === 'string') { + error = new errors.GeneralError(error); + } + else if ( !(error instanceof errors.FeathersError) ) { + let oldError = error; + error = new errors.GeneralError(oldError.message, {errors: oldError.errors}); + + if (oldError.stack) { + error.stack = oldError.stack; + } + } + + // Don't show stack trace if it is a 404 error + if (error.code === 404) { + error.stack = null; + } + + res.status(code); + + res.format({ + 'text/html': function() { + const file = code === 404 ? '404.html' : '500.html'; + res.sendFile(path.join(app.get('public'), file)); + }, + + 'application/json': function () { + let output = { + code, + message: error.message, + name: error.name + }; + + if (app.settings.env !== 'production') { + output.stack = error.stack; + } + + res.json(output); + } + }); +} From 53aa6b6aa28f9508fdd5a2753d8f1d23e907452a Mon Sep 17 00:00:00 2001 From: Eric Kryski Date: Tue, 12 Jan 2016 01:45:53 -0700 Subject: [PATCH 17/31] adding a logging middleware --- app/templates/static/server/middleware/logger.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 app/templates/static/server/middleware/logger.js diff --git a/app/templates/static/server/middleware/logger.js b/app/templates/static/server/middleware/logger.js new file mode 100644 index 00000000..3c9980ca --- /dev/null +++ b/app/templates/static/server/middleware/logger.js @@ -0,0 +1,13 @@ +export default function(error, req, res, next) { + let app = req.app; + + if (error) { + console.error(`${error.code ? `(${error.code}) ` : '' }Route: ${req.url} - ${error.message}`); + + if (app.settings.env !== 'production') { + console.trace(error.stack); + } + } + + next(error); +}; \ No newline at end of file From 275ce6c486f9b01bc6c1f548c73b135ab5fceb13 Mon Sep 17 00:00:00 2001 From: Eric Kryski Date: Tue, 12 Jan 2016 01:46:04 -0700 Subject: [PATCH 18/31] using the new middleware --- app/templates/static/server/middleware/index.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 app/templates/static/server/middleware/index.js diff --git a/app/templates/static/server/middleware/index.js b/app/templates/static/server/middleware/index.js new file mode 100644 index 00000000..6daca42e --- /dev/null +++ b/app/templates/static/server/middleware/index.js @@ -0,0 +1,15 @@ +import { static as host } from 'feathers'; +import errors from 'feathers-errors'; +import errorHandler from './error-handler'; +import logger from './logger'; + +export default function() { + const app = this; + + app.use('/', host(app.get('public'))); + app.use(function(req, res, next) { + next(new errors.NotFound('Page not found')); + }); + app.use(logger); + app.use(errorHandler); +} From d848f7d4ac4072f5987e9ed1a44cc8100b72c878 Mon Sep 17 00:00:00 2001 From: Eric Kryski Date: Tue, 12 Jan 2016 01:46:28 -0700 Subject: [PATCH 19/31] configure a user service in the generated app --- app/templates/static/server/services/index.js | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 app/templates/static/server/services/index.js diff --git a/app/templates/static/server/services/index.js b/app/templates/static/server/services/index.js new file mode 100644 index 00000000..4299e34a --- /dev/null +++ b/app/templates/static/server/services/index.js @@ -0,0 +1,8 @@ +// jshint unused:false +import userService from './user'; + +export default function() { + const app = this; + + app.configure(userService); +} From bc498c4e1141ae8f48ae4b372b1458f26b6ced38 Mon Sep 17 00:00:00 2001 From: Eric Kryski Date: Tue, 12 Jan 2016 01:46:49 -0700 Subject: [PATCH 20/31] moving to server instead of src --- app/templates/static/server/hooks/index.js | 4 ++++ app/templates/static/server/index.js | 9 +++++++++ 2 files changed, 13 insertions(+) create mode 100644 app/templates/static/server/hooks/index.js create mode 100644 app/templates/static/server/index.js diff --git a/app/templates/static/server/hooks/index.js b/app/templates/static/server/hooks/index.js new file mode 100644 index 00000000..19f19f11 --- /dev/null +++ b/app/templates/static/server/hooks/index.js @@ -0,0 +1,4 @@ +// jshint unused:false +export default function() { + const app = this; +} diff --git a/app/templates/static/server/index.js b/app/templates/static/server/index.js new file mode 100644 index 00000000..d812b23e --- /dev/null +++ b/app/templates/static/server/index.js @@ -0,0 +1,9 @@ +require('babel-core/register'); + +var app = require('./app').default; +var port = app.get('port'); +var server = app.listen(port); + +server.on('listening', function() { + console.log('Feathers application started on port ' + port); +}); From 718042e67576d5628dfda8f9dd839539beaebf92 Mon Sep 17 00:00:00 2001 From: Eric Kryski Date: Tue, 12 Jan 2016 13:45:16 -0700 Subject: [PATCH 21/31] adding compression and favicon middleware --- app/index.js | 2 ++ app/templates/static/public/favicon.ico | Bin 0 -> 5533 bytes app/templates/static/server/middleware/index.js | 9 +++++++-- 3 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 app/templates/static/public/favicon.ico diff --git a/app/index.js b/app/index.js index 260d8e43..74010770 100644 --- a/app/index.js +++ b/app/index.js @@ -149,6 +149,8 @@ module.exports = generators.Base.extend({ 'feathers@2.0.0-pre.2', 'feathers-hooks@1.0.0-pre.1', 'feathers-configuration', + 'serve-favicon', + 'compression', 'babel-core', 'babel-preset-es2015' ]; diff --git a/app/templates/static/public/favicon.ico b/app/templates/static/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..7ed25a60b04996fb9f339aa73cbfd25dd394430e GIT binary patch literal 5533 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 literal 0 HcmV?d00001 diff --git a/app/templates/static/server/middleware/index.js b/app/templates/static/server/middleware/index.js index 6daca42e..a6c4e82e 100644 --- a/app/templates/static/server/middleware/index.js +++ b/app/templates/static/server/middleware/index.js @@ -1,4 +1,7 @@ -import { static as host } from 'feathers'; +import { join } from 'path'; +import { static } from 'feathers'; +import favicon from 'serve-favicon'; +import compress from 'compression'; import errors from 'feathers-errors'; import errorHandler from './error-handler'; import logger from './logger'; @@ -6,7 +9,9 @@ import logger from './logger'; export default function() { const app = this; - app.use('/', host(app.get('public'))); + app.use(compress()) + app.use(favicon( join(app.get('public'), 'favicon.ico') )) + app.use('/', static(app.get('public'))); app.use(function(req, res, next) { next(new errors.NotFound('Page not found')); }); From f1f5ac3e9e2bf7ebe45ee75f7dd2a305364238c6 Mon Sep 17 00:00:00 2001 From: Eric Kryski Date: Tue, 12 Jan 2016 13:45:33 -0700 Subject: [PATCH 22/31] fixing initialization of nedb service --- app/templates/user-service.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/templates/user-service.js b/app/templates/user-service.js index 5340a850..57c63306 100644 --- a/app/templates/user-service.js +++ b/app/templates/user-service.js @@ -5,7 +5,7 @@ import service from 'feathers-memory'; const User = {}; <% } %> <% if (database === 'NeDB') { %> -import NeDB from 'feathers-nedb'; +import NeDB from 'nedb'; import service from 'feathers-nedb'; let User = new NeDB({ filename: './data/users.db', From e389cfa0e21ca13c1a5896596ab6b7b39492ee4f Mon Sep 17 00:00:00 2001 From: Eric Kryski Date: Tue, 12 Jan 2016 15:54:29 -0700 Subject: [PATCH 23/31] cleaning up the middleware. Adding winston as a logger. --- app/index.js | 7 ++ app/templates/app.js | 22 ++---- app/templates/middleware.js | 29 +++++++ .../static/server/middleware/error-handler.js | 79 ++++++++++--------- .../static/server/middleware/index.js | 20 ----- .../static/server/middleware/logger.js | 27 ++++--- .../server/middleware/not-found-handler.js | 8 ++ 7 files changed, 108 insertions(+), 84 deletions(-) create mode 100644 app/templates/middleware.js delete mode 100644 app/templates/static/server/middleware/index.js create mode 100644 app/templates/static/server/middleware/not-found-handler.js diff --git a/app/index.js b/app/index.js index 74010770..97b19430 100644 --- a/app/index.js +++ b/app/index.js @@ -151,6 +151,7 @@ module.exports = generators.Base.extend({ 'feathers-configuration', 'serve-favicon', 'compression', + 'winston', 'babel-core', 'babel-preset-es2015' ]; @@ -246,6 +247,12 @@ module.exports = generators.Base.extend({ this.props ); + this.fs.copyTpl( + this.templatePath('middleware.js'), + this.destinationPath('server/middleware', 'index.js'), + this.props + ); + this.fs.copyTpl( this.templatePath('user-service.js'), this.destinationPath('server/services', 'user.js'), diff --git a/app/templates/app.js b/app/templates/app.js index 1b99f6cd..bb9cef0f 100644 --- a/app/templates/app.js +++ b/app/templates/app.js @@ -1,33 +1,23 @@ import { join } from 'path'; import feathers from 'feathers'; import configuration from 'feathers-configuration'; -import hooks from 'feathers-hooks';<% if (providers.indexOf('rest') !== -1) { %> +import hooks from 'feathers-hooks'; +<% if (providers.indexOf('rest') !== -1) { %> import rest from 'feathers-rest'; import bodyParser from 'body-parser'; -<% } %><% if (providers.indexOf('socket.io') !== -1) { %>import socketio from 'feathers-socketio';<% } %> -<% if (providers.indexOf('primus') !== -1) { %>import primus from 'feathers-primus';<% } %> +<% } %><% if (providers.indexOf('socket.io') !== -1) { %>import socketio from 'feathers-socketio';<% } %><% if (providers.indexOf('primus') !== -1) { %>import primus from 'feathers-primus';<% } %> <% if (authentication.length) { %>import feathersAuth from 'feathers-authentication';<% } %> -<% if (cors) { %>import cors from 'cors';<% } %> import middleware from './middleware'; import services from './services'; import myHooks from './hooks'; -let app = feathers();<% if (cors === 'whitelisted') { %> -let whitelist = app.get('corsWhitelist'); -let corsOptions = { - origin: function(origin, callback){ - var originIsWhitelisted = whitelist.indexOf(origin) !== -1; - callback(null, originIsWhitelisted); - } -};<% } %> +let app = feathers(); app.configure(configuration(join(__dirname, '..'))) .configure(hooks())<% if(providers.indexOf('rest') !== -1) { %> - .configure(rest()) .use(bodyParser.json()) - .use(bodyParser.urlencoded({ extended: true }))<% } %><% if (cors) { %> - .options('*', cors(<% if (cors === 'whitelisted') { %>corsOptions<% } %>)) - .use(cors(<% if (cors === 'whitelisted') { %>corsOptions<% } %>))<% } %><% if (providers.indexOf('socket.io') !== -1) { %> + .use(bodyParser.urlencoded({ extended: true })) + .configure(rest())<% } %><% if (providers.indexOf('socket.io') !== -1) { %> .configure(socketio())<% } %><% if(providers.indexOf('primus') !== -1) { %> .configure(primus({ transformer: 'sockjs' diff --git a/app/templates/middleware.js b/app/templates/middleware.js new file mode 100644 index 00000000..28a2b081 --- /dev/null +++ b/app/templates/middleware.js @@ -0,0 +1,29 @@ +<% if (cors) { %>import cors from 'cors';<% } %> +import { join } from 'path'; +import { static as serveStatic } from 'feathers'; +import favicon from 'serve-favicon'; +import compress from 'compression'; +import missing from './not-found-handler'; +import error from './error-handler'; +import logger from './logger'; + +export default function() { + const app = this; + <% if (cors === 'whitelisted') { %> + let whitelist = app.get('corsWhitelist'); + let corsOptions = { + origin: function(origin, callback){ + var originIsWhitelisted = whitelist.indexOf(origin) !== -1; + callback(null, originIsWhitelisted); + } + };<% } %> + + app.use(compress())<% if (cors) { %> + .options('*', cors(<% if (cors === 'whitelisted') { %>corsOptions<% } %>)) + .use(cors(<% if (cors === 'whitelisted') { %>corsOptions<% } %>))<% } %> + .use(favicon( join(app.get('public'), 'favicon.ico') )) + .use('/', serveStatic(app.get('public'))) + .use(missing()) + .use(logger(app)) + .use(error(app)); +} diff --git a/app/templates/static/server/middleware/error-handler.js b/app/templates/static/server/middleware/error-handler.js index 7636a20d..c58e5bd6 100644 --- a/app/templates/static/server/middleware/error-handler.js +++ b/app/templates/static/server/middleware/error-handler.js @@ -2,47 +2,48 @@ import path from 'path'; import errors from 'feathers-errors'; -export default function(error, req, res, next) { - const app = req.app; - const code = parseInt(error.code, 10) !== NaN ? parseInt(error.code, 10) : 500; - - if (typeof error === 'string') { - error = new errors.GeneralError(error); - } - else if ( !(error instanceof errors.FeathersError) ) { - let oldError = error; - error = new errors.GeneralError(oldError.message, {errors: oldError.errors}); - - if (oldError.stack) { - error.stack = oldError.stack; +export default function(app) { + return function(error, req, res, next) { + if (typeof error === 'string') { + error = new errors.GeneralError(error); } - } - - // Don't show stack trace if it is a 404 error - if (error.code === 404) { - error.stack = null; - } - - res.status(code); - - res.format({ - 'text/html': function() { - const file = code === 404 ? '404.html' : '500.html'; - res.sendFile(path.join(app.get('public'), file)); - }, - - 'application/json': function () { - let output = { - code, - message: error.message, - name: error.name - }; - - if (app.settings.env !== 'production') { - output.stack = error.stack; + else if ( !(error instanceof errors.FeathersError) ) { + let oldError = error; + error = new errors.GeneralError(oldError.message, {errors: oldError.errors}); + + if (oldError.stack) { + error.stack = oldError.stack; } + } + + const code = parseInt(error.code, 10) !== NaN ? parseInt(error.code, 10) : 500; - res.json(output); + // Don't show stack trace if it is a 404 error + if (code === 404) { + error.stack = null; } - }); + + res.status(code); + + res.format({ + 'text/html': function() { + const file = code === 404 ? '404.html' : '500.html'; + res.sendFile(path.join(app.get('public'), file)); + }, + + 'application/json': function () { + let output = { + code, + message: error.message, + name: error.name + }; + + if (app.settings.env !== 'production') { + output.stack = error.stack; + } + + res.json(output); + } + }); + }; } diff --git a/app/templates/static/server/middleware/index.js b/app/templates/static/server/middleware/index.js deleted file mode 100644 index a6c4e82e..00000000 --- a/app/templates/static/server/middleware/index.js +++ /dev/null @@ -1,20 +0,0 @@ -import { join } from 'path'; -import { static } from 'feathers'; -import favicon from 'serve-favicon'; -import compress from 'compression'; -import errors from 'feathers-errors'; -import errorHandler from './error-handler'; -import logger from './logger'; - -export default function() { - const app = this; - - app.use(compress()) - app.use(favicon( join(app.get('public'), 'favicon.ico') )) - app.use('/', static(app.get('public'))); - app.use(function(req, res, next) { - next(new errors.NotFound('Page not found')); - }); - app.use(logger); - app.use(errorHandler); -} diff --git a/app/templates/static/server/middleware/logger.js b/app/templates/static/server/middleware/logger.js index 3c9980ca..a7526884 100644 --- a/app/templates/static/server/middleware/logger.js +++ b/app/templates/static/server/middleware/logger.js @@ -1,13 +1,22 @@ -export default function(error, req, res, next) { - let app = req.app; +import winston from 'winston'; - if (error) { - console.error(`${error.code ? `(${error.code}) ` : '' }Route: ${req.url} - ${error.message}`); - - if (app.settings.env !== 'production') { - console.trace(error.stack); +export default function(app) { + // Add a logger to our app object for convenience + app.logger = winston; + + return function(error, req, res, next) { + if (error) { + const message = `${error.code ? `(${error.code}) ` : '' }Route: ${req.url} - ${error.message}`; + + if (error.code === 404) { + winston.info(message); + } + else { + winston.error(message); + winston.info(error.stack); + } } - } - next(error); + next(error); + }; }; \ No newline at end of file diff --git a/app/templates/static/server/middleware/not-found-handler.js b/app/templates/static/server/middleware/not-found-handler.js new file mode 100644 index 00000000..cef71f99 --- /dev/null +++ b/app/templates/static/server/middleware/not-found-handler.js @@ -0,0 +1,8 @@ +import errors from 'feathers-errors'; + +export default function() { + return function(req, res, next) { + next(new errors.NotFound('Page not found')); + } +}; + From ac3225d18279a683f40b4957e4050c26d0ac1062 Mon Sep 17 00:00:00 2001 From: Eric Kryski Date: Tue, 12 Jan 2016 21:01:23 -0700 Subject: [PATCH 24/31] converting over to generating services dependent on the database chosen --- app/index.js | 81 +++++++++++++++++------- app/templates/services/memory-user.js | 15 +++++ app/templates/services/mongoose-user.js | 38 +++++++++++ app/templates/services/nedb-user.js | 24 +++++++ app/templates/services/sequelize-user.js | 54 ++++++++++++++++ 5 files changed, 190 insertions(+), 22 deletions(-) create mode 100644 app/templates/services/memory-user.js create mode 100644 app/templates/services/mongoose-user.js create mode 100644 app/templates/services/nedb-user.js create mode 100644 app/templates/services/sequelize-user.js diff --git a/app/index.js b/app/index.js index 97b19430..b6f7cd4d 100644 --- a/app/index.js +++ b/app/index.js @@ -86,28 +86,36 @@ module.exports = generators.Base.extend({ checked: true }, { - name: 'Memory' + name: 'Memory', + value: 'memory' }, { - name: 'MongoDB' + name: 'MongoDB', + value: 'mongodb' }, { - name: 'MySQL' + name: 'MySQL', + value: 'mysql' }, { - name: 'MariaDB' + name: 'MariaDB', + value: 'mariadb' }, { - name: 'NeDB' + name: 'NeDB', + value: 'nedb' }, { - name: 'PostgreSQL' + name: 'PostgreSQL', + value: 'postgres' }, { - name: 'SQLite' + name: 'SQLite', + value: 'sqlite' }, { - name: 'SQL Server' + name: 'SQL Server', + value: 'mssql' } ] }, @@ -178,10 +186,15 @@ module.exports = generators.Base.extend({ } switch(this.props.database) { - case 'Memory': + case 'memory': dependencies.push('feathers-memory'); + this.fs.copyTpl( + this.templatePath('services/memory-user.js'), + this.destinationPath('server/services', 'user.js'), + this.props + ); break; - case 'MongoDB': + case 'mongodb': dependencies.push('mongoose'); dependencies.push('feathers-mongoose'); this.fs.copyTpl( @@ -189,9 +202,14 @@ module.exports = generators.Base.extend({ this.destinationPath('server/models', 'user.js'), this.props ); + this.fs.copyTpl( + this.templatePath('services/mongoose-user.js'), + this.destinationPath('server/services', 'user.js'), + this.props + ); break; - case 'MySQL': - case 'MariaDB': + case 'mysql': + case 'mariadb': dependencies.push('mysql'); dependencies.push('sequelize'); dependencies.push('feathers-sequelize'); @@ -200,12 +218,22 @@ module.exports = generators.Base.extend({ this.destinationPath('server/models', 'user.js'), this.props ); + this.fs.copyTpl( + this.templatePath('services/sequelize-user.js'), + this.destinationPath('server/services', 'user.js'), + this.props + ); break; - case 'NeDB': + case 'nedb': dependencies.push('nedb'); dependencies.push('feathers-nedb'); + this.fs.copyTpl( + this.templatePath('services/nedb-user.js'), + this.destinationPath('server/services', 'user.js'), + this.props + ); break; - case 'PostgreSQL': + case 'postgres': dependencies.push('pg'); dependencies.push('pg-hstore'); dependencies.push('sequelize'); @@ -215,8 +243,13 @@ module.exports = generators.Base.extend({ this.destinationPath('server/models', 'user.js'), this.props ); + this.fs.copyTpl( + this.templatePath('services/sequelize-user.js'), + this.destinationPath('server/services', 'user.js'), + this.props + ); break; - case 'SQLite': + case 'sqlite': dependencies.push('sqlite3'); dependencies.push('sequelize'); dependencies.push('feathers-sequelize'); @@ -225,8 +258,13 @@ module.exports = generators.Base.extend({ this.destinationPath('server/models', 'user.js'), this.props ); + this.fs.copyTpl( + this.templatePath('services/sequelize-user.js'), + this.destinationPath('server/services', 'user.js'), + this.props + ); break; - case 'SQL Server': + case 'mssql': dependencies.push('tedious'); dependencies.push('sequelize'); dependencies.push('feathers-sequelize'); @@ -235,6 +273,11 @@ module.exports = generators.Base.extend({ this.destinationPath('server/models', 'user.js'), this.props ); + this.fs.copyTpl( + this.templatePath('services/sequelize-user.js'), + this.destinationPath('server/services', 'user.js'), + this.props + ); break; } @@ -252,12 +295,6 @@ module.exports = generators.Base.extend({ this.destinationPath('server/middleware', 'index.js'), this.props ); - - this.fs.copyTpl( - this.templatePath('user-service.js'), - this.destinationPath('server/services', 'user.js'), - this.props - ); this.fs.copyTpl( this.templatePath('config.default.json'), diff --git a/app/templates/services/memory-user.js b/app/templates/services/memory-user.js new file mode 100644 index 00000000..f8388681 --- /dev/null +++ b/app/templates/services/memory-user.js @@ -0,0 +1,15 @@ +import hooks from '../hooks'; +import service from 'feathers-memory'; + +export default function(){ + const app = this; + + let options = { + paginate: { + default: 5, + max: 25 + } + }; + + app.use('/v1/users', service(options)); +} \ No newline at end of file diff --git a/app/templates/services/mongoose-user.js b/app/templates/services/mongoose-user.js new file mode 100644 index 00000000..42b7df7b --- /dev/null +++ b/app/templates/services/mongoose-user.js @@ -0,0 +1,38 @@ +import hooks from '../hooks'; +import mongoose from 'mongoose'; +import service from 'feathers-mongoose'; +import User from '../models/user'; + +mongoose.Promise = global.Promise; + +export default function(){ + const app = this; + + mongoose.connect(app.get('mongodb')); + + let options = { + Model: User, + paginate: { + default: 5, + max: 25 + } + }; + + app.use('/v1/users', service(options)); + + // const service = this.service('v1/users'); + + /* * * Before hooks * * */ + // service.before({ + // all: [hooks.requireAuthForPrivate()], + // before: [hooks.setUserID()] + // }); + + // /* * * After hooks * * */ + // service.after({ + // all: [hooks.removeSomeField()] + // }); + + // /* * * Set up event filters * * */ + // service.created = service.updated = service.patched = service.removed = events.requireAuthForPrivate; +} \ No newline at end of file diff --git a/app/templates/services/nedb-user.js b/app/templates/services/nedb-user.js new file mode 100644 index 00000000..872a95f2 --- /dev/null +++ b/app/templates/services/nedb-user.js @@ -0,0 +1,24 @@ +import { join } from 'path'; +import hooks from '../hooks'; +import NeDB from 'nedb'; +import service from 'feathers-nedb'; + + +export default function(){ + const app = this; + + const db = new NeDB({ + filename: join(app.get('nedb'), 'users.db'), + autoload: true + }); + + let options = { + paginate: { + Model: db, + default: 5, + max: 25 + } + }; + + app.use('/v1/users', service(options)); +} \ No newline at end of file diff --git a/app/templates/services/sequelize-user.js b/app/templates/services/sequelize-user.js new file mode 100644 index 00000000..5599cf2c --- /dev/null +++ b/app/templates/services/sequelize-user.js @@ -0,0 +1,54 @@ +import hooks from '../hooks'; +import Sequelize from 'sequelize'; +import service from 'feathers-sequelize'; +import User from '../models/user'; + +export default function(){ + const app = this; + <% if (database === 'sqlite') { %> + const sequelize = new Sequelize('feathers', null, null, { + dialect: 'sqlite', + storage: app.get('sqlite'), + logging: false + });<% } else if (database === 'mssql') { %> + const sequelize = new Sequelize('feathers', { + dialect: '<%= database %>', + host: 'localhost', + port: 1433, + logging: false, + dialectOptions: { + instanceName: 'feathers' + } + });<% } else if (database) { %> + const sequelize = new Sequelize(app.get('<%= database %>'), { + dialect: '<%= database %>', + logging: false + }); + <% } %> + + let options = { + Model: User(sequelize), + paginate: { + default: 5, + max: 25 + } + }; + + app.use('/v1/users', service(options)); + + // const service = this.service('v1/users'); + + /* * * Before hooks * * */ + // service.before({ + // all: [hooks.requireAuthForPrivate()], + // before: [hooks.setUserID()] + // }); + + // /* * * After hooks * * */ + // service.after({ + // all: [hooks.removeSomeField()] + // }); + + // /* * * Set up event filters * * */ + // service.created = service.updated = service.patched = service.removed = events.requireAuthForPrivate; +} \ No newline at end of file From 5c8499333305b72386e84e6ce44619c22469e360 Mon Sep 17 00:00:00 2001 From: Eric Kryski Date: Tue, 12 Jan 2016 21:01:43 -0700 Subject: [PATCH 25/31] removing old user service --- app/templates/user-service.js | 51 ----------------------------------- 1 file changed, 51 deletions(-) delete mode 100644 app/templates/user-service.js diff --git a/app/templates/user-service.js b/app/templates/user-service.js deleted file mode 100644 index 57c63306..00000000 --- a/app/templates/user-service.js +++ /dev/null @@ -1,51 +0,0 @@ -import hooks from '../hooks'; -<% if (database && (database !== 'Memory' && database !== 'NeDB')) { %>import User from '../models/user';<% } %> -<% if (database === 'Memory') { %> -import service from 'feathers-memory'; -const User = {}; -<% } %> -<% if (database === 'NeDB') { %> -import NeDB from 'nedb'; -import service from 'feathers-nedb'; -let User = new NeDB({ - filename: './data/users.db', - autoload: true -}); -<% } %><% if (database === 'MongoDB') { %> -import mongoose from 'mongoose'; -import service from 'feathers-mongoose'; -mongoose.connect('mongodb://localhost:27017/feathers'); -mongoose.Promise = global.Promise; -<% } %> - -export default function(){ - const app = this; - /* * * Do any connection stuff here, if needed * * */ - - /* * * Service Type * * */ - let options = { - Model: User, - paginate: { - default: 5, - max: 25 - } - }; - - app.use('/v1/users', service(options)); - - // const service = this.service('v1/users'); - - /* * * Before hooks * * */ - // service.before({ - // all: [hooks.requireAuthForPrivate()], - // before: [hooks.setUserID()] - // }); - - // /* * * After hooks * * */ - // service.after({ - // all: [hooks.removeSomeField()] - // }); - - // /* * * Set up event filters * * */ - // service.created = service.updated = service.patched = service.removed = events.requireAuthForPrivate; -} \ No newline at end of file From d93c85badaec41424f75f7a86ac12cbee893c931 Mon Sep 17 00:00:00 2001 From: Eric Kryski Date: Tue, 12 Jan 2016 21:01:51 -0700 Subject: [PATCH 26/31] fixing spacing --- app/templates/app.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/templates/app.js b/app/templates/app.js index bb9cef0f..0b0b551f 100644 --- a/app/templates/app.js +++ b/app/templates/app.js @@ -1,8 +1,7 @@ import { join } from 'path'; import feathers from 'feathers'; import configuration from 'feathers-configuration'; -import hooks from 'feathers-hooks'; -<% if (providers.indexOf('rest') !== -1) { %> +import hooks from 'feathers-hooks';<% if (providers.indexOf('rest') !== -1) { %> import rest from 'feathers-rest'; import bodyParser from 'body-parser'; <% } %><% if (providers.indexOf('socket.io') !== -1) { %>import socketio from 'feathers-socketio';<% } %><% if (providers.indexOf('primus') !== -1) { %>import primus from 'feathers-primus';<% } %> From 617d59e93a34a7b56b1cab64374f6c05af878377 Mon Sep 17 00:00:00 2001 From: Eric Kryski Date: Tue, 12 Jan 2016 21:02:17 -0700 Subject: [PATCH 27/31] adding createdAt and updatedAt attributes to mongoose model --- app/templates/models/mongoose-user.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/templates/models/mongoose-user.js b/app/templates/models/mongoose-user.js index c50abbb1..98478586 100644 --- a/app/templates/models/mongoose-user.js +++ b/app/templates/models/mongoose-user.js @@ -3,7 +3,9 @@ const Schema = mongoose.Schema; let UserSchema = new Schema({ email: {type: String, required: true, index: true}, - password: {type: String, required: true} + password: {type: String, required: true}, + createdAt: {type: Date, 'default': Date.now}, + updatedAt: {type: Date, 'default': Date.now} }); let UserModel = mongoose.model('User', UserSchema); From 12adc23fcd5a14af92de2b40feb1a5443c8554ea Mon Sep 17 00:00:00 2001 From: Eric Kryski Date: Tue, 12 Jan 2016 21:02:33 -0700 Subject: [PATCH 28/31] showing hostname and port when server starts --- app/templates/static/server/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/templates/static/server/index.js b/app/templates/static/server/index.js index d812b23e..89feb256 100644 --- a/app/templates/static/server/index.js +++ b/app/templates/static/server/index.js @@ -5,5 +5,5 @@ var port = app.get('port'); var server = app.listen(port); server.on('listening', function() { - console.log('Feathers application started on port ' + port); + console.log(`Feathers application started on ${app.get('host')}:${port}`); }); From 52d2aa101828331b43e4a8befa3a1e12bc7831e7 Mon Sep 17 00:00:00 2001 From: Eric Kryski Date: Tue, 12 Jan 2016 21:02:53 -0700 Subject: [PATCH 29/31] fixing error handler so that it returns all attributes, including validation errors --- .../static/server/middleware/error-handler.js | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/app/templates/static/server/middleware/error-handler.js b/app/templates/static/server/middleware/error-handler.js index c58e5bd6..0895ae2d 100644 --- a/app/templates/static/server/middleware/error-handler.js +++ b/app/templates/static/server/middleware/error-handler.js @@ -32,14 +32,10 @@ export default function(app) { }, 'application/json': function () { - let output = { - code, - message: error.message, - name: error.name - }; - - if (app.settings.env !== 'production') { - output.stack = error.stack; + let output = Object.assign({}, error.toJSON()); + + if (app.settings.env === 'production') { + delete output.stack; } res.json(output); From 8c5bced8aa82da9c3851f83b73069153660076e2 Mon Sep 17 00:00:00 2001 From: Eric Kryski Date: Tue, 12 Jan 2016 21:03:07 -0700 Subject: [PATCH 30/31] adding a sequelize user model template --- app/templates/models/sequelize-user.js | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/app/templates/models/sequelize-user.js b/app/templates/models/sequelize-user.js index e69de29b..83ad8815 100644 --- a/app/templates/models/sequelize-user.js +++ b/app/templates/models/sequelize-user.js @@ -0,0 +1,24 @@ +import Sequelize from 'sequelize'; + +export default function(sequelize) { + let User = sequelize.define('user', { + email: { + type: Sequelize.STRING, + allowNull: false, + unique: true, + validate: { + isEmail: { msg: 'Must provide a valid email' } + } + }, + password: { + type: Sequelize.STRING, + allowNull: false + } + }, { + freezeTableName: true + }); + + User.sync({ force: true }); + + return User; +} \ No newline at end of file From 7f6119ce697c6171386ae69a21a9b61e9933969c Mon Sep 17 00:00:00 2001 From: Eric Kryski Date: Tue, 12 Jan 2016 21:03:17 -0700 Subject: [PATCH 31/31] updating config files to support databases --- app/templates/config.default.json | 9 ++++++++- app/templates/config.production.json | 11 +++++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/app/templates/config.default.json b/app/templates/config.default.json index 815cd1f0..d845a039 100644 --- a/app/templates/config.default.json +++ b/app/templates/config.default.json @@ -1,6 +1,13 @@ { "host": "localhost", - "port": 3030, + "port": 3030,<% if(database === 'mongodb'){ %> + "mongodb": "mongodb://localhost:27017/feathers",<% } else if(database === 'mariadb'){ %> + "mariadb": "mariadb://root:@localhost:3306/feathers",<% } else if(database === 'mssql'){ %> + "mssql": "mssql://root:@localhost:1433/feathers",<% } else if(database === 'mysql'){ %> + "mysql": "mysql://root:@localhost:3306/feathers",<% } else if(database === 'postgres'){ %> + "postgres": "postgres://postgres:@localhost:5432/feathers",<% } else if(database === 'nedb'){ %> + "nedb": "../data/",<% } else if(database === 'sqlite'){ %> + "sqlite": "../data/feathers.sqlite",<% } %> "public": "../public/"<% if(authentication.length){ %>, "auth": {<% if(authentication.indexOf('local' > -1)){ %> "local": { diff --git a/app/templates/config.production.json b/app/templates/config.production.json index 0580adbd..083df44c 100644 --- a/app/templates/config.production.json +++ b/app/templates/config.production.json @@ -1,6 +1,13 @@ { - "host": "myapp.com", - "port": 80, + "host": "<%=name%>-app.feathersjs.com", + "port": 80,<% if(database === 'mongodb'){ %> + "mongodb": "DATABASE_URL",<% } else if(database === 'mariadb'){ %> + "mariadb": "DATABASE_URL",<% } else if(database === 'mssql'){ %> + "mssql": "DATABASE_URL",<% } else if(database === 'mysql'){ %> + "mysql": "DATABASE_URL",<% } else if(database === 'postgres'){ %> + "postgres": "DATABASE_URL",<% } else if(database === 'nedb'){ %> + "nedb": "NEDB_BASE_PATH",<% } else if(database === 'sqlite'){ %> + "sqlite": "SQLITE_DB",<% } %> "public": "../public/"<% if(authentication.length){ %>, "auth": {<% if(authentication.indexOf('local' > -1)){ %> "local": {