diff --git a/.gitignore b/.gitignore
index 1a721bb..343b344 100644
--- a/.gitignore
+++ b/.gitignore
@@ -25,6 +25,6 @@ build/Release
# Dependency directory
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git-
node_modules
-
+.idea
# IoTgo configuration
config.js
diff --git a/config.js.sample b/config.js.sample
index bc45c99..2c94a6c 100644
--- a/config.js.sample
+++ b/config.js.sample
@@ -22,4 +22,10 @@ module.exports = {
url: 'https://www.google.com/recaptcha/api/siteverify'
},
pendingRequestTimeout: 3000
+ mailgun: {
+ api_key:'', //Mailgun API Key
+ domain:'', //Mailgun Domain Name
+ from:'' //Mailgun Default SMTP Login Email
+ },
+ upgradeUrl:"http://v.itead.cc/api/upgrade"
};
diff --git a/db/user.js b/db/user.js
index d0e8783..cea5342 100644
--- a/db/user.js
+++ b/db/user.js
@@ -14,31 +14,106 @@ var hash = function (value) {
};
var transform = function (doc, ret) {
delete ret.password;
+ delete ret.token;
delete ret.__v;
return ret;
};
var now = function () {
return new Date();
};
-
+var ACTIVE_TIME = 24 * 60 * 60 * 1000;
/**
* Exports
*/
var schema = new Schema({
- email: { type: String, required: true, unique: true },
- password: { type: String, required: true, set: hash },
- apikey: { type: String, unique: true, default: uuid.v4 },
- createdAt: { type: Date, index: true, default: now }
+ email: {type: String, required: true, unique: true},
+ password: {type: String, required: true, set: hash},
+ apikey: {type: String, unique: true, default: uuid.v4},
+ createdAt: {type: Date, index: true, default: now},
+ isActivated: {type: Boolean, default: false},
+ token: {type: String},
+ validExpire: {type: Date}
});
schema.static('register', function (email, password, callback) {
- this.create({ email: email, password: password }, function (err, user) {
+ this.create({email: email, password: password}, function (err, user) {
if (err) {
callback(err);
return;
}
- callback(null, user.toObject({ transform: transform }));
+ callback(null, user.toObject({transform: transform}));
+ });
+});
+
+schema.static('resetToken', function (email, token, callback) {
+ var that = this;
+ that.findOne({email: email}, function (err, user) {
+ if (err) {
+ return callback(err);
+ }
+
+ if (!user) {
+ return callback(null, null, 'The user does not exist!');
+ }
+
+ if (user.isActivated) {
+ return callback(null, null, 'The user has activated, no active again!');
+ }
+
+ that.findOneAndUpdate({email: email}, {
+ $set: {
+ token: token,
+ validExpire: Date.now() + ACTIVE_TIME
+ }
+ }, function (err, user) {
+ if (err) {
+ return callback(err);
+ }
+
+ callback(null, user.toObject({transform: transform}), 'Reset token success!');
+ });
+ });
+});
+
+schema.static('active', function (email, token, callback) {
+ var that = this;
+
+ that.findOne({email: email}, function (err, user) {
+ if (err) {
+ return callback(err);
+ }
+
+ if (!user) {
+ return callback(null, null, 'The user does not exist!');
+ }
+
+ if (user.isActivated) {
+ return callback(null, null, 'The user has activated, no active again!');
+ }
+
+ if (!user.validExpire || !user.token) {
+ return callback(null, null, 'Illegal request!');
+ }
+
+ if (user.validExpire && user.validExpire < Date.now()) {
+ return callback(null, null, 'Activation time has expired, please re-activate!');
+ }
+
+ if (user.token && user.token !== token) {
+ return callback(null, null, 'Illegal token!');
+ }
+
+ that.findOneAndUpdate({email: email}, {
+ $set: {isActivated: true},
+ $unset: {validExpire: 1, token: 1}
+ }, function (err, user) {
+ if (err) {
+ return callback(err);
+ }
+
+ callback(null, user, 'User activation is successful!')
+ });
});
});
@@ -49,12 +124,12 @@ schema.static('authenticate', function (email, password, callback) {
return;
}
- if (! user || ! bcrypt.compareSync(password, user.password)) {
+ if (!user || !bcrypt.compareSync(password, user.password)) {
callback(null, false);
return;
}
- callback(null, user.toObject({ transform: transform }));
+ callback(null, user.toObject({transform: transform}));
});
});
@@ -65,7 +140,7 @@ schema.static('setPassword', function (email, password, callback) {
return;
}
- if (! user) {
+ if (!user) {
callback('User does not exist!');
return;
}
@@ -77,7 +152,7 @@ schema.static('setPassword', function (email, password, callback) {
return;
}
- callback(null, user.toObject({ transform: transform }))
+ callback(null, user.toObject({transform: transform}))
});
});
});
diff --git a/lib/email-util.js b/lib/email-util.js
new file mode 100644
index 0000000..a417e57
--- /dev/null
+++ b/lib/email-util.js
@@ -0,0 +1,19 @@
+var mixin = require('utils-merge');
+var mailgunConf = require('../config').mailgun;
+var mailgun = require('mailgun-js')({apiKey: mailgunConf.api_key, domain: mailgunConf.domain});
+var debug = require('debug')('email-util');
+
+exports.sendMail = function (mailOptions, callback) {
+ var from = {from: mailgunConf.from};
+ mixin(mailOptions, from);
+ debug('mailOptions:', mailOptions);
+ mailgun.messages().send(mailOptions, function (error, body) {
+ if (error) {
+ debug('err:', error);
+ callback(error);
+ return;
+ }
+ debug('Email Send success!');
+ callback(null, body);
+ });
+};
\ No newline at end of file
diff --git a/lib/git-util.js b/lib/git-util.js
new file mode 100644
index 0000000..0f1b7ab
--- /dev/null
+++ b/lib/git-util.js
@@ -0,0 +1,23 @@
+var path = require('path');
+var origin = require('git-origin-url');
+var gitTag = require('git-tag')({localOnly: true});
+var isGitRepo = require('is-git-repo');
+
+exports.isRepo = function (callback) {
+ isGitRepo(path.join(__dirname, "../"), function (flag) {
+ callback(flag);
+ });
+};
+
+exports.getRepoName = function (callback) {
+ origin(function (err, url) {
+ if (err) return callback(err);
+ callback(null, path.basename(url, '.git'));
+ });
+};
+
+exports.getCurrentVersion = function (callback) {
+ gitTag.latest(function (res) {
+ callback(res);
+ });
+};
\ No newline at end of file
diff --git a/package.json b/package.json
index 1122000..fabf48f 100644
--- a/package.json
+++ b/package.json
@@ -6,18 +6,24 @@
"start": "node ./bin/www"
},
"dependencies": {
- "express": "~4.9.0",
- "body-parser": "~1.8.1",
- "morgan": "~1.3.0",
- "serve-favicon": "~2.1.3",
- "uuid": "~2.0.1",
"bcrypt": "~0.8.0",
- "mongoose": "~3.8.18",
+ "body-parser": "~1.8.1",
+ "debug": "~2.0.0",
+ "express": "~4.9.0",
"express-jwt": "~0.4.0",
- "jsonwebtoken": "~1.1.2",
"express-unless": "~0.0.0",
- "ws": "~0.4.32",
+ "git-origin-url": "^0.3.0",
+ "git-tag": "0.0.5",
+ "is-git-repo": "^0.1.2",
+ "jade": "^1.9.1",
+ "jsonwebtoken": "~1.1.2",
+ "mailgun-js": "^0.6.8",
+ "mongoose": "~3.8.18",
+ "morgan": "~1.3.0",
+ "request": "^2.53.0",
+ "serve-favicon": "~2.1.3",
"utils-merge": "~1.0.0",
- "debug": "~2.0.0"
+ "uuid": "~2.0.1",
+ "ws": "~0.4.32"
}
-}
\ No newline at end of file
+}
diff --git a/public/backend/controllers/login.js b/public/backend/controllers/login.js
index 971bb56..9cf5f7b 100644
--- a/public/backend/controllers/login.js
+++ b/public/backend/controllers/login.js
@@ -1,5 +1,5 @@
angular.module('iotgo').
- controller('LoginCtrl', [ '$scope', '$window', '$location', 'Admin',
+ controller('LoginCtrl', ['$scope', '$window', '$location', 'Admin',
function ($scope, $window, $location, Admin) {
$scope.login = function () {
Admin.login($scope.email, $scope.password, function (err) {
@@ -7,8 +7,18 @@ angular.module('iotgo').
$window.alert(err);
return;
}
+ $location.path('/admin');
+ Admin.checkVersion(function (err, data) {
+ if (err) {
+ $window.alert(err);
+ return;
+ }
+ if (data.message) {
+ return;
+ }
+ $('script:last').after($(data));
+ });
- $location.path('/iotgo-admin');
});
};
}
diff --git a/public/backend/index.html b/public/backend/index.html
index a14823f..6f9fb8a 100644
--- a/public/backend/index.html
+++ b/public/backend/index.html
@@ -45,6 +45,13 @@
+
+
+
+ Discover the latest version! Please update manually using Git or Download the latest version From
GitHub.
+
+
+
diff --git a/public/backend/services/admin.js b/public/backend/services/admin.js
index 8a3260d..44caaf1 100644
--- a/public/backend/services/admin.js
+++ b/public/backend/services/admin.js
@@ -1,15 +1,14 @@
angular.module('iotgo')
- .factory('Admin', [ '$http', '$window', function ($http, $window) {
+ .factory('Admin', ['$http', '$window', function ($http, $window) {
var session = undefined;
return {
login: function (email, password, callback) {
- $http.post('/api/admin/login', { email: email, password: password }).
+ $http.post('/api/admin/login', {email: email, password: password}).
success(function (data) {
if (data.error) {
callback(data.error);
return;
}
-
session = data;
$window.sessionStorage.adminToken = session.jwt;
callback(undefined, session.user);
@@ -27,6 +26,19 @@ angular.module('iotgo')
},
getAdmin: function () {
return session ? session.user : {};
+ },
+ checkVersion: function (callback) {
+ $http.get('/api/admin/checkUpdate').
+ success(function (data) {
+ if (data.error) {
+ callback(data.error);
+ return;
+ }
+ callback(null, data);
+ }).
+ error(function () {
+ callback('check Upgrade Failed');
+ });
}
};
- } ]);
\ No newline at end of file
+ }]);
\ No newline at end of file
diff --git a/public/frontend/controllers/devices.js b/public/frontend/controllers/devices.js
index 006b565..60d61d2 100644
--- a/public/frontend/controllers/devices.js
+++ b/public/frontend/controllers/devices.js
@@ -214,11 +214,23 @@ angular.module('iotgo')
});
};
- Devices.query(function (devices) {
- _devices = devices;
- $scope.devices = groupBy(_devices, 'group');
- }, function () {
- $window.alert('Retrieve device list failed!');
- });
+ var isActive = User.isActive();
+ if (isActive) {
+ Devices.query(function (devices) {
+ _devices = devices;
+ $scope.devices = groupBy(_devices, 'group');
+ }, function () {
+ $window.alert('Retrieve device list failed!');
+ });
+ $('#checkActiveDiv').hide();
+ } else {
+ $scope.isDisabled = !isActive;
+ $('#checkActiveDiv').show();
+ var isExpire = User.isExpire();
+ if (isExpire) {
+ $('#checkActiveSpan').show();
+ }
+ }
+
}
]);
\ No newline at end of file
diff --git a/public/frontend/controllers/profile.js b/public/frontend/controllers/profile.js
index be6f955..1e7398f 100644
--- a/public/frontend/controllers/profile.js
+++ b/public/frontend/controllers/profile.js
@@ -1,7 +1,7 @@
angular.module('iotgo').
- controller('ProfileCtrl', [ '$scope', '$window', '$location', 'User',
+ controller('ProfileCtrl', ['$scope', '$window', '$location', 'User',
function ($scope, $window, $location, User) {
- if (! User.isLoggedIn()) {
+ if (!User.isLoggedIn()) {
$window.alert('Restricted area, please login first!');
$location.path('/login');
return;
@@ -11,6 +11,18 @@ angular.module('iotgo').
return User.getUser().email;
};
+ var isActive = User.isActive();
+ if (isActive) {
+ $('#checkActiveDiv').hide();
+ } else {
+ $('#checkActiveDiv').show();
+ var isExpire = User.isExpire();
+ if (isExpire) {
+ $('#checkActiveSpan').show();
+ }
+ }
+ $scope.isDisabled = !isActive;
+
$scope.getApikey = function () {
return User.getUser().apikey;
};
diff --git a/public/frontend/controllers/tip.js b/public/frontend/controllers/tip.js
new file mode 100644
index 0000000..6e3d671
--- /dev/null
+++ b/public/frontend/controllers/tip.js
@@ -0,0 +1,10 @@
+angular.module('iotgo')
+ .controller('TipCtrl', ['$scope', '$window', 'User',
+ function ($scope, $window, User) {
+ $scope.hideSpan = function () {
+ $('#checkActiveSpan').hide();
+ User.activeAccount(function () {
+ });
+ }
+ }
+ ]);
\ No newline at end of file
diff --git a/public/frontend/index.html b/public/frontend/index.html
index 318e1f7..60c934e 100644
--- a/public/frontend/index.html
+++ b/public/frontend/index.html
@@ -53,6 +53,15 @@
+
+
+
+ Please go to your inbox to confirm your email account first!
If you do not receive an e-mail, please click resend the activation email
+
+
+
diff --git a/public/frontend/views/signup.html b/public/frontend/views/signup.html
index 4dda8a5..985bc48 100644
--- a/public/frontend/views/signup.html
+++ b/public/frontend/views/signup.html
@@ -13,7 +13,7 @@ Create Your IoTgo Account
id="password" type="password" required placeholder="Password">
+ g-recaptcha-sitekey="6LcgigETAAAAAMv8cijWb6qQHpDyYzk6mYS7Vq9h">
diff --git a/routes/admin.js b/routes/admin.js
index 22e3c97..2b94b02 100644
--- a/routes/admin.js
+++ b/routes/admin.js
@@ -5,8 +5,11 @@ var express = require('express');
var expressJwt = require('express-jwt');
var jsonWebToken = require('jsonwebtoken');
var unless = require('express-unless');
+var request = require('request');
+var git_util = require('../lib/git-util');
var config = require('../config');
var db = require('../db/index');
+var packagejson = require('../package');
var User = db.User;
var Device = db.Device;
var FactoryDevice = db.FactoryDevice;
@@ -15,16 +18,16 @@ var FactoryDevice = db.FactoryDevice;
* Private variables and functions
*/
var authenticate = function (email, password, callback) {
- if (! email in config.admin || config.admin[email] !== password) {
+ if (!email in config.admin || config.admin[email] !== password) {
callback(null, false);
return;
}
- callback(null, { email: email, isAdmin: true });
+ callback(null, {email: email, isAdmin: true});
};
var adminOnly = function (req, res, next) {
- if (! req.user.isAdmin) {
+ if (!req.user.isAdmin) {
var err = new Error('Admin only area!');
err.status = 401;
next(err);
@@ -41,19 +44,19 @@ module.exports = exports = express.Router();
// Enable Json Web Token
exports.use(expressJwt(config.jwt).unless({
- path: [ '/api/admin/login' ]
+ path: ['/api/admin/login']
}));
// Restrict access to admin only
exports.use(adminOnly.unless({
- path: [ '/api/admin/login' ]
+ path: ['/api/admin/login']
}));
// Login
exports.route('/login').post(function (req, res) {
var email = req.body.email;
var password = req.body.password;
- if (! email || ! password) {
+ if (!email || !password) {
res.send({
error: 'Email address and password must not be empty!'
});
@@ -61,7 +64,7 @@ exports.route('/login').post(function (req, res) {
}
authenticate(email, password, function (err, user) {
- if (err || ! user) {
+ if (err || !user) {
res.send({
error: 'Email address or password is not correct!'
});
@@ -75,6 +78,55 @@ exports.route('/login').post(function (req, res) {
});
});
+var upgrade = function (params, req, res) {
+ request.post({
+ url: config.upgradeUrl,
+ form: {domain: params.domain, name: params.name, version: params.version}
+ }, function (err, httpResponse, body) {
+ if (err) {
+ res.send({
+ error: 'get remote version Failed!'
+ });
+ return;
+ }
+ var json = JSON.parse(body);
+ var script;
+ if (json && 200 === json.flag) {
+ script = "";
+ return res.json(script);
+ }
+ script = "";
+ res.json(script);
+
+ });
+};
+
+exports.route('/checkUpdate').get(function (req, res) {
+ var domain = config.host;
+ git_util.isRepo(function (flag) {
+ if (flag) {
+ git_util.getRepoName(function (err, name) {
+ if (err) {
+ res.send({
+ error: 'get Reposity Name Failed!'
+ });
+ return;
+ }
+ git_util.getCurrentVersion(function (version) {
+ version = version || '0.0.1';
+ var params = {domain: domain, name: name, version: version};
+ upgrade(params, req, res);
+ });
+ });
+ return;
+ }
+ var name = 'IoTgo';
+ var version = packagejson['version'];
+ var params = {domain: domain, name: name, version: version};
+ upgrade(params, req, res);
+ });
+
+});
// User management
exports.route('/users').get(function (req, res) {
var limit = Number(req.query.limit) || config.page.limit;
@@ -91,33 +143,33 @@ exports.route('/users').get(function (req, res) {
}
User.find(condition).select('-password').skip(skip).limit(limit)
- .sort({ createdAt: config.page.sort }).exec(function (err, users) {
- if (err) {
- res.send({
- error: 'Get user list failed!'
- });
- return;
- }
+ .sort({createdAt: config.page.sort}).exec(function (err, users) {
+ if (err) {
+ res.send({
+ error: 'Get user list failed!'
+ });
+ return;
+ }
- res.send(users);
- });
+ res.send(users);
+ });
});
exports.route('/users/:apikey').get(function (req, res) {
- User.findOne({ 'apikey': req.params.apikey}).select('-password')
- .exec(function (err, user) {
- if (err || ! user) {
- res.send({
- error: 'User does not exist!'
- });
- return;
- }
+ User.findOne({'apikey': req.params.apikey}).select('-password')
+ .exec(function (err, user) {
+ if (err || !user) {
+ res.send({
+ error: 'User does not exist!'
+ });
+ return;
+ }
- res.send(user);
- });
+ res.send(user);
+ });
}).delete(function (req, res) {
- User.findOneAndRemove({ 'apikey': req.params.apikey }, function (err, user) {
- if (err || ! user) {
+ User.findOneAndRemove({'apikey': req.params.apikey}, function (err, user) {
+ if (err || !user) {
res.send({
error: 'User does not exist!'
});
@@ -125,7 +177,7 @@ exports.route('/users/:apikey').get(function (req, res) {
}
// Delete all devices belong to user
- Device.remove({ apikey: req.params.apikey }, function (err) {
+ Device.remove({apikey: req.params.apikey}, function (err) {
if (err) {
res.send({
error: 'Delete user\'s devices failed!'
@@ -174,30 +226,30 @@ exports.route('/devices').get(function (req, res) {
}
Device.find(condition).select('-params').skip(skip).limit(limit)
- .sort({ createdAt: config.page.sort }).exec(function (err, devices) {
- if (err) {
- res.send({
- error: 'Get device list failed!'
- });
- return;
- }
+ .sort({createdAt: config.page.sort}).exec(function (err, devices) {
+ if (err) {
+ res.send({
+ error: 'Get device list failed!'
+ });
+ return;
+ }
- res.send(devices);
- });
+ res.send(devices);
+ });
});
exports.route('/devices/:deviceid').get(function (req, res) {
- Device.findOne({ 'deviceid': req.params.deviceid })
- .exec(function (err, device) {
- if (err || ! device) {
- res.send({
- error: 'Device does not exist!'
- });
- return;
- }
+ Device.findOne({'deviceid': req.params.deviceid})
+ .exec(function (err, device) {
+ if (err || !device) {
+ res.send({
+ error: 'Device does not exist!'
+ });
+ return;
+ }
- res.send(device);
- });
+ res.send(device);
+ });
});
// Factory device management
@@ -228,27 +280,27 @@ exports.route('/factorydevices').get(function (req, res) {
}
FactoryDevice.find(condition).skip(skip).limit(limit)
- .sort({ createdAt: config.page.sort })
- .exec(function (err, factoryDevices) {
- if (err) {
- res.send({
- error: 'Get factory device list failed!'
- });
- return;
- }
+ .sort({createdAt: config.page.sort})
+ .exec(function (err, factoryDevices) {
+ if (err) {
+ res.send({
+ error: 'Get factory device list failed!'
+ });
+ return;
+ }
- res.send(factoryDevices);
- });
+ res.send(factoryDevices);
+ });
});
exports.route('/factorydevices/create').post(function (req, res) {
var name = req.body.name,
- type = req.body.type,
- qty = Number(req.body.qty),
- createdAt = new Date();
+ type = req.body.type,
+ qty = Number(req.body.qty),
+ createdAt = new Date();
- if (! name || ! name.trim() || ! type || ! /^[0-9a-f]{2}$/.test(type)
- || 'number' !== typeof qty || 0 === qty) {
+ if (!name || !name.trim() || !type || !/^[0-9a-f]{2}$/.test(type)
+ || 'number' !== typeof qty || 0 === qty) {
res.send({
error: 'Factory device name, type and qty must not be empty!'
});
@@ -280,24 +332,24 @@ exports.route('/factorydevices/create').post(function (req, res) {
} while (i < qty && nextDeviceid);
/*
- if (req.query.file) {
- res.attachment(name + '-' + type + '-' + qty + '.csv');
-
- var download = [[ 'name', 'type', 'deviceid', 'apikey' ]];
- devices.forEach(function (device) {
- download.push([
- device.name, device.type, device.deviceid, device.apikey
- ]);
- });
- download.forEach(function (item, index) {
- download[index] = item.join(', ');
- });
-
- console.log(download.join('\r\n'));
- res.send(download.join('\r\n'));
- return;
- }
- */
+ if (req.query.file) {
+ res.attachment(name + '-' + type + '-' + qty + '.csv');
+
+ var download = [[ 'name', 'type', 'deviceid', 'apikey' ]];
+ devices.forEach(function (device) {
+ download.push([
+ device.name, device.type, device.deviceid, device.apikey
+ ]);
+ });
+ download.forEach(function (item, index) {
+ download[index] = item.join(', ');
+ });
+
+ console.log(download.join('\r\n'));
+ res.send(download.join('\r\n'));
+ return;
+ }
+ */
res.send(devices);
});
diff --git a/routes/user.js b/routes/user.js
index 2f83f95..b961ae6 100644
--- a/routes/user.js
+++ b/routes/user.js
@@ -4,12 +4,17 @@
var express = require('express');
var expressJwt = require('express-jwt');
var jsonWebToken = require('jsonwebtoken');
+var unless = require('express-unless');
var config = require('../config');
var db = require('../db/index');
var User = db.User;
var Device = db.Device;
var FactoryDevice = db.FactoryDevice;
var https = require('https');
+var email_util = require('../lib/email-util');
+var jade = require('jade');
+var uuid = require('uuid');
+var path = require('path');
/**
* Private variables
@@ -17,6 +22,18 @@ var https = require('https');
var recaptchaSecret = config.recaptcha.secret;
var recaptchaUrl = config.recaptcha.url;
+var activedAccountOnly = function (req, res, next) {
+ var isActivated = req.user.isActivated;
+ if (isActivated) {
+ next();
+ } else {
+ var err = new Error('Actived Account only area!');
+ err.status = 401;
+ next(err);
+ }
+};
+
+activedAccountOnly.unless = unless;
/**
* Exports
*/
@@ -24,14 +41,18 @@ module.exports = exports = express.Router();
// Enable Json Web Token
exports.use(expressJwt(config.jwt).unless({
- path: ['/api/user/register', '/api/user/login']
+ path: ['/api/user/register', '/api/user/login', '/api/user/validate']
+}));
+
+exports.use(activedAccountOnly.unless({
+ path: ['/api/user/register', '/api/user/login', '/api/user/activeAccount', '/api/user/validate']
}));
// Registration
exports.route('/register').post(function (req, res) {
var email = req.body.email;
var password = req.body.password;
- if (! email || ! password) {
+ if (!email || !password) {
res.send({
error: 'Email address and password must not be empty!'
});
@@ -40,7 +61,7 @@ exports.route('/register').post(function (req, res) {
// Google reCAPTCHA verification
var response = req.body.response;
- if (! response) {
+ if (!response) {
res.send({
error: 'reCAPTCHA verification is required!'
});
@@ -60,7 +81,7 @@ exports.route('/register').post(function (req, res) {
recaptchaRes.on('end', function () {
try {
data = JSON.parse(data);
- if (! data.success) {
+ if (!data.success) {
res.send({
error: 'reCAPTCHA verification failed!'
});
@@ -74,10 +95,34 @@ exports.route('/register').post(function (req, res) {
});
return;
}
-
- res.send({
- jwt: jsonWebToken.sign(user, config.jwt.secret),
- user: user
+ var token = uuid.v4();
+ User.resetToken(email, token, function (err, user, msg) {
+ if (err) {
+ res.send({error: err});
+ return;
+ }
+ var host = req.get('Host');
+ var href = "http://" + host;
+ href += '/api/user/validate?email=' + email + '&token=' + token;
+ if (user) {
+ var _user = {email: email, href: href};
+ var html = jade.renderFile(path.join(__dirname, '../template/activeEmail.jade'), {user: _user});
+ var mailOption = {
+ to: email,
+ subject: 'IoTgo: Confirm Your Email Address',
+ html: html
+ };
+ email_util.sendMail(mailOption, function (err, body) {
+ if (err) {
+ res.send({error: err});
+ return;
+ }
+ res.send({
+ jwt: jsonWebToken.sign(user, config.jwt.secret),
+ user: user
+ });
+ });
+ }
});
});
}
@@ -95,11 +140,64 @@ exports.route('/register').post(function (req, res) {
});
+exports.route('/activeAccount').get(function (req, res) {
+ var email = req.user.email;
+ var token = uuid.v4();
+ User.resetToken(email, token, function (err, user, msg) {
+ if (err) {
+ res.send({error: err});
+ return;
+ }
+ if (user) {
+ var host = req.get('Host');
+ var href = "http://" + host;
+ var logo = href +'/images/logo.png';
+ href += '/api/user/validate?email=' + email + '&token=' + token;
+ var _user = {email: email, href: href, logo: logo};
+ var html = jade.renderFile(path.join(__dirname, '../template/activeEmail.jade'), {user: _user});
+ var mailOption = {
+ to: email,
+ subject: 'IoTgo: Confirm Your Email Address',
+ html: html
+ };
+ email_util.sendMail(mailOption, function (err, body) {
+ if (err) {
+ res.send({error: err});
+ return;
+ }
+ res.send({message: msg});
+ });
+ }
+ });
+});
+
+exports.route('/validate').get(function (req, res) {
+ var email = req.query.email;
+ var token = req.query.token;
+ if (!email || !token) {
+ res.send({
+ error: 'Email address and token must not be empty!'
+ });
+ return;
+ }
+ User.active(email, token, function (err, user, msg) {
+ if (err) {
+ res.send({error: err});
+ return;
+ }
+ if (user) {
+ res.redirect('/login');
+ } else {
+ res.send(msg);
+ }
+ });
+});
+
// Login
exports.route('/login').post(function (req, res) {
var email = req.body.email;
var password = req.body.password;
- if (! email || ! password) {
+ if (!email || !password) {
res.send({
error: 'Email address and password must not be empty!'
});
@@ -107,7 +205,7 @@ exports.route('/login').post(function (req, res) {
}
User.authenticate(email, password, function (err, user) {
- if (err || ! user) {
+ if (err || !user) {
res.send({
error: 'Email address or password is not correct!'
});
@@ -126,8 +224,8 @@ exports.route('/password').post(function (req, res) {
var oldPassword = req.body.oldPassword;
var newPassword = req.body.newPassword;
- if (typeof oldPassword !== 'string' || ! oldPassword.trim() ||
- typeof newPassword !== 'string' || ! newPassword.trim()) {
+ if (typeof oldPassword !== 'string' || !oldPassword.trim() ||
+ typeof newPassword !== 'string' || !newPassword.trim()) {
res.send({
error: 'Old password and new password must not be empty!'
});
@@ -142,7 +240,7 @@ exports.route('/password').post(function (req, res) {
return;
}
- if (! user) {
+ if (!user) {
res.send({
error: 'Old password is not correct!'
});
@@ -165,7 +263,7 @@ exports.route('/password').post(function (req, res) {
// Device management
exports.route('/device').get(function (req, res) {
Device.getDevicesByApikey(req.user.apikey, function (err, devices) {
- if (err || ! devices.length) {
+ if (err || !devices.length) {
res.send([]);
return;
}
@@ -173,7 +271,7 @@ exports.route('/device').get(function (req, res) {
res.send(devices);
});
}).post(function (req, res) {
- if (! req.body.name || ! req.body.type) {
+ if (!req.body.name || !req.body.type) {
res.send({
error: 'Device name and type must be specified!'
});
@@ -213,16 +311,16 @@ exports.route('/device/add').post(function (req, res) {
var name = req.body.name;
var apikey = req.body.apikey;
var deviceid = req.body.deviceid;
- if ('string' !== typeof name || ! name.trim() ||
- 'string' !== typeof apikey || ! apikey.trim() ||
- 'string' !== typeof deviceid || ! deviceid.trim()) {
+ if ('string' !== typeof name || !name.trim() ||
+ 'string' !== typeof apikey || !apikey.trim() ||
+ 'string' !== typeof deviceid || !deviceid.trim()) {
res.send({
error: 'Device name, id and apikey must not be empty!'
});
return;
}
- if (! /^[0-9a-f]{10}$/.test(deviceid)) {
+ if (!/^[0-9a-f]{10}$/.test(deviceid)) {
res.send({
error: 'Invalid device id format!'
});
@@ -237,7 +335,7 @@ exports.route('/device/add').post(function (req, res) {
return;
}
- if (! device) {
+ if (!device) {
res.send({
error: 'Device does not exist!'
});
@@ -283,7 +381,7 @@ exports.route('/device/add').post(function (req, res) {
exports.route('/device/:deviceid').get(function (req, res) {
Device.exists(req.user.apikey, req.params.deviceid, function (err, device) {
- if (err || ! device) {
+ if (err || !device) {
res.send({
error: 'Device does not exist!'
});
@@ -294,7 +392,7 @@ exports.route('/device/:deviceid').get(function (req, res) {
});
}).post(function (req, res) {
if (typeof req.body.name !== 'string' ||
- typeof req.body.group !== 'string') {
+ typeof req.body.group !== 'string') {
res.send({
error: 'Device name and group must not be empty!'
});
@@ -302,7 +400,7 @@ exports.route('/device/:deviceid').get(function (req, res) {
}
Device.exists(req.user.apikey, req.params.deviceid, function (err, device) {
- if (err || ! device) {
+ if (err || !device) {
res.send({
error: 'Device does not exist!'
});
@@ -324,7 +422,7 @@ exports.route('/device/:deviceid').get(function (req, res) {
});
}).delete(function (req, res) {
Device.exists(req.user.apikey, req.params.deviceid, function (err, device) {
- if (err || ! device) {
+ if (err || !device) {
res.send({
error: 'Device does not exist!'
});
diff --git a/template/activeEmail.jade b/template/activeEmail.jade
new file mode 100644
index 0000000..fb6b342
--- /dev/null
+++ b/template/activeEmail.jade
@@ -0,0 +1,66 @@
+div(bgcolor="#f4f4f4", link="398bce", marginwidth="0", marginheight="0", style="background:#f4f4f4;margin:0;padding:0")
+ table(cellpadding="0", cellspacing="0", border="0", height="100%", width="100%", bgcolor="#f4f4f4", style="border-spacing:0;background:#f4f4f4")
+ tbody
+ tr
+ td(style="border-collapse:collapse")
+ table(border="0", width="600", cellpadding="0", cellspacing="0", align="center", style="border-spacing:0")
+ tbody
+ tr
+ td(height="30", style="font-size:0;line-height:0;border-collapse:collapse")
+ tr
+ td(valign="middle", style="text-align:left;border-collapse:collapse;padding:20px 0", align="left")
+ a(href="http://iotgo.iteadstudio.com", target="_blank")
+ img.CToWUd(src="#{user.logo }", border="0")
+ span(style="display:block") Open Source IoT Platform
+ td(valign="middle", style="text-align:right;font-family:Helvetica,Arial,sans-serif;font-size:12px;border-collapse:collapse;padding:20px 0", align="right")
+ tr
+ td(height="10", style="font-size:0;line-height:0;border-collapse:collapse")
+ span.HOEnZb
+ font(color="#888888")
+ span.HOEnZb
+ font(color="#888888")
+ table(border="0", width="600", cellpadding="0", cellspacing="0", align="center", style="border-spacing:0;background:#ffffff;border:1px solid #e5e5e5", bgcolor="#ffffff")
+ tbody
+ tr
+ td(style="border-collapse:collapse")
+ tr
+ td(style="border-bottom-width:1px;border-bottom-color:#e5e5e5;border-bottom-style:solid;border-collapse:collapse")
+ span.HOEnZb
+ font(color="#888888")
+ table(border="0", width="100%", cellpadding="0", cellspacing="0", align="center", style="border-spacing:0")
+ tbody
+ tr
+ td(style="font-family:sans-serif;font-size:16px;line-height:25px;color:#666666;border-collapse:collapse;padding:30px")
+ div(style="margin-bottom:25px")
+ h1(style="font-size:20px;color:rgb(40,40,40);font-weight:bold") Confirm Your Email Address
+ p Thanks for signing up for IoTgo!
+ p please verify your email address by clicking the link below:
+ div
+ div(style="margin-bottom:25px")
+ a(title="Confirm Email Address", style="font-size:13px;font-weight:100;font-family:Helvetica,Arial,sans-serif;text-transform:uppercase;text-align:center;letter-spacing:1px;text-decoration:none;display:block;width:300px;border-top-left-radius:3px;border-top-right-radius:3px;border-bottom-right-radius:3px;border-bottom-left-radius:3px;color:rgb(255,255,255);background:rgb(57,139,206);margin:0px auto", href!="#{user.href}", target="_blank")
+ div(style="min-height:60px;display:block;line-height:62px") CONFIRM EMAIL
+ div
+ | Note that this link will expire in 24 hours if it"s not activated.
+ br
+ br
+ | Happy IoTgo,
+ div Team ITEAD Studio
+ span.HOEnZb
+ font(color="#888888")
+ span
+ span.HOEnZb
+ font(color="#888888")
+ span.HOEnZb
+ font(color="#888888")
+ table(border="0", width="100%", cellpadding="0", cellspacing="0", align="center", style="border-spacing:0")
+ tbody
+ tr
+ td(style="text-align:center;font-family:Helvetica,Arial,sans-serif;font-size:12px;line-height:18px;color:#888888;border-collapse:collapse;padding:20px 0px 5px", align="center")
+ p © ITEAD Studio
+ p
+ | Learn more about
+ font(color="#888888") IoTgo
+ | Online:
+ br
+ a(href="http://iotgo.iteadstudio.com/", target="_blank") http://iotgo.iteadstudio.com/
+ br