mirror of
https://github.com/hardillb/node-red-alexa-home-skill-web.git
synced 2025-12-11 09:29:44 +01:00
Big Update
This adds: - google analytics to the command handling - the start of support for the new temperature queries - fixes the oauth token refresh code - reuses still active oauth tokens
This commit is contained in:
48
googleMeasurement.js
Normal file
48
googleMeasurement.js
Normal file
@@ -0,0 +1,48 @@
|
||||
var request = require('request');
|
||||
var querystring = require('querystring');
|
||||
|
||||
var baseURL = "https://www.google-analytics.com/";
|
||||
|
||||
var Measurements = function(tid) {
|
||||
this.tid = tid;
|
||||
};
|
||||
|
||||
Measurements.prototype.send = function(options) {
|
||||
|
||||
if (!this.tid){
|
||||
return;
|
||||
}
|
||||
var required = {
|
||||
v: 1,
|
||||
tid: this.tid,
|
||||
t: 'event'
|
||||
};
|
||||
var url = baseURL;
|
||||
var body;
|
||||
if (Array.isArray(options)) {
|
||||
for (var i=0; i<options.length && i < 20; i++) {
|
||||
var temp = Object.assign(required, options[i]);
|
||||
body += querystring.stringify(temp);
|
||||
body += "\n";
|
||||
}
|
||||
|
||||
url += "batch";
|
||||
} else {
|
||||
options = Object.assign(required, options);
|
||||
body = querystring.stringify(options);
|
||||
url += "collect";
|
||||
}
|
||||
// console.log(body);
|
||||
request.post({url: url, body: body},
|
||||
function(err, response, body){
|
||||
if (err) {
|
||||
console.log(err);
|
||||
}
|
||||
|
||||
// console.log("Analytics response: %d",response.statusCode);
|
||||
// console.log(response.headers);
|
||||
// console.log(body);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = Measurements;
|
||||
57
index.js
57
index.js
@@ -10,6 +10,7 @@ var session = require('express-session');
|
||||
var passport = require('passport');
|
||||
var mongoose = require('mongoose');
|
||||
var bodyParser = require('body-parser');
|
||||
var Measurement = require('./googleMeasurement.js');
|
||||
var cookieParser = require('cookie-parser');
|
||||
var BasicStrategy = require('passport-http').BasicStrategy;
|
||||
var LocalStrategy = require('passport-local').Strategy;
|
||||
@@ -26,6 +27,9 @@ var mqtt_user = (process.env.MQTT_USER || undefined);
|
||||
var mqtt_password = (process.env.MQTT_PASSWORD || undefined);
|
||||
console.log(mqtt_url);
|
||||
|
||||
var googleAnalyicsTID = process.env.GOOGLE_ANALYTICS_TID;
|
||||
var measurement = new Measurement(googleAnalyicsTID);
|
||||
|
||||
var mqttClient;
|
||||
|
||||
var mqttOptions = {
|
||||
@@ -68,6 +72,7 @@ if (process.env.VCAP_SERVICES) {
|
||||
}
|
||||
|
||||
console.log(mongo_url);
|
||||
mongoose.Promise = global.Promise;
|
||||
var mongoose_options = {
|
||||
server: {
|
||||
auto_reconnect:true,
|
||||
@@ -246,14 +251,24 @@ app.get('/login', function(req,res){
|
||||
|
||||
app.get('/logout', function(req,res){
|
||||
req.logout();
|
||||
res.redirect('/');
|
||||
if (req.query.next) {
|
||||
console.log(req.query.next);
|
||||
res.redirect(req.query.next);
|
||||
} else {
|
||||
res.redirect('/');
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
//app.post('/login',passport.authenticate('local', { failureRedirect: '/login', successRedirect: '/2faCheck', failureFlash: true }));
|
||||
app.post('/login',
|
||||
passport.authenticate('local', { failureRedirect: '/login', failureFlash: true }),
|
||||
passport.authenticate('local', { failureRedirect: '/login', failureFlash: true, session: true }),
|
||||
function(req,res){
|
||||
res.redirect('/devices');
|
||||
if (req.query.next) {
|
||||
res.reconnect(req.query.next);
|
||||
} else {
|
||||
res.redirect('/devices');
|
||||
}
|
||||
});
|
||||
|
||||
function ensureAuthenticated(req,res,next) {
|
||||
@@ -303,6 +318,12 @@ app.post('/newuser', function(req,res){
|
||||
|
||||
passport.authenticate('local')(req, res, function () {
|
||||
console.log("created new user %s", req.body.username);
|
||||
measurement.send({
|
||||
t:'event',
|
||||
ec:'System',
|
||||
ea: 'NewUser',
|
||||
uid: req.body.username
|
||||
});
|
||||
res.status(201).send();
|
||||
});
|
||||
|
||||
@@ -340,7 +361,7 @@ app.get('/auth/start',oauthServer.authorize(function(applicationID, redirectURI,
|
||||
|
||||
res.render('pages/oauth', {
|
||||
transaction_id: req.oauth2.transactionID,
|
||||
currentURL: req.originalUrl,
|
||||
currentURL: encodeURIComponent(req.originalUrl),
|
||||
response_type: req.query.response_type,
|
||||
errors: req.flash('error'),
|
||||
scope: req.oauth2.req.scope,
|
||||
@@ -365,7 +386,7 @@ app.post('/auth/finish',function(req,res,next) {
|
||||
next();
|
||||
} else if (!error){
|
||||
//console.log("not authed");
|
||||
req.flash('error', 'Your email or password was incorrect. Try again.');
|
||||
req.flash('error', 'Your email or password was incorrect. Please try again.');
|
||||
res.redirect(req.body['auth_url'])
|
||||
}
|
||||
})(req,res,next);
|
||||
@@ -397,6 +418,12 @@ app.get('/api/v1/devices',
|
||||
function(req,res,next){
|
||||
|
||||
console.log("all good, doing discover devices");
|
||||
measurement.send({
|
||||
t:'event',
|
||||
ec:'discover',
|
||||
ea: req.body.header ? req.body.header.name : "Node-RED",
|
||||
uid: req.user.username
|
||||
});
|
||||
|
||||
var user = req.user.username
|
||||
Devices.find({username: user},function(error, data){
|
||||
@@ -450,6 +477,13 @@ mqttClient.on('message',function(topic,message){
|
||||
}
|
||||
}
|
||||
delete onGoingCommands[payload.messageId];
|
||||
// should really parse uid out of topic
|
||||
measurement.send({
|
||||
t:'event',
|
||||
ec:'command',
|
||||
ea: 'complete',
|
||||
uid: waiting.user
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -466,6 +500,12 @@ var timeout = setInterval(function(){
|
||||
console.log("timed out");
|
||||
waiting.res.status(504).send('{"error": "timeout"}');
|
||||
delete onGoingCommands[keys[key]];
|
||||
measurement.send({
|
||||
t:'event',
|
||||
ec:'command',
|
||||
ea: 'timeout',
|
||||
uid: waiting.user
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -476,6 +516,12 @@ app.post('/api/v1/command',
|
||||
function(req,res,next){
|
||||
console.log(req.user.username);
|
||||
console.log(req.body);
|
||||
measurement.send({
|
||||
e:'event',
|
||||
ec:'command',
|
||||
ea: req.body.header.name,
|
||||
uid: req.user.username
|
||||
});
|
||||
var topic = "command/" +req.user.username + "/" + req.body.payload.appliance.applianceId;
|
||||
delete req.body.payload.accessToken;
|
||||
var message = JSON.stringify(req.body);
|
||||
@@ -485,6 +531,7 @@ app.post('/api/v1/command',
|
||||
|
||||
}
|
||||
var command = {
|
||||
user: req.user.username,
|
||||
res: res,
|
||||
timestamp: Date.now()
|
||||
};
|
||||
|
||||
@@ -35,7 +35,7 @@ var AccessTokenSchema = new Schema({
|
||||
scope: [ { type: String }],
|
||||
expires: { type: Date, default: function(){
|
||||
var today = new Date();
|
||||
var length = 60 * 24 * 365 * 5; // Length (in minutes) of our access token
|
||||
var length = 60 * 24 * 90; // Length (in minutes) of our access token
|
||||
return new Date(today.getTime() + length*60000);
|
||||
} },
|
||||
active: { type: Boolean, get: function(value) {
|
||||
|
||||
137
oauth.js
137
oauth.js
@@ -7,15 +7,31 @@ server.grant(oauth2orize.grant.code({
|
||||
scopeSeparator: [ ' ', ',' ]
|
||||
}, function(application, redirectURI, user, ares, done) {
|
||||
//console.log("grant user: ", user);
|
||||
OAuth.GrantCode.findOne({application: application, user: user},function(error,grant){
|
||||
if (!error && grant) {
|
||||
done(null,grant.code);
|
||||
} else if (!error) {
|
||||
var grant = new OAuth.GrantCode({
|
||||
application: application,
|
||||
user: user,
|
||||
scope: ares.scope
|
||||
});
|
||||
grant.save(function(error) {
|
||||
done(error, error ? null : grant.code);
|
||||
});
|
||||
} else {
|
||||
done(error,null);
|
||||
}
|
||||
});
|
||||
|
||||
var grant = new OAuth.GrantCode({
|
||||
application: application,
|
||||
user: user,
|
||||
scope: ares.scope
|
||||
});
|
||||
grant.save(function(error) {
|
||||
done(error, error ? null : grant.code);
|
||||
});
|
||||
// var grant = new OAuth.GrantCode({
|
||||
// application: application,
|
||||
// user: user,
|
||||
// scope: ares.scope
|
||||
// });
|
||||
// grant.save(function(error) {
|
||||
// done(error, error ? null : grant.code);
|
||||
// });
|
||||
}));
|
||||
|
||||
server.exchange(oauth2orize.exchange.code({
|
||||
@@ -23,31 +39,106 @@ server.exchange(oauth2orize.exchange.code({
|
||||
}, function(application, code, redirectURI, done) {
|
||||
OAuth.GrantCode.findOne({ code: code }, function(error, grant) {
|
||||
if (grant && grant.active && grant.application == application.id) {
|
||||
|
||||
OAuth.AccessToken.findOne({application:application, user: grant.user, active: true}, function(error,token){
|
||||
if (token) {
|
||||
OAuth.RefreshToken.findOne({application:application, user: grant.user},function(error, refreshToken){
|
||||
if (refreshToken){
|
||||
done(null,token.token, refreshToken.token,{token_type: 'standard'});
|
||||
} else {
|
||||
// Shouldn't get here unless there is an error as there
|
||||
// should be a refresh token if there is an access token
|
||||
done(error);
|
||||
}
|
||||
});
|
||||
} else if (!error) {
|
||||
var token = new OAuth.AccessToken({
|
||||
application: grant.application,
|
||||
user: grant.user,
|
||||
grant: grant,
|
||||
scope: grant.scope
|
||||
});
|
||||
|
||||
token.save(function(error){
|
||||
//delete old refreshToken or reuse?
|
||||
OAuth.RefreshToken.findOne({application:application, user: grant.user},function(error, refreshToken){
|
||||
if (refreshToken) {
|
||||
done(error, error ? null : token.token, refreshToken.token, error ? null : { token_type: 'standard' });
|
||||
} else if (!error) {
|
||||
var refreshToken = new OAuth.RefreshToken({
|
||||
user: grant.user,
|
||||
application: grant.application
|
||||
});
|
||||
|
||||
refreshToken.save(function(error){
|
||||
done(error, error ? null : token.token, refreshToken.token, error ? null : { token_type: 'standard' });
|
||||
});
|
||||
} else {
|
||||
done(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
done(error);
|
||||
}
|
||||
});
|
||||
|
||||
//console.log("exchange user ", grant.user);
|
||||
var token = new OAuth.AccessToken({
|
||||
application: grant.application,
|
||||
user: grant.user,
|
||||
grant: grant,
|
||||
scope: grant.scope
|
||||
});
|
||||
// var token = new OAuth.AccessToken({
|
||||
// application: grant.application,
|
||||
// user: grant.user,
|
||||
// grant: grant,
|
||||
// scope: grant.scope
|
||||
// });
|
||||
|
||||
token.save(function(error) {
|
||||
// token.save(function(error) {
|
||||
|
||||
var refreshToken = new OAuth.RefreshToken({
|
||||
user: grant.user,
|
||||
application: grant.application
|
||||
});
|
||||
// var refreshToken = new OAuth.RefreshToken({
|
||||
// user: grant.user,
|
||||
// application: grant.application
|
||||
// });
|
||||
|
||||
refreshToken.save(function(error){
|
||||
done(error, error ? null : token.token, refreshToken.token, error ? null : { token_type: 'standard' });
|
||||
});
|
||||
});
|
||||
// refreshToken.save(function(error){
|
||||
// done(error, error ? null : token.token, refreshToken.token, error ? null : { token_type: 'standard' });
|
||||
// });
|
||||
// });
|
||||
} else {
|
||||
done(error, false);
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
server.exchange(oauth2orize.exchange.refreshToken({
|
||||
userProperty: 'appl'
|
||||
}, function(application, token, scope, done){
|
||||
console.log("Yay!");
|
||||
OAuth.RefreshToken.findOne({token: token}, function(error, refresh){
|
||||
if (refresh && refresh.application == application.id) {
|
||||
OAuth.GrantCode.findOne({},function(error, grant){
|
||||
if (grant && grant.active && grant.application == application.id){
|
||||
var newToken = new OAuth.AccessToken({
|
||||
application: refresh.application,
|
||||
user: refresh.user,
|
||||
grant: grant,
|
||||
scope: scope
|
||||
});
|
||||
|
||||
newToken.save(function(error){
|
||||
if (!error) {
|
||||
done(null, newToken.token);
|
||||
} else {
|
||||
done(error,false);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
done(error,null);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
done(error, false);
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
server.serializeClient(function(application, done) {
|
||||
done(null, application.id);
|
||||
|
||||
@@ -32,6 +32,8 @@
|
||||
"passport-local": "^1.0.0",
|
||||
"passport-local-mongoose": "^4.0.0",
|
||||
"passport-totp": "0.0.2",
|
||||
"querystring": "^0.2.0",
|
||||
"request": "^2.79.0",
|
||||
"uid2": "0.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 618 B After Width: | Height: | Size: 620 B |
BIN
static/images/getTargetTemperature.png
Normal file
BIN
static/images/getTargetTemperature.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.0 KiB |
BIN
static/images/getTemperatureReading.png
Normal file
BIN
static/images/getTemperatureReading.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 890 B |
Binary file not shown.
|
Before Width: | Height: | Size: 641 B After Width: | Height: | Size: 641 B |
@@ -14,8 +14,9 @@ app.use(session({
|
||||
// return genuuid() // use UUIDs for session IDs
|
||||
// },
|
||||
secret: 'boo',
|
||||
resave: false,
|
||||
resave: true,
|
||||
saveUninitialized: false,
|
||||
name: 'bar',
|
||||
cookie: {
|
||||
secure: true
|
||||
}
|
||||
@@ -30,7 +31,7 @@ passport.use(new OAuth2Strategy({
|
||||
// tokenURL: 'https://alexa-node-red.eu-gb.mybluemix.net/auth/exchange',
|
||||
authorizationURL: 'https://localhost:3000/auth/start',
|
||||
tokenURL: 'https://localhost:3000/auth/exchange',
|
||||
clientID: '1',
|
||||
clientID: '2',
|
||||
clientSecret: 'password1234',
|
||||
scope: "access_devices",
|
||||
callbackURL: 'http://localhost:3001/callback'
|
||||
|
||||
@@ -37,7 +37,6 @@
|
||||
<input type="checkbox" name="actions" id="turnOn" value="turnOn">
|
||||
<label for="turnOff">Off: </label>
|
||||
<input type="checkbox" name="actions" id="turnOff" value="turnOff">
|
||||
<br>
|
||||
<fieldset id="percentCheck">
|
||||
<label for="setPercentage">%: </label>
|
||||
<input type="checkbox" name="actions" id="setPercentage" value="setPercentage" onclick='checkCapability(this)'>
|
||||
@@ -46,7 +45,6 @@
|
||||
<label for="decrementPercentage">-%: </label>
|
||||
<input type="checkbox" name="actions" id="decrementPercentage" value="decrementPercentage" onclick='checkCapability(this)'>
|
||||
</fieldset>
|
||||
<br>
|
||||
<fieldset id="temperatureCheck">
|
||||
<label for="setTargetTemperature">°C/F: </label>
|
||||
<input type="checkbox" name="actions" id="setTargetTemperature" value="setTargetTemperature" onclick='checkCapability(this)'>
|
||||
@@ -55,6 +53,14 @@
|
||||
<label for="decrementTargetTemperature">-°C/F: </label>
|
||||
<input type="checkbox" name="actions" id="decrementTargetTemperature" value="decrementTargetTemperature" onclick='checkCapability(this)'>
|
||||
</fieldset>
|
||||
<!--
|
||||
<fieldset id="queryTemperature">
|
||||
<label ofr="getTargetTemperature">Query Set Point: </label>
|
||||
<input type="checkbox" name="actions" id="getTargetTemperature" value="getTargetTemperature" onclick='checkCapability(this)'>
|
||||
<label ofr="getTemperatureReading">Query Current Temp: </label>
|
||||
<input type="checkbox" name="actions" id="getTemperatureReading" value="getTemperatureReading" onclick='checkCapability(this)'>
|
||||
</fieldset>
|
||||
-->
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
@@ -197,8 +203,10 @@
|
||||
var temp = $('#setTargetTemperature').prop('checked');
|
||||
var incTemp = $('#incrementTargetTemperature').prop('checked');
|
||||
var decTemp = $('#decrementTargetTemperature').prop('checked');
|
||||
var qSetTemp = $('#getTargetTemperature').prop('checked');
|
||||
var qCurTemp = $('#getTemperatureReading').prop('checked');
|
||||
|
||||
var t = temp | incTemp | decTemp;
|
||||
var t = temp | incTemp | decTemp | qSetTemp | qCurTemp;
|
||||
|
||||
if (p & t) {
|
||||
alert("You can not control both percentage and temperature on the same device");
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<input type="hidden" name="transaction_id" value="<%= transaction_id %>">
|
||||
<input type="hidden" name="response_type" value="<%= response_type %>">
|
||||
<input type="hidden" name="client_id" value="<%= application.oauth_id %>">
|
||||
<input type="hidden" name="auth_url" value="<%= currentURL %>">
|
||||
<input type="hidden" name="auth_url" value="<%= decodeURIComponent(currentURL) %>">
|
||||
<input type="hidden" name="scope" value="<%= scope.join(',') %>">
|
||||
|
||||
<div class="">
|
||||
@@ -26,10 +26,13 @@
|
||||
<% if (user) { %>
|
||||
<div class="form-group">
|
||||
<p>Signed in as <strong><%= user.name%></strong>.</p>
|
||||
<a href="/logout?next=">Not </a><%= user.username %>?
|
||||
<a href="/logout?next=<%= currentURL %>">Not </a><%= user.username %>?
|
||||
<input type="submit" value="Authorise">
|
||||
</div>
|
||||
<% } else { %>
|
||||
<% if (errors) { %>
|
||||
<p style="color: red"><%= errors %></p>
|
||||
<% } %>
|
||||
<div class="form-group">
|
||||
<label for="username">Username: </label>
|
||||
<input type="text" id="username" name="username"/>
|
||||
|
||||
@@ -24,6 +24,11 @@
|
||||
var password = document.getElementById('password').value;
|
||||
var passwordAgain = document.getElementById('passwordAgain').value;
|
||||
|
||||
if (username.length < 1) {
|
||||
alert("Please enter a valid username");
|
||||
return;
|
||||
}
|
||||
|
||||
if (password !== passwordAgain) {
|
||||
alert("Passwords don't match");
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user