From 29bade1914d712862705aac7abf78f740c01a7f5 Mon Sep 17 00:00:00 2001
From: Keith Irwin
Date: Tue, 25 Apr 2017 20:22:18 -0400
Subject: [PATCH 01/21] Prepared for v0.6.0 release
---
README.md | 12 ++++++++----
config/sockets.js | 4 ++--
server.js | 2 +-
static/js/settings.js | 3 ---
4 files changed, 11 insertions(+), 10 deletions(-)
diff --git a/README.md b/README.md
index cfe5605..6938e9f 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
# Tracman
###### v 0.6.0
-node.js application to display a map with user's location.
+node.js application to display a sharable map with user's location.
## Installation
@@ -25,15 +25,19 @@ $ npm run nodemon
## Contributing
-Tracman will be updated according to [this branching model](http://nvie.com/posts/a-successful-git-branching-model).
+Tracman will be updated according to [this branching model](http://nvie.com/posts/a-successful-git-branching-model)... more or less.
## Changelog
#### v0.6.0
-* Added more login options
-* Replaced some callbacks with promises
+* [#32](https://github.com/Tracman-org/Server/issues/32), [#57](https://github.com/Tracman-org/Server/issues/57), [#58](https://github.com/Tracman-org/Server/issues/58), [#60](https://github.com/Tracman-org/Server/issues/60) Added more login options
+* [#50](https://github.com/Tracman-org/Server/issues/50) Replaced some callbacks with promises
* Minified static files
+* [#51](https://github.com/Tracman-org/Server/issues/51), [#52](https://github.com/Tracman-org/Server/issues/52) Added settings validations
+* [#54](https://github.com/Tracman-org/Server/issues/54), [#55](https://github.com/Tracman-org/Server/issues/55) Made map work better
+* [#61](https://github.com/Tracman-org/Server/issues/61) New MongoDB security
+* [#62](https://github.com/Tracman-org/Server/issues/62) Fixed error handling
#### v0.5.1
diff --git a/config/sockets.js b/config/sockets.js
index 6d0f849..563af44 100644
--- a/config/sockets.js
+++ b/config/sockets.js
@@ -29,7 +29,7 @@ module.exports = {
init: (io)=>{
io.on('connection', (socket)=>{
- // console.log(`${socket.id} connected.`);
+ //console.log(`${socket.id} connected.`);
// Set a few variables
socket.ip = socket.client.request.headers['x-real-ip'];
@@ -61,7 +61,7 @@ module.exports = {
// Set location
socket.on('set', (loc)=>{
- // console.log(`${socket.id} set location for ${loc.usr}`);
+ //console.log(`${socket.id} set location for ${loc.usr}`);
loc.time = Date.now();
// Check for user and sk32 token
diff --git a/server.js b/server.js
index 7beffee..eda5e8c 100755
--- a/server.js
+++ b/server.js
@@ -79,7 +79,7 @@ const
// Path for redirects
let nextPath = ( req.path.substring(0, req.path.indexOf('#')) || req.path );
if ( nextPath.substring(0,6)!=='/login' && nextPath.substring(0,7)!=='/logout' ){
- // console.log(`Setting redirect path to ${nextPath}#`);
+ //console.log(`Setting redirect path to ${nextPath}#`);
req.session.next = nextPath+'#';
}
diff --git a/static/js/settings.js b/static/js/settings.js
index ce3c0a5..931b26d 100644
--- a/static/js/settings.js
+++ b/static/js/settings.js
@@ -57,9 +57,6 @@ $(function(){
$.get('/validate?'+input+'='+$('#'+input+'-input').val())
.fail(function(data,status){
- console.log(typeof status);
- console.log(status);
-
// Input is not unique
if (status===400) {
$('#'+input+'-help').show().text("That "+input+" is already in use by another user. ");
From 55b3859512d67ab7345c3638d6d2a5aa04eb37b0 Mon Sep 17 00:00:00 2001
From: Keith Irwin
Date: Wed, 26 Apr 2017 15:10:07 -0400
Subject: [PATCH 02/21] Removed code from error page title
---
config/models.js | 15 +++++++++------
views/error.html | 2 +-
2 files changed, 10 insertions(+), 7 deletions(-)
diff --git a/config/models.js b/config/models.js
index 3da2b55..46ab877 100644
--- a/config/models.js
+++ b/config/models.js
@@ -47,17 +47,20 @@ const userSchema = new mongoose.Schema({
/* User methods */ {
//TODO: Return promises instead of taking callbacks
+ // See https://gist.github.com/7h1b0/5154fda207e68ad1cefc#file-random-js
+ // For an example
// Create email confirmation token
userSchema.methods.createEmailToken = function(next){
var user = this;
- crypto.randomBytes(16)
- .then( (buf)=>{
- user.emailToken = buf.toString('hex');
- user.save();
- })
- .catch( (err)=>{ next(err,null); });
+ crypto.randomBytes(16, (err,buf)=>{
+ if (err){ next(err,null); }
+ if (buf){
+ user.emailToken = buf.toString('hex');
+ user.save();
+ }
+ });
};
diff --git a/views/error.html b/views/error.html
index e4df486..f8c18e0 100644
--- a/views/error.html
+++ b/views/error.html
@@ -3,7 +3,7 @@
{% block main %}
- ❌️ {% if code %}{{code}} {% endif %}{% if message %}{{message}}{% endif %}
+ {% if message %}❌️ {{message}} {% endif %}
{% if stack %}{{stack}}
{% else %}
{% if code == '404' %}This page does not exist. Maybe you followed a dead link here.
{% else %}Would you please report this error ?
{% endif %}
From dc11bdd271eb9db751e50c714487bb4f04e4fdf7 Mon Sep 17 00:00:00 2001
From: Keith Irwin
Date: Wed, 26 Apr 2017 22:47:23 -0400
Subject: [PATCH 03/21] Added some debug logging
---
config/models.js | 9 +++++++--
config/routes/settings.js | 8 +++++++-
2 files changed, 14 insertions(+), 3 deletions(-)
diff --git a/config/models.js b/config/models.js
index 46ab877..d82bf14 100644
--- a/config/models.js
+++ b/config/models.js
@@ -52,13 +52,17 @@ const userSchema = new mongoose.Schema({
// Create email confirmation token
userSchema.methods.createEmailToken = function(next){
+ // next(err,hash);
+ console.log('user.createEmailToken() called');
var user = this;
crypto.randomBytes(16, (err,buf)=>{
if (err){ next(err,null); }
- if (buf){
+ if (buf){
+ //console.log(`Buffer ${buf.toString('hex')} created`);
user.emailToken = buf.toString('hex');
user.save();
+ next(null,user.emailToken);
}
});
@@ -66,11 +70,12 @@ const userSchema = new mongoose.Schema({
// Generate hash for new password
userSchema.methods.generateHash = function(password,next){
+ // next(err,hash);
bcrypt.genSalt(8)
.then( (salt)=>{
bcrypt.hash(password, salt, null, next);
})
- .catch( (err)=>{ return next(err); });
+ .catch( (err)=>{ return next(err,null); });
};
// Create password reset token
diff --git a/config/routes/settings.js b/config/routes/settings.js
index ae7b4ed..c72dc65 100644
--- a/config/routes/settings.js
+++ b/config/routes/settings.js
@@ -30,6 +30,7 @@ router.route('/')
.post( (req,res,next)=>{
function setSettings(){
+ //console.log('setSettings() called');
// Set values
req.user.name = xss(req.body.name);
@@ -43,10 +44,12 @@ router.route('/')
showAlt: (req.body.showAlt)?true:false,
showStreetview: (req.body.showStreet)?true:false
};
-
+
// Save user and send response
+ //console.log(`Saving new settings for user ${req.user.name}...`);
req.user.save()
.then( ()=>{
+ //console.log(`DONE! Redirecting user...`);
req.flash('success', 'Settings updated. ');
res.redirect('/settings');
})
@@ -71,9 +74,11 @@ router.route('/')
// Email changed
if (req.user.email!==req.body.email) {
+ //console.log(`Email changed to ${req.body.email}`);
req.user.newEmail = req.body.email;
// Create token
+ //console.log(`Creating email token...`);
req.user.createEmailToken((err,token)=>{
if (err){
mw.throwErr(err,req);
@@ -81,6 +86,7 @@ router.route('/')
}
// Send token to user by email
+ //console.log(`Mailing new email token to ${req.body.email}...`);
mail.send({
to: `"${req.user.name}" <${req.body.email}>`,
from: mail.from,
From 4421b35ba6329fea65d465bec1572dcd2cffd150 Mon Sep 17 00:00:00 2001
From: Keith Irwin
Date: Wed, 26 Apr 2017 23:00:50 -0400
Subject: [PATCH 04/21] Fixed update time/speed/altitude not updating
---
static/js/map.js | 55 +++++++++++++++++++++++++++++++++++-------------
1 file changed, 40 insertions(+), 15 deletions(-)
diff --git a/static/js/map.js b/static/js/map.js
index 636c486..4dbdcb6 100644
--- a/static/js/map.js
+++ b/static/js/map.js
@@ -23,7 +23,7 @@ function onConnect(socket,userid,mapuserid) {
console.log("🚹 Receiving updates for",mapuserid);
// Can set location too
- if (mapuserid==userid) {
+ if (mapuserid===userid) {
socket.emit('can-set', userid );
console.log("🚹 Sending updates for",userid);
}
@@ -75,9 +75,10 @@ $(function() {
// Google maps API callback
window.gmapsCb = function() {
+ //console.log("gmapsCb() called");
+
// Make sure everything's ready...
waitForElements([mapuser,disp,noHeader], function() {
- //console.log("gmapsCb() called");
// Create map
if (disp!=='1') {
@@ -145,8 +146,8 @@ window.gmapsCb = function() {
// Create altitude block
if (mapuser.settings.showAlt) {
//console.log("Creating altitude sign...");
- var elevator = new google.maps.ElevationService;
- const altitudeSign = document.createElement('div'),
+ const elevator = new google.maps.ElevationService,
+ altitudeSign = document.createElement('div'),
altitudeLabel = document.createElement('div'),
altitudeText = document.createElement('div'),
altitudeUnit = document.createElement('div');
@@ -175,29 +176,53 @@ window.gmapsCb = function() {
}
});
+
};
-// Get location
+// Got location
socket.on('get', function(loc) {
+ console.log("🌐️ Got location:",loc.lat+", "+loc.lon);
+ // Parse location
loc = parseLoc(loc);
- // Update street view
- if (disp!=='0' && mapuser.settings.showStreetview) {
- $('.tim').text('location updated '+loc.time);
- if (mapuser.settings.showSpeed) { $('.spd').text(loc.spd.toFixed()); }
+ // Update map
+ if (disp!=='1') {
+
+ // Update time
+ $('#timestamp').text('location updated '+loc.time);
+
+ // Show or hide map
+ toggleMaps(loc);
+
+ // Update marker and map center
+ map.setCenter({ lat:loc.lat, lng:loc.lon });
+ marker.setPosition({ lat:loc.lat, lng:loc.lon });
+
+ // Update speed
+ if (mapuser.settings.showSpeed) {
+ $('#spd').text( loc.spd.toFixed() );
+ }
+
+ // Update altitude
if (mapuser.settings.showAlt) {
- getAltitude({lat:loc.lat,lng:loc.lon}, elevator, function(alt) {
- if (alt) { $('.alt').text((mapuser.settings.units=='standard')?(alt*3.28084).toFixed():alt.toFixed()); }
+ getAltitude({
+ lat:loc.lat,
+ lng:loc.lon
+ }, elevator, function(alt) {
+ if (alt) {
+ $('#alt').text( (mapuser.settings.units=='standard')?(alt*3.28084).toFixed():alt.toFixed() );
+ }
});
}
- toggleMaps(loc);
- map.setCenter({lat:loc.lat,lng:loc.lon});
- marker.setPosition({lat:loc.lat,lng:loc.lon});
+
+ }
+
+ // Update street view
+ if (disp!=='0' && mapuser.settings.showStreetview) {
updateStreetView(loc,10);
}
- console.log("🌐️ Got location:",loc.lat+", "+loc.lon);
});
// Check altitude
From 2516708cd03899cc084039276b2d1902f971f0a2 Mon Sep 17 00:00:00 2001
From: Keith Irwin
Date: Wed, 26 Apr 2017 23:13:14 -0400
Subject: [PATCH 05/21] Moved environment files to own folder
---
.gitignore | 4 ++--
README.md | 11 ++++++++++-
config/{env-sample.js => env/sample.js} | 0
config/mail.js | 2 +-
config/middleware.js | 2 +-
config/passport.js | 2 +-
config/routes/auth.js | 2 +-
config/routes/map.js | 2 +-
config/routes/settings.js | 2 +-
server.js | 2 +-
10 files changed, 19 insertions(+), 10 deletions(-)
rename config/{env-sample.js => env/sample.js} (100%)
diff --git a/.gitignore b/.gitignore
index b3dca0c..585443d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,8 +2,8 @@
node_modules
# Secret stuff
-config/env*
-!config/env-sample.js
+config/env/*
+!config/env/sample.js
# Minified static files (can be built with `npm run minify`)
static/**/*.min.*
diff --git a/README.md b/README.md
index 6938e9f..b443e6e 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,16 @@ node.js application to display a sharable map with user's location.
$ git clone https://github.com/Tracman-org/Server.git && (cd Server && exec npm install)
```
-You will need to set up a configuration file at `config/env.js`. Use `config/env-sample.js` for an example. You can get API keys at the [google developer's console](https://console.developers.google.com/apis/credentials). You will need to set up approved hosts and auth callbacks. There is more information in [their documentation](https://support.google.com/googleapi/answer/6158857?hl=en).
+You will need to set up a configuration file at `config/env/env.js`. Use `config/env/sample.js` for an example. You can get API keys at the [google developer's console](https://console.developers.google.com/apis/credentials). You will need to set up approved hosts and auth callbacks. There is more information in [their documentation](https://support.google.com/googleapi/answer/6158857?hl=en).
+
+A good method is to simply copy the sample configuration and point `config/env/env.js` to the new version:
+
+```sh
+$ cp config/env/sample.js config/env/my-config.js
+$ echo "module.exports = require('./my-config.js');" > config/env/env.js
+```
+
+Then edit `config/env/my-config.js` to match your local environment.
## Running
diff --git a/config/env-sample.js b/config/env/sample.js
similarity index 100%
rename from config/env-sample.js
rename to config/env/sample.js
diff --git a/config/mail.js b/config/mail.js
index 4f44fa4..fe4a578 100644
--- a/config/mail.js
+++ b/config/mail.js
@@ -1,7 +1,7 @@
'use strict';
const nodemailer = require('nodemailer'),
- env = require('./env.js');
+ env = require('./env/env.js');
let transporter = nodemailer.createTransport({
host: 'keithirwin.us',
diff --git a/config/middleware.js b/config/middleware.js
index 0c19bc9..12192be 100644
--- a/config/middleware.js
+++ b/config/middleware.js
@@ -1,6 +1,6 @@
'use strict';
-const env = require('./env.js');
+const env = require('./env/env.js');
module.exports = {
diff --git a/config/passport.js b/config/passport.js
index c8f8a67..ccf4c04 100644
--- a/config/passport.js
+++ b/config/passport.js
@@ -8,7 +8,7 @@ const
GoogleTokenStrategy = require('passport-google-id-token'),
FacebookTokenStrategy = require('passport-facebook-token'),
TwitterTokenStrategy = require('passport-twitter-token'),
- env = require('./env.js'),
+ env = require('./env/env.js'),
mw = require('./middleware.js'),
User = require('./models.js').user;
diff --git a/config/routes/auth.js b/config/routes/auth.js
index d0137d0..d40eefb 100644
--- a/config/routes/auth.js
+++ b/config/routes/auth.js
@@ -5,7 +5,7 @@ const
mail = require('../mail.js'),
User = require('../models.js').user,
crypto = require('crypto'),
- env = require('../env.js');
+ env = require('../env/env.js');
module.exports = (app, passport) => {
diff --git a/config/routes/map.js b/config/routes/map.js
index d5a20f1..7c3e8bd 100644
--- a/config/routes/map.js
+++ b/config/routes/map.js
@@ -3,7 +3,7 @@
const router = require('express').Router(),
mw = require('../middleware.js'),
- env = require('../env.js'),
+ env = require('../env/env.js'),
User = require('../models.js').user;
// Redirect to real slug
diff --git a/config/routes/settings.js b/config/routes/settings.js
index c72dc65..c3564dd 100644
--- a/config/routes/settings.js
+++ b/config/routes/settings.js
@@ -6,7 +6,7 @@ const slug = require('slug'),
mw = require('../middleware.js'),
User = require('../models.js').user,
mail = require('../mail.js'),
- env = require('../env.js'),
+ env = require('../env/env.js'),
router = require('express').Router();
// Validate email addresses
diff --git a/server.js b/server.js
index eda5e8c..7b165ca 100755
--- a/server.js
+++ b/server.js
@@ -11,7 +11,7 @@ const
nunjucks = require('nunjucks'),
passport = require('passport'),
flash = require('connect-flash-plus'),
- env = require('./config/env.js'),
+ env = require('./config/env/env.js'),
User = require('./config/models.js').user,
app = express(),
http = require('http').Server(app),
From 10a8f2c62e3af4837c8f04e02677f8e8951cf4b8 Mon Sep 17 00:00:00 2001
From: Keith Irwin
Date: Wed, 26 Apr 2017 23:26:46 -0400
Subject: [PATCH 06/21] Added info to README
---
README.md | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/README.md b/README.md
index b443e6e..520f541 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,4 @@
+![Logo](https://tracman.org/static/img/icon/by/72.png "The Tracman Logo")
# Tracman
###### v 0.6.0
@@ -20,21 +21,25 @@ $ echo "module.exports = require('./my-config.js');" > config/env/env.js
Then edit `config/env/my-config.js` to match your local environment.
-## Running
+## Usage
+
+Run Tracman with npm:
```sh
$ npm run minify && npm start
```
-or, using [nodemon](https://nodemon.io/):
+...or with [nodemon](https://nodemon.io/):
```sh
$ npm run nodemon
```
+Nodemon will automatically minify files and restart the app when you make changes. Check out the `nodemon.json` configuration.
+
## Contributing
-Tracman will be updated according to [this branching model](http://nvie.com/posts/a-successful-git-branching-model)... more or less.
+Tracman will be updated according to [this branching model](http://nvie.com/posts/a-successful-git-branching-model)... more or less. If you know anything about programming Android, [the Tracman android app](https://github.com/Tracman-org/Android) is more desperate for help.
## Changelog
From 77920a0bfc9762529c85bece5f0c0c7660dbe58e Mon Sep 17 00:00:00 2001
From: Keith Irwin
Date: Wed, 26 Apr 2017 23:29:43 -0400
Subject: [PATCH 07/21] Added style to logo
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 520f541..dddca30 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-![Logo](https://tracman.org/static/img/icon/by/72.png "The Tracman Logo")
+![Logo](https://tracman.org/static/img/icon/by/72.png "The Tracman Logo"){:style="float:left;}
# Tracman
###### v 0.6.0
From a353e547f1319607824a4925f78e5a7096fd15db Mon Sep 17 00:00:00 2001
From: Keith Irwin
Date: Wed, 26 Apr 2017 23:30:54 -0400
Subject: [PATCH 08/21] Fixed style to logo
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index dddca30..1b80e11 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-![Logo](https://tracman.org/static/img/icon/by/72.png "The Tracman Logo"){:style="float:left;}
+![Logo](https://tracman.org/static/img/icon/by/72.png "The Tracman Logo"){:style="float:left;"}
# Tracman
###### v 0.6.0
From aca939257b8e7cff9460acbfd43335ffc286e88b Mon Sep 17 00:00:00 2001
From: Keith Irwin
Date: Wed, 26 Apr 2017 23:31:53 -0400
Subject: [PATCH 09/21] Fixed style to logo
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 1b80e11..a7f37c3 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-![Logo](https://tracman.org/static/img/icon/by/72.png "The Tracman Logo"){:style="float:left;"}
+![Logo](https://tracman.org/static/img/icon/by/72.png "The Tracman Logo"){:align="left"}
# Tracman
###### v 0.6.0
From d469ae4da20122d6693b889f638db27396321a21 Mon Sep 17 00:00:00 2001
From: Keith Irwin
Date: Wed, 26 Apr 2017 23:33:11 -0400
Subject: [PATCH 10/21] Fixed style to logo
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index a7f37c3..10468c6 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-![Logo](https://tracman.org/static/img/icon/by/72.png "The Tracman Logo"){:align="left"}
+
# Tracman
###### v 0.6.0
From c4f9279444e8ace5f9753654f82008d5c4814198 Mon Sep 17 00:00:00 2001
From: Keith Irwin
Date: Wed, 26 Apr 2017 23:33:58 -0400
Subject: [PATCH 11/21] Fixed style to logo
---
README.md | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/README.md b/README.md
index 10468c6..20c145d 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,4 @@
-
-# Tracman
+# Tracman
###### v 0.6.0
node.js application to display a sharable map with user's location.
From 5d38691a8e2db549adc853a180a1b0fa81d89d2d Mon Sep 17 00:00:00 2001
From: Keith Irwin
Date: Wed, 26 Apr 2017 23:34:54 -0400
Subject: [PATCH 12/21] Fixed style to logo
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 20c145d..efbd416 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# Tracman
+# Tracman
###### v 0.6.0
node.js application to display a sharable map with user's location.
From b539b4fb8318adcc3382b28fdc3b239d52b0e4e1 Mon Sep 17 00:00:00 2001
From: Keith Irwin
Date: Wed, 26 Apr 2017 23:55:26 -0400
Subject: [PATCH 13/21] Final readme updates
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index efbd416..1daafb0 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# Tracman
+# Tracman
###### v 0.6.0
node.js application to display a sharable map with user's location.
From 0662741cd2af84c56c22bc8c2c4860c2b642f938 Mon Sep 17 00:00:00 2001
From: Keith Irwin
Date: Thu, 27 Apr 2017 00:49:11 -0400
Subject: [PATCH 14/21] Fixed control button font sizes
---
static/css/map.css | 106 +++++++++++++++++++++++---------------
static/js/map-controls.js | 4 +-
views/map.html | 2 +-
3 files changed, 68 insertions(+), 44 deletions(-)
diff --git a/static/css/map.css b/static/css/map.css
index c8438eb..cd4e9d0 100644
--- a/static/css/map.css
+++ b/static/css/map.css
@@ -61,15 +61,56 @@ main {
padding: 2%;
border-radius: 3px;
margin: 3%;
-} #spd-sign {
+}
+#spd-sign {
color: #000;
background-color: #FFF;
border: 2px solid #000;
-} #alt-sign {
+}
+#alt-sign {
color: #FFF;
background-color: #009800;
border: 2px solid #FFF;
}
+@media (max-width:300px) {
+ #spd, #alt {
+ height: 20px;
+ font-size: 18px;
+ }
+ #alt-unit, #spd-unit {
+ font-size: 8px;
+ }
+ #alt-label, #spd-label {
+ font-size: 9px;
+ height: 9px;
+ }
+}
+@media (min-width:300px) and (max-width:350px) {
+ #spd, #alt {
+ height: 22px;
+ font-size: 20px;
+ }
+ #alt-unit, #spd-unit {
+ font-size: 9px;
+ }
+ #alt-label, #spd-label {
+ font-size: 11px;
+ height: 11px;
+ }
+}
+@media (min-width:350px) and (max-width:400px) {
+ #spd, #alt {
+ height: 30px;
+ font-size: 28px;
+ }
+ #alt-unit, #spd-unit {
+ font-size: 10px;
+ }
+ #alt-label, #spd-label {
+ font-size: 14px;
+ height: 14px;
+ }
+}
@media (min-width:400px) {
#spd, #alt {
height: 40px;
@@ -82,44 +123,9 @@ main {
font-size: 18px;
height: 18px;
}
-} @media (min-width:350px) and (max-width:400px) {
- #spd, #alt {
- height: 30px;
- font-size: 28px;
- }
- #alt-unit, #spd-unit {
- font-size: 10px;
- }
- #alt-label, #spd-label {
- font-size: 14px;
- height: 14px;
- }
-} @media (min-width:300px) and (max-width:350px) {
- #spd, #alt {
- height: 22px;
- font-size: 20px;
- }
- #alt-unit, #spd-unit {
- font-size: 9px;
- }
- #alt-label, #spd-label {
- font-size: 11px;
- height: 11px;
- }
-} @media (min-width:0px) and (max-width:300px) {
- #spd, #alt {
- height: 20px;
- font-size: 18px;
- }
- #alt-unit, #spd-unit {
- font-size: 8px;
- }
- #alt-label, #spd-label {
- font-size: 9px;
- height: 9px;
- }
}
+
/* Control buttons */
#controls {
width: 100vw;
@@ -127,16 +133,34 @@ main {
bottom: 50px;
display: flex;
justify-content: space-around;
-} #controls .btn {
+}
+#controls .btn {
z-index: 50;
background: #222;
height: 10vh;
padding: 2vh 0;
-} #controls .btn:hover {
+}
+#controls .btn .fa {
+ margin: 0 2vw;
+}
+#controls .btn:hover {
background: #333;
}
#controls .btn.set, #controls .btn.clear {
width: 30vw;
-} #controls .btn.track {
+}
+#controls .btn.track {
width: 35vw;
+}
+@media (max-width:250px) {
+ #controls .btn { font-size:.8em; }
+}
+@media (min-width:250px) and (max-width:350px) {
+ #controls .btn { font-size:1em; }
+}
+@media (min-width:350px) and (max-width:450px) {
+ #controls .btn { font-size:1.15em; }
+}
+@media (min-width:450px) {
+ #controls .btn { font-size:1.3em; }
}
\ No newline at end of file
diff --git a/static/js/map-controls.js b/static/js/map-controls.js
index ef2f4a9..6542466 100644
--- a/static/js/map-controls.js
+++ b/static/js/map-controls.js
@@ -48,7 +48,7 @@ $(function(){
if (!wpid) {
if (!navigator.geolocation) { alert('Unable to track location. '); }
else {
- $('#track-loc').html(' Stop').prop('title',"Click here to stop tracking your location. ");
+ $('#track-loc').html(' Stop').prop('title',"Click here to stop tracking your location. ");
wpid = navigator.geolocation.watchPosition(
// Success callback
@@ -80,7 +80,7 @@ $(function(){
// Stop tracking
else {
- $('#track-loc').html(' Track').prop('title',"Click here to track your location. ");
+ $('#track-loc').html(' Track').prop('title',"Click here to track your location. ");
navigator.geolocation.clearWatch(wpid);
wpid = undefined;
}
diff --git a/views/map.html b/views/map.html
index c2e4daf..890af57 100644
--- a/views/map.html
+++ b/views/map.html
@@ -66,7 +66,7 @@
{% endif %}
Set
- Track
+ Track
Clear
From a43568a009387a9528c290f8256825d65a237d43 Mon Sep 17 00:00:00 2001
From: Keith Irwin
Date: Thu, 27 Apr 2017 15:52:24 -0400
Subject: [PATCH 15/21] Fixed changing password, added expiration time string,
fixed #51 clientside validation
---
config/models.js | 21 +++++++++++----------
1 file changed, 11 insertions(+), 10 deletions(-)
diff --git a/config/models.js b/config/models.js
index d82bf14..e698f04 100644
--- a/config/models.js
+++ b/config/models.js
@@ -51,8 +51,7 @@ const userSchema = new mongoose.Schema({
// For an example
// Create email confirmation token
- userSchema.methods.createEmailToken = function(next){
- // next(err,hash);
+ userSchema.methods.createEmailToken = function(next){// next(err,hash)
console.log('user.createEmailToken() called');
var user = this;
@@ -62,7 +61,7 @@ const userSchema = new mongoose.Schema({
//console.log(`Buffer ${buf.toString('hex')} created`);
user.emailToken = buf.toString('hex');
user.save();
- next(null,user.emailToken);
+ return next(null,user.emailToken);
}
});
@@ -96,13 +95,15 @@ const userSchema = new mongoose.Schema({
// Create new token
else {
- crypto.randomBytes(16)
- .then( (buf)=>{
- user.auth.passToken = buf.toString('hex');
- user.auth.passTokenExpires = Date.now() + 3600000; // 1 hour
- user.save();
- })
- .catch( (err)=>{ return next(err,null); });
+ crypto.randomBytes(16, (err,buf)=>{
+ if (err){ return next(err,null); }
+ if (buf) {
+ user.auth.passToken = buf.toString('hex');
+ user.auth.passTokenExpires = Date.now() + 3600000; // 1 hour
+ user.save();
+ return next(null,user.passToken);
+ }
+ });
}
};
From 3bdaf1b03493270baa4ec9aba318bd25bb6df6bd Mon Sep 17 00:00:00 2001
From: Keith Irwin
Date: Thu, 27 Apr 2017 16:44:49 -0400
Subject: [PATCH 16/21] #52 Added server-side uniqueness checks
---
config/models.js | 83 ++++++++-------
config/routes/index.js | 16 ++-
config/routes/map.js | 1 -
config/routes/settings.js | 139 ++++++++++++++++++-------
config/sockets.js | 17 +--
server.js | 2 +-
static/css/settings.css | 1 -
static/js/settings.js | 214 +++++++++++++++++++++++---------------
8 files changed, 302 insertions(+), 171 deletions(-)
diff --git a/config/models.js b/config/models.js
index e698f04..9edb081 100644
--- a/config/models.js
+++ b/config/models.js
@@ -51,8 +51,8 @@ const userSchema = new mongoose.Schema({
// For an example
// Create email confirmation token
- userSchema.methods.createEmailToken = function(next){// next(err,hash)
- console.log('user.createEmailToken() called');
+ userSchema.methods.createEmailToken = function(next){ // next(err,token)
+ //console.log('user.createEmailToken() called');
var user = this;
crypto.randomBytes(16, (err,buf)=>{
@@ -60,12 +60,56 @@ const userSchema = new mongoose.Schema({
if (buf){
//console.log(`Buffer ${buf.toString('hex')} created`);
user.emailToken = buf.toString('hex');
- user.save();
- return next(null,user.emailToken);
+ user.save()
+ .then( ()=>{
+ return next(null,user.emailToken);
+ })
+ .catch( (err)=>{
+ return next(err,null);
+ });
+
}
});
};
+
+ // Create password reset token
+ userSchema.methods.createPassToken = function(next){ // next(err,token,expires)
+ var user = this;
+
+ // Reuse old token, resetting clock
+ if ( user.auth.passTokenExpires >= Date.now() ){
+ console.log(`Reusing old password token...`);
+ user.auth.passTokenExpires = Date.now() + 3600000; // 1 hour
+ user.save()
+ .then( ()=>{
+ return next(null,user.auth.passToken,user.auth.passTokenExpires);
+ })
+ .catch( (err)=>{
+ return next(err,null,null);
+ });
+ }
+
+ // Create new token
+ else {
+ console.log(`Creating new password token...`);
+ crypto.randomBytes(16, (err,buf)=>{
+ if (err){ return next(err,null,null); }
+ if (buf) {
+ user.auth.passToken = buf.toString('hex');
+ user.auth.passTokenExpires = Date.now() + 3600000; // 1 hour
+ user.save()
+ .then( ()=>{
+ return next(null,user.auth.passToken,user.auth.passTokenExpires);
+ })
+ .catch( (err)=>{
+ return next(err,null,null);
+ });
+ }
+ });
+ }
+
+ };
// Generate hash for new password
userSchema.methods.generateHash = function(password,next){
@@ -77,37 +121,6 @@ const userSchema = new mongoose.Schema({
.catch( (err)=>{ return next(err,null); });
};
- // Create password reset token
- userSchema.methods.createPassToken = function(next){
- var user = this;
-
- // Reuse old token, resetting clock
- if ( user.auth.passTokenExpires <= Date.now() ){
- user.auth.passTokenExpires = Date.now() + 3600000; // 1 hour
- user.save()
- .then( ()=>{
- return next(null,user.auth.passToken);
- })
- .catch( (err)=>{
- return next(err,user.auth.passToken);
- });
- }
-
- // Create new token
- else {
- crypto.randomBytes(16, (err,buf)=>{
- if (err){ return next(err,null); }
- if (buf) {
- user.auth.passToken = buf.toString('hex');
- user.auth.passTokenExpires = Date.now() + 3600000; // 1 hour
- user.save();
- return next(null,user.passToken);
- }
- });
- }
-
- };
-
// Check for valid password
userSchema.methods.validPassword = function(password,next){
bcrypt.compare(password, this.auth.password, next);
diff --git a/config/routes/index.js b/config/routes/index.js
index 4f4f589..285dd6a 100644
--- a/config/routes/index.js
+++ b/config/routes/index.js
@@ -40,7 +40,7 @@ module.exports = router
})
// Endpoint to validate forms
- .get('/validate', (req,res)=>{
+ .get('/validate', (req,res,next)=>{
// Validate unique slug
if (req.query.slug) {
@@ -51,7 +51,10 @@ module.exports = router
}
else { res.sendStatus(200); }
})
- .catch( (err)=>{ mw.throwErr(err,req); });
+ .catch( (err)=>{
+ console.error(err);
+ res.sendStatus(500);
+ });
}
// Validate unique email
@@ -63,7 +66,10 @@ module.exports = router
}
else { res.sendStatus(200); }
})
- .catch( (err)=>{ mw.throwErr(err,req); });
+ .catch( (err)=>{
+ console.error(err);
+ res.sendStatus(500);
+ });
}
// Create slug
@@ -71,10 +77,14 @@ module.exports = router
res.send(slug(xss(req.query.slugify)));
}
+ // Sanitize for XSS
else if (req.query.xss) {
res.send(xss(req.query.xss));
}
+ // 404
+ else { next(); }
+
})
// Link to androidapp in play store
diff --git a/config/routes/map.js b/config/routes/map.js
index 7c3e8bd..3c657b3 100644
--- a/config/routes/map.js
+++ b/config/routes/map.js
@@ -1,5 +1,4 @@
'use strict';
-//TODO: Use promises
const router = require('express').Router(),
mw = require('../middleware.js'),
diff --git a/config/routes/settings.js b/config/routes/settings.js
index c3564dd..5ce7f6c 100644
--- a/config/routes/settings.js
+++ b/config/routes/settings.js
@@ -3,6 +3,7 @@
const slug = require('slug'),
xss = require('xss'),
mellt = require('mellt'),
+ moment = require('moment'),
mw = require('../middleware.js'),
User = require('../models.js').user,
mail = require('../mail.js'),
@@ -34,7 +35,6 @@ router.route('/')
// Set values
req.user.name = xss(req.body.name);
- req.user.slug = slug(xss(req.body.slug));
req.user.settings = {
units: req.body.units,
defaultMap: req.body.map,
@@ -72,41 +72,101 @@ router.route('/')
else {
- // Email changed
- if (req.user.email!==req.body.email) {
- //console.log(`Email changed to ${req.body.email}`);
- req.user.newEmail = req.body.email;
+ // Check if email changed
+ let checkEmailChanged = new Promise( (resolve,reject)=>{
- // Create token
- //console.log(`Creating email token...`);
- req.user.createEmailToken((err,token)=>{
- if (err){
- mw.throwErr(err,req);
- res.redirect(req.session.next||'/settings');
- }
+ // Email changed
+ if (req.user.email!==req.body.email) {
+ //console.log(`Email changed to ${req.body.email}`);
- // Send token to user by email
- //console.log(`Mailing new email token to ${req.body.email}...`);
- mail.send({
- to: `"${req.user.name}" <${req.body.email}>`,
- from: mail.from,
- subject: 'Confirm your new email address for Tracman',
- text: mail.text(`A request has been made to change your Tracman email address. If you did not initiate this request, please disregard it. \n\nTo confirm your email, follow this link:\n${env.url}/settings/email/${token}. `),
- html: mail.html(`A request has been made to change your Tracman email address. If you did not initiate this request, please disregard it.
To confirm your email, follow this link:${env.url}/settings/email/${token} .
`)
+ // Check uniqueness
+ User.findOne({ email: req.body.email })
+ .then( (existingUser)=>{
+
+ // Not unique!
+ if (existingUser && existingUser.id!==req.user.id) {
+ //console.log("Email not unique!");
+ req.flash('warning', `That email, ${req.body.email} , is already in use by another user! `);
+ }
+
+ // It's unique
+ else {
+ //console.log("Email is unique");
+ req.user.newEmail = req.body.email;
+
+ // Create token
+ //console.log(`Creating email token...`);
+ req.user.createEmailToken((err,token)=>{
+ if (err){ reject(err); }
+
+ // Send token to user by email
+ //console.log(`Mailing new email token to ${req.body.email}...`);
+ mail.send({
+ to: `"${req.user.name}" <${req.body.email}>`,
+ from: mail.from,
+ subject: 'Confirm your new email address for Tracman',
+ text: mail.text(`A request has been made to change your Tracman email address. If you did not initiate this request, please disregard it. \n\nTo confirm your email, follow this link:\n${env.url}/settings/email/${token}. `),
+ html: mail.html(`A request has been made to change your Tracman email address. If you did not initiate this request, please disregard it.
To confirm your email, follow this link:${env.url}/settings/email/${token} .
`)
+ })
+ .then( ()=>{
+ req.flash('warning',`An email has been sent to ${req.body.email} . Check your inbox to confirm your new email address. `);
+ })
+ .catch( (err)=>{
+ reject(err);
+ });
+
+ });
+
+ }
+
})
- .then( ()=>{
- req.flash('warning',`An email has been sent to ${req.body.email} . Check your inbox to confirm your new email address. `);
- setSettings();
- })
- .catch( (err)=>{
+ .then(resolve)
+ .catch( (err)=>{
mw.throwErr(err,req);
+ res.redirect('/settings');
});
- });
- }
+ } else { resolve(); }
+ });
- // Email not changed
- else { setSettings(); }
+ // Check if slug changed
+ let checkSlugChanged = new Promise( (resolve,reject)=>{
+
+ // Slug changed
+ if (req.user.slug!==req.body.slug) {
+
+ // Check uniqueness
+ User.findOne({ slug: req.body.slug })
+ .then( (existingUser)=>{
+
+ // Not unique!
+ if (existingUser && existingUser.id!==req.user.id) {
+ req.flash('warning', `That slug, ${req.body.slug} , is already in use by another user! `);
+ }
+
+ // It's unique
+ else {
+ req.user.slug = slug(xss(req.body.slug));
+ }
+
+ })
+ .then(resolve)
+ .catch( (err)=>{
+ mw.throwErr(err,req);
+ res.redirect('/settings');
+ });
+
+ } else { resolve(); }
+
+ });
+
+ // Set settings when done
+ Promise.all([checkEmailChanged, checkSlugChanged])
+ .then(setSettings)
+ .catch( (err)=>{
+ mw.throwErr(err,req);
+ res.redirect('/settings');
+ });
}
@@ -115,8 +175,6 @@ router.route('/')
// Delete user account
.delete( (req,res,next)=>{
- //TODO: Reenter password?
-
User.findByIdAndRemove(req.user)
.then( ()=>{
req.flash('success', 'Your account has been deleted. ');
@@ -174,28 +232,33 @@ router.route('/password')
.get( (req,res,next)=>{
// Create token for password change
- req.user.createPassToken( (err,token)=>{
+ req.user.createPassToken( (err,token,expires)=>{
if (err){
mw.throwErr(err,req);
- res.redirect(req.session.next||'/settings');
+ res.redirect((req.user)?'/settings':'/login');
}
+ // Figure out expiration time
+ let expirationTimeString = (req.query.tz)?
+ moment(expires).utcOffset(req.query.tz).toDate().toLocaleTimeString(req.acceptsLanguages[0]):
+ moment(expires).toDate().toLocaleTimeString(req.acceptsLanguages[0])+" UTC";
+
// 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(`A request has been made to change your tracman password. If you did not initiate this request, please contact support at keith@tracman.org .
To change your password, follow this link:${env.url}/settings/password/${token} .
This request will expire in 1 hour.
`)
+ 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 at ${expirationTimeString}. `),
+ html: mail.html(`A request has been made to change your tracman password. If you did not initiate this request, please contact support at keith@tracman.org .
To change your password, follow this link:${env.url}/settings/password/${token} .
This request will expire at ${expirationTimeString}.
`)
})
.then( ()=>{
// Alert user to check email.
- req.flash('success',`An email has been sent to ${req.user.email} . Check your inbox to complete your password change. `);
- res.redirect('/login#login');
+ req.flash('success',`An link has been sent to ${req.user.email} . Click on the link to complete your password change. This link will expire in one hour (${expirationTimeString}). `);
+ res.redirect((req.user)?'/settings':'/login');
})
.catch( (err)=>{
mw.throwErr(err,req);
- res.redirect('/login#login');
+ res.redirect((req.user)?'/settings':'/login');
});
});
diff --git a/config/sockets.js b/config/sockets.js
index 563af44..1150edf 100644
--- a/config/sockets.js
+++ b/config/sockets.js
@@ -1,8 +1,7 @@
'use strict';
// Imports
-const mw = require('./middleware.js'),
- User = require('./models.js').user;
+const User = require('./models.js').user;
// Check for tracking clients
function checkForUsers(io, user) {
@@ -10,10 +9,14 @@ 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( (id)=>{
- return io.sockets.connected[id];
+ /* if (Object.values(io.sockets.connected).some( (socket)=>{
+ * return socket.gets==user;
+ * })) {
+ */
+ if (Object.keys(io.sockets.connected).map( (key)=>{
+ return io.sockets.connected[key];
}).some( (socket)=>{
- return socket.gets==user;
+ return socket.gets===user;
})) {
//console.log(`Activating updates for ${user}.`);
io.to(user).emit('activate','true');
@@ -32,8 +35,8 @@ module.exports = {
//console.log(`${socket.id} connected.`);
// Set a few variables
- socket.ip = socket.client.request.headers['x-real-ip'];
- socket.ua = socket.client.request.headers['user-agent'];
+ //socket.ip = socket.client.request.headers['x-real-ip'];
+ //socket.ua = socket.client.request.headers['user-agent'];
/* Log */
//socket.on('log', (text)=>{
diff --git a/server.js b/server.js
index 7b165ca..40b33c9 100755
--- a/server.js
+++ b/server.js
@@ -79,8 +79,8 @@ const
// Path for redirects
let nextPath = ( req.path.substring(0, req.path.indexOf('#')) || req.path );
if ( nextPath.substring(0,6)!=='/login' && nextPath.substring(0,7)!=='/logout' ){
- //console.log(`Setting redirect path to ${nextPath}#`);
req.session.next = nextPath+'#';
+ //console.log(`Set redirect path to ${nextPath}#`);
}
// User account
diff --git a/static/css/settings.css b/static/css/settings.css
index 6206095..50c4a5f 100644
--- a/static/css/settings.css
+++ b/static/css/settings.css
@@ -54,7 +54,6 @@
/* Submit buttons */
#submit-group {
- padding: 0 0 60px;
justify-content: space-around;
}
#submit-group .main {
diff --git a/static/js/settings.js b/static/js/settings.js
index 931b26d..77be716 100644
--- a/static/js/settings.js
+++ b/static/js/settings.js
@@ -8,7 +8,7 @@ function validateEmail(email) {
}
// Replace inputed value with response
-function validateFromEndpoint(type, selector, cb) {
+function replaceFromEndpoint(type, selector, cb) {
$.get('/validate?'+type+'='+$(selector).val())
.done(function(data){
$(selector).val(data);
@@ -18,95 +18,17 @@ function validateFromEndpoint(type, selector, cb) {
// On page load
$(function(){
-
- function validateForm(input) {
-
- // Everything passed - make sure no help texts are visible
- function recheckInputs() {
- if ($('#email-help').is(":visible")) {
- $('#submit-group .main').prop('disabled',true).prop('title',"You need to supply a different email address. ");
- }
- else if ($('#slug-help').is(":visible")) {
- $('#submit-group .main').prop('disabled',true).prop('title',"You need to supply a different slug. ");
- }
- else {
- $('#submit-group .main').prop('disabled',false).prop('title',"Click here to save your changes. ");
- }
- }
-
- // Empty fields
- if ($('#slug-input').val()===''){
- $('#slug-help').show().text("A slug is required. ");
- $('#submit-group .main').prop('disabled',true).prop('title',"You need to enter a slug. ");
- }
- else if ($('#email-input').val()===''){
- $('#email-help').show().text("An email is required. ");
- $('#submit-group .main').prop('disabled',true).prop('title',"You need to enter an email address. ");
- }
-
- // Is email
- else if (!validateEmail($('#email-input').val())) {
- $('#email-help').show().text("You must enter a valid email address. ");
- $('#submit-group .main').prop('disabled',true).prop('title',"You need to enter a valid email address. ");
- }
-
- // Validate unique fields with server
- else if (input) {
-
- // Make AJAX request
- $.get('/validate?'+input+'='+$('#'+input+'-input').val())
- .fail(function(data,status){
-
- // Input is not unique
- if (status===400) {
- $('#'+input+'-help').show().text("That "+input+" is already in use by another user. ");
- $('#submit-group .main').prop('disabled',true).prop('title',"You need to supply a different "+input+". ");
- }
-
- // Server error
- else {
- $('#'+input+'-help').show().text("Unable to confirm unique "+input+" with the server. This might not work... ");
- recheckInputs();
- }
-
- })
-
- // Input is unique
- .done(function(){
- $('#'+input+'-help').hide();
- recheckInputs();
- });
-
- }
-
- // All passed
- else { recheckInputs(); }
-
- }
-
- // Validate slug
- $('#slug-input').change(function(){
- validateFromEndpoint('slugify','#slug-input',function(){
- validateForm(slug);
- });
- });
-
- // Validate email
- $('#email-input').change(function(){
- validateForm('email');
- });
-
- // Validate name
- $('#name-input').change(function(){
- validateFromEndpoint('xss','#name-input',validateForm);
- });
+ var slugNotUnique, emailNotUnique;
+ // Set timezone in password change link
+ $('#password').attr('href',"/settings/password?tz="+new Date().getTimezoneOffset());
+
// Delete account
$('#delete').click(function(){
if (confirm("Are you sure you want to delete your account? This CANNOT be undone! ")) {
$.ajax({
- url: "/settings",
- type: "DELETE",
+ url: '/settings',
+ type: 'DELETE',
success: function(){
location.reload();
},
@@ -117,4 +39,126 @@ $(function(){
}
});
+ function validateForm(input) {
+
+ // Perform basic check, then validate uniqueness
+ basicCheck(function(){ validateUniqueness(input); });
+
+ function basicCheck(cb){
+ var checkedCount = 0;
+
+ // Check slug
+ if (!$('#slug-input').val()){
+ $('#slug-help').show().text("A slug is required. ");
+ $('#submit-group .main').prop('disabled',true).prop('title',"You need to enter a slug. ");
+ if (checkedCount>0) {cb();} else {checkedCount++;}
+ }
+ else {
+ if (!slugNotUnique){ $('#slug-help').hide(); }
+ if (checkedCount>0) {cb();} else {checkedCount++;}
+ }
+
+ // Check email
+ if (!$('#email-input').val()){
+ $('#email-help').show().text("An email is required. ");
+ $('#submit-group .main').prop('disabled',true).prop('title',"You need to enter an email address. ");
+ if (checkedCount>0) {cb();} else {checkedCount++;}
+ }
+ else if (!validateEmail($('#email-input').val())) {
+ $('#email-help').show().text("You must enter a valid email address. ");
+ $('#submit-group .main').prop('disabled',true).prop('title',"You need to enter a valid email address. ");
+ if (checkedCount>0) {cb();} else {checkedCount++;}
+ }
+ else {
+ if (!emailNotUnique){ $('#email-help').hide(); }
+ if (checkedCount>0) {cb();} else {checkedCount++;}
+ }
+ }
+
+ function validateUniqueness(input){
+
+ function recheckBasic(){
+ if ($('#email-help').is(":visible") && $('#email-help').text().substring(0,25)!=="Unable to confirm unique ") {
+ $('#submit-group .main').prop('disabled',true).prop('title',"You need to supply a different email address. ");
+ }
+ else if ($('#slug-help').is(":visible") && $('#slug-help').text().substring(0,25)!=="Unable to confirm unique ") {
+ $('#submit-group .main').prop('disabled',true).prop('title',"You need to supply a different slug. ");
+ }
+ else if ( $('#slug-help').text().substring(0,25)==="Unable to confirm unique " ) {
+ $('#submit-group .main').prop('title',"Unable to confirm unique slug with the server. This might not work... ");
+ }
+ else if ( $('#email-help').text().substring(0,25)==="Unable to confirm unique " ) {
+ $('#submit-group .main').prop('title',"Unable to confirm unique email with the server. This might not work... ");
+ }
+ else {
+ $('#submit-group .main').prop('disabled',false).prop('title',"Click here to save your changes. ");
+ }
+ }
+
+ // Should server be queried for unique values?
+ if (input && $('#'+input+'-input').val()) {
+ if (input==='email' && !validateEmail($('#email-input').val())) {}
+
+ // Query server for unique values
+ else {
+ $.ajax({
+ url: '/validate?'+input+'='+$('#'+input+'-input').val(),
+ type: 'GET',
+ statusCode: {
+
+ // Is unique
+ 200: function(){
+ $('#'+input+'-help').hide();
+ if (input==='slug'){ slugNotUnique=false; }
+ else if (input==='email'){ emailNotUnique=false; }
+ recheckBasic();
+ },
+
+ // Isn't unique
+ 400: function(){
+ if (input==='slug'){ slugNotUnique=true; }
+ else if (input==='email'){ emailNotUnique=true; }
+ $('#'+input+'-help').show().text("That "+input+" is already in use by another user. ");
+ $('#submit-group .main').prop('disabled',true).prop('title',"You need to supply a different "+input+". ");
+ }
+
+ } })
+
+ // Server error
+ .error( function(){
+ if (input==='slug'){ slugNotUnique=undefined; }
+ else if (input==='email'){ emailNotUnique=undefined; }
+ $('#'+input+'-help').show().text("Unable to confirm unique "+input+". This might not work... ");
+ recheckBasic();
+ });
+
+ } }
+
+ // Nothing changed. Recheck basic validations
+ else { recheckBasic(); }
+
+ }
+
+ }
+
+ // Input change listeners
+ $('#slug-input').change(function(){
+ if (!$('#slug-input').val()){
+ $('#slug-help').show().text("A slug is required. ");
+ $('#submit-group .main').prop('disabled',true).prop('title',"You need to enter a slug. ");
+ }
+ else {
+ $('#slug-help').hide();
+ replaceFromEndpoint('slugify','#slug-input',function(){
+ validateForm('slug');
+ });
+ }
+ });
+ $('#email-input').change(function(){
+ validateForm('email');
+ });
+ $('#name-input').change(function(){
+ replaceFromEndpoint('xss','#name-input',validateForm);
+ });
+
});
From 1216127add9575dfc9507f1e51bc0dbb144c4627 Mon Sep 17 00:00:00 2001
From: Keith Irwin
Date: Thu, 27 Apr 2017 17:25:16 -0400
Subject: [PATCH 17/21] Fixed promises
---
config/routes/auth.js | 4 +-
config/routes/settings.js | 219 +++++++++++++++++++-------------------
2 files changed, 109 insertions(+), 114 deletions(-)
diff --git a/config/routes/auth.js b/config/routes/auth.js
index d40eefb..f028cde 100644
--- a/config/routes/auth.js
+++ b/config/routes/auth.js
@@ -115,7 +115,7 @@ module.exports = (app, passport) => {
user.slug = slug(user.email.substring(0, user.email.indexOf('@')));
// Generate unique slug
- let slug = new Promise((resolve,reject) => {
+ const slug = new Promise((resolve,reject) => {
(function checkSlug(s,cb){
User.findOne({slug:s})
@@ -150,7 +150,7 @@ module.exports = (app, passport) => {
});
// Generate sk32
- let sk32 = new Promise((resolve,reject) => {
+ const sk32 = new Promise((resolve,reject) => {
crypto.randomBytes(32)
.then( (buf)=>{
user.sk32 = buf.toString('hex');
diff --git a/config/routes/settings.js b/config/routes/settings.js
index 5ce7f6c..ad5d147 100644
--- a/config/routes/settings.js
+++ b/config/routes/settings.js
@@ -30,8 +30,109 @@ router.route('/')
// Set new settings
.post( (req,res,next)=>{
- function setSettings(){
- //console.log('setSettings() called');
+ // Validate email
+ const checkEmail = new Promise( (resolve,reject)=>{
+
+ // Check validity
+ if (!validateEmail(req.body.email)) {
+ req.flash('warning', `${req.body.email} is not a valid email address. `);
+ resolve();
+ }
+
+ // Check if unchanged
+ else if (req.user.email===req.body.email) {
+ resolve();
+ }
+
+ // Check uniqueness
+ else {
+ User.findOne({ email: req.body.email })
+ .then( (existingUser)=>{
+
+ // Not unique!
+ if (existingUser && existingUser.id!==req.user.id) {
+ //console.log("Email not unique!");
+ req.flash('warning', `That email, ${req.body.email} , is already in use by another user! `);
+ resolve();
+ }
+
+ // It's unique
+ else {
+ //console.log("Email is unique");
+ req.user.newEmail = req.body.email;
+
+ // Create token
+ //console.log(`Creating email token...`);
+ req.user.createEmailToken((err,token)=>{
+ if (err){ reject(err); }
+
+ // Send token to user by email
+ //console.log(`Mailing new email token to ${req.body.email}...`);
+ mail.send({
+ to: `"${req.user.name}" <${req.body.email}>`,
+ from: mail.from,
+ subject: 'Confirm your new email address for Tracman',
+ text: mail.text(`A request has been made to change your Tracman email address. If you did not initiate this request, please disregard it. \n\nTo confirm your email, follow this link:\n${env.url}/settings/email/${token}. `),
+ html: mail.html(`A request has been made to change your Tracman email address. If you did not initiate this request, please disregard it.
To confirm your email, follow this link:${env.url}/settings/email/${token} .
`)
+ })
+ .then( ()=>{
+ req.flash('warning',`An email has been sent to ${req.body.email} . Check your inbox to confirm your new email address. `);
+ resolve();
+ })
+ .catch(reject);
+
+ });
+
+ }
+
+ })
+ .catch(reject);
+ }
+
+ });
+
+ // Validate slug
+ const checkSlug = new Promise( (resolve,reject)=>{
+
+ // Check existence
+ if (req.body.slug==='') {
+ req.flash('warning', `You must supply a slug. `);
+ resolve();
+ }
+
+ // Check if unchanged
+ else if (req.user.slug===slug(xss(req.body.slug))) {
+ resolve();
+ }
+
+ // Check uniqueness
+ else {
+
+ User.findOne({ slug: req.body.slug })
+ .then( (existingUser)=>{
+
+ // Not unique!
+ if (existingUser && existingUser.id!==req.user.id) {
+ req.flash('warning', `That slug, ${req.body.slug} , is already in use by another user! `);
+ }
+
+ // It's unique
+ else {
+ req.user.slug = slug(xss(req.body.slug));
+ }
+
+ })
+ .then(resolve)
+ .catch(reject);
+
+ }
+
+ });
+
+ // Set settings when done
+ Promise.all([checkEmail, checkSlug])
+ .then( ()=>{
+ //console.log('Setting settings... ');
// Set values
req.user.name = xss(req.body.name);
@@ -58,117 +159,11 @@ router.route('/')
res.redirect('/settings');
});
- }
-
- // Validations
- if (req.body.slug==='') {
- req.flash('warning', `You must supply a slug. `);
+ })
+ .catch( (err)=>{
+ mw.throwErr(err,req);
res.redirect('/settings');
- }
- else if (!validateEmail(req.body.email)) {
- req.flash('warning', `${req.body.email} is not a valid email address. `);
- res.redirect('/settings');
- }
-
- else {
-
- // Check if email changed
- let checkEmailChanged = new Promise( (resolve,reject)=>{
-
- // Email changed
- if (req.user.email!==req.body.email) {
- //console.log(`Email changed to ${req.body.email}`);
-
- // Check uniqueness
- User.findOne({ email: req.body.email })
- .then( (existingUser)=>{
-
- // Not unique!
- if (existingUser && existingUser.id!==req.user.id) {
- //console.log("Email not unique!");
- req.flash('warning', `That email, ${req.body.email} , is already in use by another user! `);
- }
-
- // It's unique
- else {
- //console.log("Email is unique");
- req.user.newEmail = req.body.email;
-
- // Create token
- //console.log(`Creating email token...`);
- req.user.createEmailToken((err,token)=>{
- if (err){ reject(err); }
-
- // Send token to user by email
- //console.log(`Mailing new email token to ${req.body.email}...`);
- mail.send({
- to: `"${req.user.name}" <${req.body.email}>`,
- from: mail.from,
- subject: 'Confirm your new email address for Tracman',
- text: mail.text(`A request has been made to change your Tracman email address. If you did not initiate this request, please disregard it. \n\nTo confirm your email, follow this link:\n${env.url}/settings/email/${token}. `),
- html: mail.html(`A request has been made to change your Tracman email address. If you did not initiate this request, please disregard it.
To confirm your email, follow this link:${env.url}/settings/email/${token} .
`)
- })
- .then( ()=>{
- req.flash('warning',`An email has been sent to ${req.body.email} . Check your inbox to confirm your new email address. `);
- })
- .catch( (err)=>{
- reject(err);
- });
-
- });
-
- }
-
- })
- .then(resolve)
- .catch( (err)=>{
- mw.throwErr(err,req);
- res.redirect('/settings');
- });
-
- } else { resolve(); }
- });
-
- // Check if slug changed
- let checkSlugChanged = new Promise( (resolve,reject)=>{
-
- // Slug changed
- if (req.user.slug!==req.body.slug) {
-
- // Check uniqueness
- User.findOne({ slug: req.body.slug })
- .then( (existingUser)=>{
-
- // Not unique!
- if (existingUser && existingUser.id!==req.user.id) {
- req.flash('warning', `That slug, ${req.body.slug} , is already in use by another user! `);
- }
-
- // It's unique
- else {
- req.user.slug = slug(xss(req.body.slug));
- }
-
- })
- .then(resolve)
- .catch( (err)=>{
- mw.throwErr(err,req);
- res.redirect('/settings');
- });
-
- } else { resolve(); }
-
- });
-
- // Set settings when done
- Promise.all([checkEmailChanged, checkSlugChanged])
- .then(setSettings)
- .catch( (err)=>{
- mw.throwErr(err,req);
- res.redirect('/settings');
- });
-
- }
+ });
} )
From dc2a833c20c5bb10b5b3f7818e6bb473c53a5f07 Mon Sep 17 00:00:00 2001
From: Keith Irwin
Date: Fri, 28 Apr 2017 03:21:16 -0400
Subject: [PATCH 18/21] Login not needed to view /help
---
config/routes/index.js | 2 +-
views/templates/header.html | 4 +++-
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/config/routes/index.js b/config/routes/index.js
index 285dd6a..44fa002 100644
--- a/config/routes/index.js
+++ b/config/routes/index.js
@@ -14,7 +14,7 @@ module.exports = router
})
// Help
- .get('/help', mw.ensureAuth, (req,res)=>{
+ .get('/help', (req,res)=>{
res.render('help');
})
diff --git a/views/templates/header.html b/views/templates/header.html
index cf46b34..d2c6a1a 100644
--- a/views/templates/header.html
+++ b/views/templates/header.html
@@ -16,10 +16,12 @@
Map
Settings
{% if user.isAdmin %}Admin {% endif %}
+ {% endif %}
+ About
Help
+ {% if user %}
Logout
{% else %}
- About
Demo
Login
Join
From 692f647143b8f7a82af1bd39bfb3bb9d2c989585 Mon Sep 17 00:00:00 2001
From: Keith Irwin
Date: Fri, 28 Apr 2017 03:24:03 -0400
Subject: [PATCH 19/21] Fixed margins, rearranged base.css
---
static/css/base.css | 108 ++++++++++++++++++++++----------------------
1 file changed, 54 insertions(+), 54 deletions(-)
diff --git a/static/css/base.css b/static/css/base.css
index de43f40..83e4d07 100644
--- a/static/css/base.css
+++ b/static/css/base.css
@@ -31,60 +31,6 @@ body {
}
/* Elements */
-h1, h2, h3 {
- margin: 0 0 5% 0;
- position: relative;
- z-index: 6;
-}
-h1, h2, h3, h4 { font-weight: 600; }
-h1 {
- font-size: 48px;
- line-height: 46px; }
-h2 {
- font-size: 40px;
- line-height: 36px; }
-h3 { font-size: 28px; }
-h4 { font-size: 20px; }
-p {
- margin-top: 0;
- margin-bottom: 10vh;
-}
-
-a {
- color: #fbc93d;
- text-decoration: none;
-}
-main a:hover:not(.btn) {
- color: #fbc93d;
- text-decoration: underline;
-}
-a.underline {
- text-decoration: underline;
-}
-a.underline:hover:not(.btn) {
- text-decoration: none;
-}
-
-hr {
- width: 90%;
- margin: 10% auto;
-}
-img {
- max-width: 100%;
-}
-p img {
- display: block;
- margin: auto;
-}
-pre {
- white-space: pre-wrap;
- white-space: -moz-pre-wrap;
- white-space: -pre-wrap;
- white-space: -o-pre-wrap;
- word-wrap: break-word;
-}
-
-
main {
top: 59px;
position: absolute;
@@ -108,6 +54,60 @@ section {
padding: 10vh 0 5vh;
}
+h1, h2, h3 {
+ margin: 0 0 5% 0;
+ position: relative;
+ z-index: 6;
+}
+/* Font sizes */
+h1, h2, h3, h4 { font-weight: 600; }
+h1 {
+ font-size: 48px;
+ line-height: 46px; }
+h2 {
+ font-size: 40px;
+ line-height: 36px; }
+h3 { font-size: 28px; }
+h4 { font-size: 20px; }
+
+p, main ul {
+ margin-top: 0;
+ margin-bottom: 10vh;
+}
+hr {
+ width: 90%;
+ margin: 10% auto;
+}
+img {
+ max-width: 100%;
+}
+p img {
+ display: block;
+ margin: auto;
+}
+pre {
+ white-space: pre-wrap;
+ white-space: -moz-pre-wrap;
+ white-space: -pre-wrap;
+ white-space: -o-pre-wrap;
+ word-wrap: break-word;
+}
+
+a {
+ color: #fbc93d;
+ text-decoration: none;
+}
+main a:hover:not(.btn) {
+ color: #fbc93d;
+ text-decoration: underline;
+}
+a.underline {
+ text-decoration: underline;
+}
+a.underline:hover:not(.btn) {
+ text-decoration: none;
+}
+
/* Modifiers */
.hide { display: none !important; }
.red, .red:hover { color: #fb6e3d !important; }
From a6e18ed0e3c5da922b992d73bdbaeb7cca9cadea Mon Sep 17 00:00:00 2001
From: Keith Irwin
Date: Fri, 28 Apr 2017 03:26:09 -0400
Subject: [PATCH 20/21] Removed map button
---
views/help.html | 2 --
1 file changed, 2 deletions(-)
diff --git a/views/help.html b/views/help.html
index 051f87e..dc4b398 100644
--- a/views/help.html
+++ b/views/help.html
@@ -96,7 +96,5 @@
iOS apps can only be built using a mac.
- Go to map
-
{% endblock %}
\ No newline at end of file
From ae22b3f59aae02af5840a3bfe6adc5a956042bd1 Mon Sep 17 00:00:00 2001
From: Keith Irwin
Date: Fri, 28 Apr 2017 03:32:42 -0400
Subject: [PATCH 21/21] Removed share buttons, added privacy/terms to footer
---
views/templates/footer.html | 8 +++-----
1 file changed, 3 insertions(+), 5 deletions(-)
diff --git a/views/templates/footer.html b/views/templates/footer.html
index c731aa9..1beecbe 100644
--- a/views/templates/footer.html
+++ b/views/templates/footer.html
@@ -4,14 +4,12 @@
Design by Fraser Boag .