Skip to content

Commit 8784106

Browse files
committed
feat(gen) added passport boilerplate question
generates * Passport configuration and a User model * API routes for account creation/sign in * Login and signup views with form validation, including handling server validation errors * Settings view for changing your password * Angular Auth service for creating accounts/logining closes #25, #36
1 parent e6eff5d commit 8784106

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+1369
-120
lines changed

Diff for: app/index.js

+70-8
Original file line numberDiff line numberDiff line change
@@ -250,8 +250,17 @@ Generator.prototype.askForMongo = function askForMongo() {
250250
name: 'mongo',
251251
message: 'Would you like to include MongoDB with Mongoose?',
252252
default: false
253+
}, {
254+
type: 'confirm',
255+
name: 'mongoPassportUser',
256+
message: 'Would you like to include a Passport authentication boilerplate?',
257+
default: false,
258+
when: function (props) {
259+
return props.mongo;
260+
}
253261
}], function (props) {
254262
this.mongo = props.mongo;
263+
this.mongoPassportUser = props.mongoPassportUser;
255264

256265
cb();
257266
}.bind(this));
@@ -335,21 +344,24 @@ function appendFilesToJade(jadeOrOptions, fileType, optimizedPath, sourceFileLis
335344
return updatedContent;
336345
}
337346

