From a8494b29da91b4fa65b8bbc06ec1c46aceea2aba Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Tue, 9 Jun 2015 16:43:08 -0700 Subject: [PATCH] add user signup through loopback --- common/models/User.js | 19 --- common/models/user.json | 1 + public/js/main_0.0.2.js | 48 +++--- server/boot/a-extendUser.js | 84 ++++++++++ server/boot/user.js | 203 +------------------------ server/datasources.json | 3 + server/datasources.local.js | 13 ++ server/model-config.json | 7 +- server/views/account/email-signin.jade | 4 +- server/views/account/email-signup.jade | 2 +- 10 files changed, 140 insertions(+), 244 deletions(-) delete mode 100644 common/models/User.js create mode 100644 server/boot/a-extendUser.js diff --git a/common/models/User.js b/common/models/User.js deleted file mode 100644 index 83273683f5..0000000000 --- a/common/models/User.js +++ /dev/null @@ -1,19 +0,0 @@ -var debug = require('debug')('freecc:models:user'); - -module.exports = function(User) { - debug('setting up user hooks'); - /* - * NOTE(berks): not sure if this is still needed - User.observe('before save', function setUsername(ctx, next) { - // set username from twitter - if (ctx.instance.username && ctx.instance.username.match(/twitter/g)) { - ctx.instance.username = - ctx.instance.username.match(/twitter/g) ? - ctx.instance.username.split('.').pop().toLowerCase() : - ctx.instance.username; - debug('username set', ctx.instance.username); - } - next(); - }); - */ -}; diff --git a/common/models/user.json b/common/models/user.json index 1f3af30fa8..6733437349 100644 --- a/common/models/user.json +++ b/common/models/user.json @@ -3,6 +3,7 @@ "base": "User", "trackChanges": false, "idInjection": true, + "strict": true, "properties": { "email": { "type": "string", diff --git a/public/js/main_0.0.2.js b/public/js/main_0.0.2.js index 209ccde97e..c8e67d6aaf 100644 --- a/public/js/main_0.0.2.js +++ b/public/js/main_0.0.2.js @@ -422,14 +422,18 @@ profileValidation.directive('uniqueUsername', ['$http', function($http) { link: function (scope, element, attrs, ngModel) { element.bind("keyup", function (event) { ngModel.$setValidity('unique', true); - if (element.val()) { - $http.get("/api/checkUniqueUsername/" + element.val()).success(function (data) { - if (element.val() === scope.storedUsername) { - ngModel.$setValidity('unique', true); - } else if (data) { - ngModel.$setValidity('unique', false); - } - }); + var username = element.val(); + if (username) { + var config = { params: { username: username } }; + $http + .get('/api/users/exists', config) + .success(function (exists) { + if (username === scope.storedUsername) { + ngModel.$setValidity('unique', true); + } else if (exists) { + ngModel.$setValidity('unique', false); + } + }); } }); } @@ -449,11 +453,13 @@ profileValidation.directive('existingUsername', element.removeClass('ng-dirty'); ngModel.$setPristine(); } - if (element.val()) { + var username = element.val(); + if (username) { + var config = { params: { username: username } }; $http - .get('/api/checkExistingUsername/' + element.val()) - .success(function (data) { - ngModel.$setValidity('exists', data); + .get('/api/users/exists', config) + .success(function(exists) { + ngModel.$setValidity('exists', exists); }); } }); @@ -468,14 +474,18 @@ profileValidation.directive('uniqueEmail', ['$http', function($http) { link: function getUnique (scope, element, attrs, ngModel) { element.bind("keyup", function (event) { ngModel.$setValidity('unique', true); + var email = element.val(); if (element.val()) { - $http.get("/api/checkUniqueEmail/" + encodeURIComponent(element.val())).success(function (data) { - if (element.val() === scope.storedEmail) { - ngModel.$setValidity('unique', true); - } else if (data) { - ngModel.$setValidity('unique', false); - } - }); + var config = { params: { email: email } }; + $http + .get('/api/users/exists', config) + .success(function (exists) { + if (email === scope.storedEmail) { + ngModel.$setValidity('unique', true); + } else if (exists) { + ngModel.$setValidity('unique', false); + } + }); }; }); } diff --git a/server/boot/a-extendUser.js b/server/boot/a-extendUser.js new file mode 100644 index 0000000000..23613db13d --- /dev/null +++ b/server/boot/a-extendUser.js @@ -0,0 +1,84 @@ +var debug = require('debug')('freecc:extendUser'); + +module.exports = function(app) { + var User = app.models.User; + debug('setting up user hooks'); + // send verification email to new camper + User.afterRemote('create', function(ctx, user, next) { + debug('user created, sending email'); + if (!user.email) { return next(); } + + var mailOptions = { + type: 'email', + to: user.email, + from: 'Team@freecodecamp.com', + subject: 'Welcome to Free Code Camp!', + redirect: '/', + text: [ + 'Greetings from San Francisco!\n\n', + 'Thank you for joining our community.\n', + 'Feel free to email us at this address if you have ', + 'any questions about Free Code Camp.\n', + 'And if you have a moment, check out our blog: ', + 'blog.freecodecamp.com.\n', + 'Good luck with the challenges!\n\n', + '- the Free Code Camp Volunteer Team' + ].join('') + }; + user.verify(mailOptions, function(err) { + if (err) { return next(err); } + debug('verification email sent'); + ctx.req.flash({ + msg: 'Please check your email and click on the verification link ' + + 'before logging in.' + }); + ctx.res.redirect('/'); + }); + }); + + User.doesExist = function doesExist(username, email, cb) { + debug('checking existence'); + var where = {}; + if (username) { + where.username = username; + } else { + where.email = email; + } + User.count( + { where: where }, + function (err, count) { + if (err) { return cb(err); } + if (count > 0) { + return cb(null, true); + } + return cb(null, false); + } + ); + }; + + User.remoteMethod( + 'doesExist', + { + description: 'checks whether a user exists using email or username', + accepts: [ + { + arg: 'username', + type: 'string' + }, + { + arg: 'email', + type: 'string' + } + ], + returns: [ + { + arg: 'exists', + type: 'boolean' + } + ], + http: { + path: '/exists' + } + } + ); +}; diff --git a/server/boot/user.js b/server/boot/user.js index ec19a18f67..1f7c74a7c5 100644 --- a/server/boot/user.js +++ b/server/boot/user.js @@ -35,12 +35,8 @@ module.exports = function(app) { router.post('/reset/:token', postReset); router.get('/email-signup', getEmailSignup); router.get('/email-signin', getEmailSignin); - router.post('/email-signup', postEmailSignup); - // router.post('/email-signin', postSignin); + // router.post('/email-signup', postEmailSignup); router.get('/account/api', getAccountAngular); - router.get('/api/checkUniqueUsername/:username', checkUniqueUsername); - router.get('/api/checkExistingUsername/:username', checkExistingUsername); - router.get('/api/checkUniqueEmail/:email', checkUniqueEmail); router.post('/account/profile', postUpdateProfile); router.post('/account/password', postUpdatePassword); router.post('/account/delete', postDeleteAccount); @@ -65,49 +61,6 @@ module.exports = function(app) { }); } - /** - * POST /signin - * Sign in using email and password. - */ - - /* - * TODO(berks): this should be done using loopback - function postSignin (req, res, next) { - req.assert('email', 'Email is not valid').isEmail(); - req.assert('password', 'Password cannot be blank').notEmpty(); - - var errors = req.validationErrors(); - - if (errors) { - req.flash('errors', errors); - return res.redirect('/signin'); - } - - passport.authenticate('local', function(err, user, info) { - if (err) { - return next(err); - } - if (!user) { - req.flash('errors', { msg: info.message }); - return res.redirect('/signin'); - } - req.logIn(user, function(err) { - if (err) { - return next(err); - } - req.flash('success', { msg: 'Success! You are logged in.' }); - if (/hotStories/.test(req.session.returnTo)) { - return res.redirect('../news'); - } - if (/field-guide/.test(req.session.returnTo)) { - return res.redirect('../field-guide'); - } - return res.redirect(req.session.returnTo || '/'); - }); - })(req, res, next); - } - */ - /** * GET /signout * Log out. @@ -146,107 +99,6 @@ module.exports = function(app) { }); } - /** - * POST /email-signup - * Create a new local account. - */ - - function postEmailSignup (req, res, next) { - req.assert('email', 'valid email required').isEmail(); - var errors = req.validationErrors(); - - if (errors) { - req.flash('errors', errors); - return res.redirect('/email-signup'); - } - - var possibleUserData = req.body; - - if (possibleUserData.password.length < 8) { - req.flash('errors', { - msg: 'Your password is too short' - }); - return res.redirect('email-signup'); - } - - if (possibleUserData.username.length < 5 || possibleUserData.length > 20) { - req.flash('errors', { - msg: 'Your username must be between 5 and 20 characters' - }); - return res.redirect('email-signup'); - } - - - var user = new User({ - email: req.body.email.trim(), - password: req.body.password, - profile: { - username: req.body.username.trim(), - picture: - 'https://s3.amazonaws.com/freecodecamp/camper-image-placeholder.png' - } - }); - - User.findOne({ email: req.body.email }, function(err, existingEmail) { - if (err) { - return next(err); - } - - if (existingEmail) { - req.flash('errors', { - msg: 'Account with that email address already exists.' - }); - return res.redirect('/email-signup'); - } - User.findOne( - { 'profile.username': req.body.username }, - function(err, existingUsername) { - if (err) { - return next(err); - } - if (existingUsername) { - req.flash('errors', { - msg: 'Account with that username already exists.' - }); - return res.redirect('/email-signup'); - } - - user.save(function(err) { - if (err) { return next(err); } - req.logIn(user, function(err) { - if (err) { return next(err); } - res.redirect('/email-signup'); - }); - }); - var transporter = nodemailer.createTransport({ - service: 'Mandrill', - auth: { - user: secrets.mandrill.user, - pass: secrets.mandrill.password - } - }); - var mailOptions = { - to: user.email, - from: 'Team@freecodecamp.com', - subject: 'Welcome to Free Code Camp!', - text: [ - 'Greetings from San Francisco!\n\n', - 'Thank you for joining our community.\n', - 'Feel free to email us at this address if you have ', - 'any questions about Free Code Camp.\n', - 'And if you have a moment, check out our blog: ', - 'blog.freecodecamp.com.\n', - 'Good luck with the challenges!\n\n', - '- the Free Code Camp Volunteer Team' - ].join('') - }; - transporter.sendMail(mailOptions, function(err) { - if (err) { return err; } - }); - }); - }); - } - /** * GET /account * Profile page. @@ -268,59 +120,6 @@ module.exports = function(app) { }); } - /** - * Unique username check API Call - */ - - function checkUniqueUsername (req, res, next) { - User.count( - { 'profile.username': req.params.username.toLowerCase() }, - function (err, data) { - if (err) { return next(err); } - if (data === 1) { - return res.send(true); - } else { - return res.send(false); - } - }); - } - - /** - * Existing username check - */ - - function checkExistingUsername (req, res, next) { - User.count( - { 'profile.username': req.params.username.toLowerCase() }, - function (err, data) { - if (err) { return next(err); } - if (data === 1) { - return res.send(true); - } else { - return res.send(false); - } - } - ); - } - - /** - * Unique email check API Call - */ - - function checkUniqueEmail (req, res, next) { - User.count( - { email: decodeURIComponent(req.params.email).toLowerCase() }, - function (err, data) { - if (err) { return next(err); } - if (data === 1) { - return res.send(true); - } else { - return res.send(false); - } - } - ); - } - /** * GET /campers/:username diff --git a/server/datasources.json b/server/datasources.json index 006eaaf8b5..4e770f72b3 100644 --- a/server/datasources.json +++ b/server/datasources.json @@ -2,5 +2,8 @@ "db": { "name": "db", "connector": "mongodb" + }, + "mail": { + "connector": "mail" } } diff --git a/server/datasources.local.js b/server/datasources.local.js index f7777c5e9f..03a6648e94 100644 --- a/server/datasources.local.js +++ b/server/datasources.local.js @@ -1,6 +1,19 @@ +var secrets = require('../config/secrets'); + module.exports = { db: { connector: 'mongodb', url: process.env.MONGOHQ_URL + }, + mail: { + connector: 'mail', + transports: [{ + type: 'smtp', + service: 'Mandrill', + auth: { + user: secrets.mandrill.user, + pass: secrets.mandrill.password + } + }] } }; diff --git a/server/model-config.json b/server/model-config.json index 23f21c295b..f944fb292a 100644 --- a/server/model-config.json +++ b/server/model-config.json @@ -8,7 +8,8 @@ ] }, "User": { - "dataSource": "db" + "dataSource": "db", + "public": false }, "AccessToken": { "dataSource": "db", @@ -26,6 +27,10 @@ "dataSource": "db", "public": false }, + "Email": { + "dataSource": "mail", + "public": false + }, "bonfire": { "dataSource": "db", "public": true diff --git a/server/views/account/email-signin.jade b/server/views/account/email-signin.jade index c4250fd697..26047d3c6d 100644 --- a/server/views/account/email-signin.jade +++ b/server/views/account/email-signin.jade @@ -2,7 +2,7 @@ extends ../layout block content .jumbotron.text-center(ng-controller="emailSignInController") h2 Sign in with an email address here: - form(method='POST', action='/email-signin') + form(method='POST', action='/api/user/login') input(type='hidden', name='_csrf', value=_csrf) .col-sm-6.col-sm-offset-3 .form-group @@ -24,4 +24,4 @@ block content br br br - br \ No newline at end of file + br diff --git a/server/views/account/email-signup.jade b/server/views/account/email-signup.jade index 9c4fe0e337..c270b2470c 100644 --- a/server/views/account/email-signup.jade +++ b/server/views/account/email-signup.jade @@ -2,7 +2,7 @@ extends ../layout block content .jumbotron.text-center h2 Sign up with an email address here: - form.form-horizontal(method='POST', action='/email-signup', name="signupForm", novalidate="novalidate") + form.form-horizontal(method='POST', action='/api/users', name="signupForm", novalidate="novalidate") input(type='hidden', name='_csrf', value=_csrf) .form-group .col-sm-6.col-sm-offset-3