Last active
February 7, 2022 07:39
-
-
Save Hendrixer/8869852 to your computer and use it in GitHub Desktop.
Forgot password feature with Node, Epxress, Mongo
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# for API and DB endpoints | |
user = require './controllers/userController' | |
mainFeed = require './controllers/mainFeedController' | |
{isLoggedIn} = require './middleWare' | |
{alreadyLoggedOut} = require './middleWare' | |
module.exports = (app, passport) -> | |
#========================= | |
# Routes here!! | |
#========================= | |
# serves splash! Change splash to a real splash and not signup/sign in | |
app.get '/', user.index | |
# Signup new users route | |
app.post '/signup', passport.authenticate('local-signup'), (req, res) -> | |
res.json 201, req.user | |
# log in returning users | |
app.post '/login', passport.authenticate('local-login'), (req, res) -> | |
res.json req.user | |
# log current user out the session | |
app.get '/logout', alreadyLoggedOut, user.logout | |
# delete user out from app and DB, no comming back | |
app.delete '/users/delete', isLoggedIn, user.deleteUser | |
app.post '/user/forgot/password', user.forgotPassword | |
app.get '/user/reset/:token?', user.resetPassword | |
# this route will accept new password from user and update it | |
# app.post '/user/reset/:token?', user.updatePassword | |
# users stream route | |
app.get '/api/users/:from?/:to?', isLoggedIn, mainFeed.allUsersActivity | |
# get current users stats | |
app.get '/api/user/:from?/:to?', isLoggedIn, user.userActivity | |
app.get '/api/compare/:userid?', isLoggedIn, mainFeed.compare | |
# this is no the same as the isLogged in middleware | |
# this route lets the front end know on the fly if the user is auth | |
# the isLoggedIn middleware is to let the server know who is auth or not | |
# isLoggedIn sends back an additional '401' for angular | |
# to intercept if no auth | |
app.get '/loggedin', user.loggedIn | |
# /api/user/2014-01-20 | |
#================================ | |
# fitbit api here | |
#================================ | |
# auth with fitbit | |
app.get '/connect/fitbit', isLoggedIn, passport.authorize 'fitbit', | |
display: 'touch' | |
# fitbit call back route/ authorize not authenticate here, small diff | |
# must use (req, res) callback here for this to work prop with authorize | |
app.get '/connect/fitbit/callback', isLoggedIn, | |
passport.authorize('fitbit', failureRedirect: '/login'), (req, res) -> | |
res.redirect '#/main/stream' | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use strict' | |
Q = require 'q' | |
mongoose = require 'mongoose' | |
nodemailer = require 'nodemailer' | |
uuid = require 'node-uuid' | |
UserSchema = new mongoose.Schema( | |
username: | |
type: String | |
unique: true | |
required: true | |
email: | |
type: String | |
unique: true | |
required: true | |
# Should make seperate schema for token here and associate it with the user | |
# this will allow us to have an expiring reset token for reset token | |
pass_reset: | |
type: String | |
password: | |
type: String | |
required: true | |
salt: String | |
createdAt: | |
type: Date | |
default: Date.now | |
updatedAt: | |
type: Date | |
default: Date.now | |
pro: Boolean | |
groups: | |
[{type: mongoose.Schema.ObjectId, ref: 'Group'}] | |
authData: | |
fitbit: | |
avatar: String | |
access_token: String | |
access_token_secret: String | |
) | |
### | |
Static methods to increase flow and tedious queries | |
### | |
# Find user by email, mainly used in password reset but not soley | |
UserSchema.statics.findByEmail = (email) -> | |
defer = Q.defer() | |
@findOne 'email': email, (err, user) -> | |
if err then defer.reject err | |
if user then defer.resolve user | |
# FIXME | |
if not user then console.log 'no user by that email' | |
defer.promise | |
UserSchema.statics.resetPassword = (user) -> | |
User = mongoose.model 'User' | |
defer = Q.defer() | |
# get set up the given user's _id to search | |
id = user._id | |
# define what fields will be updated and with what values | |
update = | |
pass_reset: uuid.v4() # genrates a v4 random uuid | |
# define options on the query, this one will return the reset | |
# token that was just updated | |
options = | |
select: | |
'pass_reset': true | |
console.log 'id', id, 'update', update, 'options', options | |
User.findByIdAndUpdate id, update, options, (err, reset_token) -> | |
if err then defer.reject err | |
if reset_token | |
userAndEmail = | |
email: user.email | |
reset: reset_token | |
username: user.username | |
defer.resolve userAndEmail | |
if not reset_token then console.log 'could not get reset_token' | |
defer.promise | |
UserSchema.statics.emailPassword = (user) -> | |
# create a reusable transport method with nodemailer | |
defer = Q.defer() | |
smtpTransport = nodemailer.createTransport( | |
'SMTP', {service: 'Gmail', auth: { | |
user: 'willscottmoss@gmail.com' | |
pass: 'ballin35' | |
} | |
} | |
) | |
console.log 'reset', user.reset.pass_reset | |
# setup email options to be sent can HTML if need be, All Unicode | |
mailOptions = | |
'from': 'Scott Moss <scottmoss35@gmail.com>' | |
'to': user.email | |
'subject': 'Sweatr reset password' | |
'html': "<h1> Hello #{user.username}!</h1>"+ | |
"<p> Here is the link to reset your password </p>" + | |
"<a href=http://localhost:3000/user/reset/"+ | |
"#{user.reset.pass_reset} >Reset</a>" | |
smtpTransport.sendMail mailOptions, (err, response) -> | |
if err then defer.reject err | |
if response then defer.resolve response | |
defer.promise | |
module.exports = mongoose.model 'User', UserSchema |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
User = require '../../models/user' | |
Stats = require '../../models/stat' | |
moment = require 'moment' | |
{getDailyActivities} = require './helpers' | |
{saveStats} = require './helpers' | |
{dateRange} = require './helpers' | |
module.exports = | |
#========================== | |
# static assets | |
#========================== | |
index: (req, res) -> | |
# by default express will send index.html on GET '/' | |
# so this is just optional | |
# send back splash/landing instead | |
# of jsut login/signup | |
res.sendfile('index.html') | |
#========================== | |
# CRUD ops | |
#========================== | |
# logout helper | |
logout: (req, res) -> | |
id = req.user._id | |
User.findById id, (err, user) -> | |
if err | |
throw new Error err, ' cannot find user to log' | |
if user | |
user.lastLoggedIn = Date.now() | |
user.save (err) -> | |
throw new Error err if err | |
req.logout() | |
res.redirect '/' | |
# get curent user on the fly if need it, should not need this, security issue | |
getUser: (req, res) -> | |
id = req.params.id | |
res.send 401 if id isnt String req.user._id # can only get logged in user | |
User.findById id, (err, user) -> | |
if err | |
throw new Error err, 'User.findOne error ' | |
if not user | |
# user isn't in the db | |
res.send 204 | |
if user | |
res.json user | |
# method to retrieve forgotten password. User posts email | |
# generate access-token (should expire), email user link | |
# to update password, access token is attached as param to | |
# link. Check the access-token, if valid, then accept the | |
# new password and redirect somewhere | |
forgotPassword: (req, res) -> | |
email = req.body.email | |
User.findByEmail(email) | |
.then(User.resetPassword) | |
.then(User.emailPassword) | |
.then (response) -> | |
console.log 'sent message', response | |
res.send 200 | |
.fail (err) -> | |
console.log 'err somewhere in reset pass', err | |
res.send 500 | |
resetPassword: (req, res) -> | |
# send a redirect to an angular template instead of a pure html file | |
# this will allow for proper design and control over the password rest form | |
res.sendfile 'password.html', | |
root:"#{__dirname}/../../../../public/" | |
userActivity: (req, res) -> | |
# define the DB query to get results | |
today = moment().subtract('days', 1).format 'YYYY-MM-DD' | |
query = user: req.user._id | |
dateRange today, today, query | |
Stats.find query, (err, stats) -> | |
if err | |
throw new Error err, 'error getting api/user data' | |
else if stats.length | |
# if stats, send back reqested range of stats along with user data | |
data = | |
username: req.user.username | |
pic: req.user.authData.fitbit.avatar | |
stats: stats[0] | |
res.json data | |
else if !stats.length | |
# if no stats in db, go to fitbit and get 7 days | |
# worth of stats and save to db | |
date = moment().subtract('days', 7) | |
toDate = moment().subtract('days', 1) | |
query = | |
'user': query.user | |
'date': today | |
while date <= toDate | |
# helper function that goes to fitbit and gets a weeks data set | |
getDailyActivities req, res, date.format('YYYY-MM-DD'), saveStats | |
date = date.add 'days', 1 | |
deleteUser: (req, res) -> | |
id = req.user._id | |
User.findById id, (err, user) -> | |
if err | |
throw new Error err, 'could not find user to delete' | |
if not user | |
# user is not in DB anyways.. | |
res.send 204 | |
else | |
user.remove (err, user) -> # remove user record | |
if err | |
throw new Error err, 'could not delete user' | |
req.logout() | |
res.redirect '/' | |
# helper to protect angular routes on client | |
loggedIn: (req, res) -> | |
res.send if req.isAuthenticated() then req.user else "0" | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
fdgsdgf