-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
367 lines (316 loc) · 17.3 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
"use strict";
var fs = require('fs');
var net = require('net');
var http = require('http');
var request = require('request');
var userData = require('./userData.js');
//i2c for connecting to the arduino responsible for sending the 433 MHz signal
var i2c = require('i2c');
var address = 0x04;
var wire = new i2c(address, {
device: '/dev/i2c-1'
});
//if there is an internal alias for the given username, use it - otherwise return the given username
function procAlias(username) {
if (Object.keys(userData.alias).indexOf(username) > -1)
return userData.alias[username];
return username;
}
//decrement every *SecsLeft variable greater than zero
//=> if we lock or wake someone, this variable is increased by the time to wait/lock in seconds
setInterval(function() {
for (var userid of Object.keys(userData.tunnelUsers)) {
if (userData.tunnelUsers[userid].lockedSecsLeft > 0)
userData.tunnelUsers[userid].lockedSecsLeft -= 1;
if (userData.tunnelUsers[userid].wakeLockSecsLeft > 0)
userData.tunnelUsers[userid].wakeLockSecsLeft -= 1;
}
}, 1000);
//passport init stuff start
var express = require('express');
var passport = require('passport');
var Strategy = require('passport-local').Strategy;
//tell passport to use a local username->password dict saved in userData.js
passport.use(new Strategy(
function(locusername, locpassword, callb) {//basically it's a function to find a user in our db - if existing
for (var i = 0; i < userData.users.length; i++) {//go through all users in our DB
var user = userData.users[i];//save current user
if (user.username === locusername) {//if it's the searched user
return callb(null, user);//return the user together with the password
}
}
return callb(null, null);//otherwise, if locusername hasn't been found, return null
}));
passport.serializeUser(function(user, callb) {
callb(null, user.id);
});
passport.deserializeUser(function(id, callb) {
console.log("id"+id);
var ind = -1;
for( var i = 0; i<userData.users.length; i++){
if(userData.users[i].id === id){
ind = i;
break;
}
}
if(ind==-1){
callb(null, false);
}
else {
callb(null, userData.users[ind]);
}
});
//passport init stuff end
// Create a new Express application.
var app = express();
app.use(require('cookie-parser')());
app.use(require('body-parser').urlencoded({
extended: true
}));
app.use(require('express-session')({
secret: 'iuofoofressuszouxzrzru77r75 7c7',
resave: false,
saveUninitialized: false
}));
// Initialize Passport and restore authentication state, if any, from the session.
app.use(passport.initialize());
app.use(passport.session());
//handle commands by event
const EventEmitter = require('events');
class CommandEmitter extends EventEmitter {}
const comEmit = new CommandEmitter();
//express app.post/app.get emit an event to handle the wake/lock
comEmit.on("WAKE", (usrp, wakeusrp) => {
var usr = procAlias(usrp);
var wakeusr = procAlias(wakeusrp);
if (Object.keys(userData.tunnelUsers).indexOf(usr) > -1 &&//if calling user
Object.keys(userData.tunnelUsers).indexOf(wakeusr) > -1 &&// and the user to wake are in our DB
userData.tunnelUsers[wakeusr].wakeLockSecsLeft === 0) {//and the calling user is not locked, so can wake someone
//run the wake command
userData.tunnelUsers[wakeusr].wakeLockSecsLeft = 10;//currently proceeding wake -> lock the calling user for the next 10 secs
console.log('Sending wake to ' + usr);//log that we are waking somebody
for (var i = 0; i < usr.length; i++) {//order our sending arduino to send the given username to wake it up
wire.writeByte(usr.charCodeAt(i), function(err) {});
}
if (usr.length > 0)//if we have sent something
wire.writeByte('%'.charCodeAt(0), function(err) {});//send our delimiter to tell the arduino that there's the end of the username
}
});
comEmit.on("LOCK", (usr, time, lockusr) => {
if (usr === lockusr) {//users can only lock themselves -> calling user and user to lock sould be equal
console.log("LOCK: " + lockusr + " " + usr);//log that we are locking someone
userData.tunnelUsers[pricAlias(usr)].lockedSecsLeft = time * 60;//time is given to us in minutes, save it in seconds
}
});
var btn =
`<button type="button" class="btn {class}" id="{id}"{state}>
{text}
</button>`;//template for a html/bootstrap button
//create the custom webpage for a specific user (wake/lock buttons, logout etc.)
function getBtns(username_unproceeded) {
var username = procAlias(username_unproceeded);
var formHTML = `<div>
<h2>Hallo, ` + username + `!</h2>`;//greet the user
if (userData.tunnelUsers[username].lockedSecsLeft === 0) {//if the logged-in user is not already locked
var lockMe = btn.replace("{class}", 'lock btn-secondary');
lockMe = lockMe.replace("{state}", '');
lockMe = lockMe.replace("{id}", username);
lockMe = lockMe.replace("{text}", "Mich 30 Min. sperren");//add "lock me"-Button
formHTML += lockMe;
} else//if the user is still locked, show how long
formHTML += "<span>Du bist noch " + (userData.tunnelUsers[username].lockedSecsLeft / 60.0).toFixed(2) + " Min. gesperrt!</span>";
formHTML += `<button type="button" class="btn logout btn-danger" id="` + username + `">
Ausloggen
</button><br/>`;//log out button
for (var userid of Object.keys(userData.tunnelUsers)) {//go through (other) users
if (userid === username)//do not make buttons for waking yourself
continue;
if (userData.tunnelUsers[userid].lockedSecsLeft > 0) {//if the user is locked
var tbtn = btn.replace("{class}", 'wake btn-danger');
tbtn = tbtn.replace("{state}", ' disabled=""');
tbtn = tbtn.replace("{id}", userid);
tbtn = tbtn.replace("{text}", userData.tunnelUsers[userid].name + " ist gesperrt, " + (userData.tunnelUsers[userid].lockedSecsLeft / 60.0).toFixed(2) + " Min. verbleiben");
formHTML += "<h3>" + userData.tunnelUsers[userid].name + "</h3>" + tbtn + "\n";//make a disabled button with the locking time left
} else if (userData.tunnelUsers[username].wakeLockSecsLeft > 0) {//if the logged-in user is locked to wake anybody
var tbtn = btn.replace("{class}", 'wake btn-danger');
tbtn = tbtn.replace("{state}", ' disabled=""');
tbtn = tbtn.replace("{id}", userid);
tbtn = tbtn.replace("{text}", "Du kannst noch " + userData.tunnelUsers[username].wakeLockSecsLeft + " Sek. niemanden aufwecken");
formHTML += "<h3>" + userData.tunnelUsers[userid].name + "</h3>" + tbtn + "\n";//disable buttons and show how long that user cannot wake anyone
} else {//if the user is not locked and the logged-in user can wake someone
var tbtn = btn.replace("{class}", 'wake btn-danger');
tbtn = tbtn.replace("{state}", '');
tbtn = tbtn.replace("{id}", userid);//add wake button
tbtn = tbtn.replace("{text}", userData.tunnelUsers[userid].name + " aufwecken");
formHTML += "<h3>" + userData.tunnelUsers[userid].name + "</h3>" + tbtn + "\n";
}
};
formHTML += "</div>";//end container
return formHTML;
}
function respondSlack(url, text){//send response over url from slack request -> avoid sending directly because of timeout
request.post(//it's just a post request
url, {//to the given URL
json: {//containing a json with
text: text,//the given answer to print in chat
attachments: [{}]//propably unnecessary
}
},
function(error, response, body) {//logging and error handling
if (!error && response.statusCode == 200) {
console.log("Success!");
console.log(body);
}
else{
console.log("Error with POST-request:");
console.log(error);
}
}
);
}
//express request handlers
app.get('/',//main page
function(req, res) {
if (req.user) {//if the request comes from a logged in user
fs.readFile(__dirname + "/index.html", function(err, data, ending) {//load the template file
var formHTML = getBtns(req.user.username);//generate custom content for the webpage
var tresp = data.toString();//make string from read file data
if (data.indexOf("{{content}}") > -1)//id there is {{content}} somewhere (that's where we put the custom content)
tresp = tresp.replace("{{content}}", formHTML);//and replace it with the generated stuff
res.send(tresp);//send response
});
} else {//otherwise redirect to login
res.redirect('/login');
}
});
app.get('/getContent',//JSON get from main page -> self-refreshing without completely reloading the site
function(req, res) {
if (req.user) {//if the requester is logged in
res.send(getBtns(req.user.username));//send him what he wants
} else {//otherwise -> not logged in
res.end();//send nothing
}
});
app.get('/login',//login page
function(req, res) {//just send the login template from disk
fs.readFile(__dirname + "/views/login.html", function(err, data, ending) {
res.send(data.toString());
});
});
app.post('/login',//make passport handle login
passport.authenticate('local', {
failureRedirect: '/login'
}),
function(req, res) {
res.redirect('/');//after successful login, redirect to main page
});
app.get('/logout',
function(req, res) {
req.logout();
res.redirect('/');
});
app.post('/wakeUp',//personal pages of each user can send a POST request to this path to wake someone up
function(req, res) {
if (req.user) {//check if a user is logged in (only those users have the permission to wake someone)
if (typeof req.body.user !== 'undefined')//if the username to wake has been sent
comEmit.emit("WAKE", req.body.user, req.user.username);//emit the event to wake that user
res.redirect('/');//redirect back to main page after wake (I don't think this does anything, because we use preventDefault)
return res.end();
} else {
res.redirect('/login');//if not logged in, redirect to login
}
});
app.post('/lock',//personal pages of each user can send a POST request to this path to lock theirselve
function(req, res) {
if (req.user) {//check if the user is logged in
if (typeof req.body.user !== 'undefined' && typeof req.body.time !== 'undefined')//check if needed fields are defined
comEmit.emit("LOCK", req.body.user, req.body.time, req.user.username);//emit lock event
res.redirect('/');//and go back to main page
return res.end();
} else {//no user logged in
res.redirect('/login');//redirect to login page
}
});
/*
Slack slash-commands send a post request to some predefined URL -> handled by slack.js
slack.js sends - after verifying the message - a post to this server, handled below
*/
app.post('/wakeUpSlack',//no authentification needed here -> posted by other NodeJS server
function(req, res) {
if (typeof req.body.user !== 'undefined' &&//check if all needed fields are defined
typeof req.body.calluser !== 'undefined' &&
typeof req.body.response_url !== 'undefined') {
var user = procAlias(req.body.user);//save fields and translate to internal alias
var calluser = procAlias(req.body.calluser);
var response_url = req.body.response_url;
if (Object.keys(userData.tunnelUsers).indexOf(user) > -1) {//both the calling user and the to-wake user should be in our DB
if (Object.keys(userData.tunnelUsers).indexOf(calluser) > -1) {//if calling user is also in db
if (userData.tunnelUsers[user].lockedSecsLeft === 0) {//the user is not locked
if (userData.tunnelUsers[calluser].wakeLockSecsLeft === 0) {//and the calling user is not wake-locked
console.log(req.body);
comEmit.emit("WAKE", user, calluser);//wake/proceed request
respondSlack(response_url, "OK, " + req.body.user + " has been woken!");
res.send("OK, " + req.body.user + " has been woken!");
} else {//if user is wake-locked
respondSlack(response_url, "You ("+req.body.calluser+") are locked! " + userData.tunnelUsers[calluser].wakeLockSecsLeft + " sec. left.");
res.send("You ("+req.body.calluser+") are locked! " + userData.tunnelUsers[calluser].wakeLockSecsLeft + " sec. left.");
}
} else {//if to-wake user is locked
respondSlack(response_url, req.body.calluser+" is locked! " + (userData.tunnelUsers[calluser].lockedSecsLeft / 60.0).toFixed(2) + " min. left.");
res.send(req.body.calluser+" is locked! " + (userData.tunnelUsers[calluser].lockedSecsLeft / 60.0).toFixed(2) + " min. left.");
}
} else {//if username of calling user has not been found
if (userData.tunnelUsers[user].lockedSecsLeft === 0) {//if to-wake user is not locked
console.log(req.body);
comEmit.emit("WAKE", user, user);//wake nevertheless, but respond with warning in slack
respondSlack(response_url, "OK, " + req.body.user + " has been woken! But you ("+req.body.calluser+") are no registered user...");
res.send("OK, " + req.body.user + " has been woken! But you ("+req.body.calluser+") are no registered user...");
} else {//if to-wake user is locked
respondSlack(response_url, "The user you want to wake is locked! " + (userData.tunnelUsers[calluser].lockedSecsLeft / 60.0).toFixed(2) + " min. left. But you are no registered user...");
res.send("The user you want to wake is locked! " + (userData.tunnelUsers[calluser].lockedSecsLeft / 60.0).toFixed(2) + " min. left. But you are no registered user...");
}
}
} else {//if to-wake user has not been found
var respon = "Wrong username! Possible usernames:\n";//there's nothing we can do -> respond with list of valid usernames
for (var userid of Object.keys(userData.tunnelUsers)) {
if (userData.tunnelUsers[userid].lockedSecsLeft > 0)
respon += userid + " (" + userData.tunnelUsers[userid].name + "), still locked for " + (userData.tunnelUsers[userid].lockedSecsLeft / 60.0).toFixed(2) + " min.\n";
else
respon += userid + " (" + userData.tunnelUsers[userid].name + ")\n";
}
respondSlack(response_url, respon);
res.send(respon);
}
} else{//if not all needed fields are defined
res.send("Error!");
console.log("Error! Malformed wake request...");
}
});
app.post('/lockSlack',
function(req, res) {
if (typeof req.body.lockuser !== 'undefined' &&//check if all needed fields are defined
typeof req.body.response_url !== 'undefined') {
var lockuser = procAlias(req.body.lockuser);//save fields and translate to internal alias
var response_url = req.body.response_url;
if (Object.keys(userData.tunnelUsers).indexOf(lockuser) > -1) {//if user to lock has been found
if (userData.tunnelUsers[lockuser].lockedSecsLeft === 0) {//and user is not already locked
console.log(req.body);
comEmit.emit("LOCK", lockuser, req.body.time, lockuser);//lock user
respondSlack(response_url, "OK, you ("+req.body.lockuser+") have been locked for " + req.body.time + " min.!");
res.send("OK, you ("+req.body.lockuser+") have been locked for " + req.body.time + " min.!");
} else {//if user is already locked
respondSlack(response_url, "You ("+req.body.lockuser+") are still locked! " + userData.tunnelUsers[lockuser].lockedSecsLeft + " Sec. left.");
res.send("You ("+req.body.lockuser+") are still locked! " + userData.tunnelUsers[lockuser].lockedSecsLeft + " Sec. left.");
}
} else {//if username to lock has not been found
respondSlack(response_url, "Your username ("+req.body.lockuser+") is not registered at the DeTunnelMe-server.");
res.send("Your username ("+req.body.lockuser+") is not registered at the DeTunnelMe-server.");
}
} else {//if not all needed fields are defined
console.log("Error! Malformed lock request...");
res.send("Error!");
}
});
var httpServer = http.createServer(app);
httpServer.listen(8080);
console.log('listening on http://localhost:8080');