Added account creation, switched to arrow functions
parent
3e08a462f2
commit
67061fd99c
386
config/auth.js
386
config/auth.js
|
@ -4,10 +4,12 @@ const
|
|||
mw = require('./middleware.js'),
|
||||
mail = require('./mail.js'),
|
||||
User = require('./models.js').user,
|
||||
slug = require('slug'),
|
||||
crypto = require('crypto'),
|
||||
env = require('./env.js');
|
||||
|
||||
module.exports = function(app, passport) {
|
||||
|
||||
module.exports = (app, passport) => {
|
||||
|
||||
// Methods for success and failure
|
||||
const
|
||||
loginOutcome = {
|
||||
|
@ -18,130 +20,209 @@ module.exports = function(app, passport) {
|
|||
failureRedirect: '/settings',
|
||||
failureFlash: true
|
||||
},
|
||||
loginCallback = function(req,res){
|
||||
loginCallback = (req,res)=>{
|
||||
res.redirect( req.session.next || '/settings' );
|
||||
delete req.session.next;
|
||||
};
|
||||
|
||||
|
||||
// Login/-out
|
||||
app.route('/login')
|
||||
.get( function(req,res){
|
||||
if (req.isAuthenticated()){
|
||||
res.redirect('/settings'); }
|
||||
.get( (req,res)=>{
|
||||
req.session.next = req.header('Referer');
|
||||
if (req.isAuthenticated()){
|
||||
res.redirect(req.session.next||'/settings'); }
|
||||
else { res.render('login'); }
|
||||
})
|
||||
.post( passport.authenticate('local',loginOutcome), loginCallback );
|
||||
app.get('/logout', function(req,res){
|
||||
app.get('/logout', (req,res)=>{
|
||||
req.logout();
|
||||
req.flash('success',`You have been logged out.`);
|
||||
res.redirect('/');
|
||||
});
|
||||
|
||||
|
||||
// Signup
|
||||
app.post('/signup', function(req,res,next){
|
||||
User.findOne({'email':req.body.email}, function(err,user){
|
||||
if (err){ next(err); }
|
||||
|
||||
// User already exists
|
||||
else if (user){
|
||||
req.flash('warning','A user with that email already exists! If you forgot your password, use <a href="/login/forgot">this form</a>.');
|
||||
res.redirect('/login');
|
||||
} else {
|
||||
|
||||
// Create user
|
||||
var newUser = new User();
|
||||
newUser.email = req.body.email;
|
||||
newUser.created = Date.now();
|
||||
|
||||
newUser.createToken(function(err,token){
|
||||
if (err){ next(err); }
|
||||
mail({
|
||||
from: '"Tracman" <NoReply@tracman.org>',
|
||||
to: req.body.email,
|
||||
subject: 'Complete your Tracman registration',
|
||||
text: `Welcome to Tracman! \n\nTo complete your registration, follow this link and set your password:\n${env.url}/settings/password/${token}`,
|
||||
html: `<p>Welcome to Tracman! </p><p>To complete your registration, follow this link and set your password:<br><a href="${env.url}/settings/password/${token}">${env.url}/settings/password/${token}</a></p>`
|
||||
}).then(function(){
|
||||
req.flash('success',`An email has been sent to <u>${req.body.email}</u>. Check your inbox to complete your registration.`);
|
||||
res.redirect('/');
|
||||
}).catch(function(err){
|
||||
next(err);
|
||||
});
|
||||
app.get('/signup', (req,res)=>{
|
||||
res.redirect('/login#signup');
|
||||
}).post('/signup', (req,res,next)=>{
|
||||
|
||||
// Send token and alert user
|
||||
function sendToken(user){
|
||||
|
||||
// Create a password token
|
||||
user.createToken(function(err,token){
|
||||
if (err){ mw.throwErr(err,req); }
|
||||
|
||||
// Email the instructions to continue
|
||||
mail.send({
|
||||
from: mail.from,
|
||||
to: `<${user.email}>`,
|
||||
subject: 'Complete your Tracman registration',
|
||||
text: mail.text(`Welcome to Tracman! \n\nTo complete your registration, follow this link and set your password:\n${env.url}/settings/password/${token}`),
|
||||
html: mail.html(`<p>Welcome to Tracman! </p><p>To complete your registration, follow this link and set your password:<br><a href="${env.url}/settings/password/${token}">${env.url}/settings/password/${token}</a></p>`)
|
||||
}).catch(function(err){
|
||||
mw.throwErr(err,req);
|
||||
res.redirect('/login#signup');
|
||||
}).then(function(){
|
||||
req.flash('success', `An email has been sent to <u>${user.email}</u>. Check your inbox to complete your registration. `);
|
||||
res.redirect('/');
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Forgot password
|
||||
app.route('/login/forgot')
|
||||
.all( function(req,res,next){
|
||||
if (req.isAuthenticated()){ res.redirect('/settings'); }
|
||||
else { next(); }
|
||||
})
|
||||
.get( function(req,res,next){
|
||||
res.render('forgot');
|
||||
})
|
||||
.post( function(req,res,next){
|
||||
|
||||
//TODO: Validate and sanitize email
|
||||
// req.assert('email', 'Please enter a valid email address.').isEmail();
|
||||
// req.sanitize('email').normalizeEmail({ remove_dots: false });
|
||||
|
||||
User.findOne({'email':req.body.email},function(err,user){
|
||||
if (err){ next(err); }
|
||||
else if (!user) {
|
||||
req.flash('danger', `No user has <u>${req.body.email}</u> set as their email address. `);
|
||||
res.redirect('/login/forgot');
|
||||
} else {
|
||||
|
||||
// Set reset token to user
|
||||
user.createToken( function(err,token){
|
||||
if (err){ next(err); }
|
||||
|
||||
// Email reset link
|
||||
mail({
|
||||
from: '"Tracman" <NoReply@tracman.org>',
|
||||
to: `"${user.name}"" <${user.email}>`,
|
||||
subject: 'Reset your Tracman password',
|
||||
text: `Hi, \n\nDid you request to reset your Tracman password? If so, follow this link to do so:\n${env.url}/settings/password/${token}\n\nIf you didn't initiate this request, just ignore this email. `,
|
||||
html: `<p>Hi, </p><p>Did you request to reset your Tracman password? If so, follow this link to do so:<br><a href="${env.url}/settings/password/${token}">${env.url}/settings/password/${token}</a></p><p>If you didn't initiate this request, just ignore this email. </p>`
|
||||
}).then(function(){
|
||||
req.flash('success', `An email has been sent to <u>${req.body.email}</u>. Check your email for instructions to reset your password. `);
|
||||
res.redirect('/');
|
||||
}).catch(function(err){
|
||||
next(err);
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
// Check if somebody already has that email
|
||||
User.findOne({'email':req.body.email}, (err,user)=>{
|
||||
if (err){ mw.throwErr(err,req); }
|
||||
|
||||
// User already exists
|
||||
if (user && user.auth.password) {
|
||||
req.flash('warning','A user with that email already exists! If you forgot your password, you can <a href="/login/forgot">reset it here</a>.');
|
||||
res.redirect('/login#login');
|
||||
next();
|
||||
}
|
||||
|
||||
// User exists but hasn't created a password yet
|
||||
else if (user) {
|
||||
// Send another token (or the same one if it hasn't expired)
|
||||
sendToken(user);
|
||||
}
|
||||
|
||||
// Create user
|
||||
else {
|
||||
|
||||
user = new User();
|
||||
user.created = Date.now();
|
||||
user.email = req.body.email;
|
||||
user.slug = slug(user.email.substring(0, user.email.indexOf('@')));
|
||||
|
||||
// Generate unique slug
|
||||
var generateSlug = new Promise((resolve,reject) => {
|
||||
(function checkSlug(s,cb){
|
||||
User.findOne({slug:s}, function(err, existingUser){
|
||||
if (err) { mw.throwErr(err,req); }
|
||||
|
||||
// Slug in use: generate a random one and retry
|
||||
if (existingUser){
|
||||
s = '';
|
||||
while (s.length<6) {
|
||||
s+='abcdefghijkmnpqrtuvwxy346789'.charAt(Math.floor(Math.random()*28));
|
||||
}
|
||||
checkSlug(s,cb);
|
||||
|
||||
// Unique slug: proceed
|
||||
} else { cb(s); }
|
||||
});
|
||||
})(user.slug, function(newSlug){
|
||||
user.slug = newSlug;
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
// Generate sk32
|
||||
var generateSk32 = new Promise((resolve,reject) => {
|
||||
crypto.randomBytes(32, (err,buf)=>{
|
||||
if (err) { mw.throwErr(err,req); }
|
||||
user.sk32 = buf.toString('hex');
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
// Save user and send the token by email
|
||||
Promise.all([generateSlug, generateSk32])
|
||||
.catch(err => {
|
||||
mw.throwErr(err,req);
|
||||
}).then(() => {
|
||||
user.save( (err)=>{
|
||||
if (err){ mw.throwErr(err,req); }
|
||||
sendToken(user);
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
// Forgot password
|
||||
// app.route('/login/forgot')
|
||||
// .all( (req,res,next)=>{
|
||||
// if (req.isAuthenticated()){ res.redirect('/settings'); }
|
||||
// else { next(); }
|
||||
// })
|
||||
// .get( (req,res,next)=>{
|
||||
// res.render('forgot');
|
||||
// })
|
||||
// .post( (req,res,next)=>{
|
||||
|
||||
// //TODO: Validate and sanitize email
|
||||
// // req.assert('email', 'Please enter a valid email address.').isEmail();
|
||||
// // req.sanitize('email').normalizeEmail({ remove_dots: false });
|
||||
|
||||
// User.findOne( {'email':req.body.email}, (err,user)=>{
|
||||
// if (err){ next(err); }
|
||||
// else if (!user) {
|
||||
// req.flash('danger', `No user has <u>${req.body.email}</u> set as their email address. `);
|
||||
// res.redirect('/login/forgot');
|
||||
// } else {
|
||||
|
||||
// // Set reset token to user
|
||||
// user.createToken( (err,token)=>{
|
||||
// if (err){ next(err); }
|
||||
|
||||
// // Email reset link
|
||||
// mail({
|
||||
// from: '"Tracman" <NoReply@tracman.org>',
|
||||
// to: `"${user.name}"" <${user.email}>`,
|
||||
// subject: 'Reset your Tracman password',
|
||||
// text: `Hi, \n\nDid you request to reset your Tracman password? If so, follow this link to do so:\n${env.url}/settings/password/${token}\n\nIf you didn't initiate this request, just ignore this email. `,
|
||||
// html: `<p>Hi, </p><p>Did you request to reset your Tracman password? If so, follow this link to do so:<br><a href="${env.url}/settings/password/${token}">${env.url}/settings/password/${token}</a></p><p>If you didn't initiate this request, just ignore this email. </p>`
|
||||
// }).then(()=>{
|
||||
// req.flash('success', `An email has been sent to <u>${req.body.email}</u>. Check your email for instructions to reset your password. `);
|
||||
// res.redirect('/');
|
||||
// }).catch((err)=>{
|
||||
// next(err);
|
||||
// });
|
||||
|
||||
// });
|
||||
// }
|
||||
// });
|
||||
|
||||
// });
|
||||
|
||||
// Social
|
||||
app.get('/login/:service', function(req,res,next){
|
||||
app.get('/login/:service', (req,res,next)=>{
|
||||
var service = req.params.service;
|
||||
if (service==='google'){
|
||||
var sendParams = {scope:['profile']};
|
||||
}
|
||||
if (!req.user) { // Social login
|
||||
|
||||
// Social login
|
||||
if (!req.user) {
|
||||
passport.authenticate(service, sendParams)(req,res,next);
|
||||
} else if (!req.user.auth[service]) { // Connect social account
|
||||
}
|
||||
|
||||
// Connect social account
|
||||
else if (!req.user.auth[service]) {
|
||||
passport.authorize(service, sendParams)(req,res,next);
|
||||
} else { // Disconnect social account
|
||||
}
|
||||
|
||||
// Disconnect social account
|
||||
else {
|
||||
req.user.auth[service] = undefined;
|
||||
req.user.save(function(err){
|
||||
if (err){ mw.throwErr(err,req); }
|
||||
req.user.save( (err)=>{
|
||||
if (err) {
|
||||
mw.throwErr(err,req);
|
||||
res.redirect('/settings');
|
||||
}
|
||||
else {
|
||||
req.flash('success', `${mw.capitalize(service)} account disconnected. `);
|
||||
res.redirect('/settings');
|
||||
}
|
||||
res.redirect('/settings');
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
app.get('/login/:service/cb', function(req,res,next){
|
||||
app.get('/login/:service/cb', (req,res,next)=>{
|
||||
var service = req.params.service;
|
||||
if (!req.user) {
|
||||
passport.authenticate(service, loginOutcome)(req,res,next);
|
||||
|
@ -151,123 +232,12 @@ module.exports = function(app, passport) {
|
|||
passport.authenticate(service, connectOutcome)(req,res,next);
|
||||
}
|
||||
}, loginCallback);
|
||||
|
||||
// Old google auth
|
||||
// app.get('/auth/google', passport.authenticate('google', { scope: [
|
||||
// 'https://www.googleapis.com/auth/plus.login',
|
||||
// 'https://www.googleapis.com/auth/plus.profile.emails.read'
|
||||
// ] }));
|
||||
// app.get('/auth/google/callback', passport.authenticate('google', {
|
||||
// failureRedirect: '/',
|
||||
// failureFlash: true,
|
||||
// successRedirect: '/',
|
||||
// successFlash: true
|
||||
// } ));
|
||||
|
||||
|
||||
// Android auth
|
||||
//TODO: See if there's a better method
|
||||
app.get('/auth/google/idtoken', passport.authenticate('google-id-token'), function (req,res) {
|
||||
app.get('/auth/google/idtoken', passport.authenticate('google-id-token'), (req,res)=>{
|
||||
if (!req.user) { res.sendStatus(401); }
|
||||
else { res.send(req.user); }
|
||||
} );
|
||||
|
||||
|
||||
};
|
||||
|
||||
// passport.use(new GoogleStrategy({
|
||||
// clientID: env.googleClientId,
|
||||
// clientSecret: env.googleClientSecret,
|
||||
// callbackURL: env.url+'/auth/google/callback',
|
||||
// passReqToCallback: true
|
||||
// }, function(req, accessToken, refreshToken, profile, done) {
|
||||
|
||||
// // Check for user
|
||||
// User.findOne({googleID: profile.id}, function(err, user){
|
||||
|
||||
// // Error
|
||||
// if (err) { console.log('Error finding user with google ID: '+profile.id+'\n'+err); }
|
||||
|
||||
// // User found
|
||||
// if (!err && user !== null) /* Log user in */ {
|
||||
// if (!user.name) { user.name=profile.displayName; }
|
||||
// user.lastLogin = Date.now();
|
||||
// user.save(function (err, raw) {
|
||||
// if (err) { throwErr(err,req); }
|
||||
// }); done(null, user);
|
||||
// }
|
||||
|
||||
// // User not found
|
||||
// else /* create user */ {
|
||||
// user = new User();
|
||||
// user.googleID = profile.id;
|
||||
// user.name = profile.displayName;
|
||||
// user.email = profile.emails[0].value;
|
||||
// user.slug = slug(profile.displayName).toLowerCase();
|
||||
// user.created = Date.now();
|
||||
// user.lastLogin = Date.now();
|
||||
// // user.settings = { units:'standard', defaultMap:'road', defaultZoom:11, showSpeed:false, showTemp:false, showAlt:false, showStreetview:false },
|
||||
// // user.last = { lat:0, lon:0, dir:0, alt:0, spd:0 },
|
||||
// // user.isPro = false;
|
||||
// // user.isAdmin = false;
|
||||
// var cbc = 2;
|
||||
// var successMessage, failMessage;
|
||||
|
||||
// // Generate slug
|
||||
// (function checkSlug(s,cb) {
|
||||
// //console.log('checking ',s);
|
||||
// User.findOne({slug:s}, function(err, existingUser){
|
||||
// if (err) { console.log('No user found for ',slug,':',err); }
|
||||
// if (existingUser){
|
||||
// s = '';
|
||||
// while (s.length<6) {
|
||||
// s+='abcdefghijkmnpqrtuvwxy346789'.charAt(Math.floor(Math.random()*28));
|
||||
// }
|
||||
// checkSlug(s,cb);
|
||||
// } else { cb(s); }
|
||||
// });
|
||||
// })(user.slug, function(newSlug){
|
||||
// user.slug = newSlug;
|
||||
// if (cbc>1) /* waiting on other calls */ { cbc--; }
|
||||
// else { done(null, user, { success:successMessage, failure:failMessage }); }
|
||||
// });
|
||||
|
||||
// // Generate sk32
|
||||
// crypto.randomBytes(32, function(err,buf) {
|
||||
// if (err) {console.log('Unable to get random bytes:',err);}
|
||||
// if (!buf) {console.log('Unable to get random buffer');}
|
||||
// else {
|
||||
// user.sk32 = buf.toString('hex');
|
||||
// user.save(function(err) {
|
||||
// if (err) {
|
||||
// console.log('Error saving new user '+err);
|
||||
// var failMessage = 'Something went wrong creating your account. Would you like to <a href="/bug">report this error</a>?';
|
||||
// } else { successMessage = 'Your account has been created. Next maybe you should download the <a href="/android">android app</a>. ' }
|
||||
// if (cbc>1) /* waiting on other calls */ { cbc--; }
|
||||
// else { done(null, user, { success:successMessage, failure:failMessage }); }
|
||||
// });
|
||||
// }
|
||||
// });
|
||||
|
||||
// }
|
||||
|
||||
// });
|
||||
|
||||
// }));
|
||||
|
||||
// passport.use(new GoogleTokenStrategy({
|
||||
// clientID: env.googleClientId
|
||||
// }, function(parsedToken, googleId, done) {
|
||||
// User.findOne({googleID:googleId}, function(err, user) {
|
||||
// if (err) {
|
||||
// console.log('Error finding user for gToken login with google profile ID: '+googleId+'\n'+err); }
|
||||
// if (!err && user !== null) { // Log in
|
||||
// user.lastLogin = Date.now();
|
||||
// user.save(function (err) {
|
||||
// if (err) {
|
||||
// console.log('Error saving user\'s lastLogin for gToken login with google profile ID: '+googleId+'\n'+err); }
|
||||
// });
|
||||
// return done(err, user);
|
||||
// } else { // No such user
|
||||
// done(null, false);
|
||||
// }
|
||||
// });
|
||||
// }));
|
||||
|
|
|
@ -17,39 +17,26 @@ let transporter = nodemailer.createTransport({
|
|||
});
|
||||
|
||||
/* Confirm login */
|
||||
// transporter.verify(function(err, success) {
|
||||
// transporter.verify( (err,success)=>{
|
||||
// if (err){ console.error(`SMTP Error: ${err}`); }
|
||||
// console.log(`SMTP ${!success?'not ':''}ready...`);
|
||||
// });
|
||||
|
||||
/* Send test email */
|
||||
// transporter.sendMail({
|
||||
// to: `"Keith Irwin" <hypergeek14@gmail.com>`,
|
||||
// from: '"Tracman" <NoReply@tracman.org>',
|
||||
// subject: 'Test email',
|
||||
// text: "Looks like everything's working",
|
||||
// html: ""
|
||||
// }).then(function(){
|
||||
// console.log("Email should have sent...");
|
||||
// }).catch(function(err){
|
||||
// console.error(err);
|
||||
// });
|
||||
// } );
|
||||
|
||||
module.exports = {
|
||||
|
||||
send: transporter.sendMail.bind(transporter),
|
||||
|
||||
text: function(text) {
|
||||
text: (text)=>{
|
||||
return `Tracman\n\n${text}\n\nDo not reply to this email\nFor information about why you received this email, see the privacy policy at ${env.url}/privacyy#email`;
|
||||
},
|
||||
|
||||
html: function(text) {
|
||||
html: (text)=>{
|
||||
return `<h1><a href="/" style="text-decoration:none;"><span style="color:#000;font-family:sans-serif;font-size:36px;font-weight:bold"><img src="${env.url}/static/img/icon/by/32.png" alt="+" style="margin-right:10px">Tracman</span></a></h1>${text}<p style="font-size:8px;">Do not reply to this email. For information about why you recieved this email, see our <a href="${env.url}/privacy#email">privacy policy</a>. </p>`;
|
||||
},
|
||||
|
||||
from: `"Tracman" <NoReply@tracman.org>`,
|
||||
|
||||
to: function(user) {
|
||||
to: (user)=>{
|
||||
return `"${user.name}" <${user.email}>`;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,28 +5,30 @@ const env = require('./env.js');
|
|||
module.exports = {
|
||||
|
||||
// Throw error
|
||||
throwErr: function(err,req=null){
|
||||
console.error('Middleware error:'+err.message+'\nfor request:\n'+req);
|
||||
if (env.mode==='production') {
|
||||
req.flash('danger', 'An error occured. <br>Would you like to <a href="https://github.com/Tracman-org/Server/issues/new">report it</a>?');
|
||||
} else { // development
|
||||
req.flash('danger', err);
|
||||
throwErr: (err,req=null)=>{
|
||||
console.error(`⛔️ ${err.stack}`);
|
||||
if (req){
|
||||
if (env.mode==='production') {
|
||||
req.flash('danger', 'An error occured. <br>Would you like to <a href="https://github.com/Tracman-org/Server/issues/new">report it</a>?');
|
||||
} else { // development
|
||||
req.flash('danger', err.message);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
// Capitalize the first letter of a string
|
||||
capitalize: function(str){ 'use strict';
|
||||
capitalize: (str)=>{
|
||||
return str.charAt(0).toUpperCase() + str.slice(1);
|
||||
},
|
||||
|
||||
|
||||
// Ensure authentication
|
||||
ensureAuth: function(req,res,next){
|
||||
ensureAuth: (req,res,next)=>{
|
||||
if (req.isAuthenticated()) { return next(); }
|
||||
else { res.redirect('/login'); }
|
||||
},
|
||||
|
||||
|
||||
// Ensure administrator
|
||||
ensureAdmin: function(req,res,next){
|
||||
ensureAdmin: (req,res,next)=>{
|
||||
if (req.user.isAdmin){ return next(); }
|
||||
else { res.sendStatus(401); }
|
||||
//TODO: test this by logging in as !isAdmin and go to /admin
|
||||
|
|
|
@ -6,7 +6,7 @@ const mongoose = require('mongoose'),
|
|||
crypto = require('crypto');
|
||||
|
||||
const userSchema = new mongoose.Schema({
|
||||
name: {type:String, required:true},
|
||||
name: {type:String},
|
||||
email: {type:String, required:true},
|
||||
slug: {type:String, required:true, unique:true},
|
||||
auth: {
|
||||
|
@ -19,52 +19,52 @@ const userSchema = new mongoose.Schema({
|
|||
},
|
||||
isAdmin: {type:Boolean, required:true, default:false},
|
||||
isPro: {type:Boolean, required:true, default:false},
|
||||
created: Date,
|
||||
created: {type:Date, required:true},
|
||||
lastLogin: Date,
|
||||
settings: {
|
||||
units: {type:String, default:'standard'},
|
||||
defaultMap: {type:String, default:'road'},
|
||||
defaultZoom: {type:Number, default:11},
|
||||
showSpeed: {type:Boolean, default:false},
|
||||
showTemp: {type:Boolean, default:false},
|
||||
showAlt: {type:Boolean, default:false},
|
||||
showStreetview: {type:Boolean, default:false}
|
||||
units: {type:String, required:true, default:'standard'},
|
||||
defaultMap: {type:String, required:true, default:'road'},
|
||||
defaultZoom: {type:Number, required:true, default:11},
|
||||
showSpeed: {type:Boolean, required:true, default:false},
|
||||
showTemp: {type:Boolean, required:true, default:false},
|
||||
showAlt: {type:Boolean, required:true, default:false},
|
||||
showStreetview: {type:Boolean, required:true, default:false}
|
||||
},
|
||||
last: {
|
||||
time: Date,
|
||||
lat: {type:Number, default:0},
|
||||
lon: {type:Number, default:0},
|
||||
dir: {type:Number, default:0},
|
||||
alt: {type:Number, default:0},
|
||||
spd: {type:Number, default:0}
|
||||
lat: {type:Number, required:true, default:0},
|
||||
lon: {type:Number, required:true, default:0},
|
||||
dir: {type:Number, required:true, default:0},
|
||||
alt: {type:Number, required:true, default:0},
|
||||
spd: {type:Number, required:true, default:0}
|
||||
},
|
||||
sk32: {type:String, required:true, unique:true}
|
||||
}).plugin(unique);
|
||||
|
||||
/* User methods */ {
|
||||
|
||||
|
||||
// Generate hash for new password
|
||||
userSchema.methods.generateHash = function(password, next) {
|
||||
bcrypt.genSalt(8, function(err,salt){
|
||||
userSchema.methods.generateHash = (password,next)=>{
|
||||
bcrypt.genSalt(8, (err,salt)=>{
|
||||
if (err){ return next(err); }
|
||||
bcrypt.hash(password, salt, null, next);
|
||||
});
|
||||
};
|
||||
|
||||
// Create password reset token
|
||||
userSchema.methods.createToken = function(next){
|
||||
userSchema.methods.createToken = (next)=>{
|
||||
var user = this;
|
||||
if ( user.auth.tokenExpires <= Date.now() ){
|
||||
|
||||
|
||||
// Reuse old token, resetting clock
|
||||
user.auth.tokenExpires = Date.now() + 3600000; // 1 hour
|
||||
user.save();
|
||||
return next(null.user.auth.passToken);
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
|
||||
// Create new token
|
||||
crypto.randomBytes(16, function(err,buf){
|
||||
crypto.randomBytes(16, (err,buf)=>{
|
||||
if (err){ next(err,null); }
|
||||
else {
|
||||
user.auth.passToken = buf.toString('hex');
|
||||
|
@ -73,15 +73,15 @@ const userSchema = new mongoose.Schema({
|
|||
return next(null,user.auth.passToken);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Check for valid password
|
||||
userSchema.methods.validPassword = function(password, next) {
|
||||
userSchema.methods.validPassword = (password,next)=>{
|
||||
bcrypt.compare(password, this.auth.password, next);
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
|
|
@ -9,81 +9,55 @@ const
|
|||
mw = require('./middleware.js'),
|
||||
User = require('./models.js').user;
|
||||
|
||||
module.exports = function(passport) {
|
||||
module.exports = (passport)=>{
|
||||
|
||||
// Serialize/deserialize users
|
||||
passport.serializeUser(function(user,done) {
|
||||
passport.serializeUser((user,done)=>{
|
||||
done(null, user.id);
|
||||
});
|
||||
passport.deserializeUser(function(id,done) {
|
||||
User.findById(id, function(err, user) {
|
||||
passport.deserializeUser((id,done)=>{
|
||||
User.findById(id, (err,user)=>{
|
||||
if(!err){ done(null, user); }
|
||||
else { done(err, null); }
|
||||
});
|
||||
});
|
||||
|
||||
// Signup
|
||||
// passport.use('signup', new LocalStrategy({
|
||||
// usernameField: 'email',
|
||||
// passwordField: 'password',
|
||||
// passReqToCallback : true
|
||||
// }, function(req, email, password, done) {
|
||||
// process.nextTick(function() {
|
||||
// User.findOne({'email':email }, function(err, user) {
|
||||
// if (err){ return done(err); }
|
||||
|
||||
// // Check for existing user
|
||||
// if (user) {
|
||||
// return done( null, false, req.flash('warning','That email is already in use. Try logging in below.') );
|
||||
|
||||
// // Create user
|
||||
// } else {
|
||||
// var newUser = new User();
|
||||
// newUser.email = email;
|
||||
// newUser.created = Date.now();
|
||||
// newUser.lastLogin = Date.now();
|
||||
// newUser.generateHash(password, function(err, hash){
|
||||
// if (err){ return done(err); }
|
||||
// newUser.auth.password = hash;
|
||||
// newUser.save(function(err) {
|
||||
// if (err){ return done(err); }
|
||||
// return done( null, newUser );
|
||||
// });
|
||||
// });
|
||||
// }
|
||||
|
||||
// });
|
||||
// });
|
||||
// })
|
||||
// );
|
||||
|
||||
// Local
|
||||
passport.use('local', new LocalStrategy({
|
||||
usernameField: 'email',
|
||||
passwordField: 'password',
|
||||
passReqToCallback : true
|
||||
}, function(req, email, password, done) {
|
||||
User.findOne({ 'email':email }, function (err, user) {
|
||||
passReqToCallback: true
|
||||
}, (req,email,password,done)=>{
|
||||
User.findOne( {'email':email}, (err,user)=>{
|
||||
if (err){ return done(err); }
|
||||
|
||||
// Wrong username
|
||||
// No user with that email
|
||||
if (!user) {
|
||||
return done( null, false, req.flash('danger','No account exists for that email.') );
|
||||
// Username correct, password incorrect
|
||||
} else {
|
||||
return done( null, false, req.flash('danger','Incorrect email or password.') );
|
||||
}
|
||||
|
||||
// User exists
|
||||
else {
|
||||
|
||||
// Check password
|
||||
user.validPassword(password, function(err,res){
|
||||
if (err){ console.log('Passport error:\n',err); }
|
||||
if (!res) { // Password incorrect
|
||||
return done( null, false, req.flash('danger','Incorrect password.') );
|
||||
} else { // Successful login
|
||||
user.validPassword( password, (err,res)=>{
|
||||
if (err){ return done(err); }
|
||||
|
||||
// Password incorrect
|
||||
if (!res) {
|
||||
return done( null, false, req.flash('danger','Incorrect email or password.') );
|
||||
}
|
||||
|
||||
// Successful login
|
||||
else {
|
||||
user.lastLogin = Date.now();
|
||||
user.save();
|
||||
return done( null, user );
|
||||
return done(null,user);
|
||||
}
|
||||
});
|
||||
|
||||
} );
|
||||
}
|
||||
});
|
||||
} );
|
||||
}
|
||||
));
|
||||
|
||||
|
@ -96,35 +70,39 @@ module.exports = function(passport) {
|
|||
|
||||
var query = {};
|
||||
query['auth.'+service] = profileId;
|
||||
User.findOne(query, function (err, user) {
|
||||
User.findOne(query, (err,user)=>{
|
||||
if (err){ return done(err); }
|
||||
|
||||
// Can't find user
|
||||
else if (!user){
|
||||
// console.log('User not found.');
|
||||
|
||||
// Lazy update from old googleId field
|
||||
if (service==='google') {
|
||||
User.findOne({'googleID':parseInt(profileId)}, function(err,user){
|
||||
// console.log(`searched for user with googleID ${profileId}`);
|
||||
if (err){ mw.throwErr(err,req); }
|
||||
User.findOne( {'googleID':parseInt(profileId)}, (err,user)=>{
|
||||
if (err){ return done(err); }
|
||||
if (user) {
|
||||
// console.log(`Lazily updating schema for ${user.name}.`);
|
||||
user.auth.google = profileId;
|
||||
user.googleId = null;
|
||||
user.save(function(err){
|
||||
user.save( (err)=>{
|
||||
if (err){ mw.throwErr(err,req); }
|
||||
else { console.info(`🗂️ Lazily updated schema for ${user.name}.`); }
|
||||
return done(null, user);
|
||||
});
|
||||
} );
|
||||
} else {
|
||||
req.flash('danger',`There's no user for that ${service} account. `);
|
||||
return done();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
|
||||
} );
|
||||
}
|
||||
|
||||
// No googleId either
|
||||
else {
|
||||
req.flash('danger',`There's no user for that ${service} account. `);
|
||||
return done();
|
||||
}
|
||||
}
|
||||
|
||||
// Successfull social login
|
||||
else {
|
||||
// console.log(`Found user: ${user}`);
|
||||
return done(null, user);
|
||||
|
@ -134,12 +112,12 @@ module.exports = function(passport) {
|
|||
|
||||
// Connect account
|
||||
else {
|
||||
console.log(`Connecting ${service} account.`);
|
||||
// console.log(`Connecting ${service} account.`);
|
||||
req.user.auth[service] = profileId;
|
||||
req.user.save(function(err){
|
||||
req.user.save( (err)=>{
|
||||
if (err){ return done(err); }
|
||||
else { return done(null, req.user); }
|
||||
});
|
||||
} );
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -150,7 +128,7 @@ module.exports = function(passport) {
|
|||
clientSecret: env.googleClientSecret,
|
||||
callbackURL: env.url+'/login/google/cb',
|
||||
passReqToCallback: true
|
||||
}, function(req, accessToken, refreshToken, profile, done) {
|
||||
}, (req, accessToken, refreshToken, profile, done)=>{
|
||||
socialLogin(req, 'google', profile.id, done);
|
||||
}
|
||||
));
|
||||
|
@ -161,7 +139,7 @@ module.exports = function(passport) {
|
|||
clientSecret: env.facebookAppSecret,
|
||||
callbackURL: env.url+'/login/facebook/cb',
|
||||
passReqToCallback: true
|
||||
}, function(req, accessToken, refreshToken, profile, done) {
|
||||
}, (req, accessToken, refreshToken, profile, done)=>{
|
||||
socialLogin(req, 'facebook', profile.id, done);
|
||||
}
|
||||
));
|
||||
|
@ -172,7 +150,7 @@ module.exports = function(passport) {
|
|||
consumerSecret: env.twitterConsumerSecret,
|
||||
callbackURL: env.url+'/login/twitter/cb',
|
||||
passReqToCallback: true
|
||||
}, function(req, token, tokenSecret, profile, done) {
|
||||
}, (req, token, tokenSecret, profile, done)=>{
|
||||
socialLogin(req, 'twitter', profile.id, done);
|
||||
}
|
||||
));
|
||||
|
|
|
@ -2,69 +2,35 @@
|
|||
|
||||
const router = require('express').Router(),
|
||||
mw = require('../middleware.js'),
|
||||
User = require('../models.js').user,
|
||||
mail = require('../mail.js');
|
||||
User = require('../models.js').user;
|
||||
|
||||
router.route('/')
|
||||
.all(mw.ensureAdmin, function(req,res,next){
|
||||
.all(mw.ensureAdmin, (req,res,next)=>{
|
||||
next();
|
||||
}).get(function(req,res){
|
||||
|
||||
var cbc = 0;
|
||||
var checkCBC = function(req,res,err){
|
||||
if (err) {
|
||||
req.flash('error', err.message);
|
||||
console.error(err);
|
||||
}
|
||||
if (cbc<1){ cbc++; }
|
||||
else { // done
|
||||
res.render('admin', {
|
||||
noFooter: '1',
|
||||
success:req.flash('success')[0],
|
||||
error:req.flash('error')[0]
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
User.findById(req.user, function(err, found) {
|
||||
res.locals.user = found;
|
||||
checkCBC(req,res,err);
|
||||
});
|
||||
|
||||
User.find({}).sort({lastLogin:-1}).exec(function(err, found){
|
||||
res.locals.users = found;
|
||||
checkCBC(req,res,err);
|
||||
});
|
||||
|
||||
});
|
||||
} )
|
||||
|
||||
router.route('/users')
|
||||
.all(mw.ensureAdmin, function(req,res,next){
|
||||
next();
|
||||
}).post(function(req,res,next){
|
||||
.get( (req,res)=>{
|
||||
|
||||
User.find({}).sort({lastLogin:-1})
|
||||
.catch( (err)=>{
|
||||
mw.throwErr(err);
|
||||
}).then( (found)=>{
|
||||
res.render('admin', {
|
||||
noFooter: '1',
|
||||
users: found
|
||||
});
|
||||
});
|
||||
|
||||
} )
|
||||
|
||||
.post( (req,res,next)=>{
|
||||
if (req.body.delete) {
|
||||
User.findOneAndRemove({'_id':req.body.delete}, function(err,user){
|
||||
User.findOneAndRemove( {'_id':req.body.delete}, (err,user)=>{
|
||||
if (err){ req.flash('error', err.message); }
|
||||
else { req.flash('success', '<i>'+user.name+'</i> deleted.'); }
|
||||
res.redirect('/admin#users');
|
||||
});
|
||||
} else { console.error('ERROR! POST without action sent. '); next(); }
|
||||
});
|
||||
|
||||
router.route('/testmail').get(function(req,res,next){
|
||||
mail.send({
|
||||
to: `"Keith Irwin" <hypergeek14@gmail.com>`,
|
||||
from: mail.from,
|
||||
subject: 'Test email',
|
||||
text: mail.text("Looks like everything's working! "),
|
||||
html: mail.html("<p>Looks like everything's working! </p>")
|
||||
}).then(function(){
|
||||
console.log("Test email should have sent...");
|
||||
res.sendStatus(200);
|
||||
}).catch(function(err){
|
||||
mw.throwErr(err,req);
|
||||
next();
|
||||
});
|
||||
});
|
||||
|
||||
} );
|
||||
} else { console.error(new Error('POST without action sent. ')); next(); }
|
||||
} );
|
||||
|
||||
module.exports = router;
|
|
@ -6,24 +6,25 @@ const mw = require('../middleware.js'),
|
|||
User = require('../models.js').user;
|
||||
|
||||
// Index
|
||||
router.get('/', function(req,res,next) {
|
||||
router.get('/', (req,res,next)=>{
|
||||
res.render('index');
|
||||
});
|
||||
|
||||
// Help
|
||||
router.get('/help', mw.ensureAuth, function(req,res){
|
||||
res.render('help');
|
||||
});
|
||||
router.get('/help', mw.ensureAuth, (req,res)=>{
|
||||
res.render('help');
|
||||
});
|
||||
|
||||
// Terms of Service and Privacy Policy
|
||||
router.get('/terms', function(req,res){
|
||||
router.get('/terms', (req,res)=>{
|
||||
res.render('terms');
|
||||
}).get('/privacy', function(req,res){
|
||||
})
|
||||
.get('/privacy', (req,res)=>{
|
||||
res.render('privacy');
|
||||
});
|
||||
|
||||
// robots.txt
|
||||
router.get('/robots.txt', function(req,res){
|
||||
router.get('/robots.txt', (req,res)=>{
|
||||
res.type('text/plain');
|
||||
res.send("User-agent: *\n"+
|
||||
"Disallow: /map/*\n"
|
||||
|
@ -31,28 +32,28 @@ router.get('/robots.txt', function(req,res){
|
|||
});
|
||||
|
||||
// favicon.ico
|
||||
router.get('/favicon.ico', function(req,res){
|
||||
router.get('/favicon.ico', (req,res)=>{
|
||||
res.redirect('/static/img/icon/by/16-32-48.ico');
|
||||
});
|
||||
|
||||
// Endpoint to validate forms
|
||||
router.get('/validate', function(req,res){
|
||||
router.get('/validate', (req,res)=>{
|
||||
if (req.query.slug) { // validate unique slug
|
||||
User.findOne({slug:slug(req.query.slug)}, function(err, existingUser){
|
||||
User.findOne( {slug:slug(req.query.slug)}, (err,existingUser)=>{
|
||||
if (err) { console.log('/validate error:',err); }
|
||||
if (existingUser && existingUser.id!==req.user) { res.sendStatus(400); }
|
||||
else { res.sendStatus(200); }
|
||||
});
|
||||
} );
|
||||
}
|
||||
});
|
||||
|
||||
// Link to androidapp in play store
|
||||
router.get('/android', function(req,res){
|
||||
router.get('/android', (req,res)=>{
|
||||
res.redirect('https://play.google.com/store/apps/details?id=us.keithirwin.tracman');
|
||||
});
|
||||
|
||||
// Link to iphone app in the apple store
|
||||
router.get('/ios', function(req,res){
|
||||
router.get('/ios', (req,res)=>{
|
||||
res.sendStatus(404);
|
||||
//TODO: Add link to info about why there's no ios app
|
||||
});
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
'use strict';
|
||||
//TODO: Use promises
|
||||
|
||||
const router = require('express').Router(),
|
||||
mw = require('../middleware.js'),
|
||||
|
@ -6,12 +7,12 @@ const router = require('express').Router(),
|
|||
User = require('../models.js').user;
|
||||
|
||||
// Redirect to real slug
|
||||
router.get('/', mw.ensureAuth, function(req,res){
|
||||
router.get('/', mw.ensureAuth, (req,res)=>{
|
||||
res.redirect(`/map/${req.user.slug}`);
|
||||
});
|
||||
|
||||
// Show map
|
||||
router.get('/:slug?', function(req,res,next){
|
||||
router.get('/:slug?', (req,res,next)=>{
|
||||
var mapuser='', user='', cbc=0;
|
||||
|
||||
// Confirm sucessful queries
|
||||
|
|
|
@ -11,20 +11,20 @@ const slug = require('slug'),
|
|||
|
||||
// Settings form
|
||||
router.route('/')
|
||||
.all(mw.ensureAuth, function(req,res,next){
|
||||
.all( mw.ensureAuth, (req,res,next)=>{
|
||||
next();
|
||||
})
|
||||
|
||||
} )
|
||||
|
||||
// Get settings form
|
||||
.get(function(req,res,next){
|
||||
User.findById(req.user, function(err,user){
|
||||
.get( (req,res,next)=>{
|
||||
User.findById( req.user, (err,user)=>{
|
||||
if (err){ mw.throwErr(err,req); }
|
||||
res.render('settings');
|
||||
});
|
||||
})
|
||||
|
||||
} );
|
||||
} )
|
||||
|
||||
// Set new settings
|
||||
.post(function(req,res,next){
|
||||
.post( (req,res,next)=>{
|
||||
User.findByIdAndUpdate(req.user, {$set:{
|
||||
name: xss(req.body.name),
|
||||
slug: slug(xss(req.body.slug)),
|
||||
|
@ -37,65 +37,71 @@ router.route('/')
|
|||
showAlt: (req.body.showAlt)?true:false,
|
||||
showStreetview: (req.body.showStreet)?true:false
|
||||
}
|
||||
}}, function(err, user){
|
||||
if (err) { mw.throwErr(err,req); }
|
||||
else { req.flash('success', 'Settings updated. '); }
|
||||
res.redirect('/settings');
|
||||
});
|
||||
})
|
||||
}}, (err,user)=>{
|
||||
if (err) {
|
||||
mw.throwErr(err,req);
|
||||
res.redirect('/settings');
|
||||
}
|
||||
else {
|
||||
req.flash('success', 'Settings updated. ');
|
||||
res.redirect('/settings');
|
||||
}
|
||||
});
|
||||
} )
|
||||
|
||||
// Delete user account
|
||||
.delete(function(req,res,next){
|
||||
User.findByIdAndRemove( req.user,
|
||||
function(err) {
|
||||
if (err) {
|
||||
mw.throwErr(err,req);
|
||||
} else {
|
||||
req.flash('success', 'Your account has been deleted. ');
|
||||
}
|
||||
.delete( (req,res,next)=>{
|
||||
User.findByIdAndRemove( req.user, (err)=>{
|
||||
if (err) {
|
||||
mw.throwErr(err,req);
|
||||
res.redirect('/settings');
|
||||
} else {
|
||||
req.flash('success', 'Your account has been deleted. ');
|
||||
res.redirect('/');
|
||||
}
|
||||
);
|
||||
});
|
||||
} );
|
||||
} );
|
||||
|
||||
|
||||
// Set password
|
||||
router.route('/password/')
|
||||
.all(mw.ensureAuth,function(req,res,next){
|
||||
.all( mw.ensureAuth, (req,res,next)=>{
|
||||
next();
|
||||
})
|
||||
|
||||
} )
|
||||
|
||||
// Email user a token, proceed at /password/:token
|
||||
.get(function(req,res,next){
|
||||
.get( (req,res,next)=>{
|
||||
|
||||
// Create token for password change
|
||||
req.user.createToken(function(err,token){
|
||||
req.user.createToken( (err,token)=>{
|
||||
if (err){ next(err); }
|
||||
|
||||
// Confirm password change request by email.
|
||||
|
||||
// Confirm password change request by email.
|
||||
mail.send({
|
||||
to: mail.to(req.user),
|
||||
from: mail.from,
|
||||
subject: 'Request to change your Tracman password',
|
||||
text: mail.text(`A request has been made to change your tracman password. If you did not initiate this request, please contact support at keith@tracman.org. \n\nTo change your password, follow this link:\n${env.url}/settings/password/${token}. \n\nThis request will expire in 1 hour. `),
|
||||
html: mail.html(`<p>A request has been made to change your tracman password. If you did not initiate this request, please contact support at <a href="mailto:keith@tracman.org">keith@tracman.org</a>. </p><p>To change your password, follow this link:<br><a href="${env.url}/settings/password/${token}">${env.url}/settings/password/${token}</a>. </p><p>This request will expire in 1 hour. </p>`)
|
||||
}).catch(function(err){
|
||||
}).catch( err=>{
|
||||
mw.throwErr(err,req);
|
||||
}).then(function(){
|
||||
|
||||
// Alert user to check email.
|
||||
res.redirect('/login#login');
|
||||
}).then( ()=>{
|
||||
|
||||
// Alert user to check email.
|
||||
req.flash('success',`An email has been sent to <u>${req.user.email}</u>. Check your inbox to complete your password change. `);
|
||||
res.redirect( (req.isAuthenticated)?'/settings':'/login' );
|
||||
|
||||
res.redirect('/login#login');
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
} );
|
||||
|
||||
} );
|
||||
|
||||
router.route('/password/:token')
|
||||
|
||||
|
||||
// Check token
|
||||
.all(function(req,res,next){
|
||||
.all( (req,res,next)=>{
|
||||
User
|
||||
.findOne({'auth.passToken': req.params.token})
|
||||
.where('auth.tokenExpires').gt(Date.now())
|
||||
|
@ -110,71 +116,66 @@ router.route('/password/:token')
|
|||
next();
|
||||
}
|
||||
});
|
||||
})
|
||||
} )
|
||||
|
||||
// Show password change form
|
||||
.get(function(req,res){
|
||||
.get( (req,res)=>{
|
||||
res.render('password');
|
||||
})
|
||||
|
||||
.post(function(req,res,next){
|
||||
} )
|
||||
|
||||
.post( (req,res,next)=>{
|
||||
|
||||
// Validate matching passwords
|
||||
if (req.body.password!==req.body.repassword) {
|
||||
mw.throwErr( new Error('Passwords do not match. '), req );
|
||||
} else {
|
||||
//TODO: Validate password
|
||||
|
||||
//TODO: Add logic for new users
|
||||
|
||||
// Delete token
|
||||
res.locals.passwordUser.auth.passToken = undefined;
|
||||
res.locals.passwordUser.auth.tokenExpires = undefined;
|
||||
|
||||
// Create hash
|
||||
res.locals.passwordUser.generateHash(req.body.password, function(err,hash){
|
||||
if (err){ mw.throwErr(err); }
|
||||
else {
|
||||
|
||||
// Save new password to db
|
||||
res.locals.passwordUser.auth.password = hash;
|
||||
res.locals.passwordUser.save( function(err){
|
||||
if (err){ mw.throwErr(err,req); }
|
||||
else {
|
||||
req.flash('success', 'Password set. You can use it to log in now. ');
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
// Delete token
|
||||
res.locals.passwordUser.auth.passToken = undefined;
|
||||
res.locals.passwordUser.auth.tokenExpires = undefined;
|
||||
|
||||
res.redirect('/login');
|
||||
// Create hash
|
||||
res.locals.passwordUser.generateHash( req.body.password, (err,hash)=>{
|
||||
if (err){ mw.throwErr(err,req); }
|
||||
else {
|
||||
|
||||
// Save new password to db
|
||||
res.locals.passwordUser.auth.password = hash;
|
||||
res.locals.passwordUser.save( (err)=>{
|
||||
if (err){
|
||||
mw.throwErr(err,req);
|
||||
res.redirect('/login#signup');
|
||||
}
|
||||
else {
|
||||
req.flash('success', 'Password set. You can use it to log in now. ');
|
||||
res.redirect('/login#login');
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
} );
|
||||
|
||||
});
|
||||
} );
|
||||
|
||||
|
||||
// Tracman pro
|
||||
router.route('/pro')
|
||||
.all(mw.ensureAuth, function(req,res,next){
|
||||
.all( mw.ensureAuth, (req,res,next)=>{
|
||||
next();
|
||||
})
|
||||
|
||||
} )
|
||||
|
||||
// Get info about pro
|
||||
.get(function(req,res,next){
|
||||
.get( (req,res,next)=>{
|
||||
res.render('pro');
|
||||
})
|
||||
|
||||
} )
|
||||
|
||||
// Join Tracman pro
|
||||
.post(function(req,res){
|
||||
.post( (req,res)=>{
|
||||
User.findByIdAndUpdate(req.user.id,
|
||||
{$set:{ isPro:true }},
|
||||
function(err, user){
|
||||
(err,user)=>{
|
||||
if (err){ mw.throwErr(err,req); }
|
||||
else { req.flash('success','You have been signed up for pro. '); }
|
||||
res.redirect('/map');
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
} );
|
||||
|
||||
module.exports = router;
|
|
@ -6,24 +6,24 @@ const router = require('express').Router(),
|
|||
|
||||
router
|
||||
|
||||
.get('/mail', function(req,res,next){
|
||||
.get('/mail', (req,res,next)=>{
|
||||
mail.send({
|
||||
to: `"Keith Irwin" <hypergeek14@gmail.com>`,
|
||||
from: mail.from,
|
||||
subject: 'Test email',
|
||||
text: mail.text("Looks like everything's working! "),
|
||||
html: mail.html("<p>Looks like everything's working! </p>")
|
||||
}).then(function(){
|
||||
}).then(()=>{
|
||||
console.log("Test email should have sent...");
|
||||
res.sendStatus(200);
|
||||
}).catch(function(err){
|
||||
}).catch((err)=>{
|
||||
mw.throwErr(err,req);
|
||||
next();
|
||||
});
|
||||
})
|
||||
|
||||
.get('/password', function(req,res){
|
||||
.get('/password', (req,res)=>{
|
||||
res.render('password');
|
||||
})
|
||||
});
|
||||
|
||||
module.exports = router;
|
|
@ -1,7 +1,8 @@
|
|||
'use strict';
|
||||
|
||||
// Imports
|
||||
const User = require('./models.js').user;
|
||||
const mw = require('./middleware.js'),
|
||||
User = require('./models.js').user;
|
||||
|
||||
// Check for tracking clients
|
||||
function checkForUsers(io, user) {
|
||||
|
@ -9,9 +10,9 @@ function checkForUsers(io, user) {
|
|||
|
||||
// Checks if any sockets are getting updates for this user
|
||||
//TODO: Use Object.values() after upgrading to node v7
|
||||
if (Object.keys(io.sockets.connected).map( function(id){
|
||||
if (Object.keys(io.sockets.connected).map( (id)=>{
|
||||
return io.sockets.connected[id];
|
||||
}).some( function(socket){
|
||||
}).some( (socket)=>{
|
||||
return socket.gets==user;
|
||||
})) {
|
||||
//console.log(`Activating updates for ${user}.`);
|
||||
|
@ -26,56 +27,56 @@ module.exports = {
|
|||
|
||||
checkForUsers: checkForUsers,
|
||||
|
||||
init: function(io){
|
||||
io.on('connection', function(socket) {
|
||||
init: (io)=>{
|
||||
io.on('connection', (socket)=>{
|
||||
//console.log(`${socket.id} connected.`);
|
||||
|
||||
// Log
|
||||
//socket.on('log', function(text){
|
||||
/* Log */
|
||||
//socket.on('log', (text)=>{
|
||||
//console.log(`LOG: ${text}`);
|
||||
//});
|
||||
|
||||
// This socket can set location (app)
|
||||
socket.on('can-set', function(userId){
|
||||
socket.on('can-set', (userId)=>{
|
||||
//console.log(`${socket.id} can set updates for ${userId}.`);
|
||||
socket.join(userId, function(){
|
||||
socket.join(userId, ()=>{
|
||||
//console.log(`${socket.id} joined ${userId}`);
|
||||
});
|
||||
checkForUsers( io, userId );
|
||||
});
|
||||
|
||||
// This socket can receive location (map)
|
||||
socket.on('can-get', function(userId){
|
||||
socket.on('can-get', (userId)=>{
|
||||
socket.gets = userId;
|
||||
//console.log(`${socket.id} can get updates for ${userId}.`);
|
||||
socket.join(userId, function(){
|
||||
socket.join(userId, ()=>{
|
||||
//console.log(`${socket.id} joined ${userId}`);
|
||||
socket.to(userId).emit('activate', 'true');
|
||||
});
|
||||
});
|
||||
|
||||
// Set location
|
||||
socket.on('set', function(loc){
|
||||
socket.on('set', (loc)=>{
|
||||
//console.log(`${socket.id} set location for ${loc.usr}`);
|
||||
loc.time = Date.now();
|
||||
|
||||
// Check for sk32 token
|
||||
if (!loc.tok) { console.log('!loc.tok for loc:',loc) }
|
||||
if (!loc.tok) { mw.throwErr(new Error(`⛔️ !loc.tok for loc: ${loc}`)) }
|
||||
else {
|
||||
|
||||
// Get loc.usr
|
||||
User.findById(loc.usr, function(err, user) {
|
||||
if (err) { console.log('Error finding user:',err); }
|
||||
if (!user) { console.log('User not found for loc:',loc); }
|
||||
else {
|
||||
User.findById(loc.usr, (err,user)=>{
|
||||
if (err) { mw.throwErr(err); }
|
||||
if (user) {
|
||||
|
||||
// Confirm sk32 token
|
||||
if (loc.tok!=user.sk32) { console.log('loc.tok!=user.sk32 || ',loc.tok,'!=',user.sk32); }
|
||||
if (loc.tok!=user.sk32) { mw.throwErr(new Error(`⛔️ loc.tok!=user.sk32\n\t${loc.tok} != ${user.sk32}`)); }
|
||||
else {
|
||||
|
||||
// Broadcast location
|
||||
io.to(loc.usr).emit('get', loc);
|
||||
//console.log(`Broadcasting ${loc.lat}, ${loc.lon} to ${loc.usr}`);
|
||||
|
||||
// Save in db as last seen
|
||||
user.last = {
|
||||
lat: parseFloat(loc.lat),
|
||||
|
@ -84,9 +85,9 @@ module.exports = {
|
|||
spd: parseFloat(loc.spd||0),
|
||||
time: loc.time
|
||||
};
|
||||
user.save(function(err) {
|
||||
if (err) { console.log('Error saving user last location:'+loc.user+'\n'+err); }
|
||||
});
|
||||
user.save( (err)=>{
|
||||
if (err) { mw.throwErr(err); }
|
||||
} );
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -96,7 +97,7 @@ module.exports = {
|
|||
});
|
||||
|
||||
// Shutdown (check for remaining clients)
|
||||
socket.on('disconnect', function(reason){
|
||||
socket.on('disconnect', (reason)=>{
|
||||
//console.log(`${socket.id} disconnected because of a ${reason}.`);
|
||||
|
||||
// Check if client was receiving updates
|
||||
|
@ -108,8 +109,8 @@ module.exports = {
|
|||
});
|
||||
|
||||
// Log errors
|
||||
socket.on('error', function(err){
|
||||
console.log('Socket error! ',err);
|
||||
socket.on('error', (err)=>{
|
||||
mw.throwErr(err);
|
||||
});
|
||||
|
||||
});
|
||||
|
|
103
server.js
103
server.js
|
@ -1,7 +1,7 @@
|
|||
'use strict';
|
||||
|
||||
/* IMPORTS */
|
||||
const
|
||||
/* IMPORTS */
|
||||
const
|
||||
express = require('express'),
|
||||
bodyParser = require('body-parser'),
|
||||
cookieParser = require('cookie-parser'),
|
||||
|
@ -11,6 +11,7 @@ const
|
|||
passport = require('passport'),
|
||||
flash = require('connect-flash'),
|
||||
env = require('./config/env.js'),
|
||||
mw = require('./config/middleware.js'),
|
||||
User = require('./config/models.js').user,
|
||||
app = express(),
|
||||
http = require('http').Server(app),
|
||||
|
@ -19,14 +20,18 @@ const
|
|||
|
||||
|
||||
/* SETUP */ {
|
||||
|
||||
|
||||
/* Database */ mongoose.connect(env.mongoSetup, {
|
||||
server:{socketOptions:{
|
||||
keepAlive:1, connectTimeoutMS:30000 }},
|
||||
replset:{socketOptions:{
|
||||
keepAlive:1, connectTimeoutMS:30000 }}
|
||||
}).catch((err)=>{
|
||||
mw.throwErr(err);
|
||||
}).then(()=>{
|
||||
console.log(`💿 Mongoose connected to database`);
|
||||
});
|
||||
|
||||
|
||||
/* Templates */ {
|
||||
nunjucks.configure(__dirname+'/views', {
|
||||
autoescape: true,
|
||||
|
@ -34,7 +39,7 @@ const
|
|||
});
|
||||
app.set('view engine','html');
|
||||
}
|
||||
|
||||
|
||||
/* Session */ {
|
||||
app.use(cookieParser(env.cookie));
|
||||
app.use(cookieSession({
|
||||
|
@ -49,7 +54,7 @@ const
|
|||
}));
|
||||
app.use(flash());
|
||||
}
|
||||
|
||||
|
||||
/* Auth */ {
|
||||
require('./config/passport.js')(passport);
|
||||
app.use(passport.initialize());
|
||||
|
@ -58,116 +63,116 @@ const
|
|||
// app.use(passport.initialize());
|
||||
// app.use(passport.session());
|
||||
// require('./config/auth.js');
|
||||
// passport.serializeUser(function(user,done) {
|
||||
// passport.serializeUser( (user,done)=>{
|
||||
// done(null, user.id);
|
||||
// });
|
||||
// passport.deserializeUser(function(id,done) {
|
||||
// User.findById(id, function(err, user) {
|
||||
// } );
|
||||
// passport.deserializeUser( (id,done)=>{
|
||||
// User.findById( id, (err,user)=>{
|
||||
// if(!err) done(null, user);
|
||||
// else done(err, null);
|
||||
// });
|
||||
// });
|
||||
// } );
|
||||
// } );
|
||||
}
|
||||
|
||||
|
||||
/* Routes */ {
|
||||
|
||||
|
||||
// Static files (keep this before setting default locals)
|
||||
app.use('/static', express.static(__dirname+'/static'));
|
||||
|
||||
app.use('/static', express.static( __dirname+'/static', {dotfiles:'allow'} ));
|
||||
|
||||
// Set default locals available to all views (keep this after static files)
|
||||
app.get('/*', function(req,res,next){
|
||||
app.get( '/*', (req,res,next)=>{
|
||||
// console.log(`Setting local variables for request to ${req.path}.`);
|
||||
|
||||
|
||||
// User account
|
||||
res.locals.user = req.user;
|
||||
// console.log(`User set as ${res.locals.user}. `);
|
||||
|
||||
|
||||
// Flash messages
|
||||
res.locals.successes = req.flash('success');
|
||||
res.locals.dangers = req.flash('danger');
|
||||
res.locals.warnings = req.flash('warning');
|
||||
// console.log(`Flash messages set as:\nSuccesses: ${res.locals.successes}\nWarnings: ${res.locals.warnings}\nDangers: ${res.locals.dangers}`);
|
||||
|
||||
|
||||
next();
|
||||
});
|
||||
|
||||
} );
|
||||
|
||||
// Main routes
|
||||
app.use( '/', require('./config/routes/index.js') );
|
||||
|
||||
|
||||
// Settings
|
||||
app.use( '/settings', require('./config/routes/settings.js') );
|
||||
|
||||
|
||||
// Map
|
||||
app.use( ['/map','/trac'], require('./config/routes/map.js') );
|
||||
|
||||
|
||||
// Site administration
|
||||
app.use( '/admin', require('./config/routes/admin.js') );
|
||||
|
||||
|
||||
// Testing
|
||||
if (env.mode == 'development') {
|
||||
app.use( '/test', require('./config/routes/test.js' ) );
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Errors */ {
|
||||
// Catch-all for 404s
|
||||
app.use(function(req,res,next) {
|
||||
app.use( (req,res,next)=>{
|
||||
if (!res.headersSent) {
|
||||
var err = new Error('404: Not found: '+req.url);
|
||||
var err = new Error('404 Not found: '+req.url);
|
||||
err.status = 404;
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
|
||||
} );
|
||||
|
||||
// Handlers
|
||||
if (env.mode=='production') {
|
||||
app.use(function(err,req,res,next) {
|
||||
app.use( (err,req,res,next)=>{
|
||||
if (res.headersSent) { return next(err); }
|
||||
res.status(err.status||500);
|
||||
res.render('error', {
|
||||
code: err.status
|
||||
});
|
||||
});
|
||||
} );
|
||||
}
|
||||
else /* Development */{
|
||||
app.use(function(err,req,res,next) {
|
||||
app.use( (err,req,res,next)=>{
|
||||
console.log(err);
|
||||
if (res.headersSent) { return next(err); }
|
||||
res.status(err.status||500);
|
||||
res.render('error', {
|
||||
code: err.status,
|
||||
message: err.message,
|
||||
error: err
|
||||
error: err.stack
|
||||
});
|
||||
});
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Sockets */ {
|
||||
sockets.init(io);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/* RUNTIME */ {
|
||||
|
||||
console.log('Starting Tracman server...');
|
||||
|
||||
|
||||
console.log('🖥 Starting Tracman server...');
|
||||
|
||||
// Listen
|
||||
http.listen(env.port, function(){
|
||||
console.log(`Listening in ${env.mode} mode on port ${env.port}. `);
|
||||
|
||||
http.listen( env.port, ()=>{
|
||||
console.log(`🌐 Listening in ${env.mode} mode on port ${env.port}... `);
|
||||
|
||||
// Check for clients for each user
|
||||
User.find({}, function(err, users){
|
||||
User.find( {}, (err,users)=>{
|
||||
if (err) { console.log(`DB error finding all users: ${err.message}`); }
|
||||
users.forEach( function(user){
|
||||
users.forEach( (user)=>{
|
||||
sockets.checkForUsers( io, user.id );
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
module.exports = app;
|
||||
|
|
|
@ -51,7 +51,10 @@ header nav {
|
|||
} header nav ul li a:hover,
|
||||
header nav ul li a:focus,
|
||||
header nav ul li a.active,
|
||||
|
||||
header .logo:hover {
|
||||
text-decoration: none;
|
||||
background: rgba(255,255,255,0.1);
|
||||
}
|
||||
|
||||
/* Hamburger */
|
||||
header .hamburger {
|
||||
|
@ -72,8 +75,8 @@ header .hamburger {
|
|||
} header .hamburger-inner {
|
||||
top: 50%;
|
||||
margin-top: -2px;
|
||||
} header .hamburger-inner,
|
||||
header .hamburger-inner::before,
|
||||
} header .hamburger-inner,
|
||||
header .hamburger-inner::before,
|
||||
header .hamburger-inner::after {
|
||||
width: 40px;
|
||||
height: 4px;
|
||||
|
|
81
test.js
81
test.js
|
@ -5,15 +5,7 @@ const chai = require('chai'),
|
|||
chai.use(chaiHttp);
|
||||
|
||||
|
||||
describe('Index', function() {
|
||||
// I think this restarts the server after each try?
|
||||
// var server;
|
||||
// beforeEach(function() {
|
||||
// server = require('./server');
|
||||
// });
|
||||
// afterEach(function() {
|
||||
// server.close();
|
||||
// });
|
||||
describe('Pages', function() {
|
||||
|
||||
it('Displays homepage', function(done){
|
||||
request(server).get('/')
|
||||
|
@ -21,6 +13,24 @@ describe('Index', function() {
|
|||
.end(function(err,res){ done(); });
|
||||
});
|
||||
|
||||
it('Displays help page', function(done){
|
||||
request(server).get('/help')
|
||||
.expect(200)
|
||||
.end(function(err,res){ done(); });
|
||||
});
|
||||
|
||||
it('Displays terms of service', function(done){
|
||||
request(server).get('/terms')
|
||||
.expect(200)
|
||||
.end(function(err,res){ done(); });
|
||||
});
|
||||
|
||||
it('Displays privacy policy', function(done){
|
||||
request(server).get('/privacy')
|
||||
.expect(200)
|
||||
.end(function(err,res){ done(); });
|
||||
});
|
||||
|
||||
it('Displays robots.txt', function(done){
|
||||
request(server).get('/robots.txt')
|
||||
.expect(200)
|
||||
|
@ -36,54 +46,13 @@ describe('Index', function() {
|
|||
|
||||
});
|
||||
|
||||
// describe('Auth', function() {
|
||||
describe('Auth', function() {
|
||||
|
||||
// it('Creates an account', function(done){
|
||||
// request(server).get('/login')
|
||||
// .expect(200)
|
||||
// .end(function(err,res){
|
||||
// //TODO: google authentication
|
||||
// it('Logs out', function(done){
|
||||
// request(server).get('/logout')
|
||||
// .expect(200)
|
||||
// .end(function(err,res){
|
||||
// it('Logs in', function(done){
|
||||
// request(server).get('/logout')
|
||||
// .expect(200)
|
||||
// .end(function(err,res){
|
||||
// cbc=2;
|
||||
// var deletesAccount = function(done){
|
||||
// it('Deletes own account', function(){
|
||||
// //TODO: Delete account via GUI
|
||||
// });
|
||||
// }
|
||||
// it('Shows own map', function(done){
|
||||
// request(server).get('/map')
|
||||
// .expect(200)
|
||||
// //TODO: Expect no js errors
|
||||
// .end(function(err,res){
|
||||
// if (cbc<2){ deletesAccount(); }
|
||||
// else { cbc--; }
|
||||
// done();
|
||||
// });
|
||||
// });
|
||||
|
||||
// it('Has the correct account info', function(done){
|
||||
// //TODO: Check account info
|
||||
// if (cbc<2){ deletesAccount(); }
|
||||
// else { cbc--; }
|
||||
// done();
|
||||
// });
|
||||
|
||||
// done();
|
||||
// });
|
||||
// });
|
||||
// done();
|
||||
// });
|
||||
// });
|
||||
// done();
|
||||
// });
|
||||
// });
|
||||
it('Creates an account', function(done){
|
||||
request(server).post('/signup',{"email":"test@tracman.org"})
|
||||
.expect(200)
|
||||
.end(function(err,res){ done(); });
|
||||
});
|
||||
|
||||
//TODO: it('Has the correct account info', function(done){
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
<a href="https://plus.google.com/{{usr.googleID}}/">Google</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td id='{{usr.id}}-edit'><form action="/admin/users" method="POST">
|
||||
<td id='{{usr.id}}-edit'><form method="POST">
|
||||
<button type="submit" class='btn btn-block btn-danger' name="delete" value="{{usr.id}}">DELETE</button>
|
||||
</form></td>
|
||||
</tr>
|
||||
|
|
|
@ -1,14 +1,12 @@
|
|||
{% extends 'templates/base.html' %}
|
||||
{% block title %}{{ super() }} | {{ code }} Error{% endblock %}
|
||||
{% block title %}{{super()}} | Error{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
<section class='dark'>
|
||||
<div class='container'>
|
||||
{% if code %}<h2>{{code}}</h2>{% endif %}
|
||||
{% if message %}<h3>{{message}}</h3>{% endif %}
|
||||
{% if error %}<p>{{error}}</p>{% endif %}
|
||||
<p>I would really appreciate it if you would <a href="https://github.com/Tracman-org/Server/issues/new">report this error</a>. </p>
|
||||
{% if code %}<img style="width:100%" src="https://http.cat/{{code}}.jpg">{% endif %}
|
||||
</div>
|
||||
<section class='container'>
|
||||
{% if code %}<h2>{{code}}</h2>{% endif %}
|
||||
{% if message %}<h3>{{message}}</h3>{% endif %}
|
||||
{% if stack %}<p>{{stack}}</p>{% else %}
|
||||
<p>I would really appreciate it if you would <a href="https://github.com/Tracman-org/Server/issues/new">report this error</a>. </p>{% endif %}
|
||||
{% if code %}<img style="width:100%" src="https://http.cat/{{code}}.jpg">{% endif %}
|
||||
</section>
|
||||
{% endblock %}
|
|
@ -1,15 +1,15 @@
|
|||
<header class='shadow'>
|
||||
|
||||
|
||||
<!-- Logo -->
|
||||
<a href="/"><span class='logo'><img class='icon' src="/static/img/style/logo-28.png" alt="+">Tracman</span></a>
|
||||
|
||||
|
||||
<!-- Hamburger -->
|
||||
<div class='hamburger hamburger--slider' aria-label="Menu" aria-controls="navigation">
|
||||
<div class='hamburger-box'>
|
||||
<div class='hamburger-inner'></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Navigation -->
|
||||
<nav id='navigation'><ul>
|
||||
{% if user %}
|
||||
|
@ -25,7 +25,7 @@
|
|||
<li><a href="/login#signup">Join</a></li>
|
||||
{% endif %}
|
||||
</ul></nav>
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
<!-- Flash messages -->
|
||||
|
|
Loading…
Reference in New Issue