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 @@ +
+ +
+
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 @@ +
+ +
+
-
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