338-
Generator.prototype.navBarScript = function navBarScript() {
339-
var ext = 'js';
340-
var folder = 'javascript';
341-
var minsafe = '';
347+
var copyScriptWithEnvOptions = function copyScriptWithEnvOptions(that, fileToCopy, destinationFolder) {
348+
var ext = 'js',
349+
minsafe = '',
350+
sourceFolder = 'javascript';
342351

343-
if(this.env.options.coffee) {
352+
if(that.env.options.coffee) {
344353
ext = 'coffee';
345-
folder = 'coffeescript';
354+
sourceFolder = 'coffeescript';
346355
}
347356

348-
if(this.env.options.minsafe) {
357+
if(that.env.options.minsafe) {
349358
minsafe = '-min';
350359
}
360+
that.copy('../../templates/' + sourceFolder + minsafe + '/' + fileToCopy + '.' + ext, destinationFolder + fileToCopy + '.' + ext);
361+
};
351362

352-
this.copy('../../templates/' + folder + minsafe + '/navbar.' + ext, 'app/scripts/controllers/navbar.' + ext);
363+
Generator.prototype.navBarScript = function navBarScript() {
364+
copyScriptWithEnvOptions(this, 'controllers/navbar', 'app/scripts/');
353365
};
354366

355367
Generator.prototype.appJs = function appJs() {
@@ -360,6 +372,18 @@ Generator.prototype.appJs = function appJs() {
360372
sourceFileList: ['scripts/app.js', 'scripts/controllers/main.js', 'scripts/controllers/navbar.js'],
361373
searchPath: ['.tmp', 'app']
362374
};
375+
376+
// only reference authentication controllers when required
377+
if (this.mongoPassportUser) {
378+
appendOptions.sourceFileList.push('scripts/controllers/login.js');
379+
appendOptions.sourceFileList.push('scripts/controllers/signup.js');
380+
appendOptions.sourceFileList.push('scripts/controllers/settings.js');
381+
appendOptions.sourceFileList.push('scripts/services/auth.js');
382+
appendOptions.sourceFileList.push('scripts/services/session.js');
383+
appendOptions.sourceFileList.push('scripts/services/user.js');
384+
appendOptions.sourceFileList.push('scripts/directives/mongooseError.js');
385+
}
386+
363387
if (this.jade) {
364388
this.indexFile = appendFilesToJade(appendOptions);
365389
} else {
@@ -380,6 +404,11 @@ Generator.prototype.addJadeViews = function addHtmlJade() {
380404
if(this.jade) {
381405
this.copy('../../templates/views/jade/partials/main.jade', 'app/views/partials/main.jade');
382406
this.copy('../../templates/views/jade/partials/navbar.jade', 'app/views/partials/navbar.jade');
407+
if(this.mongoPassportUser) {
408+
this.copy('../../templates/views/jade/partials/login.jade', 'app/views/partials/login.jade');
409+
this.copy('../../templates/views/jade/partials/signup.jade', 'app/views/partials/signup.jade');
410+
this.copy('../../templates/views/jade/partials/settings.jade', 'app/views/partials/settings.jade');
411+
}
383412
this.copy('../../templates/views/jade/404.jade', 'app/views/404.jade');
384413
}
385414
};
@@ -388,6 +417,11 @@ Generator.prototype.addHtmlViews = function addHtmlViews() {
388417
if(!this.jade) {
389418
this.copy('../../templates/views/html/partials/main.html', 'app/views/partials/main.html');
390419
this.copy('../../templates/views/html/partials/navbar.html', 'app/views/partials/navbar.html');
420+
if(this.mongoPassportUser) {
421+
this.copy('../../templates/views/html/partials/login.html', 'app/views/partials/login.html');
422+
this.copy('../../templates/views/html/partials/signup.html', 'app/views/partials/signup.html');
423+
this.copy('../../templates/views/html/partials/settings.html', 'app/views/partials/settings.html');
424+
}
391425
this.copy('../../templates/views/html/404.html', 'app/views/404.html');
392426
}
393427
};
@@ -443,11 +477,39 @@ Generator.prototype.serverFiles = function () {
443477
};
444478

445479
Generator.prototype.mongoFiles = function () {
480+
446481
if (!this.mongo) {
447482
return; // Skip if disabled.
448483
}
484+
this.env.options.mongo = this.mongo;
449485

450486
this.template('../../templates/express/mongo/mongo.js', 'lib/db/mongo.js');
451487
this.template('../../templates/express/mongo/dummydata.js', 'lib/db/dummydata.js');
452488
this.template('../../templates/express/mongo/thing.js', 'lib/models/thing.js');
489+
490+
if(!this.mongoPassportUser) {
491+
return; // Skip if disabled.
492+
}
493+
this.env.options.mongoPassportUser = this.mongoPassportUser;
494+
495+
// frontend
496+
copyScriptWithEnvOptions(this, 'controllers/login', 'app/scripts/');
497+
copyScriptWithEnvOptions(this, 'controllers/signup', 'app/scripts/');
498+
copyScriptWithEnvOptions(this, 'controllers/settings', 'app/scripts/');
499+
500+
copyScriptWithEnvOptions(this, 'services/auth', 'app/scripts/');
501+
copyScriptWithEnvOptions(this, 'services/session', 'app/scripts/');
502+
copyScriptWithEnvOptions(this, 'services/user', 'app/scripts/');
503+
504+
copyScriptWithEnvOptions(this, 'directives/mongooseError', 'app/scripts/');
505+
506+
// middleware
507+
this.template('../../templates/express/middleware.js', 'lib/middleware.js');
508+
// config
509+
this.template('../../templates/express/config/passport.js', 'lib/config/passport.js');
510+
// models
511+
this.template('../../templates/express/mongo/user.js', 'lib/models/user.js');
512+
// controllers
513+
this.template('../../templates/express/session.js', 'lib/controllers/session.js');
514+
this.template('../../templates/express/users.js', 'lib/controllers/users.js');
453515
};

Diff for: main/index.js

+2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ util.inherits(Generator, ScriptBase);
1313

1414
Generator.prototype.createAppFile = function createAppFile() {
1515
this.angularModules = this.env.options.angularDeps;
16+
this.mongo = this.env.options.mongo;
17+
this.mongoPassportUser = this.env.options.mongoPassportUser;
1618
this.ngRoute = this.env.options.ngRoute;
1719
this.appTemplate('app', 'scripts/app');
1820
};

Diff for: package.json

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
"description": "Yeoman generator for AngularJS with Express server",
55
"keywords": [
66
"yeoman-generator",
7+
"mean",
78
"scaffold",
89
"express",
910
"fullstack",

Diff for: templates/coffeescript-min/app.coffee

+10
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,16 @@ angular.module('<%= scriptAppName %>', [<%= angularModules %>])<% if (ngRoute) {
66
.when '/',
77
templateUrl: 'partials/main'
88
controller: 'MainCtrl'
9+
<% if(mongo && mongoPassportUser) {%>
10+
.when '/login',
11+
templateUrl: 'partials/login'
12+
controller: 'LoginCtrl'
13+
.when '/logout',
14+
controller: 'LogoutCtrl'
15+
.when '/signup',
16+
templateUrl: 'partials/signup'
17+
controller: 'SignupCtrl'
18+
<% } %>
919
.otherwise
1020
redirectTo: '/'
1121
$locationProvider.html5Mode(true)

Diff for: templates/coffeescript-min/auth.coffee

Whitespace-only changes.

Diff for: templates/coffeescript-min/navbar.coffee

+21-14
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,24 @@
11
'use strict'
22

33
angular.module('<%= scriptAppName %>')
4-
.controller 'NavbarCtrl', ['$scope', '$location', ($scope, $location) ->
5-
$scope.menu = [
6-
title: 'Home'
7-
link: '/'
8-
,
9-
title: 'About'
10-
link: '#'
11-
,
12-
title: 'Contact'
13-
link: '#'
14-
]
15-
$scope.isActive = (route) ->
16-
route is $location.path()
17-
]
4+
.controller 'NavbarCtrl', ['$scope', '$location', ($scope, $location) ->
5+
$scope.menu = [
6+
title: 'Home'
7+
link: '/'
8+
,
9+
title: 'About'
10+
link: '#'
11+
,
12+
title: 'Contact'
13+
link: '#'
14+
<% if(mongo && mongoPassportUser) { %>,
15+
title: 'Sign Up'
16+
link: '#/signup'
17+
,
18+
title: 'Login'
19+
link: '#/login'
20+
}<% } %>
21+
]
22+
$scope.isActive = (route) ->
23+
route is $location.path()
24+
]

Diff for: templates/coffeescript/app.coffee

+28-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,37 @@
11
'use strict'
22

33
angular.module('<%= scriptAppName %>', [<%= angularModules %>])<% if (ngRoute) { %>
4-
.config ($routeProvider, $locationProvider) ->
4+
.config ($routeProvider, $locationProvider<% if (mongoPassportUser) { %>, $httpProvider<% } %>) ->
55
$routeProvider
66
.when '/',
77
templateUrl: 'partials/main'
88
controller: 'MainCtrl'
9+
<% if(mongoPassportUser) {%>
10+
.when '/login',
11+
templateUrl: 'partials/login'
12+
controller: 'LoginCtrl'
13+
.when '/signup',
14+
templateUrl: 'partials/signup'
15+
controller: 'SignupCtrl'
16+
.when '/settings',
17+
templateUrl: 'partials/settings'
18+
controller: 'SettingsCtrl'
19+
authenticate: true<% } %>
920
.otherwise
1021
redirectTo: '/'
11-
$locationProvider.html5Mode(true)<% } %>
22+
23+
$locationProvider.html5Mode true<% if (mongoPassportUser) { %>
24+
25+
$httpProvider.interceptors.push ['$q', '$location', ($q, $location) ->
26+
responseError: (response) ->
27+
if response.status is 401 or response.status is 403
28+
$location.path '/login'
29+
$q.reject response
30+
else
31+
$q.reject response
32+
]
33+
.run ($rootScope, $location, Auth) ->
34+
35+
# Redirect to login if route requires auth and you're not logged in
36+
$rootScope.$on '$routeChangeStart', (event, next) ->
37+
$location.path '/login' if next.authenticate and not Auth.isLoggedIn()<% } %><% } %>

Diff for: templates/coffeescript/controllers/login.coffee

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
'use strict'
2+
3+
angular.module('<%= scriptAppName %>')
4+
.controller 'LoginCtrl', ($scope, Auth, $location) ->
5+
$scope.user = {}
6+
$scope.errors = {}
7+
8+
$scope.login = (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 ->
17+
# Logged in, redirect to home
18+
$location.path '/'
19+
.catch (err) ->
20+
err = err.data;
21+
$scope.errors.other = err.message;

Diff for: templates/coffeescript/controllers/navbar.coffee

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
'use strict'
2+
3+
angular.module('<%= scriptAppName %>')
4+
.controller 'NavbarCtrl', ($scope, $location<% if (mongoPassportUser) { %>, Auth<% } %>) ->
5+
$scope.menu = [
6+
title: 'Home'
7+
link: '/'
8+
<% if(mongoPassportUser) { %>,
9+
title: 'Settings'
10+
link: '/settings'
11+
<% } %>]
12+
<% if(mongoPassportUser) { %>
13+
$scope.logout = ->
14+
Auth.logout().then ->
15+
$location.path "/login"
16+
<% } %>
17+
$scope.isActive = (route) ->
18+
route is $location.path()

Diff for: templates/coffeescript/controllers/settings.coffee

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
'use strict'
2+
3+
angular.module('<%= scriptAppName %>')
4+
.controller 'SettingsCtrl', ($scope, User, Auth) ->
5+
$scope.errors = {}
6+
7+
$scope.changePassword = (form) ->
8+
$scope.submitted = true
9+
10+
if form.$valid
11+
Auth.changePassword($scope.user.oldPassword, $scope.user.newPassword)
12+
.then(->
13+
$scope.message = 'Password successfully changed.'
14+
).catch( ->
15+
form.password.$setValidity 'mongoose', false
16+
$scope.errors.other = 'Incorrect password'
17+
)

Diff for: templates/coffeescript/controllers/signup.coffee

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
'use strict'
2+
3+
angular.module('<%= scriptAppName %>')
4+
.controller 'SignupCtrl', ($scope, Auth, $location) ->
5+
$scope.user = {}
6+
$scope.errors = {}
7+
8+
$scope.register = (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+
).then( ->
17+
# Account created, redirect to home
18+
$location.path '/'
19+
).catch( (err) ->
20+
err = err.data
21+
$scope.errors = {}
22+
23+
# Update validity of form fields that match the mongoose errors
24+
angular.forEach err.errors, (error, field) ->
25+
form[field].$setValidity 'mongoose', false
26+
$scope.errors[field] = error.type
27+
)
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
'use strict'
2+
3+
###
4+
Removes server error when user updates input
5+
###
6+
angular.module('<%= scriptAppName %>')
7+
.directive 'mongooseError', ->
8+
restrict: 'A'
9+
require: 'ngModel'
10+
link: (scope, element, attrs, ngModel) ->
11+
element.on 'keydown', ->
12+
ngModel.$setValidity 'mongoose', true
13+

Diff for: templates/coffeescript/navbar.coffee

-16
This file was deleted.

0 commit comments

Comments
 (0)