├── .gitattributes
├── openshift
├── templates
│ └── hot_deploy
└── USAGE
├── app
├── templates
│ ├── .buildignore
│ ├── .gitattributes
│ ├── client
│ │ ├── robots.txt
│ │ ├── app
│ │ │ ├── admin(auth)
│ │ │ │ ├── admin(less).less
│ │ │ │ ├── admin(css).css
│ │ │ │ ├── admin(sass).scss
│ │ │ │ ├── admin(stylus).styl
│ │ │ │ ├── admin.controller(coffee).coffee
│ │ │ │ ├── admin(jade).jade
│ │ │ │ ├── admin(coffee).coffee
│ │ │ │ ├── admin.controller(js).js
│ │ │ │ ├── admin(html).html
│ │ │ │ └── admin(js).js
│ │ │ ├── main
│ │ │ │ ├── main(coffee).coffee
│ │ │ │ ├── main(stylus).styl
│ │ │ │ ├── main(js).js
│ │ │ │ ├── main(css).css
│ │ │ │ ├── main(less).less
│ │ │ │ ├── main(sass).scss
│ │ │ │ ├── main.controller(coffee).coffee
│ │ │ │ ├── main.controller.spec(coffee).coffee
│ │ │ │ ├── main.controller.spec(js).js
│ │ │ │ ├── main.controller(js).js
│ │ │ │ ├── main(jade).jade
│ │ │ │ └── main(html).html
│ │ │ ├── account(auth)
│ │ │ │ ├── login
│ │ │ │ │ ├── login(css).css
│ │ │ │ │ ├── login(stylus).styl
│ │ │ │ │ ├── login.controller(coffee).coffee
│ │ │ │ │ ├── login.controller(js).js
│ │ │ │ │ ├── login(less).less
│ │ │ │ │ ├── login(sass).scss
│ │ │ │ │ ├── login(jade).jade
│ │ │ │ │ └── login(html).html
│ │ │ │ ├── settings
│ │ │ │ │ ├── settings.controller(coffee).coffee
│ │ │ │ │ ├── settings.controller(js).js
│ │ │ │ │ ├── settings(jade).jade
│ │ │ │ │ └── settings(html).html
│ │ │ │ ├── signup
│ │ │ │ │ ├── signup.controller(coffee).coffee
│ │ │ │ │ ├── signup.controller(js).js
│ │ │ │ │ ├── signup(jade).jade
│ │ │ │ │ └── signup(html).html
│ │ │ │ ├── account(coffee).coffee
│ │ │ │ └── account(js).js
│ │ │ ├── app(less).less
│ │ │ ├── app(sass).scss
│ │ │ ├── app(coffee).coffee
│ │ │ ├── app(css).css
│ │ │ ├── app(stylus).styl
│ │ │ └── app(js).js
│ │ ├── favicon.ico
│ │ ├── assets
│ │ │ └── images
│ │ │ │ └── !yeoman.png
│ │ ├── components
│ │ │ ├── socket(socketio)
│ │ │ │ ├── socket.mock(coffee).coffee
│ │ │ │ ├── socket.mock.js
│ │ │ │ ├── socket.service(coffee).coffee
│ │ │ │ └── socket.service.js
│ │ │ ├── sql-error(auth)
│ │ │ │ ├── sql-error.directive(coffee).coffee
│ │ │ │ └── sql-error.directive(js).js
│ │ │ ├── auth(auth)
│ │ │ │ ├── user.service(coffee).coffee
│ │ │ │ ├── user.service(js).js
│ │ │ │ ├── auth.service(coffee).coffee
│ │ │ │ └── auth.service(js).js
│ │ │ ├── modal(uibootstrap)
│ │ │ │ ├── modal(jade).jade
│ │ │ │ ├── modal(stylus).styl
│ │ │ │ ├── modal(css).css
│ │ │ │ ├── modal(html).html
│ │ │ │ ├── modal(less).less
│ │ │ │ ├── modal(sass).scss
│ │ │ │ ├── modal.service(coffee).coffee
│ │ │ │ └── modal.service(js).js
│ │ │ └── navbar
│ │ │ │ ├── navbar.controller(coffee).coffee
│ │ │ │ ├── navbar.controller(js).js
│ │ │ │ ├── navbar(jade).jade
│ │ │ │ └── navbar(html).html
│ │ ├── .jshintrc
│ │ └── index.html
│ ├── .bowerrc
│ ├── _.gitignore
│ ├── .travis.yml
│ ├── server
│ │ ├── .jshintrc-spec
│ │ ├── config
│ │ │ ├── environment
│ │ │ │ ├── test.js
│ │ │ │ ├── development.js
│ │ │ │ ├── production.js
│ │ │ │ └── index.js
│ │ │ ├── _local.env.js
│ │ │ ├── _local.env.sample.js
│ │ │ ├── socketio(socketio).js
│ │ │ ├── seed(sql).js
│ │ │ └── express.js
│ │ ├── .jshintrc
│ │ ├── api
│ │ │ ├── thing
│ │ │ │ ├── thing.model(sql).js
│ │ │ │ ├── index.js
│ │ │ │ ├── thing.spec.js
│ │ │ │ ├── thing.socket(socketio).js
│ │ │ │ └── thing.controller.js
│ │ │ ├── user(auth)
│ │ │ │ ├── index.js
│ │ │ │ ├── user.spec.js
│ │ │ │ ├── user.controller.js
│ │ │ │ └── user.model.js
│ │ │ └── index.js
│ │ ├── components
│ │ │ └── errors
│ │ │ │ └── index.js
│ │ ├── auth(auth)
│ │ │ ├── twitter(twitterAuth)
│ │ │ │ ├── index.js
│ │ │ │ └── passport.js
│ │ │ ├── facebook(facebookAuth)
│ │ │ │ ├── index.js
│ │ │ │ └── passport.js
│ │ │ ├── local
│ │ │ │ ├── index.js
│ │ │ │ └── passport.js
│ │ │ ├── google(googleAuth)
│ │ │ │ ├── index.js
│ │ │ │ └── passport.js
│ │ │ ├── index.js
│ │ │ └── auth.service.js
│ │ ├── routes.js
│ │ ├── app.js
│ │ └── views
│ │ │ ├── 404(jade).jade
│ │ │ └── 404(html).html
│ ├── e2e
│ │ └── main
│ │ │ ├── main.po.js
│ │ │ └── main.spec.js
│ ├── .editorconfig
│ ├── _bower.json
│ ├── protractor.conf.js
│ ├── karma.conf.js
│ └── _package.json
├── USAGE
└── index.js
├── heroku
├── templates
│ └── Procfile
├── USAGE
└── index.js
├── generators
├── readme.md
├── value
│ └── index.js
├── view
│ └── index.js
├── constant
│ └── index.js
└── deploy
│ └── index.js
├── test
├── fixtures
│ ├── .bowerrc
│ ├── bower.json
│ ├── .yo-rc.json
│ └── package.json
└── readme.md
├── .gitignore
├── .travis.yml
├── .editorconfig
├── .jshintrc
├── route
└── index.js
├── filter
└── index.js
├── factory
└── index.js
├── provider
└── index.js
├── service
└── index.js
├── controller
└── index.js
├── decorator
└── index.js
├── directive
└── index.js
├── endpoint
├── templates
│ ├── name.model(sql).js
│ ├── index.js
│ ├── name.socket(socketio).js
│ ├── name.spec.js
│ └── name.controller.js
└── index.js
├── script-base.js
├── package.json
├── contributing.md
├── util.js
└── Gruntfile.js
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
2 |
--------------------------------------------------------------------------------
/openshift/templates/hot_deploy:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/templates/.buildignore:
--------------------------------------------------------------------------------
1 | *.coffee
--------------------------------------------------------------------------------
/app/templates/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
--------------------------------------------------------------------------------
/heroku/templates/Procfile:
--------------------------------------------------------------------------------
1 | web: node server/app.js
2 |
--------------------------------------------------------------------------------
/generators/readme.md:
--------------------------------------------------------------------------------
1 | This folder is for deprecated generators only.
--------------------------------------------------------------------------------
/test/fixtures/.bowerrc:
--------------------------------------------------------------------------------
1 | {
2 | "directory": "bower_components"
3 | }
4 |
--------------------------------------------------------------------------------
/app/templates/client/robots.txt:
--------------------------------------------------------------------------------
1 | # robotstxt.org
2 |
3 | User-agent: *
4 |
--------------------------------------------------------------------------------
/app/templates/.bowerrc:
--------------------------------------------------------------------------------
1 | {
2 | "directory": "client/bower_components"
3 | }
4 |
--------------------------------------------------------------------------------
/app/templates/client/app/admin(auth)/admin(less).less:
--------------------------------------------------------------------------------
1 | .trash { color:rgb(209, 91, 71); }
--------------------------------------------------------------------------------
/app/templates/client/app/admin(auth)/admin(css).css:
--------------------------------------------------------------------------------
1 | .trash { color:rgb(209, 91, 71); }
2 |
--------------------------------------------------------------------------------
/app/templates/client/app/admin(auth)/admin(sass).scss:
--------------------------------------------------------------------------------
1 | .trash { color:rgb(209, 91, 71); }
2 |
--------------------------------------------------------------------------------
/app/templates/client/app/admin(auth)/admin(stylus).styl:
--------------------------------------------------------------------------------
1 | .trash
2 | color rgb(209, 91, 71)
--------------------------------------------------------------------------------
/test/readme.md:
--------------------------------------------------------------------------------
1 | Run bower install and npm install in the fixtures folder before running tests
--------------------------------------------------------------------------------
/app/USAGE:
--------------------------------------------------------------------------------
1 | Description:
2 | Creates an full-stack AngularJS + Node app
3 |
4 | Example:
5 | yo angular-fullstack
6 |
--------------------------------------------------------------------------------
/app/templates/client/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tashrafy/generator-sql-fullstack/HEAD/app/templates/client/favicon.ico
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | bower_components
3 | test/temp
4 | demo
5 | .idea
6 | .DS_Store
7 | release.txt
8 | fixtures/bower.json
9 | fixtures/package.json
--------------------------------------------------------------------------------
/app/templates/client/assets/images/!yeoman.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tashrafy/generator-sql-fullstack/HEAD/app/templates/client/assets/images/!yeoman.png
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - '0.10'
4 | - '0.11'
5 | before_install:
6 | - gem update --system
7 | - gem install sass --version "=3.3.7"
8 | - npm install -g bower grunt-cli
9 |
--------------------------------------------------------------------------------
/app/templates/_.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | public
3 | .tmp<% if(filters.sass) { %>
4 | .sass-cache<% } %>
5 | .idea
6 | client/bower_components
7 | dist
8 | /server/config/local.env.js
9 | npm-debug.log
10 |
--------------------------------------------------------------------------------
/app/templates/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - '0.10'
4 | - '0.11'
5 | before_script:
6 | - npm install -g bower grunt-cli<% if (filters.sass) { %>
7 | - gem install sass<% } %>
8 | - bower install
9 |
--------------------------------------------------------------------------------
/app/templates/server/.jshintrc-spec:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ".jshintrc",
3 | "globals": {
4 | "describe": true,
5 | "it": true,
6 | "before": true,
7 | "beforeEach": true,
8 | "after": true,
9 | "afterEach": true
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/heroku/USAGE:
--------------------------------------------------------------------------------
1 | Description:
2 | Initalizes a heroku app and generates a `dist` folder which is ready to push to heroku.
3 |
4 | Example:
5 | yo angular-fullstack:heroku
6 |
7 | This will create:
8 | a dist folder and initialize a heroku app
9 |
--------------------------------------------------------------------------------
/openshift/USAGE:
--------------------------------------------------------------------------------
1 | Description:
2 | Initalizes an openshift app and generates a `dist` folder and pushes it to openshift.
3 |
4 | Example:
5 | yo angular-fullstack:openshift
6 |
7 | This will create:
8 | a dist folder and initialize an openshift app
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = true
11 |
12 | [*.md]
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/app/templates/server/config/environment/test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Test specific configuration
4 | // ===========================
5 | module.exports = {
6 | // MongoDB connection options
7 | mongo: {
8 | uri: 'mongodb://localhost/<%= _.slugify(appname) %>-test'
9 | }
10 | };
--------------------------------------------------------------------------------
/app/templates/client/components/socket(socketio)/socket.mock(coffee).coffee:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | angular.module 'socketMock', []
4 | .factory 'socket', ->
5 | socket:
6 | connect: ->
7 |
8 | on: ->
9 |
10 | emit: ->
11 |
12 | receive: ->
13 |
14 | syncUpdates: ->
15 | unsyncUpdates: ->
16 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "node": true,
3 | "esnext": true,
4 | "bitwise": false,
5 | "curly": false,
6 | "eqeqeq": true,
7 | "eqnull": true,
8 | "immed": true,
9 | "latedef": true,
10 | "newcap": true,
11 | "noarg": true,
12 | "undef": true,
13 | "strict": false,
14 | "trailing": true,
15 | "smarttabs": true
16 | }
17 |
--------------------------------------------------------------------------------
/route/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var yeoman = require('yeoman-generator');
3 |
4 | var Generator = yeoman.generators.Base.extend({
5 | compose: function() {
6 | this.composeWith('ng-component:route', {arguments: this.arguments}, { local: require.resolve('generator-ng-component/route') });
7 | }
8 | });
9 |
10 | module.exports = Generator;
--------------------------------------------------------------------------------
/filter/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var yeoman = require('yeoman-generator');
3 |
4 | var Generator = yeoman.generators.Base.extend({
5 | compose: function() {
6 | this.composeWith('ng-component:filter', {arguments: this.arguments}, { local: require.resolve('generator-ng-component/filter') });
7 | }
8 | });
9 |
10 | module.exports = Generator;
--------------------------------------------------------------------------------
/factory/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var yeoman = require('yeoman-generator');
3 |
4 | var Generator = yeoman.generators.Base.extend({
5 | compose: function() {
6 | this.composeWith('ng-component:factory', {arguments: this.arguments}, { local: require.resolve('generator-ng-component/factory') });
7 | }
8 | });
9 |
10 | module.exports = Generator;
--------------------------------------------------------------------------------
/provider/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var yeoman = require('yeoman-generator');
3 |
4 | var Generator = yeoman.generators.Base.extend({
5 | compose: function() {
6 | this.composeWith('ng-component:provider', {arguments: this.arguments}, { local: require.resolve('generator-ng-component/provider') });
7 | }
8 | });
9 |
10 | module.exports = Generator;
--------------------------------------------------------------------------------
/service/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var yeoman = require('yeoman-generator');
3 |
4 | var Generator = yeoman.generators.Base.extend({
5 | compose: function() {
6 | this.composeWith('ng-component:service', {arguments: this.arguments}, { local: require.resolve('generator-ng-component/service') });
7 | }
8 | });
9 |
10 | module.exports = Generator;
--------------------------------------------------------------------------------
/app/templates/server/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "node": true,
3 | "esnext": true,
4 | "bitwise": true,
5 | "eqeqeq": true,
6 | "immed": true,
7 | "latedef": "nofunc",
8 | "newcap": false,
9 | "noarg": true,
10 | "regexp": true,
11 | "undef": true,
12 | "smarttabs": true,
13 | "asi": true,
14 | "debug": true,
15 | "laxcomma": true
16 | }
17 |
--------------------------------------------------------------------------------
/controller/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var yeoman = require('yeoman-generator');
3 |
4 | var Generator = yeoman.generators.Base.extend({
5 | compose: function() {
6 | this.composeWith('ng-component:controller', {arguments: this.arguments}, { local: require.resolve('generator-ng-component/controller') });
7 | }
8 | });
9 |
10 | module.exports = Generator;
--------------------------------------------------------------------------------
/decorator/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var yeoman = require('yeoman-generator');
3 |
4 | var Generator = yeoman.generators.Base.extend({
5 | compose: function() {
6 | this.composeWith('ng-component:decorator', {arguments: this.arguments}, { local: require.resolve('generator-ng-component/decorator') });
7 | }
8 | });
9 |
10 | module.exports = Generator;
--------------------------------------------------------------------------------
/directive/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var yeoman = require('yeoman-generator');
3 |
4 | var Generator = yeoman.generators.Base.extend({
5 | compose: function() {
6 | this.composeWith('ng-component:directive', {arguments: this.arguments}, { local: require.resolve('generator-ng-component/directive') });
7 | }
8 | });
9 |
10 | module.exports = Generator;
--------------------------------------------------------------------------------
/app/templates/client/app/admin(auth)/admin.controller(coffee).coffee:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | angular.module '<%= scriptAppName %>'
4 | .controller 'AdminCtrl', ($scope, $http, Auth, User) ->
5 |
6 | $http.get '/api/users'
7 | .success (users) ->
8 | $scope.users = users
9 |
10 | $scope.delete = (user) ->
11 | User.remove id: user._id
12 | _.remove $scope.users, user
--------------------------------------------------------------------------------
/app/templates/client/components/sql-error(auth)/sql-error.directive(coffee).coffee:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | ###
4 | Removes server error when user updates input
5 | ###
6 | angular.module '<%= scriptAppName %>'
7 | .directive 'sqlError', ->
8 | restrict: 'A'
9 | require: 'ngModel'
10 | link: (scope, element, attrs, ngModel) ->
11 | element.on 'keydown', ->
12 | ngModel.$setValidity 'sql', true
13 |
--------------------------------------------------------------------------------
/app/templates/client/components/auth(auth)/user.service(coffee).coffee:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | angular.module '<%= scriptAppName %>'
4 | .factory 'User', ($resource) ->
5 | $resource '/api/users/:id/:controller',
6 | id: '@_id'
7 | ,
8 | changePassword:
9 | method: 'PUT'
10 | params:
11 | controller: 'password'
12 |
13 | get:
14 | method: 'GET'
15 | params:
16 | id: 'me'
17 |
18 |
--------------------------------------------------------------------------------
/app/templates/client/components/socket(socketio)/socket.mock.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | angular.module('socketMock', [])
4 | .factory('socket', function() {
5 | return {
6 | socket: {
7 | connect: function() {},
8 | on: function() {},
9 | emit: function() {},
10 | receive: function() {}
11 | },
12 |
13 | syncUpdates: function() {},
14 | unsyncUpdates: function() {}
15 | };
16 | });
--------------------------------------------------------------------------------
/app/templates/server/api/thing/thing.model(sql).js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = function(sequelize, DataTypes) {
4 | var Thing = sequelize.define('Thing', {
5 | _id: {
6 | type: DataTypes.INTEGER,
7 | allowNull: false,
8 | primaryKey: true,
9 | autoIncrement: true
10 | },
11 | name: DataTypes.STRING,
12 | info: DataTypes.STRING,
13 | active: DataTypes.BOOLEAN,
14 | });
15 |
16 | return Thing;
17 | };
18 |
--------------------------------------------------------------------------------
/app/templates/client/app/admin(auth)/admin(jade).jade:
--------------------------------------------------------------------------------
1 | div(ng-include='"components/navbar/navbar.html"')
2 | .container
3 | p
4 | | The delete user and user index api routes are restricted to users with the 'admin' role.
5 | ul.list-group
6 | li.list-group-item(ng-repeat='user in users')
7 | strong {{user.name}}
8 | br
9 | span.text-muted {{user.email}}
10 | a.trash(ng-click='delete(user)')
11 | span.glyphicon.glyphicon-trash.pull-right
--------------------------------------------------------------------------------
/generators/value/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var chalk = require('chalk');
3 | var yeoman = require('yeoman-generator');
4 | var util = require('util');
5 |
6 | var Generator = module.exports = function Generator() {
7 | yeoman.generators.Base.apply(this, arguments);
8 | };
9 |
10 | util.inherits(Generator, yeoman.generators.Base);
11 |
12 | Generator.prototype.deprecated = function deprecated() {
13 | this.log(chalk.yellow('This sub-generator is deprecated. \n'));
14 | };
--------------------------------------------------------------------------------
/generators/view/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var chalk = require('chalk');
3 | var yeoman = require('yeoman-generator');
4 | var util = require('util');
5 |
6 | var Generator = module.exports = function Generator() {
7 | yeoman.generators.Base.apply(this, arguments);
8 | };
9 |
10 | util.inherits(Generator, yeoman.generators.Base);
11 |
12 | Generator.prototype.deprecated = function deprecated() {
13 | this.log(chalk.yellow('This sub-generator is deprecated. \n'));
14 | };
--------------------------------------------------------------------------------
/generators/constant/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var chalk = require('chalk');
3 | var yeoman = require('yeoman-generator');
4 | var util = require('util');
5 |
6 | var Generator = module.exports = function Generator() {
7 | yeoman.generators.Base.apply(this, arguments);
8 | };
9 |
10 | util.inherits(Generator, yeoman.generators.Base);
11 |
12 | Generator.prototype.deprecated = function deprecated() {
13 | this.log(chalk.yellow('This sub-generator is deprecated. \n'));
14 | };
--------------------------------------------------------------------------------
/app/templates/client/app/main/main(coffee).coffee:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | angular.module '<%= scriptAppName %>'
4 | <% if(filters.ngroute) { %>.config ($routeProvider) ->
5 | $routeProvider
6 | .when '/',
7 | templateUrl: 'app/main/main.html'
8 | controller: 'MainCtrl'
9 | <% } %><% if(filters.uirouter) { %>.config ($stateProvider) ->
10 | $stateProvider
11 | .state 'main',
12 | url: '/'
13 | templateUrl: 'app/main/main.html'
14 | controller: 'MainCtrl'
15 | <% } %>
--------------------------------------------------------------------------------
/endpoint/templates/name.model(sql).js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = function(sequelize, DataTypes) {
4 | var <%= classedName %> = sequelize.define('<%= classedName %>', {
5 | _id: {
6 | type: DataTypes.INTEGER,
7 | allowNull: false,
8 | primaryKey: true,
9 | autoIncrement: true
10 | },
11 | title: DataTypes.STRING,
12 | info: DataTypes.STRING,
13 | active: DataTypes.BOOLEAN,
14 | });
15 |
16 | return <%= classedName %>;
17 | };
18 |
--------------------------------------------------------------------------------
/app/templates/e2e/main/main.po.js:
--------------------------------------------------------------------------------
1 | /**
2 | * This file uses the Page Object pattern to define the main page for tests
3 | * https://docs.google.com/presentation/d/1B6manhG0zEXkC-H-tPo2vwU06JhL8w9-XCF9oehXzAQ
4 | */
5 |
6 | 'use strict';
7 |
8 | var MainPage = function() {
9 | this.heroEl = element(by.css('.hero-unit'));
10 | this.h1El = this.heroEl.element(by.css('h1'));
11 | this.imgEl = this.heroEl.element(by.css('img'));
12 | };
13 |
14 | module.exports = new MainPage();
15 |
16 |
--------------------------------------------------------------------------------
/app/templates/server/components/errors/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Error responses
3 | */
4 |
5 | 'use strict';
6 |
7 | module.exports[404] = function pageNotFound(req, res) {
8 | var viewFilePath = '404';
9 | var statusCode = 404;
10 | var result = {
11 | status: statusCode
12 | };
13 |
14 | res.status(result.status);
15 | res.render(viewFilePath, function (err) {
16 | if (err) { return res.json(result, result.status); }
17 |
18 | res.render(viewFilePath);
19 | });
20 | };
21 |
--------------------------------------------------------------------------------
/app/templates/client/components/modal(uibootstrap)/modal(jade).jade:
--------------------------------------------------------------------------------
1 | .modal-header
2 | button.close(ng-if='modal.dismissable', type='button', ng-click='$dismiss()') ×
3 | h4.modal-title(ng-if='modal.title', ng-bind='modal.title')
4 | .modal-body
5 | p(ng-if='modal.text', ng-bind='modal.text')
6 | div(ng-if='modal.html', ng-bind-html='modal.html')
7 | .modal-footer
8 | button.btn(ng-repeat='button in modal.buttons', ng-class='button.classes', ng-click='button.click($event)', ng-bind='button.text')
9 |
--------------------------------------------------------------------------------
/endpoint/templates/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var express = require('express');
4 | var controller = require('./<%= name %>.controller');
5 |
6 | var router = express.Router();
7 |
8 | router.get('/', controller.index);<% if(filters.sql) { %>
9 | router.get('/:id', controller.show);
10 | router.post('/', controller.create);
11 | router.put('/:id', controller.update);
12 | router.patch('/:id', controller.update);
13 | router.delete('/:id', controller.destroy);<% } %>
14 |
15 | module.exports = router;
16 |
--------------------------------------------------------------------------------
/app/templates/client/app/admin(auth)/admin(coffee).coffee:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | angular.module '<%= scriptAppName %>'
4 | <% if(filters.ngroute) { %>.config ($routeProvider) ->
5 | $routeProvider
6 | .when '/admin',
7 | templateUrl: 'app/admin/admin.html'
8 | controller: 'AdminCtrl'
9 | <% } %><% if(filters.uirouter) { %>.config ($stateProvider) ->
10 | $stateProvider
11 | .state 'admin',
12 | url: '/admin'
13 | templateUrl: 'app/admin/admin.html'
14 | controller: 'AdminCtrl'
15 | <% } %>
--------------------------------------------------------------------------------
/app/templates/server/api/thing/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var express = require('express');
4 | var controller = require('./thing.controller');
5 |
6 | var router = express.Router();
7 |
8 | router.get('/', controller.index);<% if(filters.sql) { %>
9 | router.get('/:id', controller.show);
10 | router.post('/', controller.create);
11 | router.put('/:id', controller.update);
12 | router.patch('/:id', controller.update);
13 | router.delete('/:id', controller.destroy);<% } %>
14 |
15 | module.exports = router;
16 |
--------------------------------------------------------------------------------
/app/templates/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig helps developers define and maintain consistent
2 | # coding styles between different editors and IDEs
3 | # editorconfig.org
4 |
5 | root = true
6 |
7 |
8 | [*]
9 |
10 | # Change these settings to your own preference
11 | indent_style = space
12 | indent_size = 2
13 |
14 | # We recommend you to keep these unchanged
15 | end_of_line = lf
16 | charset = utf-8
17 | trim_trailing_whitespace = true
18 | insert_final_newline = true
19 |
20 | [*.md]
21 | trim_trailing_whitespace = false
22 |
--------------------------------------------------------------------------------
/app/templates/e2e/main/main.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('Main View', function() {
4 | var page;
5 |
6 | beforeEach(function() {
7 | browser.get('/');
8 | page = require('./main.po');
9 | });
10 |
11 | it('should include jumbotron with correct data', function() {
12 | expect(page.h1El.getText()).toBe('\'Allo, \'Allo!');
13 | expect(page.imgEl.getAttribute('src')).toMatch(/assets\/images\/yeoman.png$/);
14 | expect(page.imgEl.getAttribute('alt')).toBe('I\'m Yeoman');
15 | });
16 | });
17 |
--------------------------------------------------------------------------------
/app/templates/client/components/sql-error(auth)/sql-error.directive(js).js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Removes server error when user updates input
5 | */
6 | angular.module('<%= scriptAppName %>')
7 | .directive('sqlError', function () {
8 | return {
9 | restrict: 'A',
10 | require: 'ngModel',
11 | link: function(scope, element, attrs, ngModel) {
12 | element.on('keydown', function() {
13 | return ngModel.$setValidity('sql', true);
14 | });
15 | }
16 | };
17 | });
18 |
--------------------------------------------------------------------------------
/app/templates/client/app/admin(auth)/admin.controller(js).js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | angular.module('<%= scriptAppName %>')
4 | .controller('AdminCtrl', function ($scope, $http, Auth, User) {
5 |
6 | // Use the User $resource to fetch all users
7 | $scope.users = User.query();
8 |
9 | $scope.delete = function(user) {
10 | User.remove({ id: user._id });
11 | angular.forEach($scope.users, function(u, i) {
12 | if (u === user) {
13 | $scope.users.splice(i, 1);
14 | }
15 | });
16 | };
17 | });
18 |
--------------------------------------------------------------------------------
/app/templates/client/components/auth(auth)/user.service(js).js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | angular.module('<%= scriptAppName %>')
4 | .factory('User', function ($resource) {
5 | return $resource('/api/users/:id/:controller', {
6 | id: '@_id'
7 | },
8 | {
9 | changePassword: {
10 | method: 'PUT',
11 | params: {
12 | controller:'password'
13 | }
14 | },
15 | get: {
16 | method: 'GET',
17 | params: {
18 | id:'me'
19 | }
20 | }
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/app/templates/client/components/modal(uibootstrap)/modal(stylus).styl:
--------------------------------------------------------------------------------
1 | .modal-primary
2 | .modal-info
3 | .modal-success
4 | .modal-warning
5 | .modal-danger
6 | .modal-header
7 | color #fff
8 | border-radius 5px 5px 0 0
9 |
10 | .modal-primary .modal-header
11 | background #428bca
12 |
13 | .modal-info .modal-header
14 | background #5bc0de
15 |
16 | .modal-success .modal-header
17 | background #5cb85c
18 |
19 | .modal-warning .modal-header
20 | background #f0ad4e
21 |
22 | .modal-danger .modal-header
23 | background #d9534f
24 |
--------------------------------------------------------------------------------
/app/templates/client/app/main/main(stylus).styl:
--------------------------------------------------------------------------------
1 | .thing-form
2 | margin 20px 0
3 |
4 | #banner
5 | border-bottom none
6 | margin-top -20px
7 |
8 | #banner h1
9 | font-size 60px
10 | letter-spacing -1px
11 | line-height 1
12 |
13 | .hero-unit
14 | background #4393B9
15 | color #F5F5F5
16 | padding 30px 15px
17 | position relative
18 | text-align center
19 | text-shadow 0 1px 0 rgba(0, 0, 0, 0.1)
20 |
21 | .footer
22 | border-top 1px solid #E5E5E5
23 | margin-top 70px
24 | padding 30px 0
25 | text-align center
--------------------------------------------------------------------------------
/app/templates/server/auth(auth)/twitter(twitterAuth)/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var express = require('express');
4 | var passport = require('passport');
5 | var auth = require('../auth.service');
6 |
7 | var router = express.Router();
8 |
9 | router
10 | .get('/', passport.authenticate('twitter', {
11 | failureRedirect: '/signup',
12 | session: false
13 | }))
14 |
15 | .get('/callback', passport.authenticate('twitter', {
16 | failureRedirect: '/signup',
17 | session: false
18 | }), auth.setTokenCookie);
19 |
20 | module.exports = router;
--------------------------------------------------------------------------------
/app/templates/client/app/main/main(js).js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | angular.module('<%= scriptAppName %>')
4 | <% if(filters.ngroute) { %>.config(function ($routeProvider) {
5 | $routeProvider
6 | .when('/', {
7 | templateUrl: 'app/main/main.html',
8 | controller: 'MainCtrl'
9 | });
10 | });<% } %><% if(filters.uirouter) { %>.config(function ($stateProvider) {
11 | $stateProvider
12 | .state('main', {
13 | url: '/',
14 | templateUrl: 'app/main/main.html',
15 | controller: 'MainCtrl'
16 | });
17 | });<% } %>
--------------------------------------------------------------------------------
/app/templates/client/app/admin(auth)/admin(html).html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
The delete user and user index api routes are restricted to users with the 'admin' role.
5 |
6 | -
7 | {{user.name}}
8 | {{user.email}}
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/templates/server/api/thing/thing.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var should = require('should');
4 | var app = require('../../app');
5 | var request = require('supertest');
6 |
7 | describe('GET /api/things', function() {
8 |
9 | it('should respond with JSON array', function(done) {
10 | request(app)
11 | .get('/api/things')
12 | .expect(200)
13 | .expect('Content-Type', /json/)
14 | .end(function(err, res) {
15 | if (err) return done(err);
16 | res.body.should.be.instanceof(Array);
17 | done();
18 | });
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/app/templates/client/app/admin(auth)/admin(js).js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | angular.module('<%= scriptAppName %>')
4 | <% if(filters.ngroute) { %>.config(function ($routeProvider) {
5 | $routeProvider
6 | .when('/admin', {
7 | templateUrl: 'app/admin/admin.html',
8 | controller: 'AdminCtrl'
9 | });
10 | });<% } %><% if(filters.uirouter) { %>.config(function ($stateProvider) {
11 | $stateProvider
12 | .state('admin', {
13 | url: '/admin',
14 | templateUrl: 'app/admin/admin.html',
15 | controller: 'AdminCtrl'
16 | });
17 | });<% } %>
--------------------------------------------------------------------------------
/app/templates/server/config/environment/development.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Development specific configuration
4 | // ==================================
5 | module.exports = {
6 | // MongoDB connection options
7 | mongo: {
8 | uri: 'mongodb://localhost/<%= _.slugify(appname) %>-dev'
9 | },
10 |
11 | // Postgres connection options
12 | postgres: {
13 | uri: process.env.POSTGRES_URL ||
14 | 'postgres://user:pass@localhost:5432/<%= _.slugify(appname) %>'
15 | },
16 | database: 'test',
17 | username: 'postgres',
18 | password: 'root',
19 | seedDB: true
20 | };
21 |
--------------------------------------------------------------------------------
/generators/deploy/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var chalk = require('chalk');
3 | var yeoman = require('yeoman-generator');
4 | var util = require('util');
5 |
6 | var Generator = module.exports = function Generator() {
7 | yeoman.generators.Base.apply(this, arguments);
8 | };
9 |
10 | util.inherits(Generator, yeoman.generators.NamedBase);
11 |
12 | Generator.prototype.deprecated = function deprecated() {
13 | this.log(chalk.yellow(chalk.bold('yo sql-fullstack:deploy') + ' is deprecated, instead use: \n') +
14 | chalk.green('yo sql:heroku') + ' or ' + chalk.green('yo sql:openshift'));
15 | };
16 |
--------------------------------------------------------------------------------
/app/templates/server/auth(auth)/facebook(facebookAuth)/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var express = require('express');
4 | var passport = require('passport');
5 | var auth = require('../auth.service');
6 |
7 | var router = express.Router();
8 |
9 | router
10 | .get('/', passport.authenticate('facebook', {
11 | scope: ['email', 'user_about_me'],
12 | failureRedirect: '/signup',
13 | session: false
14 | }))
15 |
16 | .get('/callback', passport.authenticate('facebook', {
17 | failureRedirect: '/signup',
18 | session: false
19 | }), auth.setTokenCookie);
20 |
21 | module.exports = router;
--------------------------------------------------------------------------------
/app/templates/client/components/modal(uibootstrap)/modal(css).css:
--------------------------------------------------------------------------------
1 | .modal-primary .modal-header,
2 | .modal-info .modal-header,
3 | .modal-success .modal-header,
4 | .modal-warning .modal-header,
5 | .modal-danger .modal-header {
6 | color: #fff;
7 | border-radius: 5px 5px 0 0;
8 | }
9 | .modal-primary .modal-header {
10 | background: #428bca;
11 | }
12 | .modal-info .modal-header {
13 | background: #5bc0de;
14 | }
15 | .modal-success .modal-header {
16 | background: #5cb85c;
17 | }
18 | .modal-warning .modal-header {
19 | background: #f0ad4e;
20 | }
21 | .modal-danger .modal-header {
22 | background: #d9534f;
23 | }
--------------------------------------------------------------------------------
/app/templates/client/components/modal(uibootstrap)/modal(html).html:
--------------------------------------------------------------------------------
1 |
5 |
9 |
--------------------------------------------------------------------------------
/app/templates/client/components/modal(uibootstrap)/modal(less).less:
--------------------------------------------------------------------------------
1 | .modal-primary,
2 | .modal-info,
3 | .modal-success,
4 | .modal-warning,
5 | .modal-danger {
6 | .modal-header {
7 | color: #fff;
8 | border-radius: 5px 5px 0 0;
9 | }
10 | }
11 | .modal-primary .modal-header {
12 | background: @brand-primary;
13 | }
14 | .modal-info .modal-header {
15 | background: @brand-info;
16 | }
17 | .modal-success .modal-header {
18 | background: @brand-success;
19 | }
20 | .modal-warning .modal-header {
21 | background: @brand-warning;
22 | }
23 | .modal-danger .modal-header {
24 | background: @brand-danger;
25 | }
26 |
--------------------------------------------------------------------------------
/app/templates/client/components/modal(uibootstrap)/modal(sass).scss:
--------------------------------------------------------------------------------
1 | .modal-primary,
2 | .modal-info,
3 | .modal-success,
4 | .modal-warning,
5 | .modal-danger {
6 | .modal-header {
7 | color: #fff;
8 | border-radius: 5px 5px 0 0;
9 | }
10 | }
11 | .modal-primary .modal-header {
12 | background: $brand-primary;
13 | }
14 | .modal-info .modal-header {
15 | background: $brand-info;
16 | }
17 | .modal-success .modal-header {
18 | background: $brand-success;
19 | }
20 | .modal-warning .modal-header {
21 | background: $brand-warning;
22 | }
23 | .modal-danger .modal-header {
24 | background: $brand-danger;
25 | }
26 |
--------------------------------------------------------------------------------
/app/templates/client/components/navbar/navbar.controller(coffee).coffee:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | angular.module '<%= scriptAppName %>'
4 | .controller 'NavbarCtrl', ($scope, $location<% if(filters.auth) {%>, Auth<% } %>) ->
5 | $scope.menu = [
6 | title: 'Home'
7 | link: '/'
8 | ]
9 | $scope.isCollapsed = true<% if(filters.auth) {%>
10 | $scope.isLoggedIn = Auth.isLoggedIn
11 | $scope.isAdmin = Auth.isAdmin
12 | $scope.getCurrentUser = Auth.getCurrentUser
13 |
14 | $scope.logout = ->
15 | Auth.logout()
16 | $location.path '/login'<% } %>
17 |
18 | $scope.isActive = (route) ->
19 | route is $location.path()
--------------------------------------------------------------------------------
/app/templates/server/api/thing/thing.socket(socketio).js:
--------------------------------------------------------------------------------
1 | /**
2 | * Broadcast updates to client when the model changes
3 | */
4 |
5 | 'use strict';
6 |
7 | var thing = require('./thing.model');
8 |
9 | exports.register = function(socket) {
10 | // thing.schema.post('save', function (doc) {
11 | // onSave(socket, doc);
12 | // });
13 | // thing.schema.post('remove', function (doc) {
14 | // onRemove(socket, doc);
15 | // });
16 | }
17 |
18 | function onSave(socket, doc, cb) {
19 | socket.emit('thing:save', doc);
20 | }
21 |
22 | function onRemove(socket, doc, cb) {
23 | socket.emit('thing:remove', doc);
24 | }
25 |
--------------------------------------------------------------------------------
/app/templates/client/app/account(auth)/login/login(css).css:
--------------------------------------------------------------------------------
1 | <% if (filters.oauth) { %><% if (filters.facebookAuth) { %>.btn-facebook {
2 | color: #fff;
3 | background-color: #3B5998;
4 | border-color: #133783;
5 | }
6 | <% } if (filters.twitterAuth) { %>
7 | .btn-twitter {
8 | color: #fff;
9 | background-color: #2daddc;
10 | border-color: #0271bf;
11 | }
12 | <% } if (filters.googleAuth) { %>
13 | .btn-google-plus {
14 | color: #fff;
15 | background-color: #dd4b39;
16 | border-color: #c53727;
17 | }
18 | <% } %>
19 | .btn-github {
20 | color: #fff;
21 | background-color: #fafafa;
22 | border-color: #ccc;
23 | }<% } %>
24 |
--------------------------------------------------------------------------------
/app/templates/client/app/main/main(css).css:
--------------------------------------------------------------------------------
1 | .thing-form {
2 | margin: 20px 0;
3 | }
4 |
5 | #banner {
6 | border-bottom: none;
7 | margin-top: -20px;
8 | }
9 |
10 | #banner h1 {
11 | font-size: 60px;
12 | line-height: 1;
13 | letter-spacing: -1px;
14 | }
15 |
16 | .hero-unit {
17 | position: relative;
18 | padding: 30px 15px;
19 | color: #F5F5F5;
20 | text-align: center;
21 | text-shadow: 0 1px 0 rgba(0, 0, 0, 0.1);
22 | background: #4393B9;
23 | }
24 |
25 | .footer {
26 | text-align: center;
27 | padding: 30px 0;
28 | margin-top: 70px;
29 | border-top: 1px solid #E5E5E5;
30 | }
--------------------------------------------------------------------------------
/app/templates/client/app/main/main(less).less:
--------------------------------------------------------------------------------
1 | .thing-form {
2 | margin: 20px 0;
3 | }
4 |
5 | #banner {
6 | border-bottom: none;
7 | margin-top: -20px;
8 | }
9 |
10 | #banner h1 {
11 | font-size: 60px;
12 | line-height: 1;
13 | letter-spacing: -1px;
14 | }
15 |
16 | .hero-unit {
17 | position: relative;
18 | padding: 30px 15px;
19 | color: #F5F5F5;
20 | text-align: center;
21 | text-shadow: 0 1px 0 rgba(0, 0, 0, 0.1);
22 | background: #4393B9;
23 | }
24 |
25 | .footer {
26 | text-align: center;
27 | padding: 30px 0;
28 | margin-top: 70px;
29 | border-top: 1px solid #E5E5E5;
30 | }
--------------------------------------------------------------------------------
/app/templates/client/app/main/main(sass).scss:
--------------------------------------------------------------------------------
1 | .thing-form {
2 | margin: 20px 0;
3 | }
4 |
5 | #banner {
6 | border-bottom: none;
7 | margin-top: -20px;
8 | }
9 |
10 | #banner h1 {
11 | font-size: 60px;
12 | line-height: 1;
13 | letter-spacing: -1px;
14 | }
15 |
16 | .hero-unit {
17 | position: relative;
18 | padding: 30px 15px;
19 | color: #F5F5F5;
20 | text-align: center;
21 | text-shadow: 0 1px 0 rgba(0, 0, 0, 0.1);
22 | background: #4393B9;
23 | }
24 |
25 | .footer {
26 | text-align: center;
27 | padding: 30px 0;
28 | margin-top: 70px;
29 | border-top: 1px solid #E5E5E5;
30 | }
--------------------------------------------------------------------------------
/app/templates/client/app/account(auth)/settings/settings.controller(coffee).coffee:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | angular.module '<%= scriptAppName %>'
4 | .controller 'SettingsCtrl', ($scope, User, Auth) ->
5 | $scope.errors = {}
6 | $scope.changePassword = (form) ->
7 | $scope.submitted = true
8 |
9 | if form.$valid
10 | Auth.changePassword $scope.user.oldPassword, $scope.user.newPassword
11 | .then ->
12 | $scope.message = 'Password successfully changed.'
13 |
14 | .catch ->
15 | form.password.$setValidity 'sql', false
16 | $scope.errors.other = 'Incorrect password'
17 | $scope.message = ''
18 |
--------------------------------------------------------------------------------
/endpoint/templates/name.socket(socketio).js:
--------------------------------------------------------------------------------
1 | /**
2 | * Broadcast updates to client when the model changes
3 | */
4 |
5 | 'use strict';
6 |
7 | var <%= classedName %> = require('./<%= name %>.model');
8 |
9 | exports.register = function(socket) {
10 | <%= classedName %>.schema.post('save', function (doc) {
11 | onSave(socket, doc);
12 | });
13 | <%= classedName %>.schema.post('remove', function (doc) {
14 | onRemove(socket, doc);
15 | });
16 | }
17 |
18 | function onSave(socket, doc, cb) {
19 | socket.emit('<%= name %>:save', doc);
20 | }
21 |
22 | function onRemove(socket, doc, cb) {
23 | socket.emit('<%= name %>:remove', doc);
24 | }
--------------------------------------------------------------------------------
/app/templates/server/auth(auth)/local/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var express = require('express');
4 | var passport = require('passport');
5 | var auth = require('../auth.service');
6 |
7 | var router = express.Router();
8 |
9 | router.post('/', function(req, res, next) {
10 | passport.authenticate('local', function (err, user, info) {
11 | var error = err || info;
12 | if (error) return res.status(401).json(error);
13 | if (!user) return res.status(404).json({message: 'Something went wrong, please try again.'});
14 |
15 | var token = auth.signToken(user._id, user.role);
16 | res.json({token: token});
17 | })(req, res, next)
18 | });
19 |
20 | module.exports = router;
--------------------------------------------------------------------------------
/app/templates/server/auth(auth)/google(googleAuth)/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var express = require('express');
4 | var passport = require('passport');
5 | var auth = require('../auth.service');
6 |
7 | var router = express.Router();
8 |
9 | router
10 | .get('/', passport.authenticate('google', {
11 | failureRedirect: '/signup',
12 | scope: [
13 | 'https://www.googleapis.com/auth/userinfo.profile',
14 | 'https://www.googleapis.com/auth/userinfo.email'
15 | ],
16 | session: false
17 | }))
18 |
19 | .get('/callback', passport.authenticate('google', {
20 | failureRedirect: '/signup',
21 | session: false
22 | }), auth.setTokenCookie);
23 |
24 | module.exports = router;
--------------------------------------------------------------------------------
/app/templates/client/app/account(auth)/login/login(stylus).styl:
--------------------------------------------------------------------------------
1 | <% if (filters.oauth) { %>// Social buttons
2 | // --------------------------------------------------
3 | <% if (filters.facebookAuth) { %>
4 | .btn-facebook
5 | color: #fff;
6 | background-color: #3B5998;
7 | border-color: #133783;
8 | <% } if (filters.twitterAuth) { %>
9 | .btn-twitter
10 | color: #fff;
11 | background-color: #2daddc;
12 | border-color: #0271bf;
13 | <% } if (filters.googleAuth) { %>
14 | .btn-google-plus
15 | color: #fff;
16 | background-color: #dd4b39;
17 | border-color: #c53727;
18 | <% } %>
19 | .btn-github
20 | color: #fff;
21 | background-color: #fafafa;
22 | border-color: #ccc;<% } %>
23 |
--------------------------------------------------------------------------------
/test/fixtures/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "tempApp",
3 | "version": "0.0.0",
4 | "dependencies": {
5 | "angular": ">=1.2.*",
6 | "json3": "~3.3.1",
7 | "es5-shim": "~3.0.1",
8 | "bootstrap-sass-official": "~3.1.1",
9 | "bootstrap": "~3.1.1",
10 | "angular-resource": ">=1.2.*",
11 | "angular-cookies": ">=1.2.*",
12 | "angular-sanitize": ">=1.2.*",
13 | "angular-route": ">=1.2.*",
14 | "angular-bootstrap": "~0.11.0",
15 | "font-awesome": ">=4.1.0",
16 | "lodash": "~2.4.1",
17 | "angular-socket-io": "~0.6.0",
18 | "angular-ui-router": "~0.2.10"
19 | },
20 | "devDependencies": {
21 | "angular-mocks": ">=1.2.*",
22 | "angular-scenario": ">=1.2.*"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app/templates/server/api/user(auth)/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var express = require('express')
4 | , controller = require('./user.controller')
5 | , config = require('../../config/environment')
6 | , auth = require('../../auth/auth.service')
7 | ;
8 |
9 | var router = express.Router();
10 |
11 | router.get('/', auth.hasRole('admin'), controller.index);
12 | router.delete('/:id', auth.hasRole('admin'), controller.destroy);
13 | router.get('/me', auth.isAuthenticated(), controller.me);
14 | router.put('/:id/password', auth.isAuthenticated(), controller.changePassword);
15 | router.get('/:id', auth.isAuthenticated(), controller.show);
16 | router.post('/', controller.create);
17 |
18 | module.exports = router;
19 |
--------------------------------------------------------------------------------
/app/templates/client/components/navbar/navbar.controller(js).js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | angular.module('<%= scriptAppName %>')
4 | .controller('NavbarCtrl', function ($scope, $location<% if(filters.auth) {%>, Auth<% } %>) {
5 | $scope.menu = [{
6 | 'title': 'Home',
7 | 'link': '/'
8 | }];
9 |
10 | $scope.isCollapsed = true;<% if(filters.auth) {%>
11 | $scope.isLoggedIn = Auth.isLoggedIn;
12 | $scope.isAdmin = Auth.isAdmin;
13 | $scope.getCurrentUser = Auth.getCurrentUser;
14 |
15 | $scope.logout = function() {
16 | Auth.logout();
17 | $location.path('/login');
18 | };<% } %>
19 |
20 | $scope.isActive = function(route) {
21 | return route === $location.path();
22 | };
23 | });
--------------------------------------------------------------------------------
/app/templates/client/app/account(auth)/settings/settings.controller(js).js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | angular.module('<%= scriptAppName %>')
4 | .controller('SettingsCtrl', function ($scope, User, Auth) {
5 | $scope.errors = {};
6 |
7 | $scope.changePassword = function(form) {
8 | $scope.submitted = true;
9 | if(form.$valid) {
10 | Auth.changePassword( $scope.user.oldPassword, $scope.user.newPassword )
11 | .then( function() {
12 | $scope.message = 'Password successfully changed.';
13 | })
14 | .catch( function() {
15 | form.password.$setValidity('sql', false);
16 | $scope.errors.other = 'Incorrect password';
17 | $scope.message = '';
18 | });
19 | }
20 | };
21 | });
22 |
--------------------------------------------------------------------------------
/app/templates/client/app/account(auth)/login/login.controller(coffee).coffee:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | angular.module '<%= scriptAppName %>'
4 | .controller 'LoginCtrl', ($scope, Auth, $location<% if(filters.oauth) {%>, $window<% } %>) ->
5 | $scope.user = {}
6 | $scope.errors = {}
7 | $scope.login = (form) ->
8 | $scope.submitted = true
9 |
10 | if form.$valid
11 | # Logged in, redirect to home
12 | Auth.login
13 | email: $scope.user.email
14 | password: $scope.user.password
15 |
16 | .then ->
17 | $location.path '/'
18 |
19 | .catch (err) ->
20 | $scope.errors.other = err.message
21 | <% if(filters.oauth) {%>
22 | $scope.loginOauth = (provider) ->
23 | $window.location.href = '/auth/' + provider<% } %>
24 |
--------------------------------------------------------------------------------
/app/templates/server/routes.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Main application routes
3 | */
4 |
5 | 'use strict';
6 |
7 | var errors = require('./components/errors');
8 | var path = require('path');
9 |
10 | module.exports = function(app) {
11 |
12 | // Insert routes below
13 | app.use('/api/things', require('./api/thing'));
14 | <% if (filters.auth) { %>app.use('/api/users', require('./api/user'));
15 |
16 | app.use('/auth', require('./auth'));
17 | <% } %>
18 | // All undefined asset or api routes should return a 404
19 | app.route('/:url(api|auth|components|app|bower_components|assets)/*')
20 | .get(errors[404]);
21 |
22 | // All other routes should redirect to the index.html
23 | app.route('/*')
24 | .get(function(req, res) {
25 | res.sendFile(path.resolve(app.get('appPath') + '/index.html'));
26 | });
27 | };
28 |
--------------------------------------------------------------------------------
/app/templates/client/app/app(less).less:
--------------------------------------------------------------------------------
1 | <% if(filters.bootstrap) { %>@import 'bootstrap/less/bootstrap.less';<% } %>
2 | @import 'font-awesome/less/font-awesome.less';
3 |
4 | <% if(filters.bootstrap) { %>@icon-font-path: '/bower_components/bootstrap/fonts/';<% } %>
5 | @fa-font-path: '/bower_components/font-awesome/fonts';
6 |
7 | /**
8 | * App-wide Styles
9 | */
10 |
11 | .browsehappy {
12 | margin: 0.2em 0;
13 | background: #ccc;
14 | color: #000;
15 | padding: 0.2em 0;
16 | }
17 | <% if (!filters.bootstrap) { %>
18 | /* Responsive: Portrait tablets and up */
19 | @media screen and (min-width: 768px) {
20 | .container {
21 | max-width: 730px;
22 | }
23 | }
24 | <% } %>
25 | // injector
26 | @import 'account/login/login.less';
27 | @import 'admin/admin.less';
28 | @import 'main/main.less';
29 | // endinjector
--------------------------------------------------------------------------------
/app/templates/client/app/main/main.controller(coffee).coffee:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | angular.module '<%= scriptAppName %>'
4 | .controller 'MainCtrl', ($scope, $http<% if(filters.socketio) { %>, socket<% } %>) ->
5 | $scope.awesomeThings = []
6 |
7 | $http.get('/api/things').success (awesomeThings) ->
8 | $scope.awesomeThings = awesomeThings
9 | <% if(filters.socketio) { %>socket.syncUpdates 'thing', $scope.awesomeThings<% } %>
10 | <% if(filters.sql) { %>
11 | $scope.addThing = ->
12 | return if $scope.newThing is ''
13 | $http.post '/api/things',
14 | name: $scope.newThing
15 |
16 | $scope.newThing = ''
17 |
18 | $scope.deleteThing = (thing) ->
19 | $http.delete '/api/things/' + thing._id<% } %><% if(filters.socketio) { %>
20 |
21 | $scope.$on '$destroy', ->
22 | socket.unsyncUpdates 'thing'<% } %>
23 |
--------------------------------------------------------------------------------
/app/templates/server/config/_local.env.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Use local.env.js for environment variables that grunt will set when the server starts locally.
4 | // Use for your api keys, secrets, etc. This file should not be tracked by git.
5 | //
6 | // You will need to set these on the server you deploy to.
7 |
8 | module.exports = {
9 | DOMAIN: 'http://localhost:9000',
10 | SESSION_SECRET: "<%= _.slugify(appname) + '-secret' %>",<% if (filters.facebookAuth) { %>
11 |
12 | FACEBOOK_ID: 'app-id',
13 | FACEBOOK_SECRET: 'secret',<% } if (filters.twitterAuth) { %>
14 |
15 | TWITTER_ID: 'app-id',
16 | TWITTER_SECRET: 'secret',<% } if (filters.googleAuth) { %>
17 |
18 | GOOGLE_ID: 'app-id',
19 | GOOGLE_SECRET: 'secret',
20 | <% } %>
21 | // Control debug level for modules using visionmedia/debug
22 | DEBUG: ''
23 | };
24 |
--------------------------------------------------------------------------------
/app/templates/client/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "node": true,
3 | "browser": true,
4 | "esnext": true,
5 | "bitwise": true,
6 | "camelcase": true,
7 | "curly": true,
8 | "eqeqeq": true,
9 | "immed": true,
10 | "indent": 2,
11 | "latedef": true,
12 | "newcap": true,
13 | "noarg": true,
14 | "quotmark": "single",
15 | "regexp": true,
16 | "undef": true,
17 | "unused": true,
18 | "strict": true,
19 | "trailing": true,
20 | "smarttabs": true,
21 | "globals": {
22 | "jQuery": true,
23 | "angular": true,
24 | "console": true,
25 | "$": true,
26 | "_": true,
27 | "moment": true,
28 | "describe": true,
29 | "beforeEach": true,
30 | "module": true,
31 | "inject": true,
32 | "it": true,
33 | "expect": true,
34 | "browser": true,
35 | "element": true,
36 | "by": true
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/app/templates/server/config/_local.env.sample.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Use local.env.js for environment variables that grunt will set when the server starts locally.
4 | // Use for your api keys, secrets, etc. This file should not be tracked by git.
5 | //
6 | // You will need to set these on the server you deploy to.
7 |
8 | module.exports = {
9 | DOMAIN: 'http://localhost:9000',
10 | SESSION_SECRET: '<%= _.slugify(appname) + "-secret" %>',<% if (filters.facebookAuth) { %>
11 |
12 | FACEBOOK_ID: 'app-id',
13 | FACEBOOK_SECRET: 'secret',<% } if (filters.twitterAuth) { %>
14 |
15 | TWITTER_ID: 'app-id',
16 | TWITTER_SECRET: 'secret',<% } if (filters.googleAuth) { %>
17 |
18 | GOOGLE_ID: 'app-id',
19 | GOOGLE_SECRET: 'secret',<% } %>
20 |
21 | // Control debug level for modules using visionmedia/debug
22 | DEBUG: ''
23 | };
24 |
--------------------------------------------------------------------------------
/app/templates/server/auth(auth)/local/passport.js:
--------------------------------------------------------------------------------
1 | var passport = require('passport');
2 | var LocalStrategy = require('passport-local').Strategy;
3 |
4 | exports.setup = function (User, config) {
5 | passport.use(new LocalStrategy({
6 | usernameField: 'email',
7 | passwordField: 'password' // this is the virtual field on the model
8 | },
9 | function(email, password, done) {
10 | User.findOne({
11 | email: email.toLowerCase()
12 | }, function(err, user) {
13 | if (err) return done(err);
14 |
15 | if (!user) {
16 | return done(null, false, { message: 'This email is not registered.' });
17 | }
18 | if (!user.authenticate(password)) {
19 | return done(null, false, { message: 'This password is not correct.' });
20 | }
21 | return done(null, user);
22 | });
23 | }
24 | ));
25 | };
--------------------------------------------------------------------------------
/script-base.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var util = require('util');
3 | var path = require('path');
4 | var yeoman = require('yeoman-generator');
5 | var angularUtils = require('./util.js');
6 |
7 | var Generator = module.exports = function Generator() {
8 | yeoman.generators.NamedBase.apply(this, arguments);
9 |
10 | try {
11 | this.appname = require(path.join(process.cwd(), 'bower.json')).name;
12 | } catch (e) {
13 | this.appname = path.basename(process.cwd());
14 | }
15 | this.appname = this._.slugify(this._.humanize(this.appname));
16 | this.scriptAppName = this._.camelize(this.appname) + angularUtils.appName(this);
17 |
18 | this.cameledName = this._.camelize(this.name);
19 | this.classedName = this._.classify(this.name);
20 |
21 | this.filters = this.config.get('filters');
22 | this.sourceRoot(path.join(__dirname, '/templates'));
23 | };
24 |
25 | util.inherits(Generator, yeoman.generators.NamedBase);
--------------------------------------------------------------------------------
/app/templates/client/app/main/main.controller.spec(coffee).coffee:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | describe 'Controller: MainCtrl', ->
4 |
5 | # load the controller's module
6 | beforeEach module '<%= scriptAppName %>' <% if(filters.socketio) {%>
7 | beforeEach module 'socketMock' <% } %>
8 |
9 | MainCtrl = undefined
10 | scope = undefined
11 | $httpBackend = undefined
12 |
13 | # Initialize the controller and a mock scope
14 | beforeEach inject (_$httpBackend_, $controller, $rootScope) ->
15 | $httpBackend = _$httpBackend_
16 | $httpBackend.expectGET('/api/things').respond [
17 | 'HTML5 Boilerplate'
18 | 'AngularJS'
19 | 'Karma'
20 | 'Express'
21 | ]
22 | scope = $rootScope.$new()
23 | MainCtrl = $controller 'MainCtrl',
24 | $scope: scope
25 |
26 | it 'should attach a list of things to the scope', ->
27 | $httpBackend.flush()
28 | expect(scope.awesomeThings.length).toBe 4
--------------------------------------------------------------------------------
/app/templates/client/app/account(auth)/login/login.controller(js).js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | angular.module('<%= scriptAppName %>')
4 | .controller('LoginCtrl', function ($scope, Auth, $location<% if (filters.oauth) { %>, $window<% } %>) {
5 | $scope.user = {};
6 | $scope.errors = {};
7 |
8 | $scope.login = function(form) {
9 | $scope.submitted = true;
10 |
11 | if(form.$valid) {
12 | Auth.login({
13 | email: $scope.user.email,
14 | password: $scope.user.password
15 | })
16 | .then( function() {
17 | // Logged in, redirect to home
18 | $location.path('/');
19 | })
20 | .catch( function(err) {
21 | $scope.errors.other = err.message;
22 | });
23 | }
24 | };
25 | <% if(filters.oauth) {%>
26 | $scope.loginOauth = function(provider) {
27 | $window.location.href = '/auth/' + provider;
28 | };<% } %>
29 | });
30 |
--------------------------------------------------------------------------------
/app/templates/client/app/app(sass).scss:
--------------------------------------------------------------------------------
1 | <% if(filters.bootstrap) { %>$icon-font-path: "/bower_components/bootstrap-sass-official/vendor/assets/fonts/bootstrap/";<% } %>
2 | $fa-font-path: "/bower_components/font-awesome/fonts";
3 | <% if(filters.bootstrap) { %>
4 | @import 'bootstrap-sass-official/vendor/assets/stylesheets/bootstrap';<% } %>
5 | @import 'font-awesome/scss/font-awesome';
6 |
7 | /**
8 | * App-wide Styles
9 | */
10 |
11 | .browsehappy {
12 | margin: 0.2em 0;
13 | background: #ccc;
14 | color: #000;
15 | padding: 0.2em 0;
16 | }
17 | <% if (!filters.bootstrap) { %>
18 | /* Responsive: Portrait tablets and up */
19 | @media screen and (min-width: 768px) {
20 | .container {
21 | max-width: 730px;
22 | }
23 | }
24 | <% } %>
25 | // Component styles are injected through grunt
26 | // injector
27 | @import 'account/login/login.scss';
28 | @import 'admin/admin.scss';
29 | @import 'main/main.scss';
30 | // endinjector
--------------------------------------------------------------------------------
/app/templates/server/config/environment/production.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Production specific configuration
4 | // =================================
5 | module.exports = {
6 | // Server IP
7 | ip: process.env.OPENSHIFT_NODEJS_IP ||
8 | process.env.IP ||
9 | undefined,
10 |
11 | // Server port
12 | port: process.env.OPENSHIFT_NODEJS_PORT ||
13 | process.env.PORT ||
14 | 8080,
15 |
16 | // MongoDB connection options
17 | mongo: {
18 | uri: process.env.MONGOLAB_URI ||
19 | process.env.MONGOHQ_URL ||
20 | process.env.OPENSHIFT_MONGODB_DB_URL+process.env.OPENSHIFT_APP_NAME ||
21 | 'mongodb://localhost/<%= _.slugify(appname) %>'
22 | },
23 |
24 | // Postgres connection options
25 | postgres: {
26 | uri: process.env.POSTGRES_URL ||
27 | 'postgres://user:pass@localhost:5432/<%= _.slugify(appname) %>'
28 | }
29 | };
30 |
--------------------------------------------------------------------------------
/app/templates/_bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "<%= _.slugify(_.humanize(appname)) %>",
3 | "version": "0.0.0",
4 | "dependencies": {
5 | "angular": ">=1.2.*",
6 | "json3": "~3.3.1",
7 | "es5-shim": "~3.0.1",<% if(filters.bootstrap) { %><% if (filters.sass) { %>
8 | "bootstrap-sass-official": "~3.1.1",<% } %>
9 | "bootstrap": "~3.1.1",<% } %>
10 | "angular-resource": ">=1.2.*",
11 | "angular-cookies": ">=1.2.*",
12 | "angular-sanitize": ">=1.2.*",<% if(filters.ngroute) { %>
13 | "angular-route": ">=1.2.*",<% } %><% if(filters.uibootstrap) { %>
14 | "angular-bootstrap": "~0.11.0",<% } %>
15 | "font-awesome": ">=4.1.0",
16 | "lodash": "~2.4.1"<% if(filters.socketio) { %>,
17 | "angular-socket-io": "~0.6.0"<% } %><% if(filters.uirouter) { %>,
18 | "angular-ui-router": "~0.2.10"<% } %>
19 | },
20 | "devDependencies": {
21 | "angular-mocks": ">=1.2.*",
22 | "angular-scenario": ">=1.2.*"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app/templates/client/app/main/main.controller.spec(js).js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('Controller: MainCtrl', function () {
4 | // load the controller's module
5 | beforeEach(module('<%= scriptAppName %>'));<% if(filters.socketio) {%>
6 | beforeEach(module('socketMock'));<% } %>
7 |
8 | var MainCtrl,
9 | scope,
10 | $httpBackend;
11 |
12 | // Initialize the controller and a mock scope
13 | beforeEach(inject(function (_$httpBackend_, $controller, $rootScope) {
14 | $httpBackend = _$httpBackend_;
15 | $httpBackend.expectGET('/api/things')
16 | .respond(['HTML5 Boilerplate', 'AngularJS', 'Karma', 'Express']);
17 |
18 | scope = $rootScope.$new();
19 | MainCtrl = $controller('MainCtrl', {
20 | $scope: scope
21 | });
22 | }));
23 |
24 | it('should attach a list of things to the scope', function () {
25 | $httpBackend.flush();
26 | expect(scope.awesomeThings.length).toBe(4);
27 | });
28 | });
29 |
--------------------------------------------------------------------------------
/app/templates/server/auth(auth)/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var express = require('express');
4 | var passport = require('passport');
5 | var config = require('../config/environment');
6 | var User = require('../api').User;
7 |
8 | // Passport Configuration
9 | require('./local/passport').setup(User, config);<% if (filters.facebookAuth) { %>
10 | require('./facebook/passport').setup(User, config);<% } %><% if (filters.googleAuth) { %>
11 | require('./google/passport').setup(User, config);<% } %><% if (filters.twitterAuth) { %>
12 | require('./twitter/passport').setup(User, config);<% } %>
13 |
14 | var router = express.Router();
15 |
16 | router.use('/local', require('./local'));<% if (filters.facebookAuth) { %>
17 | router.use('/facebook', require('./facebook'));<% } %><% if (filters.twitterAuth) { %>
18 | router.use('/twitter', require('./twitter'));<% } %><% if (filters.googleAuth) { %>
19 | router.use('/google', require('./google'));<% } %>
20 |
21 | module.exports = router;
--------------------------------------------------------------------------------
/app/templates/client/app/main/main.controller(js).js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | angular.module('<%= scriptAppName %>')
4 | .controller('MainCtrl', function ($scope, $http<% if(filters.socketio) { %>, socket<% } %>) {
5 | $scope.awesomeThings = [];
6 |
7 | $http.get('/api/things').success(function(awesomeThings) {
8 | $scope.awesomeThings = awesomeThings;<% if(filters.socketio) { %>
9 | socket.syncUpdates('thing', $scope.awesomeThings);<% } %>
10 | });
11 | <% if(filters.sql) { %>
12 | $scope.addThing = function() {
13 | if($scope.newThing === '') {
14 | return;
15 | }
16 | $http.post('/api/things', { name: $scope.newThing });
17 | $scope.newThing = '';
18 | };
19 |
20 | $scope.deleteThing = function(thing) {
21 | $http.delete('/api/things/' + thing._id);
22 | };<% } %><% if(filters.socketio) { %>
23 |
24 | $scope.$on('$destroy', function () {
25 | socket.unsyncUpdates('thing');
26 | });<% } %>
27 | });
28 |
--------------------------------------------------------------------------------
/app/templates/server/app.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Main application file
3 | */
4 |
5 | 'use strict';
6 |
7 | // Set default node environment to development
8 | process.env.NODE_ENV = process.env.NODE_ENV || 'development';
9 |
10 | var express = require('express');
11 | var config = require('./config/environment');
12 |
13 | if (config.seedDB) { require('./config/seed'); }
14 |
15 | // Setup server
16 | var app = express();
17 | var server = require('http').createServer(app);<% if (filters.socketio) { %>
18 | var socketio = require('socket.io')(server, {
19 | serveClient: config.env !== 'production',
20 | path: '/socket.io-client'
21 | });
22 | require('./config/socketio')(socketio);<% } %>
23 | require('./config/express')(app);
24 | require('./routes')(app);
25 |
26 | // Start server
27 | server.listen(config.port, config.ip, function () {
28 | console.log('Express server listening on %d, in %s mode', config.port, app.get('env'));
29 | });
30 |
31 | // Expose app
32 | exports = module.exports = app;
33 |
--------------------------------------------------------------------------------
/app/templates/server/api/index.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var fs = require("fs");
4 | var path = require("path");
5 | var Sequelize = require("sequelize");
6 | var env = process.env.NODE_ENV || "development";
7 | var config = require('../config/environment');
8 | var sequelize = new Sequelize(config.sql.database, config.sql.username, config.sql.password, config.sql);
9 | var db = {};
10 |
11 | fs
12 | .readdirSync(__dirname)
13 | .filter(function(file) {
14 | return (file.indexOf(".") !== 0) && (file !== "index.js");
15 | })
16 | .forEach(function(file) {
17 | file = '/' + file + '/' + file + '.model.js';
18 | var model = sequelize["import"](path.join(__dirname, file));
19 | db[model.name] = model;
20 | });
21 |
22 | Object.keys(db).forEach(function(modelName) {
23 | if ("associate" in db[modelName]) {
24 | db[modelName].associate(db);
25 | }
26 | });
27 |
28 | db.sequelize = sequelize;
29 | db.Sequelize = Sequelize;
30 |
31 | module.exports = db;
32 |
--------------------------------------------------------------------------------
/app/templates/client/app/account(auth)/settings/settings(jade).jade:
--------------------------------------------------------------------------------
1 | div(ng-include='"components/navbar/navbar.html"')
2 | .container
3 | .row
4 | .col-sm-12
5 | h1 Change Password
6 | .col-sm-12
7 | form.form(name='form', ng-submit='changePassword(form)', novalidate='')
8 | .form-group
9 | label Current Password
10 | input.form-control(type='password', name='password', ng-model='user.oldPassword', sql-error='')
11 | p.help-block(ng-show='form.password.$error.sql')
12 | | {{ errors.other }}
13 | .form-group
14 | label New Password
15 | input.form-control(type='password', name='newPassword', ng-model='user.newPassword', ng-minlength='3', required='')
16 | p.help-block(ng-show='(form.newPassword.$error.minlength || form.newPassword.$error.required) && (form.newPassword.$dirty || submitted)')
17 | | Password must be at least 3 characters.
18 |
19 | p.help-block {{ message }}
20 |
21 | button.btn.btn-lg.btn-primary(type='submit') Save changes
22 |
--------------------------------------------------------------------------------
/app/templates/client/app/account(auth)/signup/signup.controller(coffee).coffee:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | angular.module '<%= scriptAppName %>'
4 | .controller 'SignupCtrl', ($scope, Auth, $location<% if(filters.oauth) {%>, $window<% } %>) ->
5 | $scope.user = {}
6 | $scope.errors = {}
7 | $scope.register = (form) ->
8 | $scope.submitted = true
9 |
10 | if form.$valid
11 | # Account created, redirect to home
12 | Auth.createUser
13 | name: $scope.user.name
14 | email: $scope.user.email
15 | password: $scope.user.password
16 |
17 | .then ->
18 | $location.path '/'
19 |
20 | .catch (err) ->
21 | err = err.data
22 | $scope.errors = {}
23 |
24 | # Update validity of form fields that match the sql errors
25 | angular.forEach err.errors, (error, field) ->
26 | form[field].$setValidity 'sql', false
27 | $scope.errors[field] = error.message
28 | <% if(filters.oauth) {%>
29 | $scope.loginOauth = (provider) ->
30 | $window.location.href = '/auth/' + provider<% } %>
31 |
--------------------------------------------------------------------------------
/app/templates/client/app/account(auth)/account(coffee).coffee:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | angular.module '<%= scriptAppName %>'
4 | <% if(filters.ngroute) { %>.config ($routeProvider) ->
5 | $routeProvider
6 | .when '/login',
7 | templateUrl: 'app/account/login/login.html'
8 | controller: 'LoginCtrl'
9 |
10 | .when '/signup',
11 | templateUrl: 'app/account/signup/signup.html'
12 | controller: 'SignupCtrl'
13 |
14 | .when '/settings',
15 | templateUrl: 'app/account/settings/settings.html'
16 | controller: 'SettingsCtrl'
17 | authenticate: true
18 | <% } %><% if(filters.uirouter) { %>.config ($stateProvider) ->
19 | $stateProvider
20 | .state 'login',
21 | url: '/login'
22 | templateUrl: 'app/account/login/login.html'
23 | controller: 'LoginCtrl'
24 |
25 | .state 'signup',
26 | url: '/signup'
27 | templateUrl: 'app/account/signup/signup.html'
28 | controller: 'SignupCtrl'
29 |
30 | .state 'settings',
31 | url: '/settings'
32 | templateUrl: 'app/account/settings/settings.html'
33 | controller: 'SettingsCtrl'
34 | authenticate: true
35 | <% } %>
--------------------------------------------------------------------------------
/app/templates/server/auth(auth)/twitter(twitterAuth)/passport.js:
--------------------------------------------------------------------------------
1 | exports.setup = function (User, config) {
2 | var passport = require('passport');
3 | var TwitterStrategy = require('passport-twitter').Strategy;
4 |
5 | passport.use(new TwitterStrategy({
6 | consumerKey: config.twitter.clientID,
7 | consumerSecret: config.twitter.clientSecret,
8 | callbackURL: config.twitter.callbackURL
9 | },
10 | function(token, tokenSecret, profile, done) {
11 | User.findOne({
12 | 'twitter.id_str': profile.id
13 | }, function(err, user) {
14 | if (err) {
15 | return done(err);
16 | }
17 | if (!user) {
18 | user = new User({
19 | name: profile.displayName,
20 | username: profile.username,
21 | role: 'user',
22 | provider: 'twitter',
23 | twitter: profile._json
24 | });
25 | user.save(function(err) {
26 | if (err) return done(err);
27 | done(err, user);
28 | });
29 | } else {
30 | return done(err, user);
31 | }
32 | });
33 | }
34 | ));
35 | };
36 |
--------------------------------------------------------------------------------
/endpoint/templates/name.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var should = require('should');
4 | var app = require('../../app');
5 | var request = require('supertest');
6 | var <%= classedName %> = app.get('models').<%= classedName %>;
7 |
8 | describe('GET <%= route %>', function() {
9 |
10 | <%= classedName %>
11 | .sync()
12 | .then(function() {
13 | return <%= classedName %>.destroy({ where: {} });
14 | })
15 | .then(function() {
16 | <%= classedName %>
17 | .create({
18 | title: "Title",
19 | info: "Info",
20 | active: true,
21 | })
22 | .then(function(){
23 | it('should respond with JSON array', function(done) {
24 | request(app)
25 | .get('<%= route %>')
26 | .expect(200)
27 | .expect('Content-Type', /json/)
28 | .end(function(err, res) {
29 | if (err) return done(err);
30 | res.body.should.be.instanceof(Array);
31 | done();
32 | });
33 | });
34 | });
35 | });
36 |
37 | });
38 |
--------------------------------------------------------------------------------
/app/templates/server/auth(auth)/google(googleAuth)/passport.js:
--------------------------------------------------------------------------------
1 | var passport = require('passport');
2 | var GoogleStrategy = require('passport-google-oauth').OAuth2Strategy;
3 |
4 | exports.setup = function (User, config) {
5 | passport.use(new GoogleStrategy({
6 | clientID: config.google.clientID,
7 | clientSecret: config.google.clientSecret,
8 | callbackURL: config.google.callbackURL
9 | },
10 | function(accessToken, refreshToken, profile, done) {
11 | User.findOne({
12 | 'google.id': profile.id
13 | }, function(err, user) {
14 | if (!user) {
15 | user = new User({
16 | name: profile.displayName,
17 | email: profile.emails[0].value,
18 | role: 'user',
19 | username: profile.username,
20 | provider: 'google',
21 | google: profile._json
22 | });
23 | user.save(function(err) {
24 | if (err) return done(err);
25 | done(err, user);
26 | });
27 | } else {
28 | return done(err, user);
29 | }
30 | });
31 | }
32 | ));
33 | };
34 |
--------------------------------------------------------------------------------
/app/templates/server/auth(auth)/facebook(facebookAuth)/passport.js:
--------------------------------------------------------------------------------
1 | var passport = require('passport');
2 | var FacebookStrategy = require('passport-facebook').Strategy;
3 |
4 | exports.setup = function (User, config) {
5 | passport.use(new FacebookStrategy({
6 | clientID: config.facebook.clientID,
7 | clientSecret: config.facebook.clientSecret,
8 | callbackURL: config.facebook.callbackURL
9 | },
10 | function(accessToken, refreshToken, profile, done) {
11 | User.findOne({
12 | 'facebook.id': profile.id
13 | },
14 | function(err, user) {
15 | if (err) {
16 | return done(err);
17 | }
18 | if (!user) {
19 | user = new User({
20 | name: profile.displayName,
21 | email: profile.emails[0].value,
22 | role: 'user',
23 | username: profile.username,
24 | provider: 'facebook',
25 | facebook: profile._json
26 | });
27 | user.save(function(err) {
28 | if (err) return done(err);
29 | done(err, user);
30 | });
31 | } else {
32 | return done(err, user);
33 | }
34 | })
35 | }
36 | ));
37 | };
38 |
--------------------------------------------------------------------------------
/app/templates/client/app/account(auth)/signup/signup.controller(js).js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | angular.module('<%= scriptAppName %>')
4 | .controller('SignupCtrl', function ($scope, Auth, $location<% if (filters.oauth) { %>, $window<% } %>) {
5 | $scope.user = {};
6 | $scope.errors = {};
7 |
8 | $scope.register = function(form) {
9 | $scope.submitted = true;
10 |
11 | if(form.$valid) {
12 | Auth.createUser({
13 | name: $scope.user.name,
14 | email: $scope.user.email,
15 | password: $scope.user.password
16 | })
17 | .then( function() {
18 | // Account created, redirect to home
19 | $location.path('/');
20 | })
21 | .catch( function(err) {
22 | err = err.data;
23 | $scope.errors = {};
24 |
25 | // Update validity of form fields that match the sql errors
26 | angular.forEach(err.errors, function(error, field) {
27 | form[field].$setValidity('sql', false);
28 | $scope.errors[field] = error.message;
29 | });
30 | });
31 | }
32 | };
33 | <% if(filters.oauth) {%>
34 | $scope.loginOauth = function(provider) {
35 | $window.location.href = '/auth/' + provider;
36 | };<% } %>
37 | });
38 |
--------------------------------------------------------------------------------
/app/templates/client/app/account(auth)/account(js).js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | angular.module('<%= scriptAppName %>')
4 | <% if(filters.ngroute) { %>.config(function ($routeProvider) {
5 | $routeProvider
6 | .when('/login', {
7 | templateUrl: 'app/account/login/login.html',
8 | controller: 'LoginCtrl'
9 | })
10 | .when('/signup', {
11 | templateUrl: 'app/account/signup/signup.html',
12 | controller: 'SignupCtrl'
13 | })
14 | .when('/settings', {
15 | templateUrl: 'app/account/settings/settings.html',
16 | controller: 'SettingsCtrl',
17 | authenticate: true
18 | });
19 | });<% } %><% if(filters.uirouter) { %>.config(function ($stateProvider) {
20 | $stateProvider
21 | .state('login', {
22 | url: '/login',
23 | templateUrl: 'app/account/login/login.html',
24 | controller: 'LoginCtrl'
25 | })
26 | .state('signup', {
27 | url: '/signup',
28 | templateUrl: 'app/account/signup/signup.html',
29 | controller: 'SignupCtrl'
30 | })
31 | .state('settings', {
32 | url: '/settings',
33 | templateUrl: 'app/account/settings/settings.html',
34 | controller: 'SettingsCtrl',
35 | authenticate: true
36 | });
37 | });<% } %>
--------------------------------------------------------------------------------
/app/templates/client/app/main/main(jade).jade:
--------------------------------------------------------------------------------
1 | div(ng-include='"components/navbar/navbar.html"')
2 |
3 | header#banner.hero-unit
4 | .container
5 | h1 'Allo, 'Allo!
6 | p.lead Kick-start your next web app with Angular Fullstack
7 | img(src='assets/images/yeoman.png', alt='I\'m Yeoman')
8 |
9 | .container
10 | .row
11 | .col-lg-12
12 | h1.page-header Features:
13 | ul.nav.nav-tabs.nav-stacked.col-md-4.col-lg-4.col-sm-6(ng-repeat='thing in awesomeThings')
14 | li
15 | a(href='#', tooltip='{{thing.info}}')
16 | | {{thing.name}}<% if(filters.socketio) { %>
17 | button.close(type='button', ng-click='deleteThing(thing)') ×<% } %><% if(filters.socketio) { %>
18 |
19 | form.thing-form
20 | label Syncs in realtime across clients
21 | p.input-group
22 | input.form-control(type='text', placeholder='Add a new thing here.', ng-model='newThing')
23 | span.input-group-btn
24 | button.btn.btn-primary(type='submit', ng-click='addThing()') Add New<% } %>
25 |
26 | footer.footer
27 | .container
28 | p
29 | | Angular Fullstack v<%= pkg.version %>
30 | = ' | '
31 | a(href='https://twitter.com/tyhenkel') @tyhenkel
32 | = ' | '
33 | a(href='https://github.com/tashrafy/generator-sql-fullstack/issues?state=open') Issues
34 |
--------------------------------------------------------------------------------
/test/fixtures/.yo-rc.json:
--------------------------------------------------------------------------------
1 | {
2 | "generator-sql-fullstack": {
3 | "insertRoutes": true,
4 | "registerRoutesFile": "server/routes.js",
5 | "routesNeedle": "// Insert routes below",
6 | "routesBase": "/api/",
7 | "pluralizeRoutes": true,
8 | "insertSockets": true,
9 | "registerSocketsFile": "server/config/socketio.js",
10 | "socketsNeedle": "// Insert sockets below",
11 | "filters": {
12 | "js": true,
13 | "html": true,
14 | "sass": true,
15 | "uirouter": true,
16 | "bootstrap": true,
17 | "uibootstrap": true,
18 | "socketio": true,
19 | "pgsql": true,
20 | "sql": true,
21 | "auth": true
22 | }
23 | },
24 | "generator-ng-component": {
25 | "routeDirectory": "client/app/",
26 | "directiveDirectory": "client/app/",
27 | "filterDirectory": "client/app/",
28 | "serviceDirectory": "client/app/",
29 | "basePath": "client",
30 | "moduleName": "",
31 | "filters": [
32 | "uirouter"
33 | ],
34 | "extensions": [
35 | "js",
36 | "html",
37 | "scss"
38 | ],
39 | "directiveSimpleTemplates": "",
40 | "directiveComplexTemplates": "",
41 | "filterTemplates": "",
42 | "serviceTemplates": "",
43 | "factoryTemplates": "",
44 | "controllerTemplates": "",
45 | "decoratorTemplates": "",
46 | "providerTemplates": "",
47 | "routeTemplates": ""
48 | }
49 | }
--------------------------------------------------------------------------------
/app/templates/client/app/account(auth)/login/login(less).less:
--------------------------------------------------------------------------------
1 | <% if(filters.bootstrap) { %>// Colors
2 | // --------------------------------------------------
3 |
4 | @btnText: #fff;
5 | @btnTextAlt: #000;<% if (filters.oauth) { %>
6 | <% if (filters.facebookAuth) { %>
7 | @btnFacebookBackground: #3B5998;
8 | @btnFacebookBackgroundHighlight: #133783;<% } if (filters.twitterAuth) { %>
9 | @btnTwitterBackground: #2daddc;
10 | @btnTwitterBackgroundHighlight: #0271bf;<% } if (filters.googleAuth) { %>
11 | @btnGooglePlusBackground: #dd4b39;
12 | @btnGooglePlusBackgroundHighlight: #c53727;<% } %>
13 | @btnGithubBackground: #fafafa;
14 | @btnGithubBackgroundHighlight: #ccc;
15 |
16 | // Social buttons
17 | // --------------------------------------------------
18 | <% if (filters.facebookAuth) { %>
19 | .btn-facebook {
20 | .button-variant(@btnText; @btnFacebookBackgroundHighlight; @btnFacebookBackgroundHighlight);
21 | }<% } if (filters.twitterAuth) { %>
22 | .btn-twitter {
23 | .button-variant(@btnText; @btnTwitterBackground; @btnTwitterBackgroundHighlight);
24 | }<% } if (filters.googleAuth) { %>
25 | .btn-google-plus {
26 | .button-variant(@btnText; @btnGooglePlusBackground; @btnGooglePlusBackgroundHighlight);
27 | }<% } %>
28 | .btn-github {
29 | .button-variant(@btnTextAlt; @btnGithubBackground; @btnGithubBackgroundHighlight);
30 | }<% } %><% } %>
31 |
--------------------------------------------------------------------------------
/app/templates/client/app/account(auth)/login/login(sass).scss:
--------------------------------------------------------------------------------
1 | <% if(filters.bootstrap) { %>// Colors
2 | // --------------------------------------------------
3 |
4 | $btnText: #fff;
5 | $btnTextAlt: #000;<% if (filters.oauth) { %>
6 | <% if (filters.facebookAuth) { %>
7 | $btnFacebookBackground: #3B5998;
8 | $btnFacebookBackgroundHighlight: #133783;<% } if (filters.twitterAuth) { %>
9 | $btnTwitterBackground: #2daddc;
10 | $btnTwitterBackgroundHighlight: #0271bf;<% } if (filters.googleAuth) { %>
11 | $btnGooglePlusBackground: #dd4b39;
12 | $btnGooglePlusBackgroundHighlight: #c53727;<% } %>
13 | $btnGithubBackground: #fafafa;
14 | $btnGithubBackgroundHighlight: #ccc;
15 |
16 | // Social buttons
17 | // --------------------------------------------------
18 | <% if (filters.facebookAuth) { %>
19 | .btn-facebook {
20 | @include button-variant($btnText, $btnFacebookBackgroundHighlight, $btnFacebookBackgroundHighlight);
21 | }<% } if (filters.twitterAuth) { %>
22 | .btn-twitter {
23 | @include button-variant($btnText, $btnTwitterBackground, $btnTwitterBackgroundHighlight);
24 | }<% } if (filters.googleAuth) { %>
25 | .btn-google-plus {
26 | @include button-variant($btnText, $btnGooglePlusBackground, $btnGooglePlusBackgroundHighlight);
27 | }<% } %>
28 | .btn-github {
29 | @include button-variant($btnTextAlt, $btnGithubBackground, $btnGithubBackgroundHighlight);
30 | }<% } %><% } %>
31 |
--------------------------------------------------------------------------------
/app/templates/client/app/account(auth)/settings/settings(html).html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
Change Password
7 |
8 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/app/templates/client/components/navbar/navbar(jade).jade:
--------------------------------------------------------------------------------
1 | div.navbar.navbar-default.navbar-static-top(ng-controller='NavbarCtrl')
2 | div.container
3 | div.navbar-header
4 | button.navbar-toggle(type='button', ng-click='isCollapsed = !isCollapsed')
5 | span.sr-only Toggle navigation
6 | span.icon-bar
7 | span.icon-bar
8 | span.icon-bar
9 | a.navbar-brand(href='/') <%= _.slugify(_.humanize(appname)) %>
10 |
11 | div#navbar-main.navbar-collapse.collapse(collapse='isCollapsed')
12 | ul.nav.navbar-nav
13 | li(ng-repeat='item in menu', ng-class='{active: isActive(item.link)}')
14 | a(ng-href='{{item.link}}') {{item.title}}<% if(filters.auth) { %>
15 |
16 | li(ng-show='isAdmin()', ng-class='{active: isActive("/admin")}')
17 | a(href='/admin') Admin<% } %><% if(filters.auth) { %>
18 |
19 | ul.nav.navbar-nav.navbar-right
20 | li(ng-hide='isLoggedIn()', ng-class='{active: isActive("/signup")}')
21 | a(href='/signup') Sign up
22 |
23 | li(ng-hide='isLoggedIn()', ng-class='{active: isActive("/login")}')
24 | a(href='/login') Login
25 |
26 | li(ng-show='isLoggedIn()')
27 | p.navbar-text Hello {{ getCurrentUser().name }}
28 |
29 | li(ng-show='isLoggedIn()', ng-class='{active: isActive("/settings")}')
30 | a(href='/settings')
31 | span.glyphicon.glyphicon-cog
32 |
33 | li(ng-show='isLoggedIn()', ng-class='{active: isActive("/logout")}')
34 | a(href='', ng-click='logout()') Logout<% } %>
--------------------------------------------------------------------------------
/app/templates/client/app/main/main(html).html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
11 |
12 |
<% if(filters.socketio) { %>
20 |
21 |
<% } %>
30 |
31 |
32 |
39 |
--------------------------------------------------------------------------------
/app/templates/client/app/app(coffee).coffee:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | angular.module '<%= scriptAppName %>', [<%= angularModules %>]
4 | <% if(filters.ngroute) { %>.config ($routeProvider, $locationProvider<% if(filters.auth) { %>, $httpProvider<% } %>) ->
5 | $routeProvider
6 | .otherwise
7 | redirectTo: '/'
8 |
9 | $locationProvider.html5Mode true<% if(filters.auth) { %>
10 | $httpProvider.interceptors.push 'authInterceptor'<% } %>
11 | <% } %><% if(filters.uirouter) { %>.config ($stateProvider, $urlRouterProvider, $locationProvider<% if(filters.auth) { %>, $httpProvider<% } %>) ->
12 | $urlRouterProvider
13 | .otherwise '/'
14 |
15 | $locationProvider.html5Mode true<% if(filters.auth) { %>
16 | $httpProvider.interceptors.push 'authInterceptor'<% } %>
17 | <% } %><% if(filters.auth) { %>
18 | .factory 'authInterceptor', ($rootScope, $q, $cookieStore, $location) ->
19 | # Add authorization token to headers
20 | request: (config) ->
21 | config.headers = config.headers or {}
22 | config.headers.Authorization = 'Bearer ' + $cookieStore.get 'token' if $cookieStore.get 'token'
23 | config
24 |
25 | # Intercept 401s and redirect you to login
26 | responseError: (response) ->
27 | if response.status is 401
28 | $location.path '/login'
29 | # remove any stale tokens
30 | $cookieStore.remove 'token'
31 |
32 | $q.reject response
33 |
34 | .run ($rootScope, $location, Auth) ->
35 | # Redirect to login if route requires auth and you're not logged in
36 | $rootScope.$on <% if(filters.ngroute) { %>'$routeChangeStart'<% } %><% if(filters.uirouter) { %>'$stateChangeStart'<% } %>, (event, next) ->
37 | Auth.isLoggedInAsync (loggedIn) ->
38 | $location.path "/login" if next.authenticate and not loggedIn
39 | <% } %>
--------------------------------------------------------------------------------
/app/templates/client/components/navbar/navbar(html).html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
12 |
13 |
<% if(filters.auth) { %>
19 |
20 |
<% } %>
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/app/templates/client/app/app(css).css:
--------------------------------------------------------------------------------
1 | <% if(filters.bootstrap) { %>
2 | /**
3 | * Bootstrap Fonts
4 | */
5 |
6 | @font-face {
7 | font-family: 'Glyphicons Halflings';
8 | src: url('../bower_components/bootstrap/fonts/glyphicons-halflings-regular.eot');
9 | src: url('../bower_components/bootstrap/fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'),
10 | url('../bower_components/bootstrap/fonts/glyphicons-halflings-regular.woff') format('woff'),
11 | url('../bower_components/bootstrap/fonts/glyphicons-halflings-regular.ttf') format('truetype'),
12 | url('../bower_components/bootstrap/fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg');
13 | }<% } %>
14 |
15 | /**
16 | *Font Awesome Fonts
17 | */
18 |
19 | @font-face {
20 | font-family: 'FontAwesome';
21 | src: url('../bower_components/font-awesome/fonts/fontawesome-webfont.eot?v=4.1.0');
22 | src: url('../bower_components/font-awesome/fonts/fontawesome-webfont.eot?#iefix&v=4.1.0') format('embedded-opentype'),
23 | url('../bower_components/font-awesome/fonts/fontawesome-webfont.woff?v=4.1.0') format('woff'),
24 | url('../bower_components/font-awesome/fonts/fontawesome-webfont.ttf?v=4.1.0') format('truetype'),
25 | url('../bower_components/font-awesome/fonts/fontawesome-webfont.svg?v=4.1.0#fontawesomeregular') format('svg');
26 | font-weight: normal;
27 | font-style: normal;
28 | }
29 |
30 | /**
31 | * App-wide Styles
32 | */
33 |
34 | .browsehappy {
35 | margin: 0.2em 0;
36 | background: #ccc;
37 | color: #000;
38 | padding: 0.2em 0;
39 | }
40 | <% if (!filters.bootstrap) { %>
41 | /* Responsive: Portrait tablets and up */
42 | @media screen and (min-width: 768px) {
43 | .container {
44 | max-width: 730px;
45 | }
46 | }<% } %>
--------------------------------------------------------------------------------
/app/templates/protractor.conf.js:
--------------------------------------------------------------------------------
1 | // Protractor configuration
2 | // https://github.com/angular/protractor/blob/master/referenceConf.js
3 |
4 | 'use strict';
5 |
6 | exports.config = {
7 | // The timeout for each script run on the browser. This should be longer
8 | // than the maximum time your application needs to stabilize between tasks.
9 | allScriptsTimeout: 110000,
10 |
11 | // A base URL for your application under test. Calls to protractor.get()
12 | // with relative paths will be prepended with this.
13 | baseUrl: 'http://localhost:' + (process.env.PORT || '9000'),
14 |
15 | // If true, only chromedriver will be started, not a standalone selenium.
16 | // Tests for browsers other than chrome will not run.
17 | chromeOnly: true,
18 |
19 | // list of files / patterns to load in the browser
20 | specs: [
21 | 'e2e/**/*.spec.js'
22 | ],
23 |
24 | // Patterns to exclude.
25 | exclude: [],
26 |
27 | // ----- Capabilities to be passed to the webdriver instance ----
28 | //
29 | // For a full list of available capabilities, see
30 | // https://code.google.com/p/selenium/wiki/DesiredCapabilities
31 | // and
32 | // https://code.google.com/p/selenium/source/browse/javascript/webdriver/capabilities.js
33 | capabilities: {
34 | 'browserName': 'chrome'
35 | },
36 |
37 | // ----- The test framework -----
38 | //
39 | // Jasmine and Cucumber are fully supported as a test and assertion framework.
40 | // Mocha has limited beta support. You will need to include your own
41 | // assertion framework if working with mocha.
42 | framework: 'jasmine',
43 |
44 | // ----- Options to be passed to minijasminenode -----
45 | //
46 | // See the full list at https://github.com/juliemr/minijasminenode
47 | jasmineNodeOpts: {
48 | defaultTimeoutInterval: 30000
49 | }
50 | };
51 |
--------------------------------------------------------------------------------
/app/templates/server/config/socketio(socketio).js:
--------------------------------------------------------------------------------
1 | /**
2 | * Socket.io configuration
3 | */
4 |
5 | 'use strict';
6 |
7 | var config = require('./environment');
8 |
9 | // When the user disconnects.. perform this
10 | function onDisconnect(socket) {
11 | }
12 |
13 | // When the user connects.. perform this
14 | function onConnect(socket) {
15 | // When the client emits 'info', this listens and executes
16 | socket.on('info', function (data) {
17 | console.info('[%s] %s', socket.address, JSON.stringify(data, null, 2));
18 | });
19 |
20 | // Insert sockets below
21 | require('../api/thing/thing.socket').register(socket);
22 | }
23 |
24 | module.exports = function (socketio) {
25 | // socket.io (v1.x.x) is powered by debug.
26 | // In order to see all the debug output, set DEBUG (in server/config/local.env.js) to including the desired scope.
27 | //
28 | // ex: DEBUG: "http*,socket.io:socket"
29 |
30 | // We can authenticate socket.io users and access their token through socket.handshake.decoded_token
31 | //
32 | // 1. You will need to send the token in `client/components/socket/socket.service.js`
33 | //
34 | // 2. Require authentication here:
35 | // socketio.use(require('socketio-jwt').authorize({
36 | // secret: config.secrets.session,
37 | // handshake: true
38 | // }));
39 |
40 | socketio.on('connection', function (socket) {
41 | socket.address = socket.handshake.address !== null ?
42 | socket.handshake.address.address + ':' + socket.handshake.address.port :
43 | process.env.DOMAIN;
44 |
45 | socket.connectedAt = new Date();
46 |
47 | // Call onDisconnect.
48 | socket.on('disconnect', function () {
49 | onDisconnect(socket);
50 | console.info('[%s] DISCONNECTED', socket.address);
51 | });
52 |
53 | // Call onConnect.
54 | onConnect(socket);
55 | console.info('[%s] CONNECTED', socket.address);
56 | });
57 | };
--------------------------------------------------------------------------------
/app/templates/client/app/app(stylus).styl:
--------------------------------------------------------------------------------
1 | @import "font-awesome/css/font-awesome.css"
2 | <% if(filters.bootstrap) { %>@import "bootstrap/dist/css/bootstrap.css"
3 |
4 | //
5 | // Bootstrap Fonts
6 | //
7 |
8 | @font-face
9 | font-family: 'Glyphicons Halflings'
10 | src: url('../bower_components/bootstrap/fonts/glyphicons-halflings-regular.eot')
11 | src: url('../bower_components/bootstrap/fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'),
12 | url('../bower_components/bootstrap/fonts/glyphicons-halflings-regular.woff') format('woff'),
13 | url('../bower_components/bootstrap/fonts/glyphicons-halflings-regular.ttf') format('truetype'),
14 | url('../bower_components/bootstrap/fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg');
15 | <% } %>
16 | //
17 | // Font Awesome Fonts
18 | //
19 |
20 | @font-face
21 | font-family: 'FontAwesome'
22 | src: url('../bower_components/font-awesome/fonts/fontawesome-webfont.eot?v=4.1.0')
23 | src: url('../bower_components/font-awesome/fonts/fontawesome-webfont.eot?#iefix&v=4.1.0') format('embedded-opentype'),
24 | url('../bower_components/font-awesome/fonts/fontawesome-webfont.woff?v=4.1.0') format('woff'),
25 | url('../bower_components/font-awesome/fonts/fontawesome-webfont.ttf?v=4.1.0') format('truetype'),
26 | url('../bower_components/font-awesome/fonts/fontawesome-webfont.svg?v=4.1.0#fontawesomeregular') format('svg');
27 | font-weight: normal
28 | font-style: normal
29 |
30 | //
31 | // App-wide Styles
32 | //
33 |
34 | .browsehappy
35 | background #ccc
36 | color #000
37 | margin 0.2em 0
38 | padding 0.2em 0
39 | <% if (!filters.bootstrap) { %>
40 | // Responsive: Portrait tablets and up
41 | @media screen and (min-width: 768px)
42 | .container
43 | max-width 730px
44 | <% } %>
45 | // Component styles are injected through grunt
46 | // injector
47 | @import "account/login/login"
48 | @import "admin/admin"
49 | @import "main/main"
50 | // endinjector
--------------------------------------------------------------------------------
/app/templates/client/components/socket(socketio)/socket.service(coffee).coffee:
--------------------------------------------------------------------------------
1 | # global io
2 |
3 | 'use strict'
4 |
5 | angular.module '<%= scriptAppName %>'
6 | .factory 'socket', (socketFactory) ->
7 |
8 | # socket.io now auto-configures its connection when we omit a connection url
9 | ioSocket = io '',
10 | # Send auth token on connection, you will need to DI the Auth service above
11 | # 'query': 'token=' + Auth.getToken()
12 | path: '/socket.io-client'
13 |
14 | socket = socketFactory ioSocket: ioSocket
15 |
16 | socket: socket
17 |
18 | ###
19 | Register listeners to sync an array with updates on a model
20 |
21 | Takes the array we want to sync, the model name that socket updates are sent from,
22 | and an optional callback function after new items are updated.
23 |
24 | @param {String} modelName
25 | @param {Array} array
26 | @param {Function} callback
27 | ###
28 | syncUpdates: (modelName, array, callback) ->
29 |
30 | ###
31 | Syncs item creation/updates on 'model:save'
32 | ###
33 | socket.on modelName + ':save', (item) ->
34 | oldItem = _.find array,
35 | _id: item._id
36 |
37 | index = array.indexOf oldItem
38 | event = 'created'
39 |
40 | # replace oldItem if it exists
41 | # otherwise just add item to the collection
42 | if oldItem
43 | array.splice index, 1, item
44 | event = 'updated'
45 | else
46 | array.push item
47 |
48 | callback? event, item, array
49 |
50 | ###
51 | Syncs removed items on 'model:remove'
52 | ###
53 | socket.on modelName + ':remove', (item) ->
54 | event = 'deleted'
55 | _.remove array,
56 | _id: item._id
57 |
58 | callback? event, item, array
59 |
60 | ###
61 | Removes listeners for a models updates on the socket
62 |
63 | @param modelName
64 | ###
65 | unsyncUpdates: (modelName) ->
66 | socket.removeAllListeners modelName + ':save'
67 | socket.removeAllListeners modelName + ':remove'
68 |
--------------------------------------------------------------------------------
/app/templates/client/app/account(auth)/login/login(jade).jade:
--------------------------------------------------------------------------------
1 | div(ng-include='"components/navbar/navbar.html"')
2 | .container
3 | .row
4 | .col-sm-12
5 | h1 Login
6 | p
7 | | Accounts are reset on server restart from
8 | code server/config/seed.js
9 | | . Default account is
10 | code test@test.com
11 | | /
12 | code test
13 | p
14 | | Admin account is
15 | code admin@admin.com
16 | | /
17 | code admin
18 |
19 | .col-sm-12
20 | form.form(name='form', ng-submit='login(form)', novalidate='')
21 | .form-group
22 | label Email
23 | input.form-control(type='text', name='email', ng-model='user.email')
24 | .form-group
25 | label Password
26 | input.form-control(type='password', name='password', ng-model='user.password')
27 |
28 | .form-group.has-error
29 | p.help-block(ng-show='form.email.$error.required && form.password.$error.required && submitted')
30 | | Please enter your email and password.
31 | p.help-block {{ errors.other }}
32 |
33 | div
34 | button.btn.btn-inverse.btn-lg.btn-login(type='submit')
35 | | Login
36 | = ' '
37 | a.btn.btn-default.btn-lg.btn-register(href='/signup')
38 | | Register
39 | <% if(filters.oauth) {%>
40 | hr
41 |
42 | div<% if(filters.facebookAuth) {%>
43 | a.btn.btn-facebook(href='', ng-click='loginOauth("facebook")')
44 | i.fa.fa-facebook
45 | | Connect with Facebook
46 | = ' '<% } %><% if(filters.googleAuth) {%>
47 | a.btn.btn-google-plus(href='', ng-click='loginOauth("google")')
48 | i.fa.fa-google-plus
49 | | Connect with Google+
50 | = ' '<% } %><% if(filters.twitterAuth) {%>
51 | a.btn.btn-twitter(href='', ng-click='loginOauth("twitter")')
52 | i.fa.fa-twitter
53 | | Connect with Twitter<% } %><% } %>
54 | hr
55 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "generator-sql-fullstack",
3 | "version": "1.0.1",
4 | "description": "Yeoman generator for creating SQL-EAN stack applications, using SQL(e.g. Postgres, MySQL, MariaDB, SQLite, MSSQL), Express, AngularJS, and Node",
5 | "keywords": [
6 | "yeoman-generator",
7 | "pean",
8 | "sean",
9 | "mssql",
10 | "mariadb",
11 | "mariasql",
12 | "sqlite",
13 | "sqlite3",
14 | "node-sqlite3",
15 | "node-postgres",
16 | "node-mariadb",
17 | "node-mssql",
18 | "sql",
19 | "sql-fullstack",
20 | "postgres",
21 | "angularjs",
22 | "express",
23 | "scaffold",
24 | "fullstack",
25 | "framework",
26 | "component",
27 | "front-end",
28 | "app"
29 | ],
30 | "homepage": "https://github.com/tashrafy/generator-sql-fullstack",
31 | "bugs": "https://github.com/tashrafy/generator-sql-fullstack/issues",
32 | "author": "Taufiqur Ashrafy",
33 | "repository": {
34 | "type": "git",
35 | "url": "git://github.com/tashrafy/generator-sql-fullstack.git"
36 | },
37 | "scripts": {
38 | "test": "grunt test"
39 | },
40 | "dependencies": {
41 | "yeoman-generator": "~0.17.0",
42 | "chalk": "~0.4.0",
43 | "wiredep": "~0.4.2",
44 | "generator-ng-component": "~0.0.4"
45 | },
46 | "peerDependencies": {
47 | "yo": ">=1.2.0"
48 | },
49 | "devDependencies": {
50 | "chai": "^1.9.1",
51 | "fs-extra": "^0.9.1",
52 | "grunt": "~0.4.1",
53 | "grunt-build-control": "DaftMonk/grunt-build-control",
54 | "grunt-contrib-clean": "^0.6.0",
55 | "grunt-contrib-jshint": "^0.10.0",
56 | "grunt-conventional-changelog": "~1.0.0",
57 | "grunt-mocha-test": "^0.11.0",
58 | "grunt-release": "~0.6.0",
59 | "load-grunt-tasks": "~0.2.0",
60 | "marked": "~0.2.8",
61 | "mocha": "~1.21.0",
62 | "q": "^1.0.1",
63 | "semver": "~2.2.1",
64 | "shelljs": "^0.3.0",
65 | "underscore.string": "^2.3.3"
66 | },
67 | "engines": {
68 | "node": ">=0.10.0",
69 | "npm": ">=1.2.10"
70 | },
71 | "licenses": [
72 | {
73 | "type": "BSD"
74 | }
75 | ]
76 | }
77 |
--------------------------------------------------------------------------------
/app/templates/server/api/user(auth)/user.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var should = require('should');
4 | var app = require('../../app');
5 | var User = require('../').User;
6 |
7 | var user;
8 |
9 | var genUser = function() {
10 | user = User.build({
11 | provider: 'local',
12 | name: 'Fake User',
13 | email: 'test@example.com',
14 | password: 'password'
15 | });
16 | return user;
17 | };
18 |
19 | describe('User Model', function() {
20 | before(function(done) {
21 | genUser();
22 | done();
23 | });
24 |
25 | afterEach(function() {
26 | return User.destroy({ where: {} });
27 | });
28 |
29 | it('should begin with no users', function(done) {
30 | return User
31 | .findAll({ where: {} })
32 | .then(function(users) {
33 | console.log("------------------------------------------------------");
34 | users.should.have.length(0);
35 | done();
36 | })
37 | .catch(function(err){
38 | console.log(err, '-------------------------------');
39 | });
40 | });
41 |
42 | it('should fail when saving a duplicate user', function(done) {
43 | var newUser = genUser();
44 | newUser.save().catch(function(err){
45 | should.exist(err);
46 | });
47 | });
48 |
49 | it('should fail when saving without an email', function(done) {
50 | user.email = '';
51 | user.save()
52 | .catch(function(err){
53 | should.exist(err);
54 | done();
55 | });
56 | });
57 |
58 | it("should authenticate user if password is valid", function(done) {
59 | User
60 | .create(user)
61 | .then(function(userInstance){
62 | var check = userInstance.authenticate('password');
63 | check.should.be.true; // jshint ignore:line
64 | done();
65 | });
66 | });
67 |
68 | it("should not authenticate user if password is invalid", function(done) {
69 | User
70 | .create(user)
71 | .then(function(userInstance){
72 | var check = userInstance.authenticate('blah');
73 | check.should.not.be.true; // jshint ignore:line
74 | done();
75 | });
76 | });
77 | });
78 |
--------------------------------------------------------------------------------
/app/templates/client/app/app(js).js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | angular.module('<%= scriptAppName %>', [<%= angularModules %>])
4 | <% if(filters.ngroute) { %>.config(function ($routeProvider, $locationProvider<% if(filters.auth) { %>, $httpProvider<% } %>) {
5 | $routeProvider
6 | .otherwise({
7 | redirectTo: '/'
8 | });
9 |
10 | $locationProvider.html5Mode(true);<% if(filters.auth) { %>
11 | $httpProvider.interceptors.push('authInterceptor');<% } %>
12 | })<% } %><% if(filters.uirouter) { %>.config(function ($stateProvider, $urlRouterProvider, $locationProvider<% if(filters.auth) { %>, $httpProvider<% } %>) {
13 | $urlRouterProvider
14 | .otherwise('/');
15 |
16 | $locationProvider.html5Mode(true);<% if(filters.auth) { %>
17 | $httpProvider.interceptors.push('authInterceptor');<% } %>
18 | })<% } %><% if(filters.auth) { %>
19 |
20 | .factory('authInterceptor', function ($rootScope, $q, $cookieStore, $location) {
21 | return {
22 | // Add authorization token to headers
23 | request: function (config) {
24 | config.headers = config.headers || {};
25 | if ($cookieStore.get('token')) {
26 | config.headers.Authorization = 'Bearer ' + $cookieStore.get('token');
27 | }
28 | return config;
29 | },
30 |
31 | // Intercept 401s and redirect you to login
32 | responseError: function(response) {
33 | if(response.status === 401) {
34 | $location.path('/login');
35 | // remove any stale tokens
36 | $cookieStore.remove('token');
37 | return $q.reject(response);
38 | }
39 | else {
40 | return $q.reject(response);
41 | }
42 | }
43 | };
44 | })
45 |
46 | .run(function ($rootScope, $location, Auth) {
47 | // Redirect to login if route requires auth and you're not logged in
48 | $rootScope.$on(<% if(filters.ngroute) { %>'$routeChangeStart'<% } %><% if(filters.uirouter) { %>'$stateChangeStart'<% } %>, function (event, next) {
49 | Auth.isLoggedInAsync(function(loggedIn) {
50 | if (next.authenticate && !loggedIn) {
51 | $location.path('/login');
52 | }
53 | });
54 | });
55 | })<% } %>;
--------------------------------------------------------------------------------
/app/templates/server/config/seed(sql).js:
--------------------------------------------------------------------------------
1 | /**
2 | * Populate DB with sample data on server start
3 | * to disable, edit config/environment/index.js, and set `seedDB: false`
4 | */
5 |
6 | 'use strict';
7 |
8 | var Thing = require('../api').Thing;
9 | <% if (filters.auth) { %>var User = require('../api').User;<% } %>
10 |
11 | Thing
12 | .sync()
13 | .then(function() {
14 | return Thing.destroy({ where: {} });
15 | })
16 | .then(function() {
17 | Thing.bulkCreate([{
18 | name: 'Development Tools',
19 | info: 'Integration with popular tools such as Bower, Grunt, Babel, Karma, ' +
20 | 'Mocha, JSHint, Node Inspector, Livereload, Protractor, Jade, ' +
21 | 'Stylus, Sass, and Less.'
22 | }, {
23 | name: 'Server and Client integration',
24 | info: 'Built with a powerful and fun stack: MongoDB, Express, ' +
25 | 'AngularJS, and Node.'
26 | }, {
27 | name: 'Smart Build System',
28 | info: 'Build system ignores `spec` files, allowing you to keep ' +
29 | 'tests alongside code. Automatic injection of scripts and ' +
30 | 'styles into your index.html'
31 | }, {
32 | name: 'Modular Structure',
33 | info: 'Best practice client and server structures allow for more ' +
34 | 'code reusability and maximum scalability'
35 | }, {
36 | name: 'Optimized Build',
37 | info: 'Build process packs up your templates as a single JavaScript ' +
38 | 'payload, minifies your scripts/css/images, and rewrites asset ' +
39 | 'names for caching.'
40 | }, {
41 | name: 'Deployment Ready',
42 | info: 'Easily deploy your app to Heroku or Openshift with the heroku ' +
43 | 'and openshift subgenerators'
44 | }]);
45 | });<% if (filters.auth) { %>
46 |
47 | User.sync()
48 | .then(function() {
49 | return User.destroy({ where: {} });
50 | })
51 | .then(function() {
52 | User.bulkCreate([{
53 | username: 'User',
54 | email: 'test@example.com',
55 | password: 'test',
56 | provider: 'local'
57 | }, {
58 | username: ' AdminUser',
59 | email: 'admin@example.com',
60 | provider: 'local',
61 | password: 'admin'
62 | }])
63 | .then(function() {
64 | console.log('finished populating users');
65 | });
66 | });
67 | <% } %>
68 |
--------------------------------------------------------------------------------
/endpoint/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var path = require('path');
3 | var yeoman = require('yeoman-generator');
4 | var util = require('util');
5 | var ngUtil = require('../util');
6 | var ScriptBase = require('../script-base.js');
7 |
8 | var Generator = module.exports = function Generator() {
9 | ScriptBase.apply(this, arguments);
10 | };
11 |
12 | util.inherits(Generator, ScriptBase);
13 |
14 | Generator.prototype.askFor = function askFor() {
15 | var done = this.async();
16 | var name = this.name;
17 |
18 | var base = this.config.get('routesBase') || '/api/';
19 | if(base.charAt(base.length-1) !== '/') {
20 | base = base + '/';
21 | }
22 |
23 | // pluralization defaults to true for backwards compat
24 | if (this.config.get('pluralizeRoutes') !== false) {
25 | name = name + 's';
26 | }
27 |
28 | var prompts = [
29 | {
30 | name: 'route',
31 | message: 'What will the url of your endpoint be?',
32 | default: base + name
33 | }
34 | ];
35 |
36 | this.prompt(prompts, function (props) {
37 | if(props.route.charAt(0) !== '/') {
38 | props.route = '/' + props.route;
39 | }
40 |
41 | this.route = props.route;
42 | done();
43 | }.bind(this));
44 | };
45 |
46 | Generator.prototype.registerEndpoint = function registerEndpoint() {
47 | if(this.config.get('insertRoutes')) {
48 | var routeConfig = {
49 | file: this.config.get('registerRoutesFile'),
50 | needle: this.config.get('routesNeedle'),
51 | splicable: [
52 | "app.use(\'" + this.route +"\', require(\'./api/" + this.name + "\'));"
53 | ]
54 | };
55 | ngUtil.rewriteFile(routeConfig);
56 | }
57 |
58 | if (this.filters.socketio) {
59 | if(this.config.get('insertSockets')) {
60 | var socketConfig = {
61 | file: this.config.get('registerSocketsFile'),
62 | needle: this.config.get('socketsNeedle'),
63 | splicable: [
64 | "require(\'../api/" + this.name + '/' + this.name + ".socket\').register(socket);"
65 | ]
66 | };
67 | ngUtil.rewriteFile(socketConfig);
68 | }
69 | }
70 | };
71 |
72 | Generator.prototype.createFiles = function createFiles() {
73 | var dest = this.config.get('endpointDirectory') || 'server/api/' + this.name;
74 | this.sourceRoot(path.join(__dirname, './templates'));
75 | ngUtil.processDirectory(this, '.', dest);
76 | };
77 |
--------------------------------------------------------------------------------
/app/templates/client/components/socket(socketio)/socket.service.js:
--------------------------------------------------------------------------------
1 | /* global io */
2 | 'use strict';
3 |
4 | angular.module('<%= scriptAppName %>')
5 | .factory('socket', function(socketFactory) {
6 |
7 | // socket.io now auto-configures its connection when we ommit a connection url
8 | var ioSocket = io('', {
9 | // Send auth token on connection, you will need to DI the Auth service above
10 | // 'query': 'token=' + Auth.getToken()
11 | path: '/socket.io-client'
12 | });
13 |
14 | var socket = socketFactory({
15 | ioSocket: ioSocket
16 | });
17 |
18 | return {
19 | socket: socket,
20 |
21 | /**
22 | * Register listeners to sync an array with updates on a model
23 | *
24 | * Takes the array we want to sync, the model name that socket updates are sent from,
25 | * and an optional callback function after new items are updated.
26 | *
27 | * @param {String} modelName
28 | * @param {Array} array
29 | * @param {Function} cb
30 | */
31 | syncUpdates: function (modelName, array, cb) {
32 | cb = cb || angular.noop;
33 |
34 | /**
35 | * Syncs item creation/updates on 'model:save'
36 | */
37 | socket.on(modelName + ':save', function (item) {
38 | var oldItem = _.find(array, {_id: item._id});
39 | var index = array.indexOf(oldItem);
40 | var event = 'created';
41 |
42 | // replace oldItem if it exists
43 | // otherwise just add item to the collection
44 | if (oldItem) {
45 | array.splice(index, 1, item);
46 | event = 'updated';
47 | } else {
48 | array.push(item);
49 | }
50 |
51 | cb(event, item, array);
52 | });
53 |
54 | /**
55 | * Syncs removed items on 'model:remove'
56 | */
57 | socket.on(modelName + ':remove', function (item) {
58 | var event = 'deleted';
59 | _.remove(array, {_id: item._id});
60 | cb(event, item, array);
61 | });
62 | },
63 |
64 | /**
65 | * Removes listeners for a models updates on the socket
66 | *
67 | * @param modelName
68 | */
69 | unsyncUpdates: function (modelName) {
70 | socket.removeAllListeners(modelName + ':save');
71 | socket.removeAllListeners(modelName + ':remove');
72 | }
73 | };
74 | });
75 |
--------------------------------------------------------------------------------
/app/templates/server/config/express.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Express configuration
3 | */
4 |
5 | 'use strict';
6 |
7 | var express = require('express');
8 | var favicon = require('serve-favicon');
9 | var morgan = require('morgan');
10 | var compression = require('compression');
11 | var bodyParser = require('body-parser');
12 | var methodOverride = require('method-override');
13 | var cookieParser = require('cookie-parser');
14 | var errorHandler = require('errorhandler');
15 | var path = require('path');
16 | var config = require('./environment');<% if (filters.auth) { %>
17 | var passport = require('passport');<% } %><% if (filters.twitterAuth) { %>
18 | var session = require('express-session');
19 | var pgStore = require('connect-pg-simple')(session); <% } %>
20 |
21 | module.exports = function(app) {
22 | var env = app.get('env');
23 |
24 | app.set('views', config.root + '/server/views');<% if (filters.html) { %>
25 | app.engine('html', require('ejs').renderFile);
26 | app.set('view engine', 'html');<% } %><% if (filters.jade) { %>
27 | app.set('view engine', 'jade');<% } %>
28 | app.set('models', require('../api'));
29 | app.use(compression());
30 | app.use(bodyParser.urlencoded({ extended: false }));
31 | app.use(bodyParser.json());
32 | app.use(methodOverride());
33 | app.use(cookieParser());
34 | <% if (filters.auth) { %>app.use(passport.initialize());<% } %><% if (filters.twitterAuth) { %>
35 |
36 | // Persist sessions with pgStore
37 | // We need to enable sessions for passport twitter because its an oauth 1.0 strategy
38 | app.use(session({
39 | secret: config.secrets.session,
40 | resave: true,
41 | saveUninitialized: true,
42 | store: new pgStore(),
43 | cookie: { maxAge: 30 * 24 * 60 * 60 * 1000 } // 30 days
44 | }));
45 | <% } %>
46 | if ('production' === env) {
47 | app.use(favicon(path.join(config.root, 'public', 'favicon.ico')));
48 | app.use(express.static(path.join(config.root, 'public')));
49 | app.set('appPath', path.join(config.root, 'public'));
50 | app.use(morgan('dev'));
51 | }
52 |
53 | if ('development' === env || 'test' === env) {
54 | app.use(require('connect-livereload')());
55 | app.use(express.static(path.join(config.root, '.tmp')));
56 | app.use(express.static(path.join(config.root, 'client')));
57 | app.set('appPath', path.join(config.root, 'client'));
58 | app.use(morgan('dev'));
59 | app.use(errorHandler()); // Error handler - has to be last
60 | }
61 | };
62 |
--------------------------------------------------------------------------------
/app/templates/client/components/modal(uibootstrap)/modal.service(coffee).coffee:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | angular.module '<%= scriptAppName %>'
4 | .factory 'Modal', ($rootScope, $modal) ->
5 |
6 | ###
7 | Opens a modal
8 | @param {Object} scope - an object to be merged with modal's scope
9 | @param {String} modalClass - (optional) class(es) to be applied to the modal
10 | @return {Object} - the instance $modal.open() returns
11 | ###
12 | openModal = (scope, modalClass) ->
13 | modalScope = $rootScope.$new()
14 | scope = scope or {}
15 | modalClass = modalClass or 'modal-default'
16 | angular.extend modalScope, scope
17 | $modal.open
18 | templateUrl: 'components/modal/modal.html'
19 | windowClass: modalClass
20 | scope: modalScope
21 |
22 |
23 | # Public API here
24 |
25 | # Confirmation modals
26 | confirm:
27 |
28 | ###
29 | Create a function to open a delete confirmation modal (ex. ng-click='myModalFn(name, arg1, arg2...)')
30 | @param {Function} del - callback, ran when delete is confirmed
31 | @return {Function} - the function to open the modal (ex. myModalFn)
32 | ###
33 | delete: (del) ->
34 | del = del or angular.noop
35 |
36 | ###
37 | Open a delete confirmation modal
38 | @param {String} name - name or info to show on modal
39 | @param {All} - any additional args are passed staight to del callback
40 | ###
41 | ->
42 | args = Array::slice.call arguments
43 | name = args.shift()
44 | deleteModal = undefined
45 | deleteModal = openModal(
46 | modal:
47 | dismissable: true
48 | title: 'Confirm Delete'
49 | html: 'Are you sure you want to delete ' + name + ' ?
'
50 | buttons: [
51 | {
52 | classes: 'btn-danger'
53 | text: 'Delete'
54 | click: (e) ->
55 | deleteModal.close e
56 | return
57 | }
58 | {
59 | classes: 'btn-default'
60 | text: 'Cancel'
61 | click: (e) ->
62 | deleteModal.dismiss e
63 | return
64 | }
65 | ]
66 | , 'modal-danger')
67 | deleteModal.result.then (event) ->
68 | del.apply event, args
69 | return
70 |
71 | return
72 |
--------------------------------------------------------------------------------
/app/templates/client/app/account(auth)/login/login(html).html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
Login
7 |
Accounts are reset on server restart from server/config/seed.js. Default account is test@test.com / test
8 |
Admin account is admin@admin.com / admin
9 |
10 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/app/templates/client/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
28 |
29 |
30 | <% if(filters.ngroute) { %><% } %><% if(filters.uirouter) { %><% } %>
31 |
32 |
33 |
42 |
43 |
47 |
48 |
49 | <% if(filters.socketio) { %>
50 | <% } %>
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/endpoint/templates/name.controller.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var _ = require('lodash');<% if (filters.sql) { %>
4 |
5 | function <%= classedName %>(req){
6 | return req.app.get('models').<%= classedName %>;
7 | }<% } %>
8 |
9 | // Get list of <%= name %>s
10 | exports.index = function(req, res) {<% if (!filters.sql) { %>
11 | res.json([]);<% } %><% if (filters.sql) { %>
12 | <%= classedName %>(req)
13 | .findAll({ where: {} })
14 | .then(function (<%= name %>s) {
15 | return res.status(200).json(<%= name %>s);
16 | })
17 | .catch(function (err){
18 | if(err) { return handleError(res, err); }
19 | });<% } %>
20 | };<% if (filters.sql) { %>
21 |
22 | // Get a single <%= name %>
23 | exports.show = function(req, res) {
24 | <%= classedName %>
25 | .findById(req.params.id)
26 | .then(function (<%= name %>) {
27 | if(!<%= name %>) { return res.status(404).send('Not Found'); }
28 | return res.json(<%= name %>);
29 | })
30 | .catch(function (err){
31 | if(err) { return handleError(res, err); }
32 | });
33 | };
34 |
35 | // Creates a new <%= name %> in the DB.
36 | exports.create = function(req, res) {
37 | <%= classedName %>
38 | .create(req.body)
39 | .then(function (<%= name %>) {
40 | return res.status(201).json(<%= name %>);
41 | })
42 | .catch(function (err){
43 | if(err) { return handleError(res, err); }
44 | });
45 | };
46 |
47 | // Updates an existing <%= name %> in the DB.
48 | exports.update = function(req, res) {
49 | if(req.body._id) { delete req.body._id; }
50 | <%= classedName %>
51 | .findById(req.params.id)
52 | .then(function (<%= name %>) {
53 | if(!<%= name %>) { return res.status(404).send('Not Found'); }
54 | var updated = _.merge(<%= name %>, req.body);
55 | updated.save(function (err) {
56 | if (err) { return handleError(res, err); }
57 | return res.status(200).json(<%= name %>);
58 | });
59 | })
60 | .catch(function (err){
61 | if (err) { return handleError(res, err); }
62 | });
63 | };
64 |
65 | // Deletes a <%= name %> from the DB.
66 | exports.destroy = function(req, res) {
67 | <%= classedName %>
68 | .findById(req.params.id)
69 | .then(function (<%= name %>) {
70 | if(!<%= name %>) { return res.status(404).send('Not Found'); }
71 | <%= name %>.destroy(function(err) {
72 | if(err) { return handleError(res, err); }
73 | return res.status(204).send('No Content');
74 | });
75 | })
76 | .catch(function (err){
77 | if(err) { return handleError(res, err); }
78 | });
79 | };
80 |
81 | function handleError(res, err) {
82 | return res.status(500).send(err);
83 | }<% } %>
84 |
--------------------------------------------------------------------------------
/app/templates/client/app/account(auth)/signup/signup(jade).jade:
--------------------------------------------------------------------------------
1 | div(ng-include='"components/navbar/navbar.html"')
2 | .container
3 | .row
4 | .col-sm-12
5 | h1 Sign up
6 | .col-sm-12
7 | form.form(name='form', ng-submit='register(form)', novalidate='')
8 | .form-group(ng-class='{ "has-success": form.name.$valid && submitted,\
9 | "has-error": form.name.$invalid && submitted }')
10 | label Name
11 | input.form-control(type='text', name='name', ng-model='user.name', required='')
12 | p.help-block(ng-show='form.name.$error.required && submitted')
13 | | A name is required
14 |
15 | .form-group(ng-class='{ "has-success": form.email.$valid && submitted,\
16 | "has-error": form.email.$invalid && submitted }')
17 | label Email
18 | input.form-control(type='email', name='email', ng-model='user.email', required='', sql-error='')
19 | p.help-block(ng-show='form.email.$error.email && submitted')
20 | | Doesn't look like a valid email.
21 | p.help-block(ng-show='form.email.$error.required && submitted')
22 | | What's your email address?
23 | p.help-block(ng-show='form.email.$error.sql')
24 | | {{ errors.email }}
25 |
26 | .form-group(ng-class='{ "has-success": form.password.$valid && submitted,\
27 | "has-error": form.password.$invalid && submitted }')
28 | label Password
29 | input.form-control(type='password', name='password', ng-model='user.password', ng-minlength='3', required='', sql-error='')
30 | p.help-block(ng-show='(form.password.$error.minlength || form.password.$error.required) && submitted')
31 | | Password must be at least 3 characters.
32 | p.help-block(ng-show='form.password.$error.sql')
33 | | {{ errors.password }}
34 |
35 | div
36 | button.btn.btn-inverse.btn-lg.btn-login(type='submit')
37 | | Sign up
38 | = ' '
39 | a.btn.btn-default.btn-lg.btn-register(href='/login')
40 | | Login
41 |
42 | <% if(filters.oauth) {%>
43 | hr
44 |
45 | div<% if(filters.facebookAuth) {%>
46 | a.btn.btn-facebook(href='', ng-click='loginOauth("facebook")')
47 | i.fa.fa-facebook
48 | | Connect with Facebook
49 | = ' '<% } %><% if(filters.googleAuth) {%>
50 | a.btn.btn-google-plus(href='', ng-click='loginOauth("google")')
51 | i.fa.fa-google-plus
52 | | Connect with Google+
53 | = ' '<% } %><% if(filters.twitterAuth) {%>
54 | a.btn.btn-twitter(href='', ng-click='loginOauth("twitter")')
55 | i.fa.fa-twitter
56 | | Connect with Twitter<% } %><% } %>
57 | hr
58 |
--------------------------------------------------------------------------------
/app/templates/server/auth(auth)/auth.service.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var passport = require('passport');
4 | var config = require('../config/environment');
5 | var jwt = require('jsonwebtoken');
6 | var expressJwt = require('express-jwt');
7 | var compose = require('composable-middleware');
8 | var validateJwt = expressJwt({
9 | secret: config.secrets.session
10 | });
11 |
12 | function User(req){
13 | if(req.app.get('models'))
14 | return req.app.get('models').User;
15 | else
16 | return req.app.settings.models.User;
17 | }
18 |
19 | /**
20 | * Attaches the user object to the request if authenticated
21 | * Otherwise returns 403
22 | */
23 | function isAuthenticated() {
24 | return compose()
25 | // Validate jwt
26 | .use(function(req, res, next) {
27 | // allow access_token to be passed through query parameter as well
28 | if (req.query && req.query.hasOwnProperty('access_token')) {
29 | req.headers.authorization = 'Bearer ' + req.query.access_token;
30 | }
31 | validateJwt(req, res, next);
32 | })
33 | // Attach user to request
34 | .use(function(req, res, next) {
35 | User(req)
36 | .find({ where: { _id: req.user._id } })
37 | .then(function(user) {
38 | if(!user){ return res.status(401).end(); }
39 | req.user = user;
40 | next();
41 | })
42 | .catch(function(err) {
43 | return next(err);
44 | });
45 | });
46 | }
47 |
48 | /**
49 | * Checks if the user role meets the minimum requirements of the route
50 | */
51 | function hasRole(roleRequired) {
52 | if (!roleRequired) {
53 | throw new Error('Required role needs to be set');
54 | }
55 |
56 | return compose()
57 | .use(isAuthenticated())
58 | .use(function meetsRequirements(req, res, next) {
59 | if (config.userRoles.indexOf(req.user.role) >=
60 | config.userRoles.indexOf(roleRequired)) {
61 | next();
62 | } else {
63 | res.status(403).send('Forbidden');
64 | }
65 | });
66 | }
67 |
68 | /**
69 | * Returns a jwt token signed by the app secret
70 | */
71 | function signToken(id, role) {
72 | return jwt.sign({ _id: id, role: role }, config.secrets.session, {
73 | expiresInMinutes: 60 * 5
74 | });
75 | }
76 |
77 | /**
78 | * Set token cookie directly for oAuth strategies
79 | */
80 | function setTokenCookie(req, res) {
81 | if (!req.user) {
82 | return res.status(404).send('Something went wrong, please try again.');
83 | }
84 | var token = signToken(req.user._id, req.user.role);
85 | res.cookie('token', token);
86 | res.redirect('/');
87 | }
88 |
89 | exports.isAuthenticated = isAuthenticated;
90 | exports.hasRole = hasRole;
91 | exports.signToken = signToken;
92 | exports.setTokenCookie = setTokenCookie;
93 |
--------------------------------------------------------------------------------
/app/templates/client/components/modal(uibootstrap)/modal.service(js).js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | angular.module('<%= scriptAppName %>')
4 | .factory('Modal', function ($rootScope, $modal) {
5 | /**
6 | * Opens a modal
7 | * @param {Object} scope - an object to be merged with modal's scope
8 | * @param {String} modalClass - (optional) class(es) to be applied to the modal
9 | * @return {Object} - the instance $modal.open() returns
10 | */
11 | function openModal(scope, modalClass) {
12 | var modalScope = $rootScope.$new();
13 | scope = scope || {};
14 | modalClass = modalClass || 'modal-default';
15 |
16 | angular.extend(modalScope, scope);
17 |
18 | return $modal.open({
19 | templateUrl: 'components/modal/modal.html',
20 | windowClass: modalClass,
21 | scope: modalScope
22 | });
23 | }
24 |
25 | // Public API here
26 | return {
27 |
28 | /* Confirmation modals */
29 | confirm: {
30 |
31 | /**
32 | * Create a function to open a delete confirmation modal (ex. ng-click='myModalFn(name, arg1, arg2...)')
33 | * @param {Function} del - callback, ran when delete is confirmed
34 | * @return {Function} - the function to open the modal (ex. myModalFn)
35 | */
36 | delete: function(del) {
37 | del = del || angular.noop;
38 |
39 | /**
40 | * Open a delete confirmation modal
41 | * @param {String} name - name or info to show on modal
42 | * @param {All} - any additional args are passed staight to del callback
43 | */
44 | return function() {
45 | var args = Array.prototype.slice.call(arguments),
46 | name = args.shift(),
47 | deleteModal;
48 |
49 | deleteModal = openModal({
50 | modal: {
51 | dismissable: true,
52 | title: 'Confirm Delete',
53 | html: 'Are you sure you want to delete ' + name + ' ?
',
54 | buttons: [{
55 | classes: 'btn-danger',
56 | text: 'Delete',
57 | click: function(e) {
58 | deleteModal.close(e);
59 | }
60 | }, {
61 | classes: 'btn-default',
62 | text: 'Cancel',
63 | click: function(e) {
64 | deleteModal.dismiss(e);
65 | }
66 | }]
67 | }
68 | }, 'modal-danger');
69 |
70 | deleteModal.result.then(function(event) {
71 | del.apply(event, args);
72 | });
73 | };
74 | }
75 | }
76 | };
77 | });
78 |
--------------------------------------------------------------------------------
/app/templates/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration
2 | // http://karma-runner.github.io/0.10/config/configuration-file.html
3 |
4 | module.exports = function(config) {
5 | config.set({
6 | // base path, that will be used to resolve files and exclude
7 | basePath: '',
8 |
9 | // testing framework to use (jasmine/mocha/qunit/...)
10 | frameworks: ['jasmine'],
11 |
12 | // list of files / patterns to load in the browser
13 | files: [
14 | 'client/bower_components/jquery/dist/jquery.js',
15 | 'client/bower_components/angular/angular.js',
16 | 'client/bower_components/angular-mocks/angular-mocks.js',
17 | 'client/bower_components/angular-resource/angular-resource.js',
18 | 'client/bower_components/angular-cookies/angular-cookies.js',
19 | 'client/bower_components/angular-sanitize/angular-sanitize.js',
20 | 'client/bower_components/angular-route/angular-route.js',<% if(filters.uibootstrap) { %>
21 | 'client/bower_components/angular-bootstrap/ui-bootstrap-tpls.js',<% } %>
22 | 'client/bower_components/lodash/dist/lodash.compat.js',<% if(filters.socketio) { %>
23 | 'client/bower_components/angular-socket-io/socket.js',<% } %><% if(filters.uirouter) { %>
24 | 'client/bower_components/angular-ui-router/release/angular-ui-router.js',<% } %>
25 | 'client/app/app.js',
26 | 'client/app/app.coffee',
27 | 'client/app/**/*.js',
28 | 'client/app/**/*.coffee',
29 | 'client/components/**/*.js',
30 | 'client/components/**/*.coffee',
31 | 'client/app/**/*.jade',
32 | 'client/components/**/*.jade',
33 | 'client/app/**/*.html',
34 | 'client/components/**/*.html'
35 | ],
36 |
37 | preprocessors: {
38 | '**/*.jade': 'ng-jade2js',
39 | '**/*.html': 'html2js',
40 | '**/*.coffee': 'coffee',
41 | },
42 |
43 | ngHtml2JsPreprocessor: {
44 | stripPrefix: 'client/'
45 | },
46 |
47 | ngJade2JsPreprocessor: {
48 | stripPrefix: 'client/'
49 | },
50 |
51 | // list of files / patterns to exclude
52 | exclude: [],
53 |
54 | // web server port
55 | port: 8080,
56 |
57 | // level of logging
58 | // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG
59 | logLevel: config.LOG_INFO,
60 |
61 |
62 | // enable / disable watching file and executing tests whenever any file changes
63 | autoWatch: false,
64 |
65 |
66 | // Start these browsers, currently available:
67 | // - Chrome
68 | // - ChromeCanary
69 | // - Firefox
70 | // - Opera
71 | // - Safari (only Mac)
72 | // - PhantomJS
73 | // - IE (only Windows)
74 | browsers: ['PhantomJS'],
75 |
76 |
77 | // Continuous Integration mode
78 | // if true, it capture browsers, run tests and exit
79 | singleRun: false
80 | });
81 | };
82 |
--------------------------------------------------------------------------------
/app/templates/server/config/environment/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var path = require('path');
4 | var _ = require('lodash');
5 |
6 | function requiredProcessEnv(name) {
7 | if(!process.env[name]) {
8 | throw new Error('You must set the ' + name + ' environment variable');
9 | }
10 | return process.env[name];
11 | }
12 |
13 | // All configurations will extend these options
14 | // ============================================
15 | var all = {
16 | env: process.env.NODE_ENV,
17 |
18 | // Root path of server
19 | root: path.normalize(__dirname + '/../../..'),
20 |
21 | // Server port
22 | port: process.env.PORT || 9000,
23 |
24 | // Server IP
25 | ip: process.env.IP || 'localhost',
26 |
27 | // Should we populate the DB with sample data?
28 | seedDB: true,
29 |
30 | // Secret for session, you will want to change this and make it an environment variable
31 | secrets: {
32 | session: '<%= _.slugify(_.humanize(appname)) + '-secret' %>'
33 | },
34 |
35 | // List of user roles
36 | userRoles: ['guest', 'user', 'admin'],
37 |
38 | // MongoDB connection options
39 | mongo: {
40 | options: {
41 | db: {
42 | safe: true
43 | }
44 | }
45 | },
46 |
47 | sql: {
48 | host: 'localhost',
49 | dialect: <% if(filters.pgsql){ %>'postgres'<% } %><% if(filters.mssql){ %>'mssql'<% } %><% if(filters.mysql){ %>'mysql'<% } %><% if(filters.mariasql){ %>'mariadb' <% } %><% if(filters.sqlite){ %>'sqlite'<% } %>,
50 | protocol:<% if(filters.pgsql){ %>'postgres'<% } %><% if(filters.mssql){ %>'mssql'<% } %><% if(filters.mysql){ %>'mysql'<% } %><% if(filters.mariasql){ %>'mariadb' <% } %><% if(filters.sqlite){ %>'sqlite'<% } %>,
51 | pool: {
52 | max: 5,
53 | min: 0,
54 | idle: 10000
55 | },
56 | database: '<%= _.slugify(appname) %>',
57 | username: 'postgres',
58 | password: 'root'
59 | },
60 | <% if(filters.facebookAuth) { %>
61 | facebook: {
62 | clientID: process.env.FACEBOOK_ID || 'id',
63 | clientSecret: process.env.FACEBOOK_SECRET || 'secret',
64 | callbackURL: (process.env.DOMAIN || '') + '/auth/facebook/callback'
65 | },
66 | <% } %><% if(filters.twitterAuth) { %>
67 | twitter: {
68 | clientID: process.env.TWITTER_ID || 'id',
69 | clientSecret: process.env.TWITTER_SECRET || 'secret',
70 | callbackURL: (process.env.DOMAIN || '') + '/auth/twitter/callback'
71 | },
72 | <% } %><% if(filters.googleAuth) { %>
73 | google: {
74 | clientID: process.env.GOOGLE_ID || 'id',
75 | clientSecret: process.env.GOOGLE_SECRET || 'secret',
76 | callbackURL: (process.env.DOMAIN || '') + '/auth/google/callback'
77 | }<% } %>
78 | };
79 |
80 | // Export the config object based on the NODE_ENV
81 | // ==============================================
82 | module.exports = _.merge(
83 | all,
84 | require('./' + process.env.NODE_ENV + '.js') || {});
85 |
--------------------------------------------------------------------------------
/contributing.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | See the [contributing docs](https://github.com/yeoman/yeoman/blob/master/contributing.md)
4 |
5 | Additionally for this generator:
6 |
7 | * Please submit PRs to the `canary` branch, it is the main development branch for this generator.
8 | * When submitting an issue, please follow the [guidelines](https://github.com/yeoman/yeoman/blob/master/contributing.md#issue-submission). Especially important is to make sure Yeoman is up-to-date, and providing the command or commands that cause the issue.
9 | * When submitting a PR, make sure that the commit messages match the [AngularJS conventions][commit-message-format] (see below).
10 | * When submitting a bugfix, write a test that exposes the bug and fails before applying your fix. Submit the test alongside the fix.
11 | * When submitting a new feature, add tests that cover the feature.
12 |
13 | ## Git Commit Guidelines
14 |
15 | These rules are adopted from the AngularJS project.
16 |
17 | ### Commit Message Format
18 | Each commit message consists of a **header**, a **body** and a **footer**. The header has a special
19 | format that includes a **type**, a **scope** and a **subject**:
20 |
21 | ```
22 | ():
23 |
24 |
25 |
26 |