diff --git a/AUTHORS.md b/AUTHORS.md index c8f2dba2..d953f551 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -1,21 +1,15 @@ -# Authors and contributors +## Core developers -Here we add short biography of each contributor to this project. +| Eugene Obrezkov | +|:-------------------------------------------------------------------------------------:| +| ![Eugene Obrezkov](http://gravatar.com/avatar/be299f224394ab488001c9cab12eae2c?s=100) | +| Lead developer of this project | +| Get in touch with me via [@ghaiklor](https://twitter.com/ghaiklor) | -### Eugene Obrezkov (aka ghaiklor) +## Contributors -![Eugene Obrezkov](http://gravatar.com/avatar/be299f224394ab488001c9cab12eae2c?s=100) +We don't have contributors yet... -Lead developer of this project. +## Special Thanks -JavaScript developer with experience about 4+ years. - -You can contact me via links below: - -+ [VK](http://vk.com/ghaiklor_xakep) -+ [Facebook](https://www.facebook.com/ghaiklor) -+ [Twitter](https://twitter.com/ghaiklor) -+ [GMail](mailto:ghaiklor@gmail.com) -+ [Habrahabr](http://habrahabr.ru/users/ghaiklor/) -+ [Stack Overflow](http://stackoverflow.com/users/2357633/eugene-obrezkov) -+ [GitHub](https://github.com/ghaiklor) ++ [@rizr](https://github.com/rizr) - for participating in testing and proposals to make it better; diff --git a/CHANGELOG.md b/CHANGELOG.md index f44427e8..54bb2653 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,32 @@ ## Edge version +## Version 0.6.0 + +- Improvement: Implemented parsing CommonJS modules from AST (Abstract Syntax Tree), so now generator is knows which modules you are really using; +- Improvement: Frozen structure of generator and project. Any serious changes in project structure; +- Improvement: Add Yahoo authorization; +- Improvement: Split generator arguments declaration to separate folder; +- Improvement: Split generator options declaration to separate folder; +- Improvement: Split generator questions declaration to separate folder; +- Improvement: Answers to questions is dynamically assign to existing `this.answers` object in `RunContext`; +- Improvement: Split each generator step to separate folder and modules; +- Improvement: Implement util `assign()` method which is using in `prompting` step for extend answers object; +- Improvement: Redesign of generator structure; +- Improvement: Add options to `check-updates.js` tool; +- Improvement: Add tools to npm scripts, so you can call `npm run-script ` now; +- Improvement: Add `verbose` flag, so you can see everything what going on in `RunContext`; +- Improvement: Add logging all `serverError` responses to log by default (you don't need call `sails.log.error()`); +- Improvement: Implement logging all responses in `silly` mode; +- Improvement: Add `ping` route to `AuthController` which just respond with OK; +- Fix: Bug when JWT issued to user `false`; +- Fix: Bug with passport-jwt upgrade; +- Fix: Bug with `bodyParser` is not parsing POST body; +- Fix: Bug with no colorful print in `fix-deps.js`; +- Fix: Bug with no colorful print in installing step in generator; +- Fix: Bug when returns total count of records even with `where` criteria; +- Fix: Change log level info to `info` in production mode; + ## Version 0.5.1 - Hotfix: Bug when with `skip-all` generator is freeze up; @@ -45,7 +71,7 @@ - Improvement: Frozen stability of the whole project, so work is going only in `services`, `test` and `doc` folder; - Improvement: Add blank tests for each `controllers`, `models`, `policies`, `responses`, `services`; -- Improvement: Each test case named **TODO: write this test** as reminder and include `assert(true)`; +- Improvement: Each test case named **write this test** as reminder and include `assert(true)`; - Improvement: Add JSON Web Token as part of `CipherService`; - Improvement: Support for `skip-install` flag. You can skip `npm install` executing; - Improvement: Add `customMiddleware` support in `config/http.js`; diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..8e69bb70 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,181 @@ +# Contributing + +At this page we explain how you can contribute to our project. + +## Creating issue + +If you have any proposal or want to report about bug, feel free to create issue. +We don't moderate issues and will answer you as soon as possible. + +## Get the project + +### Clone repository + +Clone repository to your computer. + +```bash +git clone https://github.com/ghaiklor/generator-sails-rest-api.git # via HTTPS +git clone git@github.com:ghaiklor/generator-sails-rest-api.git # via SSH +``` + +### Install dependencies + +We are using `npm` for manage dependencies. +So all development dependencies is declared into `package.json` and you can install it simply call `npm install`. + +```bash +cd generator-sails-rest-api +npm install +``` + +### Run generator-sails-rest-api locally + +When you clone the repository, you can link this repository as global npm module in your system. +Then check if this linked up by executing `npm -g list --depth=0`. +If everything is correct, you will see path to your cloned repository. +After linking, you can call `yo sails-rest-api` and run generator locally. + +```bash +git clone https://github.com/ghaiklor/generator-sails-rest-api.git +cd generator-sails-rest-api +sudo npm link +yo sails-rest-api +``` + +## Branch explanation + +Our repository has two main branches: + +1. [master](https://github.com/ghaiklor/generator-sails-rest-api/tree/master "Master Branch") +2. [dev](https://github.com/ghaiklor/generator-sails-rest-api/tree/dev "Development Branch") + +All stable releases will merge into `master` branch. +So if you want get and use last stable release ready for production - use `master` branch. + +But, of course, you can use edge version and check out our latest features. +They are may be unstable and not tested, so they are not ready for production. +All those changes and features will merge into `dev` branch. + +Other branches it's only helper branches which can close at any time. +In general we have 3 types of branches: + ++ Feature branches; ++ Release branches; ++ Hotfix branches; + +Advanced description you will read below in "Git workflow" section. + +## Git workflow + +We are using git workflow, which are nice described [here in Russian](http://habrahabr.ru/post/106912/ "Thanks to Андрей Хитрин aka zloddey") and [here in English](http://nvie.com/posts/a-successful-git-branching-model/ "Thanks to Vincent Driessen"). + +In general, we are using `master` branch for stable releases (and ONLY stable releases) and `dev` branch for development purposes. + +### Feature/bug branches + +Let's say you want realize new feature or fix something. +You **MUST** checkout to new branch from `dev` branch. +Created branch **MUST** have name that begins with **feature-...** or **bug-...**. +This name conventions simplify work with many branches and we can got what exactly each branch doing. +When you finish with branch, manually merge feature-branch into `dev`. + +```bash +git checkout dev # Checkout to dev branch +git pull # Update dev branch changes + +git checkout -b feature-awesome # Create new feature branch +git commit -am "Commit 1" # Commit changes +git commit -am "Commit 2" # Last commit + +git checkout dev # Switch back to dev branch +git pull # Update last changes in dev + +git merge feature-awesome # Merge your changes into dev branch + +git branch -d feature-awesome # And you can delete old branch +git push -u origin dev # Push changes to origin server +``` + +**_Note_**: If you don't have rights for push\merge into `dev`, create pull request and we accept it ourselves. + +### Release branches + +When we decide that `dev` branch is ready for release, we are create new branch with **release-...** prefix. +In this branch we update metadata and fix **ONLY** bugs. +When release branch will proceed all last checks and tests, we merge release branch into `master` and `dev`. +And add tag in `master` branch with appropriate release version. + +```bash +git checkout dev # Switch to development branch +git pull # Get last changes + +git checkout -b release-1.2.0 # Create new release branch + +git commit -am "Release version v1.2.0" # Commit changes into release branch +git commit -am "Allows make small changes" # Maybe small changes + +git checkout master # Switch to master branch +git merge release-1.2.0 # Merge release branch into master +git tag -a "v1.2.0" master # Add a tag to release commit in master branch + +git checkout dev # Switch to dev branch +git merge release-1.2.0 # Merge release changes to dev branch + +git branch -d release-1.2.0 # Remove unused release branch + +git push -u origin --all # And push all to origin server +git push -u origin --tags # With tags too +``` + +### Hotfix branches + +When some seriously error or bug occurs into stable release (`master`) we make hotfix branch. +Hotfix branch **MUST** begins with **hotfix-...** prefix. +After fixes we merge hotfix branch into `master` and `dev`. +Also increment patch version and publish this hotfix as new version. + +```bash +git checkout master # Switch to master branch +git checkout -b hotfix-1.2.1 # Create new hotfix branch + +git commit -am "Fix for something" # Critical bugs fixes +git commit -am "Bumped version and build" # Last commit + +git checkout master # Switch back to master branch +git merge hotfix-1.2.1 # Merge hotfix branch +git tag -a "v1.2.1" master # Add tag to this commit + +git checkout dev # Switch to dev branch +git merge hotfix-1.2.1 # Apply fixes to dev branch too + +git branch -d hotfix-1.2.1 # Remove hotfix branch +git push -u origin --all # Push all changes to origin server +git push -u origin --tags # And push tags to origin server +``` + +## Tests + +We are using Mocha for testing. +All test cases located into `test` folder and split like source files split. +For every file in `api/` folder we create appropriate in `test/` folder. + +When you create new feature or fix some bug, you **MUST** write test case for it in `test` folder. +For example, you add new feature to `api/services/cipher/BCryptCipher.js`. +After that you need modify `test/services/cipher/BCryptCipher.js` for corresponds to new feature. + +For run tests just call `npm test`. + +## Versions + +We are using "Semantic Versioning". +You can find rules how to use it [here](http://semver.org/). + +In general, 4 types of version exists: + +1. Pre-release -> `v1.2.3-4`. Pre-release changes only builds number after `-`. I.e `v1.2.3-4` -> `v1.2.3-5`. + +2. Patch -> `v1.2.3`. Patch version changes only patch version at the end and remove builds number. I.e. `v1.2.3-5` -> `v1.2.3` -> `v1.2.4`. + +3. Minor -> `v1.3.0`. Minor version changes second number in version and reset patch version and prerelease version. I.e. `v1.2.4` -> `v1.3.0`. + +4. Major -> `v2.0.0`. Major version changes first number in version and reset all other versions. I.e. `v1.3.0` -> `v2.0.0`. diff --git a/README.md b/README.md index c31aff18..e747bc5b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # generator-sails-rest-api -[![npm version](https://badge.fury.io/js/generator-sails-rest-api.svg)](http://badge.fury.io/js/generator-sails-rest-api) [![Build Status](https://secure.travis-ci.org/ghaiklor/generator-sails-rest-api.png?branch=master)](https://travis-ci.org/ghaiklor/generator-sails-rest-api) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/ghaiklor/generator-sails-rest-api?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [Documentation](https://github.com/ghaiklor/generator-sails-rest-api/wiki) +[![npm version](https://badge.fury.io/js/generator-sails-rest-api.svg)](http://badge.fury.io/js/generator-sails-rest-api) ![Dependencies is up to date](https://david-dm.org/ghaiklor/generator-sails-rest-api.svg) [![Build Status](https://travis-ci.org/ghaiklor/generator-sails-rest-api.svg?branch=master)](https://travis-ci.org/ghaiklor/generator-sails-rest-api) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/ghaiklor/generator-sails-rest-api?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [Documentation](https://github.com/ghaiklor/generator-sails-rest-api/wiki) > Stability: 2 - Unstable @@ -24,7 +24,39 @@ Yeoman generator that provides already configured and optimized Sails REST API w - Integrated Swagger doc specification in `doc` folder; - Integrated Mocha tests for all `blueprints`, `controllers`, `models`, `policies`, `responses` and `services`. After generating you can execute `npm test`; -## ready-2-use services +## Getting Started + +Start using generator is very simple. + +First of all, you need install **yeoman** and **generator-sails-rest-api**: + +```bash +npm install -g yo generator-sails-rest-api +``` + +Then create project directory and initiate the generator under the project directory: + +```bash +mkdir my-project +cd my-project +yo sails-rest-api +``` + +You will be prompted for questions. Answer to those questions that generator is asks and you will get configured Sails project. + +After scaffolding the project you can use this project as before. Just run the `app.js` file and all. + +```bash +node app.js +``` + +Congratulations, you just have setup your first Sails REST API :+1: + +## How to use project code? + +_TODO:_ Fill with information. But you can read more at our [wiki page](https://github.com/ghaiklor/generator-sails-rest-api/wiki/How-to-use) + +## Bundled Sails services | Service Name | Implemented providers | |:--------------:|:------------------------------------------------:| diff --git a/ROADMAP.md b/ROADMAP.md index 00ace78f..7362f8c8 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -2,15 +2,29 @@ ## Faraway -- [ ] Add **apn** and **gcm** push notifications; -- [ ] Add social service **Twitter**; -- [ ] Add support for **Amazon** mailer, **sendmail** mailer; -- [ ] Sub generators for each service; +- [ ] Add `md5`, `crc` Ciphers; +- [ ] Add `sendmail`, `Amazon` Mailer; +- [ ] Add `braintreepayments`, `PayPal` Payment; +- [ ] Add `Windows Phone` Pusher; +- [ ] Add `Twitter`, `VK`, `Google +` Social; +- [ ] Add `Local` Storage; - [ ] Test coverage for all `api` files; -- [ ] Add questions list where you can choose which service need to include and copy only needed; -- [ ] Make swagger documentation as part of Sails generator; +- [ ] Implement search on top of `find` blueprint; +- [ ] Implement pluralized form only for REST models; +- [ ] Think about questions "Which hook you want to leave enable?" and disable\enable hooks based on those answers; +- [ ] Think about generating not only backend REST API, so user can still have clean Sails application (maybe call spawn `sails new `; -## Next version +## Next minor version + +- [ ] Implement converting camelCase attributes to snake_case in HTTP response; +- [ ] Make global error codes for API with description and how fix it; +- [ ] Add aliases to common operation (for instance, `v1/user/recently_registered`; + +## Version 0.6.0 (released) + +- [x] Improve stability (too much new features is appears in previous version); +- [x] Freeze architecture (only bug-fixes and adding new features); +- [x] Add questions list where you can choose which service need to include; ## Version 0.5.0 (released) diff --git a/app/index.js b/app/index.js deleted file mode 100644 index aecfdf48..00000000 --- a/app/index.js +++ /dev/null @@ -1,275 +0,0 @@ -var crypto = require('crypto'), - chalk = require('chalk'), - printMessage = require('print-message'), - updateNotifier = require('update-notifier'), - yeoman = require('yeoman-generator'), - yosay = require('yosay'); - -/** - * List of questions - * @type {Array} - * @private - */ -var QUESTIONS_LIST = [{ - type: 'input', - name: 'project:name', - message: 'Type your project name', - default: 'sails-rest-api' -}, { - type: 'list', - name: 'database:adapter', - message: 'Choose database adapter', - choices: [ - 'MySQL', - 'Mongo', - 'PostgreSQL', - 'Redis' - ], - default: 1 -}, { - type: 'input', - name: 'database:user', - message: 'Type database username', - default: '' -}, { - type: 'password', - name: 'database:password', - message: 'Type database password', - default: '' -}, { - type: 'input', - name: 'database:host', - message: 'Type database host', - default: 'localhost' -}, { - type: 'input', - name: 'database:name', - message: 'Type database name', - default: 'sails-rest-api' -}, { - type: 'input', - name: 'application:app-secret-token', - message: 'Type private application token', - default: crypto.randomBytes(16).toString('hex') -}, { - type: 'input', - name: 'application:jwt-secret-token', - message: 'Type private token for JSON Web Token', - default: crypto.randomBytes(16).toString('hex') -}, { - type: 'input', - name: 'application:facebook-client-id', - message: 'Type Facebook Client ID', - default: '-' -}, { - type: 'input', - name: 'application:facebook-client-secret', - message: 'Type Facebook Client Secret', - default: '-' -}, { - type: 'input', - name: 'application:twitter-consumer-key', - message: 'Type Twitter Consumer Key', - default: '-' -}, { - type: 'input', - name: 'application:twitter-consumer-secret', - message: 'Type Twitter Consumer Secret', - default: '-' -}]; - -module.exports = yeoman.generators.Base.extend({ - /** - * Special methods may do things like set up important state controls and may not function outside of the constructor - * @type {Function} - */ - constructor: function () { - yeoman.generators.Base.apply(this, arguments); - - this.option("skip-generator-update", { - desc: "Skip checking for generator updates on running", - type: Boolean, - defaults: false, - hide: false - }); - - this.option("skip-generator-welcome", { - desc: "Skip saying welcome when generator is running", - type: Boolean, - defaults: false, - hide: false - }); - - this.option("skip-project-install", { - desc: "Skip installing npm dependencies in project", - type: Boolean, - defaults: false, - hide: false - }); - - this.option("skip-project-diagnostic", { - desc: "Skip running diagnostic tools in project", - type: Boolean, - defaults: false, - hide: false - }); - - this.option("skip-all", { - desc: "Skip everything, just project scaffolding", - type: Boolean, - defaults: false, - hide: false - }); - - this.config.save(); - }, - - /** - * Step 1 - * Your initialization methods (checking current project state, getting configs, etc) - * @type {Object} - */ - initializing: { - loadPackageInfo: function () { - this.pkg = require('../package.json'); - }, - - notifyAboutGeneratorUpdate: function () { - if (!(this.options['skip-generator-update'] || this.options['skip-all'])) { - var done = this.async(); - - this.log(chalk.yellow("Checking for updates...")); - - updateNotifier({ - pkg: this.pkg, - callback: function (error, update) { - if (update && update.type && update.type !== 'latest') { - printMessage([ - "Update available: " + chalk.green.bold(update.latest) + chalk.dim(" (current: " + update.current + ")"), - "Run " + chalk.blue("npm update -g " + update.name) + " to update." - ], { - marginTop: 0, - marginBottom: 0, - printFn: this.log - }); - - process.exit(0); - } else { - this.log(chalk.yellow("OK... You're using the latest version " + chalk.green.bold(update.current))); - done(); - } - }.bind(this) - }); - } - }, - - sayHello: function () { - if (!(this.options["skip-generator-welcome"] || this.options["skip-all"])) { - this.log(yosay('Welcome to the laudable ' + chalk.red('Sails REST API') + ' generator!')); - } - } - }, - - /** - * Step 2 - * Where you prompt users for options (where you'd call this.prompt()) - */ - prompting: { - askBaseQuestions: function () { - var done = this.async(); - - this.prompt(QUESTIONS_LIST, function (answers) { - this.answers = answers; - done(); - }.bind(this)); - } - }, - - /** - * Step 3 - * Saving configurations and configure the project (creating .editorconfig files and other metadata files) - */ - configuring: {}, - - /** - * Step 4 - * Default priority - */ - - /** - * Step 5 - * Where you write the generator specific files (routes, controllers, etc) - */ - writing: { - copyDirectory: function () { - this.directory( - this.sourceRoot(), - this.destinationRoot() - ); - } - }, - - /** - * Step 6 - * Where conflicts are handled (used internally) - */ - conflicts: {}, - - /** - * Step 7 - * Where installation are run (npm, bower) - */ - install: { - installNpmDeps: function () { - if (!(this.options['skip-project-install'] || this.options["skip-all"])) { - this.log(chalk.yellow("\nStart installing npm dependencies, please wait...\n")); - this.npmInstall(); - } - } - }, - - /** - * Step 8 - * Called last, cleanup, say good bye, etc - */ - end: { - runDiagnostic: function () { - if (!(this.options["skip-project-diagnostic"] || this.options["skip-all"])) { - var done = this.async(); - - this.log(chalk.yellow("\nStarting diagnostic, please wait...\n")); - - this.spawnCommand('node', ['tools/fix-deps.js']).on('close', function () { - this.spawnCommand('node', ['tools/update-deps.js']).on('close', done); - }.bind(this)); - } - }, - - sayUnderDevelopmentWarning: function () { - printMessage([ - "This generator under heavy development", - "If you found any bugs or have proposals, feel free to create issue", - chalk.red(this.pkg.bugs.url), - "Or you can write me the letter", - chalk.red(this.pkg.bugs.email) - ], { - marginTop: 0, - marginBottom: 0, - printFn: this.log - }); - }, - - sayNotInstalledNpmDepsWarning: function () { - if (this.options['skip-project-install'] || this.options["skip-all"]) { - printMessage([ - "You have skipped installing npm modules", - "Install them manually via " + chalk.blue("npm install") - ], { - marginTop: 0, - marginBottom: 0, - printFn: this.log - }); - } - } - } -}); diff --git a/app/templates/api/controllers/AuthController.js b/app/templates/api/controllers/AuthController.js deleted file mode 100644 index b77d0f6f..00000000 --- a/app/templates/api/controllers/AuthController.js +++ /dev/null @@ -1,94 +0,0 @@ -/** - * AuthController - * @description :: Server-side logic for manage user's authorization - */ - -var passport = require('passport'); - -module.exports = { - /** - * Sign in by local strategy in passport - */ - signin: function (req, res) { - passport.authenticate('local', function (error, user, info) { - if (error) { - sails.log.error(error); - res.serverError(error); - } else if (info) { - res.unauthorized(null, info.code, info.message); - } else { - res.ok({ - token: CipherService.create('jwt', {id: user.id}).hashSync(), - user: user - }); - } - })(req, res); - }, - - /** - * Sign up in system - */ - signup: function (req, res) { - // TODO: think about model duplicate - - User - .create(req.allParams()) - .exec(function (error, user) { - if (error) { - sails.log.error(error); - res.serverError(error); - } else { - res.created({ - token: CipherService.create('jwt', {id: user.id}).hashSync(), - user: user - }); - } - }); - }, - - /** - * Facebook authorization\linking - */ - facebook: function (req, res) { - passport.authenticate('jwt', function (error, user) { - req.user = user; - - passport.authenticate('facebook-token', function (error, user, info) { - if (error) { - sails.log.error(error); - res.serverError(error); - } else if (info) { - res.unauthorized(null, info.code, info.message); - } else { - res.ok({ - token: CipherService.create('jwt', {id: user.id}).hashSync(), - user: user - }); - } - })(req, res); - })(req, res); - }, - - /** - * Twitter authorization\linking - */ - twitter: function (req, res) { - passport.authenticate('jwt', function (error, user) { - req.user = user; - - passport.authenticate('twitter-token', function (error, user, info) { - if (error) { - sails.log.error(error); - res.serverError(error); - } else if (info) { - res.unauthorized(null, info.code, info.message); - } else { - res.ok({ - token: CipherService.create('jwt', {id: user.id}).hashSync(), - user: user - }); - } - })(req, res); - })(req, res); - } -}; diff --git a/app/templates/api/policies/isOurApp.js b/app/templates/api/policies/isOurApp.js deleted file mode 100644 index fcd64c22..00000000 --- a/app/templates/api/policies/isOurApp.js +++ /dev/null @@ -1,14 +0,0 @@ -/** - * isOurApp - * @description :: Policy to check if this request goes from our applications - */ - -module.exports = function (req, res, next) { - var token = req.headers["application-token"]; - - if (token && token === "<%= answers['application:app-secret-token'] %>") { - next(); - } else { - res.unauthorized(null, null, "You must provide application token"); - } -}; diff --git a/app/templates/tools/update-deps.js b/app/templates/tools/update-deps.js deleted file mode 100644 index a8563e20..00000000 --- a/app/templates/tools/update-deps.js +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env node - -var npmCheck = require('npm-check'), - update = require('npm-check/lib/update'), - chalk = require('chalk'); - -console.log(chalk.yellow("\nStart checking package.json for updates, please wait...\n")); - -npmCheck({ - update: true -}).then(function (data) { - // TODO: expand with more params to update() - update(data, {}); -}).done(); diff --git a/generators/app/arguments/index.js b/generators/app/arguments/index.js new file mode 100644 index 00000000..9ec08954 --- /dev/null +++ b/generators/app/arguments/index.js @@ -0,0 +1,16 @@ +/** + * Exports object contains arguments names and their configuration objects + * + * @example + * module.exports = { + * argName: { + * desc: 'Argument description', + * required: false, + * optional: true, + * type: String || Number || Array || Object, + * defaults: 'test' + * } + * }; + */ + +module.exports = {}; diff --git a/generators/app/index.js b/generators/app/index.js new file mode 100644 index 00000000..d5e28d30 --- /dev/null +++ b/generators/app/index.js @@ -0,0 +1,30 @@ +var yeoman = require('yeoman-generator'), + steps = require('./steps'); + +module.exports = yeoman.generators.Base.extend({ + constructor: function () { + yeoman.generators.Base.apply(this, arguments); + + var args = require('./arguments'), + options = require('./options'); + + Object.keys(args).forEach(function (argName) { + this.argument(argName, args[argName]); + }.bind(this)); + + Object.keys(options).forEach(function (optionName) { + this.option(optionName, options[optionName]); + }.bind(this)); + + this.description = "Yeoman generator that provides already configured and optimized Sails REST API with bundle of predefined features"; + this.config.save(); + }, + + initializing: steps.initializing, + prompting: steps.prompting, + configuring: steps.configuring, + writing: steps.writing, + conflicts: steps.conflicts, + install: steps.install, + end: steps.end +}); diff --git a/generators/app/options/index.js b/generators/app/options/index.js new file mode 100644 index 00000000..00ddd0c2 --- /dev/null +++ b/generators/app/options/index.js @@ -0,0 +1,52 @@ +/** + * Exports object contains options names and their configuration objects + * + * @example + * module.exports = { + * 'option-name': { + * desc: 'Option description', + * type: Boolean || String || Number, + * defaults: false, + * hide: false + * } + * }; + */ + +module.exports = { + 'verbose': { + desc: 'Print all information to console', + type: Boolean, + defaults: false, + hide: false + }, + 'skip-generator-update': { + desc: 'Do not check for generator updates', + type: Boolean, + defaults: false, + hide: false + }, + 'skip-generator-welcome': { + desc: 'Do not welcome at start', + type: Boolean, + defaults: false, + hide: false + }, + 'skip-project-install': { + desc: 'Do not install npm dependencies', + type: Boolean, + defaults: false, + hide: false + }, + 'skip-project-diagnostic': { + desc: 'Do not run diagnostic tools', + type: Boolean, + defaults: false, + hide: false + }, + 'skip-all': { + desc: 'Do nothing, just project scaffolding', + type: Boolean, + defaults: false, + hide: false + } +}; diff --git a/generators/app/questions/application.js b/generators/app/questions/application.js new file mode 100644 index 00000000..98e363b8 --- /dev/null +++ b/generators/app/questions/application.js @@ -0,0 +1,48 @@ +var crypto = require('crypto'); + +module.exports = [{ + type: 'input', + name: 'application:name', + message: 'Type your application name', + default: 'sails-rest-api' +}, { + type: 'input', + name: 'application:api-secret-key', + message: 'Type your private API key', + default: crypto.randomBytes(32).toString('hex') +}, { + type: 'input', + name: 'application:jwt-secret', + message: 'Type your private key for JSON Web Token', + default: crypto.randomBytes(32).toString('hex') +}, { + type: 'input', + name: 'application:facebook-app-id', + message: 'Type your Facebook App ID', + default: '-' +}, { + type: 'input', + name: 'application:facebook-app-secret', + message: 'Type your Facebook App Secret', + default: '-' +}, { + type: 'input', + name: 'application:twitter-consumer-key', + message: 'Type your Twitter Consumer Key', + default: '-' +}, { + type: 'input', + name: 'application:twitter-consumer-secret', + message: 'Type your Twitter Consumer Secret', + default: '-' +}, { + type: 'input', + name: 'application:yahoo-app-id', + message: 'Type your Yahoo App ID', + default: '-' +}, { + type: 'input', + name: 'application:yahoo-app-secret', + message: 'Type your Yahoo App Secret', + default: '-' +}]; diff --git a/generators/app/questions/database.js b/generators/app/questions/database.js new file mode 100644 index 00000000..a23946e2 --- /dev/null +++ b/generators/app/questions/database.js @@ -0,0 +1,32 @@ +module.exports = [{ + type: 'list', + name: 'database:adapter', + message: 'Choose database adapter', + default: 1, + choices: [ + 'MySQL', + 'Mongo', + 'PostgreSQL', + 'Redis' + ] +}, { + type: 'input', + name: 'database:host', + message: 'Type your database host', + default: 'localhost' +}, { + type: 'input', + name: 'database:name', + message: 'Type your database name', + default: 'sails-rest-api' +}, { + type: 'input', + name: 'database:user', + message: 'Type your database username', + default: '' +}, { + type: 'password', + name: 'database:password', + message: 'Type your database password', + default: '' +}]; diff --git a/generators/app/questions/index.js b/generators/app/questions/index.js new file mode 100644 index 00000000..44ae264c --- /dev/null +++ b/generators/app/questions/index.js @@ -0,0 +1,23 @@ +/** + * Exports object contains questions divided into sections. + * Each section should contain array with questions. + * Array with questions it's Inquirer prompts objects - https://github.com/SBoudrias/Inquirer.js#prompts-type + * + * @example + * module.exports = { + * sectionName: require('./sectionName'), + * anotherSection: [{ + * type: 'input', + * name: 'application-name', + * message: 'Typo your application name', + * default: 'sails-rest-api' + * }] + * }; + */ + +module.exports = { + application: require('./application'), + database: require('./database'), + miscellaneous: require('./miscellaneous'), + services: require('./services') +}; diff --git a/generators/app/questions/miscellaneous.js b/generators/app/questions/miscellaneous.js new file mode 100644 index 00000000..c716c466 --- /dev/null +++ b/generators/app/questions/miscellaneous.js @@ -0,0 +1,16 @@ +module.exports = [{ + type: 'confirm', + name: 'docs:include', + message: 'Is Swagger documentation needed for project?', + default: true +}, { + type: 'confirm', + name: 'tests:include', + message: 'Is Mocha tests needed for project?', + default: true +}, { + type: 'confirm', + name: 'tools:include', + message: 'Is diagnostic tools needed for project?', + default: true +}]; diff --git a/generators/app/questions/services.js b/generators/app/questions/services.js new file mode 100644 index 00000000..4269dbb8 --- /dev/null +++ b/generators/app/questions/services.js @@ -0,0 +1,89 @@ +var inquirer = require('yo/node_modules/inquirer'); + +module.exports = [{ + type: 'checkbox', + name: 'services:cipher', + message: 'Choose which Cipher services you want', + choices: [{ + name: 'bcrypt', + checked: true + }, { + name: 'jwt', + checked: true + }, new inquirer.Separator(), { + name: 'More Cipher is coming', + disabled: 'Wait for it...' + }] +}, { + type: 'checkbox', + name: 'services:mailer', + message: 'Choose which Mailer services you want', + choices: [{ + name: 'Mandrill', + checked: false + }, new inquirer.Separator(), { + name: 'More Mailer is coming', + disabled: 'Wait for it...' + }] +}, { + type: 'checkbox', + name: 'services:payment', + message: 'Choose which Payment services you want', + choices: [{ + name: 'Stripe', + checked: false + }, new inquirer.Separator(), { + name: 'More Payment is coming', + disabled: 'Wait for it...' + }] +}, { + type: 'checkbox', + name: 'services:pusher', + message: 'Choose which Pusher services you want', + choices: [{ + name: 'Apple Push Notification', + checked: false + }, { + name: 'Google Cloud Messaging', + checked: false + }, new inquirer.Separator(), { + name: 'More Pusher is coming', + disabled: 'Wait for it...' + }] +}, { + type: 'checkbox', + name: 'services:sms', + message: 'Choose which SMS services you want', + choices: [{ + name: 'Twilio', + checked: false + }, new inquirer.Separator(), { + name: 'More SMS is coming', + disabled: 'Wait for it...' + }] +}, { + type: 'checkbox', + name: 'services:social', + message: 'Choose which Social services you want', + choices: [{ + name: 'Facebook', + checked: false + }, new inquirer.Separator(), { + name: 'More Social is coming', + disabled: 'Wait for it...' + }] +}, { + type: 'checkbox', + name: 'services:storage', + message: 'Choose which Storage services you want', + choices: [{ + name: 'Amazon S3', + checked: false + }, { + name: 'Google Cloud Storage', + checked: false + }, new inquirer.Separator(), { + name: 'More Storage is coming', + disabled: 'Wait for it...' + }] +}]; diff --git a/generators/app/steps/configuring.js b/generators/app/steps/configuring.js new file mode 100644 index 00000000..83ccabd6 --- /dev/null +++ b/generators/app/steps/configuring.js @@ -0,0 +1,6 @@ +/** + * Step 3 + * Saving configurations and configure the project (creating .sails-rc files and other metadata files) + */ + +module.exports = {}; diff --git a/generators/app/steps/conflicts.js b/generators/app/steps/conflicts.js new file mode 100644 index 00000000..7314763a --- /dev/null +++ b/generators/app/steps/conflicts.js @@ -0,0 +1,6 @@ +/** + * Step 6 + * Where conflicts are handled (used internally) + */ + +module.exports = {}; diff --git a/generators/app/steps/end.js b/generators/app/steps/end.js new file mode 100644 index 00000000..26b0351b --- /dev/null +++ b/generators/app/steps/end.js @@ -0,0 +1,84 @@ +/** + * Step 8 + * Called last, cleanup, say good bye, etc + */ + +var chalk = require('chalk'), + printMessage = require('print-message'); + +/** + * Triggers when check-updates is finished + * @param {Function} done + * @param {Number} code + * @private + */ +function _onCheckUpdatesClose(done, code) { + if (code !== 0) { + printMessage('Some error was occurred', {borderColor: 'red'}); + return process.exit(code); + } + + done(); +} + +/** + * Triggers when fix-deps tools is finished + * @param {Function} done + * @param {Number} code + * @private + */ +function _onFixDepsClose(done, code) { + if (code !== 0) { + printMessage('Some error was occurred', {borderColor: 'red'}); + return process.exit(code); + } + + this.spawnCommand('npm', [ + 'run-script', + 'check-updates', + this.options.verbose ? '--verbose' : '' + ]).on('close', _onCheckUpdatesClose.bind(this, done)); +} + +module.exports = { + /** + * Run diagnostic tools + */ + runDiagnostic: function () { + if (!(this.options['skip-project-diagnostic'] || this.options['skip-all'])) { + this.log(chalk.yellow('Starting diagnostic, please wait...')); + this.spawnCommand('npm', [ + 'run-script', + 'fix-deps', + this.options.verbose ? '--verbose' : '' + ]).on('close', _onFixDepsClose.bind(this, this.async())); + } + }, + + /** + * Say warning that this generator is under development + */ + sayUnderDevelopmentWarning: function () { + // TODO: remove when will be stable version + printMessage([ + 'This generator under heavy development', + 'If you found any bugs or have proposals, feel free to create issue', + chalk.red(this.pkg.bugs.url), + 'Or you can write me the letter', + chalk.red(this.pkg.bugs.email) + ]); + }, + + /** + * Warn user if he skip installing dependencies + */ + sayNotInstalledNpmDepsWarning: function () { + // TODO: remove this message when installing step will be more stable + if (this.options['skip-project-install'] || this.options['skip-all']) { + printMessage([ + 'You have skipped installing npm modules', + 'Install them manually via ' + chalk.blue('npm install') + ]); + } + } +}; diff --git a/generators/app/steps/index.js b/generators/app/steps/index.js new file mode 100644 index 00000000..a3298803 --- /dev/null +++ b/generators/app/steps/index.js @@ -0,0 +1,13 @@ +/** + * Exports object contains each priority step for yeoman run loop + */ + +module.exports = { + configuring: require('./configuring'), + conflicts: require('./conflicts'), + end: require('./end'), + initializing: require('./initializing'), + install: require('./install'), + prompting: require('./prompting'), + writing: require('./writing') +}; diff --git a/generators/app/steps/initializing.js b/generators/app/steps/initializing.js new file mode 100644 index 00000000..2cfa27ac --- /dev/null +++ b/generators/app/steps/initializing.js @@ -0,0 +1,66 @@ +/** + * Step 1 + * Your initialization methods (checking current project state, getting configs, etc) + */ + +var chalk = require('chalk'), + updateNotifier = require('update-notifier'), + printMessage = require('print-message'), + yosay = require('yosay'); + +/** + * Triggers when updateNotifier done checking updates + * @param {Function} done + * @param {Object} error + * @param {Object} update + * @private + */ +function _onUpdateNotifier(done, error, update) { + if (error) { + console.error(error.stack || error); + return process.exit(1); + } + + if (update && update.type !== 'latest') { + printMessage([ + 'Update available: ' + chalk.green.bold(update.latest) + chalk.dim(' (current: ' + update.current + ')'), + 'Run ' + chalk.blue('npm update -g ' + update.name) + ' to update.' + ]); + + process.exit(0); + } else { + this.log(chalk.yellow('OK... You are using the latest version ' + chalk.green.bold(update.current))); + done(); + } +} + +module.exports = { + /** + * Load package.json + */ + loadPackageInfo: function () { + this.pkg = require('../../../package.json'); + }, + + /** + * Say yeoman hello + */ + sayHello: function () { + if (!(this.options['skip-generator-welcome'] || this.options['skip-all'])) { + this.log(yosay('Welcome to the laudable ' + chalk.red('Sails REST API') + ' generator!')); + } + }, + + /** + * Notify about updates of generator-sails-rest-api + */ + notifyAboutGeneratorUpdate: function () { + if (!(this.options['skip-generator-update'] || this.options['skip-all'])) { + this.log(chalk.yellow('Checking for updates...')); + updateNotifier({ + pkg: this.pkg, + callback: _onUpdateNotifier.bind(this, this.async()) + }); + } + } +}; diff --git a/generators/app/steps/install.js b/generators/app/steps/install.js new file mode 100644 index 00000000..88f91c4d --- /dev/null +++ b/generators/app/steps/install.js @@ -0,0 +1,33 @@ +/** + * Step 7 + * Where installation are run (npm, bower) + */ + +var chalk = require('chalk'); + +/** + * Triggers when npm install is done + * @param {Object} error + * @private + */ +function _onNpmInstallClose(error) { + if (error) { + console.error(error.stack || error); + return process.exit(1); + } +} + +module.exports = { + /** + * Install npm dependencies + */ + installNpmDependencies: function () { + if (!(this.options['skip-project-install'] || this.options['skip-all'])) { + this.log(chalk.yellow('Start installing npm dependencies, please wait...')); + this.npmInstall([], { + color: 'always', + verbose: this.options.verbose + }, _onNpmInstallClose.bind(this)); + } + } +}; diff --git a/generators/app/steps/prompting.js b/generators/app/steps/prompting.js new file mode 100644 index 00000000..8d2fdb6c --- /dev/null +++ b/generators/app/steps/prompting.js @@ -0,0 +1,67 @@ +/** + * Step 2 + * Where you prompt users for options (where you'd call this.prompt()). + * + * Questions is required from ../questions folder. + * Each of answers is mixin to this.answers attribute and available in templates as answers variable. + */ + +var chalk = require('chalk'), + assign = require('../util').assign, + questions = require('../questions'); + +/** + * Triggers when user finishes answering to questions from section + * @param {Function} done + * @param {Object} answers + * @private + */ +function _onDone(done, answers) { + this.answers = assign(this.answers, answers); + done(); +} + +module.exports = { + /** + * Ask database questions + */ + askDatabaseQuestions: function () { + this.log(chalk.yellow('\nDatabase questions:')); + this.prompt(questions.database, _onDone.bind(this, this.async())); + }, + + /** + * Ask application questions + */ + askApplicationQuestions: function () { + this.log(chalk.yellow('\nApplication questions:')); + this.prompt(questions.application, _onDone.bind(this, this.async())); + }, + + // TODO: uncomment this when will be done this feature + ///** + // * Ask services questions + // */ + //askServiceQuestions: function () { + // this.log(chalk.yellow('\nService questions:')); + // this.prompt(questions.services, _onDone.bind(this, this.async())); + //}, + // + ///** + // * Ask miscellaneous questions + // */ + //askMiscellaneousQuestions: function () { + // this.log(chalk.yellow('\nMiscellaneous questions:')); + // this.prompt(questions.miscellaneous, _onDone.bind(this, this.async())); + //}, + + /** + * Print generated answers object if in verbose mode + */ + printAnswersObject: function () { + if (this.options.verbose) { + this.log(chalk.yellow('\nAnswers to your questions:')); + this.log(this.answers); + } + } +}; diff --git a/generators/app/steps/writing.js b/generators/app/steps/writing.js new file mode 100644 index 00000000..46878732 --- /dev/null +++ b/generators/app/steps/writing.js @@ -0,0 +1,17 @@ +/** + * Step 5 + * Where you write the generator specific files (routes, controllers, etc) + */ + +module.exports = { + /** + * Copy template directory to source root + */ + copyDirectory: function () { + // TODO: split into separate functions + this.directory( + this.sourceRoot(), + this.destinationRoot() + ); + } +}; diff --git a/app/templates/.editorconfig b/generators/app/templates/.editorconfig similarity index 100% rename from app/templates/.editorconfig rename to generators/app/templates/.editorconfig diff --git a/app/templates/.gitignore b/generators/app/templates/.gitignore similarity index 100% rename from app/templates/.gitignore rename to generators/app/templates/.gitignore diff --git a/app/templates/.sailsrc b/generators/app/templates/.sailsrc similarity index 100% rename from app/templates/.sailsrc rename to generators/app/templates/.sailsrc diff --git a/app/templates/api/adapters/.gitkeep b/generators/app/templates/api/adapters/.gitkeep similarity index 100% rename from app/templates/api/adapters/.gitkeep rename to generators/app/templates/api/adapters/.gitkeep diff --git a/app/templates/api/blueprints/.gitkeep b/generators/app/templates/api/blueprints/.gitkeep similarity index 100% rename from app/templates/api/blueprints/.gitkeep rename to generators/app/templates/api/blueprints/.gitkeep diff --git a/app/templates/api/blueprints/add.js b/generators/app/templates/api/blueprints/add.js similarity index 100% rename from app/templates/api/blueprints/add.js rename to generators/app/templates/api/blueprints/add.js diff --git a/app/templates/api/blueprints/create.js b/generators/app/templates/api/blueprints/create.js similarity index 84% rename from app/templates/api/blueprints/create.js rename to generators/app/templates/api/blueprints/create.js index 60a6f757..c25f8402 100644 --- a/app/templates/api/blueprints/create.js +++ b/generators/app/templates/api/blueprints/create.js @@ -13,9 +13,7 @@ module.exports = function (req, res) { Model .create(data) .exec(function (error, record) { - if (error) { - return res.serverError(error); - } + if (error) return res.serverError(error); return res.created(record); }); diff --git a/app/templates/api/blueprints/destroy.js b/generators/app/templates/api/blueprints/destroy.js similarity index 72% rename from app/templates/api/blueprints/destroy.js rename to generators/app/templates/api/blueprints/destroy.js index bd9624c3..90d7938b 100644 --- a/app/templates/api/blueprints/destroy.js +++ b/generators/app/templates/api/blueprints/destroy.js @@ -13,20 +13,13 @@ module.exports = function (req, res) { query = actionUtil.populateEach(query, req); query.exec(function (error, record) { - if (error) { - return res.serverError(error); - } - - if (!record) { - return res.notFound(); - } + if (error) return res.serverError(error); + if (!record) return res.notFound(); Model .destroy(pk) .exec(function (error) { - if (error) { - return res.serverError(error); - } + if (error) return res.serverError(error); return res.ok(record); }); diff --git a/app/templates/api/blueprints/find.js b/generators/app/templates/api/blueprints/find.js similarity index 76% rename from app/templates/api/blueprints/find.js rename to generators/app/templates/api/blueprints/find.js index aad348bd..9442849a 100644 --- a/app/templates/api/blueprints/find.js +++ b/generators/app/templates/api/blueprints/find.js @@ -9,9 +9,7 @@ var actionUtil = require('sails/lib/hooks/blueprints/actionUtil'); * If an id was specified, just the instance with that unique id will be returned. */ module.exports = function (req, res) { - if (actionUtil.parsePk(req)) { - return require('./findOne')(req, res); - } + if (actionUtil.parsePk(req)) return require('./findOne')(req, res); var Model = actionUtil.parseModel(req), where = actionUtil.parseCriteria(req), @@ -22,16 +20,12 @@ module.exports = function (req, res) { query = actionUtil.populateEach(query, req); query.exec(function (error, records) { - if (error) { - return res.serverError(error); - } + if (error) return res.serverError(error); Model - .count() + .count(where) .exec(function (error, count) { - if (error) { - return res.serverError(error); - } + if (error) return res.serverError(error); var metaInfo = { start: skip, @@ -41,8 +35,8 @@ module.exports = function (req, res) { criteria: where }; - // TODO: improve with more data in headers and more correct metainfo res.set('Content-Range', metaInfo.start + '-' + metaInfo.end + '/' + metaInfo.total); + return res.ok(records, null, null, metaInfo); }); }); diff --git a/app/templates/api/blueprints/findOne.js b/generators/app/templates/api/blueprints/findOne.js similarity index 78% rename from app/templates/api/blueprints/findOne.js rename to generators/app/templates/api/blueprints/findOne.js index b57b9deb..20e36b7b 100644 --- a/app/templates/api/blueprints/findOne.js +++ b/generators/app/templates/api/blueprints/findOne.js @@ -14,13 +14,8 @@ module.exports = function (req, res) { query = actionUtil.populateEach(query, req); query.exec(function (error, record) { - if (error) { - return res.serverError(error); - } - - if (!record) { - return res.notFound(); - } + if (error) return res.serverError(error); + if (!record) return res.notFound(); return res.ok(record); }); diff --git a/app/templates/api/blueprints/populate.js b/generators/app/templates/api/blueprints/populate.js similarity index 100% rename from app/templates/api/blueprints/populate.js rename to generators/app/templates/api/blueprints/populate.js diff --git a/app/templates/api/blueprints/remove.js b/generators/app/templates/api/blueprints/remove.js similarity index 100% rename from app/templates/api/blueprints/remove.js rename to generators/app/templates/api/blueprints/remove.js diff --git a/app/templates/api/blueprints/update.js b/generators/app/templates/api/blueprints/update.js similarity index 59% rename from app/templates/api/blueprints/update.js rename to generators/app/templates/api/blueprints/update.js index ba498020..11709171 100644 --- a/app/templates/api/blueprints/update.js +++ b/generators/app/templates/api/blueprints/update.js @@ -11,40 +11,26 @@ module.exports = function (req, res) { pk = actionUtil.requirePk(req), values = actionUtil.parseValues(req); - if (!((req.body && req.body.id) || req.query.id)) { - delete values.id; - } + if (!((req.body && req.body.id) || req.query.id)) delete values.id; Model .findOne(pk) .exec(function (error, record) { - if (error) { - return res.serverError(error); - } - - if (!record) { - return res.notFound(); - } + if (error) return res.serverError(error); + if (!record) return res.notFound(); Model .update(pk, values) .exec(function (error, records) { - if (error) { - return res.serverError(error); - } + if (error) return res.serverError(error); var updatedRecord = records[0]; var query = Model.findOne(updatedRecord[Model.primaryKey]); query = actionUtil.populateEach(query, req); query.exec(function (error, populatedRecord) { - if (error) { - return res.serverError(error); - } - - if (!populatedRecord) { - return res.serverError(null, null, "Could not find record after updating"); - } + if (error) return res.serverError(error); + if (!populatedRecord) return res.serverError(null, null, "Could not find record after updating"); return res.ok(populatedRecord); }); diff --git a/app/templates/api/controllers/.gitkeep b/generators/app/templates/api/controllers/.gitkeep similarity index 100% rename from app/templates/api/controllers/.gitkeep rename to generators/app/templates/api/controllers/.gitkeep diff --git a/generators/app/templates/api/controllers/AuthController.js b/generators/app/templates/api/controllers/AuthController.js new file mode 100644 index 00000000..e6e0fa35 --- /dev/null +++ b/generators/app/templates/api/controllers/AuthController.js @@ -0,0 +1,90 @@ +/** + * AuthController + * @description :: Server-side logic for manage user's authorization + */ + +var passport = require('passport'); + +/** + * Triggers when user authenticates via passport + * @param {Object} req Request stream + * @param {Object} res Response stream + * @param {Object} error Error object + * @param {Object} user User profile + * @param {Object} info Info if some error occurs + * @private + */ +function _onPassportAuth(req, res, error, user, info) { + if (error) return res.serverError(error); + if (!user) return res.unauthorized(null, info.code, info.message); + + return res.ok({ + token: CipherService.create('jwt', {id: user.id}).hashSync(), + user: user + }); +} + +module.exports = { + /** + * Sign in by local strategy in passport + */ + signin: function (req, res) { + passport.authenticate('local', _onPassportAuth.bind(this, req, res))(req, res); + }, + + /** + * Sign up in system + */ + signup: function (req, res) { + // TODO: think about model duplicate + + User + .create(req.allParams()) + .exec(function (error, user) { + if (error) return res.serverError(error); + + return res.created({ + token: CipherService.create('jwt', {id: user.id}).hashSync(), + user: user + }); + }); + }, + + /** + * Facebook authorization\linking + */ + facebook: function (req, res) { + passport.authenticate('jwt', function (error, user) { + req.user = user; + passport.authenticate('facebook-token', _onPassportAuth.bind(this, req, res))(req, res); + })(req, res); + }, + + /** + * Twitter authorization\linking + */ + twitter: function (req, res) { + passport.authenticate('jwt', function (error, user) { + req.user = user; + passport.authenticate('twitter-token', _onPassportAuth.bind(this, req, res))(req, res); + })(req, res); + }, + + /** + * Yahoo authorization\linking + */ + yahoo: function (req, res) { + passport.authenticate('jwt', function (error, user) { + req.user = user; + passport.authenticate('yahoo-token', _onPassportAuth.bind(this, req, res))(req, res); + })(req, res); + }, + + /** + * Server ping + * Useful when need to check if it's server is down or some logic is break + */ + ping: function (req, res) { + res.ok(null, null, 'Pong'); + } +}; diff --git a/app/templates/api/controllers/UserController.js b/generators/app/templates/api/controllers/UserController.js similarity index 100% rename from app/templates/api/controllers/UserController.js rename to generators/app/templates/api/controllers/UserController.js diff --git a/app/templates/api/hooks/.gitkeep b/generators/app/templates/api/hooks/.gitkeep similarity index 100% rename from app/templates/api/hooks/.gitkeep rename to generators/app/templates/api/hooks/.gitkeep diff --git a/app/templates/api/models/.gitkeep b/generators/app/templates/api/models/.gitkeep similarity index 100% rename from app/templates/api/models/.gitkeep rename to generators/app/templates/api/models/.gitkeep diff --git a/app/templates/api/models/User.js b/generators/app/templates/api/models/User.js similarity index 92% rename from app/templates/api/models/User.js rename to generators/app/templates/api/models/User.js index 25b12913..c2085436 100644 --- a/app/templates/api/models/User.js +++ b/generators/app/templates/api/models/User.js @@ -58,6 +58,10 @@ module.exports = { type: 'object' }, + yahoo: { + type: 'object' + }, + toJSON: function () { var obj = this.toObject(); @@ -67,11 +71,6 @@ module.exports = { } }, - beforeValidate: function (values, next) { - // TODO: maybe here we need put duplicate checking - next(); - }, - beforeUpdate: function (values, next) { if (values.password) { values.password = CipherService.create('bcrypt', values.password).hashSync(); diff --git a/app/templates/api/policies/.gitkeep b/generators/app/templates/api/policies/.gitkeep similarity index 100% rename from app/templates/api/policies/.gitkeep rename to generators/app/templates/api/policies/.gitkeep diff --git a/generators/app/templates/api/policies/isAllowed.js b/generators/app/templates/api/policies/isAllowed.js new file mode 100644 index 00000000..8449e334 --- /dev/null +++ b/generators/app/templates/api/policies/isAllowed.js @@ -0,0 +1,14 @@ +/** + * isAllowed + * @description :: Policy to check if this request goes from our applications + */ + +module.exports = function (req, res, next) { + var token = req.headers['application-token']; + + if (token && token === "<%= answers['application:api-secret-key'] %>") { + next(); + } else { + res.unauthorized(null, null, 'You must provide application token'); + } +}; diff --git a/app/templates/api/policies/isAuthenticated.js b/generators/app/templates/api/policies/isAuthenticated.js similarity index 50% rename from app/templates/api/policies/isAuthenticated.js rename to generators/app/templates/api/policies/isAuthenticated.js index a3844ebc..b5808035 100644 --- a/app/templates/api/policies/isAuthenticated.js +++ b/generators/app/templates/api/policies/isAuthenticated.js @@ -7,14 +7,10 @@ var passport = require('passport'); module.exports = function (req, res, next) { passport.authenticate('jwt', function (error, user, info) { - if (error) { - sails.log.error(error); - res.serverError(error); - } else if (info) { - res.unauthorized(null, info.code, info.message); - } else { - req.user = user; - next(); - } + if (error) return res.serverError(error); + if (!user) return res.unauthorized(null, info.code, info.message); + + req.user = user; + next(); })(req, res); }; diff --git a/app/templates/api/responses/.gitkeep b/generators/app/templates/api/responses/.gitkeep similarity index 100% rename from app/templates/api/responses/.gitkeep rename to generators/app/templates/api/responses/.gitkeep diff --git a/app/templates/api/responses/badRequest.js b/generators/app/templates/api/responses/badRequest.js similarity index 55% rename from app/templates/api/responses/badRequest.js rename to generators/app/templates/api/responses/badRequest.js index e3bc1d70..c41d038e 100644 --- a/app/templates/api/responses/badRequest.js +++ b/generators/app/templates/api/responses/badRequest.js @@ -7,10 +7,14 @@ */ module.exports = function (data, code, message, root) { - this.res.status(400); - this.res.jsonx(_.assign({ - code: code || "E_BAD_REQUEST", - message: message || "The request cannot be fulfilled due to bad syntax", + var response = _.assign({ + code: code || 'E_BAD_REQUEST', + message: message || 'The request cannot be fulfilled due to bad syntax', response: data || {} - }, root)); + }, root); + + this.req._sails.log.silly('Sent (400 BAD REQUEST)\n', response); + + this.res.status(400); + this.res.jsonx(response); }; diff --git a/app/templates/api/responses/created.js b/generators/app/templates/api/responses/created.js similarity index 60% rename from app/templates/api/responses/created.js rename to generators/app/templates/api/responses/created.js index e2b83a58..f6eaffa5 100644 --- a/app/templates/api/responses/created.js +++ b/generators/app/templates/api/responses/created.js @@ -8,10 +8,14 @@ */ module.exports = function (data, code, message, root) { - this.res.status(201); - this.res.jsonx(_.assign({ - code: code || "CREATED", - message: message || "The request has been fulfilled and resulted in a new resource being created", + var response = _.assign({ + code: code || 'CREATED', + message: message || 'The request has been fulfilled and resulted in a new resource being created', response: data || {} - }, root)); + }, root); + + this.req._sails.log.silly('Sent (201 CREATED)\n', response); + + this.res.status(201); + this.res.jsonx(response); }; diff --git a/app/templates/api/responses/forbidden.js b/generators/app/templates/api/responses/forbidden.js similarity index 62% rename from app/templates/api/responses/forbidden.js rename to generators/app/templates/api/responses/forbidden.js index 428d5fd1..2b243ff2 100644 --- a/app/templates/api/responses/forbidden.js +++ b/generators/app/templates/api/responses/forbidden.js @@ -7,10 +7,14 @@ */ module.exports = function (data, code, message, root) { - this.res.status(403); - this.res.jsonx(_.assign({ - code: code || "E_FORBIDDEN", - message: message || "User not authorized to perform the operation", + var response = _.assign({ + code: code || 'E_FORBIDDEN', + message: message || 'User not authorized to perform the operation', response: data || {} - }, root)); + }, root); + + this.req._sails.log.silly('Sent (403 FORBIDDEN)\n', response); + + this.res.status(403); + this.res.jsonx(response); }; diff --git a/app/templates/api/responses/notFound.js b/generators/app/templates/api/responses/notFound.js similarity index 55% rename from app/templates/api/responses/notFound.js rename to generators/app/templates/api/responses/notFound.js index 974e91e9..985925f5 100644 --- a/app/templates/api/responses/notFound.js +++ b/generators/app/templates/api/responses/notFound.js @@ -7,10 +7,14 @@ */ module.exports = function (data, code, message, root) { - this.res.status(404); - this.res.jsonx(_.assign({ - code: code || "E_NOT_FOUND", - message: message || "The requested resource could not be found but may be available again in the future", + var response = _.assign({ + code: code || 'E_NOT_FOUND', + message: message || 'The requested resource could not be found but may be available again in the future', response: data || {} - }, root)); + }, root); + + this.req._sails.log.silly('Sent (404 NOT FOUND)\n', response); + + this.res.status(404); + this.res.jsonx(response); }; diff --git a/app/templates/api/responses/ok.js b/generators/app/templates/api/responses/ok.js similarity index 68% rename from app/templates/api/responses/ok.js rename to generators/app/templates/api/responses/ok.js index 0a65960b..326aeb5a 100644 --- a/app/templates/api/responses/ok.js +++ b/generators/app/templates/api/responses/ok.js @@ -8,10 +8,14 @@ */ module.exports = function (data, code, message, root) { - this.res.status(200); - this.res.jsonx(_.assign({ - code: code || "OK", - message: message || "Operation is successfully executed", + var response = _.assign({ + code: code || 'OK', + message: message || 'Operation is successfully executed', response: data || {} - }, root)); + }, root); + + this.req._sails.log.silly('Sent (200 OK)\n', response); + + this.res.status(200); + this.res.jsonx(response); }; diff --git a/app/templates/api/responses/serverError.js b/generators/app/templates/api/responses/serverError.js similarity index 53% rename from app/templates/api/responses/serverError.js rename to generators/app/templates/api/responses/serverError.js index 4958c140..9853d77c 100644 --- a/app/templates/api/responses/serverError.js +++ b/generators/app/templates/api/responses/serverError.js @@ -6,10 +6,14 @@ */ module.exports = function (data, code, message, root) { - this.res.status(500); - this.res.jsonx(_.assign({ - code: code || "E_INTERNAL_SERVER_ERROR", - message: message || "Something bad happened on the server", + var response = _.assign({ + code: code || 'E_INTERNAL_SERVER_ERROR', + message: message || 'Something bad happened on the server', response: data || {} - }, root)); + }, root); + + this.req._sails.log.error('Sent (500 INTERNAL SERVER ERROR)\n', response); + + this.res.status(500); + this.res.jsonx(response); }; diff --git a/app/templates/api/responses/unauthorized.js b/generators/app/templates/api/responses/unauthorized.js similarity index 57% rename from app/templates/api/responses/unauthorized.js rename to generators/app/templates/api/responses/unauthorized.js index eb2e3e59..9e08c7e5 100644 --- a/app/templates/api/responses/unauthorized.js +++ b/generators/app/templates/api/responses/unauthorized.js @@ -7,10 +7,14 @@ */ module.exports = function (data, code, message, root) { - this.res.status(401); - this.res.jsonx(_.assign({ - code: code || "E_UNAUTHORIZED", - message: message || "Missing or invalid authentication token", + var response = _.assign({ + code: code || 'E_UNAUTHORIZED', + message: message || 'Missing or invalid authentication token', response: data || {} - }, root)); + }, root); + + this.req._sails.log.silly('Sent (401 UNAUTHORIZED)\n', response); + + this.res.status(401); + this.res.jsonx(response); }; diff --git a/app/templates/api/services/.gitkeep b/generators/app/templates/api/services/.gitkeep similarity index 100% rename from app/templates/api/services/.gitkeep rename to generators/app/templates/api/services/.gitkeep diff --git a/app/templates/api/services/CipherService.js b/generators/app/templates/api/services/CipherService.js similarity index 100% rename from app/templates/api/services/CipherService.js rename to generators/app/templates/api/services/CipherService.js diff --git a/app/templates/api/services/MailerService.js b/generators/app/templates/api/services/MailerService.js similarity index 100% rename from app/templates/api/services/MailerService.js rename to generators/app/templates/api/services/MailerService.js diff --git a/app/templates/api/services/PaymentService.js b/generators/app/templates/api/services/PaymentService.js similarity index 100% rename from app/templates/api/services/PaymentService.js rename to generators/app/templates/api/services/PaymentService.js diff --git a/app/templates/api/services/PusherService.js b/generators/app/templates/api/services/PusherService.js similarity index 100% rename from app/templates/api/services/PusherService.js rename to generators/app/templates/api/services/PusherService.js diff --git a/app/templates/api/services/SmsService.js b/generators/app/templates/api/services/SmsService.js similarity index 100% rename from app/templates/api/services/SmsService.js rename to generators/app/templates/api/services/SmsService.js diff --git a/app/templates/api/services/SocialService.js b/generators/app/templates/api/services/SocialService.js similarity index 100% rename from app/templates/api/services/SocialService.js rename to generators/app/templates/api/services/SocialService.js diff --git a/app/templates/api/services/StorageService.js b/generators/app/templates/api/services/StorageService.js similarity index 100% rename from app/templates/api/services/StorageService.js rename to generators/app/templates/api/services/StorageService.js diff --git a/app/templates/api/services/cipher/BCryptCipher.js b/generators/app/templates/api/services/cipher/BCryptCipher.js similarity index 100% rename from app/templates/api/services/cipher/BCryptCipher.js rename to generators/app/templates/api/services/cipher/BCryptCipher.js diff --git a/app/templates/api/services/cipher/BaseCipher.js b/generators/app/templates/api/services/cipher/BaseCipher.js similarity index 100% rename from app/templates/api/services/cipher/BaseCipher.js rename to generators/app/templates/api/services/cipher/BaseCipher.js diff --git a/app/templates/api/services/cipher/CipherFactory.js b/generators/app/templates/api/services/cipher/CipherFactory.js similarity index 100% rename from app/templates/api/services/cipher/CipherFactory.js rename to generators/app/templates/api/services/cipher/CipherFactory.js diff --git a/app/templates/api/services/cipher/JwtCipher.js b/generators/app/templates/api/services/cipher/JwtCipher.js similarity index 95% rename from app/templates/api/services/cipher/JwtCipher.js rename to generators/app/templates/api/services/cipher/JwtCipher.js index a74ff95c..96521719 100644 --- a/app/templates/api/services/cipher/JwtCipher.js +++ b/generators/app/templates/api/services/cipher/JwtCipher.js @@ -8,7 +8,7 @@ var util = require('util'), * @type {String} * @private */ -var SECRET_KEY = "<%= answers['application:jwt-secret-token'] %>"; +var SECRET_KEY = "<%= answers['application:jwt-secret'] %>"; /** * Algorithm that using for signing JWT diff --git a/app/templates/api/services/mailer/BaseMailer.js b/generators/app/templates/api/services/mailer/BaseMailer.js similarity index 100% rename from app/templates/api/services/mailer/BaseMailer.js rename to generators/app/templates/api/services/mailer/BaseMailer.js diff --git a/app/templates/api/services/mailer/MailerFactory.js b/generators/app/templates/api/services/mailer/MailerFactory.js similarity index 100% rename from app/templates/api/services/mailer/MailerFactory.js rename to generators/app/templates/api/services/mailer/MailerFactory.js diff --git a/app/templates/api/services/mailer/MandrillMailer.js b/generators/app/templates/api/services/mailer/MandrillMailer.js similarity index 100% rename from app/templates/api/services/mailer/MandrillMailer.js rename to generators/app/templates/api/services/mailer/MandrillMailer.js diff --git a/app/templates/api/services/payment/PaymentFactory.js b/generators/app/templates/api/services/payment/PaymentFactory.js similarity index 100% rename from app/templates/api/services/payment/PaymentFactory.js rename to generators/app/templates/api/services/payment/PaymentFactory.js diff --git a/app/templates/api/services/payment/StripePayment.js b/generators/app/templates/api/services/payment/StripePayment.js similarity index 100% rename from app/templates/api/services/payment/StripePayment.js rename to generators/app/templates/api/services/payment/StripePayment.js diff --git a/app/templates/api/services/pusher/APNPushNotification.js b/generators/app/templates/api/services/pusher/APNPushNotification.js similarity index 100% rename from app/templates/api/services/pusher/APNPushNotification.js rename to generators/app/templates/api/services/pusher/APNPushNotification.js diff --git a/app/templates/api/services/pusher/BasePushNotification.js b/generators/app/templates/api/services/pusher/BasePushNotification.js similarity index 100% rename from app/templates/api/services/pusher/BasePushNotification.js rename to generators/app/templates/api/services/pusher/BasePushNotification.js diff --git a/app/templates/api/services/pusher/GCMPushNotification.js b/generators/app/templates/api/services/pusher/GCMPushNotification.js similarity index 100% rename from app/templates/api/services/pusher/GCMPushNotification.js rename to generators/app/templates/api/services/pusher/GCMPushNotification.js diff --git a/app/templates/api/services/pusher/PusherFactory.js b/generators/app/templates/api/services/pusher/PusherFactory.js similarity index 100% rename from app/templates/api/services/pusher/PusherFactory.js rename to generators/app/templates/api/services/pusher/PusherFactory.js diff --git a/app/templates/api/services/sms/BaseSms.js b/generators/app/templates/api/services/sms/BaseSms.js similarity index 100% rename from app/templates/api/services/sms/BaseSms.js rename to generators/app/templates/api/services/sms/BaseSms.js diff --git a/app/templates/api/services/sms/SmsFactory.js b/generators/app/templates/api/services/sms/SmsFactory.js similarity index 100% rename from app/templates/api/services/sms/SmsFactory.js rename to generators/app/templates/api/services/sms/SmsFactory.js diff --git a/app/templates/api/services/sms/TwilioSms.js b/generators/app/templates/api/services/sms/TwilioSms.js similarity index 100% rename from app/templates/api/services/sms/TwilioSms.js rename to generators/app/templates/api/services/sms/TwilioSms.js diff --git a/app/templates/api/services/social/FacebookSocial.js b/generators/app/templates/api/services/social/FacebookSocial.js similarity index 100% rename from app/templates/api/services/social/FacebookSocial.js rename to generators/app/templates/api/services/social/FacebookSocial.js diff --git a/app/templates/api/services/social/SocialFactory.js b/generators/app/templates/api/services/social/SocialFactory.js similarity index 100% rename from app/templates/api/services/social/SocialFactory.js rename to generators/app/templates/api/services/social/SocialFactory.js diff --git a/app/templates/api/services/storage/AmazonStorage.js b/generators/app/templates/api/services/storage/AmazonStorage.js similarity index 100% rename from app/templates/api/services/storage/AmazonStorage.js rename to generators/app/templates/api/services/storage/AmazonStorage.js diff --git a/app/templates/api/services/storage/BaseStorage.js b/generators/app/templates/api/services/storage/BaseStorage.js similarity index 100% rename from app/templates/api/services/storage/BaseStorage.js rename to generators/app/templates/api/services/storage/BaseStorage.js diff --git a/app/templates/api/services/storage/GCloudStorage.js b/generators/app/templates/api/services/storage/GCloudStorage.js similarity index 100% rename from app/templates/api/services/storage/GCloudStorage.js rename to generators/app/templates/api/services/storage/GCloudStorage.js diff --git a/app/templates/api/services/storage/StorageFactory.js b/generators/app/templates/api/services/storage/StorageFactory.js similarity index 100% rename from app/templates/api/services/storage/StorageFactory.js rename to generators/app/templates/api/services/storage/StorageFactory.js diff --git a/app/templates/app.js b/generators/app/templates/app.js similarity index 100% rename from app/templates/app.js rename to generators/app/templates/app.js diff --git a/app/templates/config/blueprints.js b/generators/app/templates/config/blueprints.js similarity index 100% rename from app/templates/config/blueprints.js rename to generators/app/templates/config/blueprints.js diff --git a/app/templates/config/bootstrap.js b/generators/app/templates/config/bootstrap.js similarity index 100% rename from app/templates/config/bootstrap.js rename to generators/app/templates/config/bootstrap.js diff --git a/app/templates/config/connections.js b/generators/app/templates/config/connections.js similarity index 100% rename from app/templates/config/connections.js rename to generators/app/templates/config/connections.js diff --git a/app/templates/config/env/development.js b/generators/app/templates/config/env/development.js similarity index 100% rename from app/templates/config/env/development.js rename to generators/app/templates/config/env/development.js diff --git a/app/templates/config/env/production.js b/generators/app/templates/config/env/production.js similarity index 88% rename from app/templates/config/env/production.js rename to generators/app/templates/config/env/production.js index 475b13da..643f547b 100644 --- a/app/templates/config/env/production.js +++ b/generators/app/templates/config/env/production.js @@ -6,6 +6,6 @@ module.exports = { port: 80, log: { - level: "silent" + level: 'info' } }; diff --git a/app/templates/config/globals.js b/generators/app/templates/config/globals.js similarity index 100% rename from app/templates/config/globals.js rename to generators/app/templates/config/globals.js diff --git a/app/templates/config/http.js b/generators/app/templates/config/http.js similarity index 92% rename from app/templates/config/http.js rename to generators/app/templates/config/http.js index d9c80021..1e32280f 100644 --- a/app/templates/config/http.js +++ b/generators/app/templates/config/http.js @@ -11,7 +11,7 @@ module.exports.http = { * * @type {Object|Boolean} */ - serverOptions: false, + serverOptions: undefined, /** * You can define own custom middleware here @@ -28,9 +28,9 @@ module.exports.http = { * To enable streaming file uploads (to disk or somewhere else) * you'll want to set this option to `false` to disable the body parser * - * @type {Function|Boolean} + * @type {Function|Boolean|Object} */ - bodyParser: false, + bodyParser: undefined, /** * Express middleware to use for every Sails request diff --git a/app/templates/config/log.js b/generators/app/templates/config/log.js similarity index 100% rename from app/templates/config/log.js rename to generators/app/templates/config/log.js diff --git a/app/templates/config/models.js b/generators/app/templates/config/models.js similarity index 100% rename from app/templates/config/models.js rename to generators/app/templates/config/models.js diff --git a/app/templates/config/passport.js b/generators/app/templates/config/passport.js similarity index 53% rename from app/templates/config/passport.js rename to generators/app/templates/config/passport.js index f02df78f..b4b9b065 100644 --- a/app/templates/config/passport.js +++ b/generators/app/templates/config/passport.js @@ -3,12 +3,12 @@ * @description :: Configuration file where you configure your passport authentication */ -// TODO: add more strategies var passport = require('passport'), LocalStrategy = require('passport-local').Strategy, JwtStrategy = require('passport-jwt').Strategy, FacebookTokenStrategy = require('passport-facebook-token').Strategy, - TwitterTokenStrategy = require('passport-twitter-token').Strategy; + TwitterTokenStrategy = require('passport-twitter-token').Strategy, + YahooTokenStrategy = require('passport-yahoo-token').Strategy; passport.use(new LocalStrategy({ usernameField: 'username', @@ -23,29 +23,24 @@ passport.use(new LocalStrategy({ }] }) .exec(function (error, user) { - if (error) { - return next(error); - } + if (error) return next(error, false, {}); - if (!user) { - return next(null, false, { - code: "E_USER_NOT_FOUND", - message: username + " is not found" - }); - } + if (!user) return next(null, false, { + code: 'E_USER_NOT_FOUND', + message: username + ' is not found' + }); - if (!CipherService.create('bcrypt', user.password).compareSync(password)) { - return next(null, false, { - code: "E_WRONG_PASSWORD", - message: "Password is wrong" - }); - } + if (!CipherService.create('bcrypt', user.password).compareSync(password)) return next(null, false, { + code: 'E_WRONG_PASSWORD', + message: 'Password is wrong' + }); - return next(null, user); + return next(null, user, {}); }); })); -passport.use(new JwtStrategy("<%= answers['application:jwt-secret-token'] %>", { +passport.use(new JwtStrategy({ + secretOrKey: "<%= answers['application:jwt-secret'] %>", tokenBodyField: 'jwt-token', tokenHeader: 'JWT' }, function (payload, next) { @@ -54,24 +49,19 @@ passport.use(new JwtStrategy("<%= answers['application:jwt-secret-token'] %>", { id: payload.id }) .exec(function (error, user) { - if (error) { - return next(error); - } - - if (!user) { - return next(null, false, { - code: "E_USER_NOT_FOUND", - message: "User with that JWT not found" - }); - } + if (error) return next(error, false, {}); + if (!user) return next(null, false, { + code: 'E_USER_NOT_FOUND', + message: 'User with that JWT not found' + }); - return next(null, user); + return next(null, user, {}); }); })); passport.use(new FacebookTokenStrategy({ - clientID: "<%= answers['application:facebook-client-id'] %>", - clientSecret: "<%= answers['application:facebook-client-secret'] %>", + clientID: "<%= answers['application:facebook-app-id'] %>", + clientSecret: "<%= answers['application:facebook-app-secret'] %>", passReqToCallback: true }, function (req, accessToken, refreshToken, profile, next) { if (!req.user) { @@ -87,18 +77,13 @@ passport.use(new FacebookTokenStrategy({ facebook: profile._json }) .exec(function (error, user) { - if (error) { - return next(error); - } - - if (!user) { - return next(null, false, { - code: "E_AUTH_FAILED", - message: "Facebook auth failed" - }); - } + if (error) return next(error, false, {}); + if (!user) return next(null, false, { + code: 'E_AUTH_FAILED', + message: 'Facebook auth failed' + }); - return next(null, user); + return next(null, user, {}); }); } else { req.user.facebook = profile._json; @@ -124,21 +109,48 @@ passport.use(new TwitterTokenStrategy({ twitter: profile._json }) .exec(function (error, user) { - if (error) { - return next(error); - } - - if (!user) { - return next(null, false, { - code: "E_AUTH_FAILED", - message: "Twitter auth failed" - }); - } + if (error) return next(error, false, {}); + if (!user) return next(null, false, { + code: 'E_AUTH_FAILED', + message: 'Twitter auth failed' + }); - return next(null, user); + return next(null, user, {}); }); } else { req.user.twitter = profile._json; req.user.save(next); } })); + +passport.use(new YahooTokenStrategy({ + clientID: "<%= answers['application:yahoo-app-id'] %>", + clientSecret: "<%= answers['application:yahoo-app-secret'] %>", + passReqToCallback: true +}, function (req, accessToken, refreshToken, profile, next) { + if (!req.user) { + User + .findOrCreate({ + 'yahoo.id': profile.id + }, { + username: req.param('username') || profile.username || profile.displayName, + email: req.param('email') || (profile.emails && profile.emails[0].value), + firstName: req.param('firstName') || (profile.displayName && profile.displayName.split(' ')[0]), + lastName: req.param('lastName') || (profile.displayName && profile.displayName.split(' ')[1]), + photo: req.param('photo') || (profile.photos && profile.photos[0].value), + yahoo: profile._json + }) + .exec(function (error, user) { + if (error) return next(error, false, {}); + if (!user) return next(null, false, { + code: 'E_AUTH_FAILED', + message: 'Yahoo auth failed' + }); + + return next(null, user, {}); + }); + } else { + req.user.yahoo = profile._json; + req.user.save(next); + } +})); diff --git a/app/templates/config/policies.js b/generators/app/templates/config/policies.js similarity index 85% rename from app/templates/config/policies.js rename to generators/app/templates/config/policies.js index 6500e82e..d1189d80 100644 --- a/app/templates/config/policies.js +++ b/generators/app/templates/config/policies.js @@ -10,9 +10,9 @@ */ module.exports.policies = { - '*': ['isOurApp', 'isAuthenticated'], + '*': ['isAllowed', 'isAuthenticated'], AuthController: { - '*': 'isOurApp' + '*': 'isAllowed' } }; diff --git a/app/templates/config/routes.js b/generators/app/templates/config/routes.js similarity index 100% rename from app/templates/config/routes.js rename to generators/app/templates/config/routes.js diff --git a/app/templates/doc/api.json b/generators/app/templates/doc/api.json similarity index 64% rename from app/templates/doc/api.json rename to generators/app/templates/doc/api.json index 33a4c65d..a5da31b6 100644 --- a/app/templates/doc/api.json +++ b/generators/app/templates/doc/api.json @@ -1,11 +1,11 @@ { "swagger": "2.0", "info": { - "title": "<%= answers['project:name'] %>", - "description": "Move your app forward with the <%= answers['project:name'] %> API", + "title": "<%= answers['application:name'] %>", + "description": "Move your app forward with the <%= answers['application:name'] %> API", "version": "1.0.0" }, - "host": "api.<%= answers['project:name'] %>.com", + "host": "api.<%= answers['application:name'] %>.com", "schemes": [ "https" ], diff --git a/app/templates/package.json b/generators/app/templates/package.json similarity index 58% rename from app/templates/package.json rename to generators/app/templates/package.json index 84df42ee..56a3d1f5 100644 --- a/app/templates/package.json +++ b/generators/app/templates/package.json @@ -1,20 +1,22 @@ { - "name": "<%= answers['project:name'] %>", + "name": "<%= answers['application:name'] %>", "private": true, "version": "0.1.0", "main": "app.js", "dependencies": {}, "devDependencies": { - "chalk": "^0.5.1", + "chalk": "^1.0.0", "dependency-check": "^2.2.2", "mocha": "^2.1.0", "npm-check": "^3.2.7", - "print-message": "^1.1.0", + "print-message": "^1.2.1", "recursive-readdir": "^1.2.1" }, "scripts": { "start": "node app.js", "debug": "node debug app.js", - "test": "mocha --recursive" + "test": "mocha --recursive", + "check-updates": "node ./tools/check-updates.js", + "fix-deps": "node ./tools/fix-deps.js" } } diff --git a/generators/app/templates/test/adapters/.gitkeep b/generators/app/templates/test/adapters/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/generators/app/templates/test/blueprints/.gitkeep b/generators/app/templates/test/blueprints/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/app/templates/test/blueprints/add.js b/generators/app/templates/test/blueprints/add.js similarity index 100% rename from app/templates/test/blueprints/add.js rename to generators/app/templates/test/blueprints/add.js diff --git a/app/templates/test/blueprints/create.js b/generators/app/templates/test/blueprints/create.js similarity index 100% rename from app/templates/test/blueprints/create.js rename to generators/app/templates/test/blueprints/create.js diff --git a/app/templates/test/blueprints/destroy.js b/generators/app/templates/test/blueprints/destroy.js similarity index 100% rename from app/templates/test/blueprints/destroy.js rename to generators/app/templates/test/blueprints/destroy.js diff --git a/app/templates/test/blueprints/find.js b/generators/app/templates/test/blueprints/find.js similarity index 100% rename from app/templates/test/blueprints/find.js rename to generators/app/templates/test/blueprints/find.js diff --git a/app/templates/test/blueprints/findOne.js b/generators/app/templates/test/blueprints/findOne.js similarity index 100% rename from app/templates/test/blueprints/findOne.js rename to generators/app/templates/test/blueprints/findOne.js diff --git a/app/templates/test/blueprints/populate.js b/generators/app/templates/test/blueprints/populate.js similarity index 100% rename from app/templates/test/blueprints/populate.js rename to generators/app/templates/test/blueprints/populate.js diff --git a/app/templates/test/blueprints/remove.js b/generators/app/templates/test/blueprints/remove.js similarity index 100% rename from app/templates/test/blueprints/remove.js rename to generators/app/templates/test/blueprints/remove.js diff --git a/app/templates/test/blueprints/update.js b/generators/app/templates/test/blueprints/update.js similarity index 100% rename from app/templates/test/blueprints/update.js rename to generators/app/templates/test/blueprints/update.js diff --git a/generators/app/templates/test/controllers/.gitkeep b/generators/app/templates/test/controllers/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/app/templates/test/controllers/AuthController.js b/generators/app/templates/test/controllers/AuthController.js similarity index 100% rename from app/templates/test/controllers/AuthController.js rename to generators/app/templates/test/controllers/AuthController.js diff --git a/app/templates/test/controllers/UserController.js b/generators/app/templates/test/controllers/UserController.js similarity index 100% rename from app/templates/test/controllers/UserController.js rename to generators/app/templates/test/controllers/UserController.js diff --git a/generators/app/templates/test/hooks/.gitkeep b/generators/app/templates/test/hooks/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/generators/app/templates/test/models/.gitkeep b/generators/app/templates/test/models/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/app/templates/test/models/User.js b/generators/app/templates/test/models/User.js similarity index 100% rename from app/templates/test/models/User.js rename to generators/app/templates/test/models/User.js diff --git a/generators/app/templates/test/policies/.gitkeep b/generators/app/templates/test/policies/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/app/templates/test/policies/isOurApp.js b/generators/app/templates/test/policies/isAllowed.js similarity index 52% rename from app/templates/test/policies/isOurApp.js rename to generators/app/templates/test/policies/isAllowed.js index 2a7f5cf6..98cfab90 100644 --- a/app/templates/test/policies/isOurApp.js +++ b/generators/app/templates/test/policies/isAllowed.js @@ -1,7 +1,7 @@ var assert = require('assert'), - isOurApp = require('../../api/policies/isOurApp'); + isAllowed = require('../../api/policies/isAllowed'); -describe("policies:isOurApp", function () { +describe("policies:isAllowed", function () { it("TODO: write this test", function () { assert(true); }); diff --git a/app/templates/test/policies/isAuthenticated.js b/generators/app/templates/test/policies/isAuthenticated.js similarity index 100% rename from app/templates/test/policies/isAuthenticated.js rename to generators/app/templates/test/policies/isAuthenticated.js diff --git a/generators/app/templates/test/responses/.gitkeep b/generators/app/templates/test/responses/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/app/templates/test/responses/badRequest.js b/generators/app/templates/test/responses/badRequest.js similarity index 100% rename from app/templates/test/responses/badRequest.js rename to generators/app/templates/test/responses/badRequest.js diff --git a/app/templates/test/responses/created.js b/generators/app/templates/test/responses/created.js similarity index 100% rename from app/templates/test/responses/created.js rename to generators/app/templates/test/responses/created.js diff --git a/app/templates/test/responses/forbidden.js b/generators/app/templates/test/responses/forbidden.js similarity index 100% rename from app/templates/test/responses/forbidden.js rename to generators/app/templates/test/responses/forbidden.js diff --git a/app/templates/test/responses/notFound.js b/generators/app/templates/test/responses/notFound.js similarity index 100% rename from app/templates/test/responses/notFound.js rename to generators/app/templates/test/responses/notFound.js diff --git a/app/templates/test/responses/ok.js b/generators/app/templates/test/responses/ok.js similarity index 100% rename from app/templates/test/responses/ok.js rename to generators/app/templates/test/responses/ok.js diff --git a/app/templates/test/responses/serverError.js b/generators/app/templates/test/responses/serverError.js similarity index 100% rename from app/templates/test/responses/serverError.js rename to generators/app/templates/test/responses/serverError.js diff --git a/app/templates/test/responses/unauthorized.js b/generators/app/templates/test/responses/unauthorized.js similarity index 100% rename from app/templates/test/responses/unauthorized.js rename to generators/app/templates/test/responses/unauthorized.js diff --git a/generators/app/templates/test/services/.gitkeep b/generators/app/templates/test/services/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/app/templates/test/services/CipherService.js b/generators/app/templates/test/services/CipherService.js similarity index 100% rename from app/templates/test/services/CipherService.js rename to generators/app/templates/test/services/CipherService.js diff --git a/app/templates/test/services/MailerService.js b/generators/app/templates/test/services/MailerService.js similarity index 100% rename from app/templates/test/services/MailerService.js rename to generators/app/templates/test/services/MailerService.js diff --git a/app/templates/test/services/PaymentService.js b/generators/app/templates/test/services/PaymentService.js similarity index 100% rename from app/templates/test/services/PaymentService.js rename to generators/app/templates/test/services/PaymentService.js diff --git a/app/templates/test/services/PusherService.js b/generators/app/templates/test/services/PusherService.js similarity index 100% rename from app/templates/test/services/PusherService.js rename to generators/app/templates/test/services/PusherService.js diff --git a/app/templates/test/services/SmsService.js b/generators/app/templates/test/services/SmsService.js similarity index 100% rename from app/templates/test/services/SmsService.js rename to generators/app/templates/test/services/SmsService.js diff --git a/app/templates/test/services/SocialService.js b/generators/app/templates/test/services/SocialService.js similarity index 100% rename from app/templates/test/services/SocialService.js rename to generators/app/templates/test/services/SocialService.js diff --git a/app/templates/test/services/StorageService.js b/generators/app/templates/test/services/StorageService.js similarity index 100% rename from app/templates/test/services/StorageService.js rename to generators/app/templates/test/services/StorageService.js diff --git a/app/templates/test/services/cipher/BCryptCipher.js b/generators/app/templates/test/services/cipher/BCryptCipher.js similarity index 100% rename from app/templates/test/services/cipher/BCryptCipher.js rename to generators/app/templates/test/services/cipher/BCryptCipher.js diff --git a/app/templates/test/services/cipher/BaseCipher.js b/generators/app/templates/test/services/cipher/BaseCipher.js similarity index 100% rename from app/templates/test/services/cipher/BaseCipher.js rename to generators/app/templates/test/services/cipher/BaseCipher.js diff --git a/app/templates/test/services/cipher/CipherFactory.js b/generators/app/templates/test/services/cipher/CipherFactory.js similarity index 100% rename from app/templates/test/services/cipher/CipherFactory.js rename to generators/app/templates/test/services/cipher/CipherFactory.js diff --git a/app/templates/test/services/cipher/JwtCipher.js b/generators/app/templates/test/services/cipher/JwtCipher.js similarity index 100% rename from app/templates/test/services/cipher/JwtCipher.js rename to generators/app/templates/test/services/cipher/JwtCipher.js diff --git a/app/templates/test/services/mailer/BaseMailer.js b/generators/app/templates/test/services/mailer/BaseMailer.js similarity index 100% rename from app/templates/test/services/mailer/BaseMailer.js rename to generators/app/templates/test/services/mailer/BaseMailer.js diff --git a/app/templates/test/services/mailer/MailerFactory.js b/generators/app/templates/test/services/mailer/MailerFactory.js similarity index 100% rename from app/templates/test/services/mailer/MailerFactory.js rename to generators/app/templates/test/services/mailer/MailerFactory.js diff --git a/app/templates/test/services/mailer/MandrillMailer.js b/generators/app/templates/test/services/mailer/MandrillMailer.js similarity index 100% rename from app/templates/test/services/mailer/MandrillMailer.js rename to generators/app/templates/test/services/mailer/MandrillMailer.js diff --git a/app/templates/test/services/payment/PaymentFactory.js b/generators/app/templates/test/services/payment/PaymentFactory.js similarity index 100% rename from app/templates/test/services/payment/PaymentFactory.js rename to generators/app/templates/test/services/payment/PaymentFactory.js diff --git a/app/templates/test/services/payment/StripePayment.js b/generators/app/templates/test/services/payment/StripePayment.js similarity index 100% rename from app/templates/test/services/payment/StripePayment.js rename to generators/app/templates/test/services/payment/StripePayment.js diff --git a/app/templates/test/services/pusher/APNPushNotification.js b/generators/app/templates/test/services/pusher/APNPushNotification.js similarity index 100% rename from app/templates/test/services/pusher/APNPushNotification.js rename to generators/app/templates/test/services/pusher/APNPushNotification.js diff --git a/app/templates/test/services/pusher/BasePushNotification.js b/generators/app/templates/test/services/pusher/BasePushNotification.js similarity index 100% rename from app/templates/test/services/pusher/BasePushNotification.js rename to generators/app/templates/test/services/pusher/BasePushNotification.js diff --git a/app/templates/test/services/pusher/GCMPushNotification.js b/generators/app/templates/test/services/pusher/GCMPushNotification.js similarity index 100% rename from app/templates/test/services/pusher/GCMPushNotification.js rename to generators/app/templates/test/services/pusher/GCMPushNotification.js diff --git a/app/templates/test/services/pusher/PusherFactory.js b/generators/app/templates/test/services/pusher/PusherFactory.js similarity index 100% rename from app/templates/test/services/pusher/PusherFactory.js rename to generators/app/templates/test/services/pusher/PusherFactory.js diff --git a/app/templates/test/services/sms/BaseSms.js b/generators/app/templates/test/services/sms/BaseSms.js similarity index 100% rename from app/templates/test/services/sms/BaseSms.js rename to generators/app/templates/test/services/sms/BaseSms.js diff --git a/app/templates/test/services/sms/SmsFactory.js b/generators/app/templates/test/services/sms/SmsFactory.js similarity index 100% rename from app/templates/test/services/sms/SmsFactory.js rename to generators/app/templates/test/services/sms/SmsFactory.js diff --git a/app/templates/test/services/sms/TwilioSms.js b/generators/app/templates/test/services/sms/TwilioSms.js similarity index 100% rename from app/templates/test/services/sms/TwilioSms.js rename to generators/app/templates/test/services/sms/TwilioSms.js diff --git a/app/templates/test/services/social/FacebookSocial.js b/generators/app/templates/test/services/social/FacebookSocial.js similarity index 100% rename from app/templates/test/services/social/FacebookSocial.js rename to generators/app/templates/test/services/social/FacebookSocial.js diff --git a/app/templates/test/services/social/SocialFactory.js b/generators/app/templates/test/services/social/SocialFactory.js similarity index 100% rename from app/templates/test/services/social/SocialFactory.js rename to generators/app/templates/test/services/social/SocialFactory.js diff --git a/app/templates/test/services/storage/AmazonStorage.js b/generators/app/templates/test/services/storage/AmazonStorage.js similarity index 100% rename from app/templates/test/services/storage/AmazonStorage.js rename to generators/app/templates/test/services/storage/AmazonStorage.js diff --git a/app/templates/test/services/storage/BaseStorage.js b/generators/app/templates/test/services/storage/BaseStorage.js similarity index 100% rename from app/templates/test/services/storage/BaseStorage.js rename to generators/app/templates/test/services/storage/BaseStorage.js diff --git a/app/templates/test/services/storage/GCloudStorage.js b/generators/app/templates/test/services/storage/GCloudStorage.js similarity index 100% rename from app/templates/test/services/storage/GCloudStorage.js rename to generators/app/templates/test/services/storage/GCloudStorage.js diff --git a/app/templates/test/services/storage/StorageFactory.js b/generators/app/templates/test/services/storage/StorageFactory.js similarity index 100% rename from app/templates/test/services/storage/StorageFactory.js rename to generators/app/templates/test/services/storage/StorageFactory.js diff --git a/generators/app/templates/tools/check-updates.js b/generators/app/templates/tools/check-updates.js new file mode 100644 index 00000000..4d4f9e84 --- /dev/null +++ b/generators/app/templates/tools/check-updates.js @@ -0,0 +1,21 @@ +#!/usr/bin/env node + +var chalk = require('chalk'), + npmCheck = require('npm-check'), + update = require('npm-check/lib/update'); + +console.log(chalk.yellow("Start checking package.json for updates, please wait...")); + +npmCheck({ + global: false, + update: true, + skipUnused: false, + ignoreDev: false, + path: process.cwd(), + debug: true +}).then(function (data) { + update(data, {debug: false}); +}).catch(function (error) { + console.error(error.stack || error); + process.exit(1); +}).done(); diff --git a/app/templates/tools/fix-deps.js b/generators/app/templates/tools/fix-deps.js similarity index 89% rename from app/templates/tools/fix-deps.js rename to generators/app/templates/tools/fix-deps.js index 6d05fda1..40c57b85 100644 --- a/app/templates/tools/fix-deps.js +++ b/generators/app/templates/tools/fix-deps.js @@ -7,11 +7,11 @@ var path = require('path'), checkDependencies = require('dependency-check'), recursive = require('recursive-readdir'); -console.log(chalk.yellow("\nStart fixing package.json, please wait...\n")); +console.log(chalk.yellow("Start fixing package.json, please wait...")); recursive('./', ['node_modules'], function (error, files) { if (error) { - console.error(error); + console.error(error.stack || error); return process.exit(1); } @@ -24,7 +24,7 @@ recursive('./', ['node_modules'], function (error, files) { entries: files }, function (error, data) { if (error) { - console.error(error); + console.error(error.stack || error); return process.exit(1); } @@ -66,9 +66,7 @@ recursive('./', ['node_modules'], function (error, files) { marginBottom: 0 }); - var npmInstall = spawn('npm', ['install', '--save'].concat(missingDependencies)); - - // TODO: make colorful piping + var npmInstall = spawn('npm', ['install', '--save', '--color', 'always'].concat(missingDependencies)); npmInstall.stdout.pipe(process.stdout); npmInstall.stderr.pipe(process.stderr); npmInstall.on('close', process.exit); diff --git a/generators/app/util/assign.js b/generators/app/util/assign.js new file mode 100644 index 00000000..ddc11db4 --- /dev/null +++ b/generators/app/util/assign.js @@ -0,0 +1,17 @@ +/** + * Extend target object with source object + * @param {Object} _target Target object + * @param {Object} _source Source object + * @returns {Object} + */ +module.exports = function assign(_target, _source) { + var target = _target || {}, + source = _source || {}, + keys = Object.keys(source); + + for (var i = 0; i < keys.length; i++) { + target[keys[i]] = source[keys[i]]; + } + + return target; +}; diff --git a/generators/app/util/index.js b/generators/app/util/index.js new file mode 100644 index 00000000..3aafaddc --- /dev/null +++ b/generators/app/util/index.js @@ -0,0 +1,7 @@ +/** + * Exports object contains helper methods or objects for generator + */ + +module.exports = { + assign: require('./assign') +}; diff --git a/package.json b/package.json index 85889872..a942128f 100644 --- a/package.json +++ b/package.json @@ -1,16 +1,16 @@ { "name": "generator-sails-rest-api", - "version": "0.5.1", - "description": "Yeoman generator for scaffolding Sails REST API with bundled ready-2-use services", + "version": "0.6.0", + "description": "Yeoman generator that provides already configured and optimized Sails REST API with bundle of predefined features.", "keywords": [ "yeoman-generator", - "yeoman", - "generator", + "sails", "boilerplate", "scaffolder", - "sails", "rest", - "api" + "api", + "yeoman", + "generator" ], "homepage": "https://github.com/ghaiklor/generator-sails-rest-api", "bugs": { @@ -36,10 +36,10 @@ "url": "https://github.com/ghaiklor/generator-sails-rest-api.git" }, "dependencies": { - "chalk": "^0.5.0", - "print-message": "^1.1.0", + "chalk": "^1.0.0", + "print-message": "^1.2.1", "update-notifier": "^0.3.0", - "yeoman-generator": "^0.18.0", + "yeoman-generator": "^0.18.9", "yosay": "^1.0.2" }, "devDependencies": { diff --git a/test/app.js b/test/app.js index 92f95185..7e46bf56 100644 --- a/test/app.js +++ b/test/app.js @@ -5,12 +5,15 @@ var path = require('path'), describe('sails-rest-api:app', function () { before(function (done) { - helpers.run(path.join(__dirname, '../app')) + helpers + .run(path.join(__dirname, '../generators/app')) .inDir(path.join(os.tmpdir(), './temp-test')) .withOptions({"skip-all": true}) .on('end', done); }); + // TODO: write normal tests for generator + it('Should properly create root files', function () { assert.file([ '.editorconfig',