-
Notifications
You must be signed in to change notification settings - Fork 14
/
Copy pathindex.js
469 lines (393 loc) · 13.5 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
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
const express = require('express');
const app = express();
const http = require('http').createServer(app);
const path = require('path');
const fs = require('fs');
const bodyParser = require('body-parser');
const multer = require('multer');
const upload = multer({
dest: 'upload/'
});
const session = require('express-session');
const WebSocket = require('ws');
const wss = new WebSocket.Server({
server: http
});
const os = require('os');
const expressWs = require('express-ws');
expressWs(app);
const lastCommands = {};
// Store registered devices and their firmware versions, MAC addresses, Wi-Fi signal strengths, and IP addresses in memory
const registeredDevices = new Map();
// Store firmware updates flags for devices
const firmwareUpdates = {};
// Store last heartbeat time for each device
const lastHeartbeatTime = new Map();
// Timeout duration for considering a device offline (in milliseconds)
const heartbeatTimeout = 20000;
// Modify your app.post('/firmwareInitiated') endpoint as follows:
app.post('/firmwareInitiated', (req, res) => {
const message = req.body;
const hostName = req.query.hostName; // Extract the hostname from the query parameter
console.log(`Received "Hello" message from ${hostName}`);
// Broadcast the "Hello" message with the hostname to all connected clients (HTML pages)
wss.clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send(`Received Firmware OTA by ${hostName}`);
}
});
res.sendStatus(200);
});
// WebSocket connection
wss.on('connection', (ws) => {
console.log('Client connected');
ws.on('message', (message) => {
console.log('Received message from client:', message);
// Broadcast the received message to all connected clients
wss.clients.forEach((client) => {
if (client !== ws && client.readyState === WebSocket.OPEN) {
client.send(message);
}
});
});
ws.on('close', () => {
console.log('Client disconnected');
});
});
// Endpoint to handle the "Hello" message from ESP8266
app.post('/firmwareInitiated', (req, res) => {
const message = req.body;
const hostName = req.query.hostName; // Extract the hostname from the query parameter
console.log(`Received "Hello" message from ${hostName}`);
// Broadcast the "Hello" message with the hostname to all connected clients (HTML pages)
wss.clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send(`Received Firmware OTA by ${hostName}`);
}
});
res.sendStatus(200);
});
// Create a new endpoint to serve the WebSocket IP and port dynamically
app.get('/getWebSocketAddress', (req, res) => {
// Get the actual IP address of the server
const serverIpAddress = getServerIpAddress();
// Get the actual port number that the server is listening on
const serverPort = getServerPort();
// Provide the IP and port of the WebSocket server
const wsAddress = {
ip: serverIpAddress,
port: serverPort
};
res.json(wsAddress);
});
// Function to get the server's IP address
function getServerIpAddress() {
const networkInterfaces = os.networkInterfaces();
for (const interfaceName in networkInterfaces) {
const networkInterface = networkInterfaces[interfaceName];
for (const interfaceInfo of networkInterface) {
if (interfaceInfo.family === 'IPv4' && !interfaceInfo.internal) {
return interfaceInfo.address;
}
}
}
// If the IP address is not found, return localhost as a fallback
return '127.0.0.1';
}
// Function to get the server's port number
function getServerPort() {
return http.address().port;
}
// Serve the login.html file
app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, 'login.html'));
});
// Middleware to parse request body
app.use(bodyParser.urlencoded({
extended: true
}));
app.use(bodyParser.json());
// Middleware to handle sessions
app.use(session({
secret: 'your-secret-key',
resave: false,
saveUninitialized: false,
}));
// The hardcoded username and password (replace these with your actual credentials)
const validUsername = 'admin';
const validPassword = 'admin';
app.post('/login', (req, res) => {
const {
username,
password
} = req.body;
console.log('Received login request:', {
username,
password
}); // Debug log to check received data
if (username === validUsername && password === validPassword) {
// Set a session variable to indicate successful login
req.session.isLoggedIn = true;
res.redirect('/index.html'); // Redirect to the index.html page
} else {
console.log('Invalid login attempt:', {
username,
password
}); // Debug log for invalid login attempts
res.status(401).send('Invalid username or password.');
}
});
// Endpoint to handle password authentication
app.post('/authenticate', (req, res) => {
const {
password
} = req.body;
// Replace 'your-password' with the correct password for authentication
const validPassword = 'admin';
if (password === validPassword) {
res.sendStatus(200); // Password is correct
} else {
res.sendStatus(401); // Password is incorrect
}
});
// Middleware to protect access to index.html
app.get('/index.html', (req, res, next) => {
if (req.session.isLoggedIn) {
res.sendFile(path.join(__dirname, 'index.html'));
} else {
res.redirect('/');
}
});
// Middleware to parse request body
app.use(bodyParser.text());
// Endpoint to handle logout
app.post('/logout', (req, res) => {
req.session.isLoggedIn = false; // Clear the isLoggedIn session variable to indicate logout
res.sendStatus(200);
});
// Endpoint to handle ESP8266 registration
app.post('/register', (req, res) => {
const data = req.body.trim().split('\n');
const hostName = data[0];
const firmwareVersion = data[1];
const macAddress = data[2];
const wifiSignalStrength = parseInt(data[3]); // Parse Wi-Fi signal strength as an integer
const ipAddress = data[4]; // Get the IP address sent by the ESP8266
if (!hostName || !firmwareVersion || !macAddress || isNaN(wifiSignalStrength) || !ipAddress) {
res.status(400).send('Bad Request.');
return;
}
// Check if the device is already registered
if (registeredDevices.has(hostName)) {
// Check if firmware version has changed
if (registeredDevices.get(hostName).firmwareVersion !== firmwareVersion) {
console.log(`Firmware version changed for ${hostName}. Re-registering...`);
firmwareUpdates[hostName] = true;
}
} else {
console.log(`Registering ${hostName}`);
}
// Store device information including hostname, firmware version, MAC address, Wi-Fi signal strength, and IP address
registeredDevices.set(hostName, {
hostName,
firmwareVersion,
macAddress,
wifiSignalStrength,
ipAddress
});
console.log('Registered devices:', Array.from(registeredDevices.entries()));
res.status(200).send('Registration successful.');
// Save registered devices to the text file
saveRegisteredDevicesToFile();
});
// Endpoint to fetch registered devices
app.get('/getDevices', (req, res) => {
res.json(Array.from(registeredDevices.keys()));
});
// Endpoint to fetch firmware version for a specific device
app.get('/getFirmwareVersion', (req, res) => {
const hostName = req.query.hostName;
if (!hostName) {
res.status(400).send('Bad Request.');
return;
}
const deviceInfo = registeredDevices.get(hostName);
if (deviceInfo) {
res.status(200).send(deviceInfo.firmwareVersion);
} else {
res.status(404).send('Firmware version not found.');
}
});
// Endpoint to handle firmware upload
app.post('/upload', upload.single('firmwareFile'), (req, res) => {
const hostName = req.query.hostName;
if (!hostName) {
res.status(400).send('Bad Request.');
return;
}
if (!req.file) {
res.status(400).send('No file uploaded.');
return;
}
const filePath = path.join(__dirname, `upload/${hostName}_firmware.bin`);
fs.renameSync(req.file.path, filePath);
console.log(`Received firmware binary for ${hostName}.`);
res.status(200).send('Firmware upload successful.');
// Set the firmware update flag for the device
firmwareUpdates[hostName] = true;
});
// Endpoint to check firmware update status
app.get('/updateStatus', (req, res) => {
const hostName = req.query.hostName;
if (!hostName) {
res.status(400).send('Bad Request.');
return;
}
// Check if the firmware update flag is set for the device
const updateAvailable = firmwareUpdates[hostName] === true;
if (updateAvailable) {
// Clear the update flag if update is available
firmwareUpdates[hostName] = false;
res.status(200).send('Update Available');
} else {
res.status(204).send('No Update Available');
}
});
// Endpoint to handle heartbeat from ESP8266
app.post('/heartbeat', (req, res) => {
const hostName = req.body;
if (!hostName) {
res.status(400).send('Bad Request.');
return;
}
// Update the last heartbeat time for the device
lastHeartbeatTime.set(hostName, Date.now());
res.status(200).send(`Heartbeat received from ${hostName}.`);
});
// Endpoint to flush all registered devices
app.post('/flushAllDevices', (req, res) => {
registeredDevices.clear();
lastHeartbeatTime.clear();
saveRegisteredDevicesToFile(); // Save the empty registered devices to the file
res.sendStatus(200);
});
// Endpoint to fetch online status and firmware version for all registered devices
app.get('/getOnlineStatus', (req, res) => {
const deviceStatusList = [];
const now = Date.now(); // Get the current timestamp
registeredDevices.forEach((deviceInfo, device) => {
const lastHeartbeat = lastHeartbeatTime.get(device);
const online = lastHeartbeat && now - lastHeartbeat <= heartbeatTimeout; // Check if the device is online based on the last heartbeat time
deviceStatusList.push({
device: device,
online: online,
firmwareVersion: deviceInfo.firmwareVersion,
macAddress: deviceInfo.macAddress,
wifiSignalStrength: deviceInfo.wifiSignalStrength,
ipAddress: deviceInfo.ipAddress
});
});
res.json(deviceStatusList);
});
// Endpoint to serve the firmware binary file
app.get('/upload/:fileName', (req, res) => {
const fileName = req.params.fileName;
const filePath = path.join(__dirname, `upload/${fileName}`);
if (fs.existsSync(filePath)) {
// Set appropriate headers for binary file download
res.setHeader('Content-Type', 'application/octet-stream');
res.setHeader('Content-Disposition', `attachment; filename=${fileName}`);
res.setHeader('Content-Length', fs.statSync(filePath).size); // Set Content-Length header
fs.createReadStream(filePath).pipe(res);
} else {
res.status(404).send('Firmware not found.');
}
});
app.post('/sendSerialData', (req, res) => {
const {
hostName
} = req.query;
const serialData = req.body; // Assuming the serial data is sent in the request body
// Process the received serial data as needed
console.log(`Received serial data from ${hostName}: ${serialData}`);
// Send a response if necessary (optional)
res.send('Serial data received successfully');
});
// Endpoint for the client to send a command
app.post('/sendCommand', (req, res) => {
const deviceHostName = req.body.hostName;
const command = req.body.command;
if (!deviceHostName) {
res.status(400).send('Bad Request.');
return;
}
// If the command is received from the ESP8266, store it in lastCommands object
if (command) {
lastCommands[deviceHostName] = command;
console.log(`Received command "${command}" from ESP8266 for ${deviceHostName}`);
} else {
console.log(`Received command "${lastCommands[deviceHostName]}" from client for ${deviceHostName}`);
}
// Send the command to the client
res.status(200).send(lastCommands[deviceHostName]);
});
// Endpoint for the ESP8266 to request a command
app.get('/getCommand', (req, res) => {
const deviceHostName = req.query.hostName;
// Send the last received command for the specific hostName to the ESP8266
res.status(200).send(lastCommands[deviceHostName] || '');
// Clear the lastCommand for the specific hostName after sending it to the ESP8266
delete lastCommands[deviceHostName];
});
// Start the server
const PORT = 3000;
http.listen(PORT, () => {
// Load registered devices from the text file
loadRegisteredDevicesFromFile();
console.log(`Server is running on port ${PORT}`);
});
// Periodically check for offline devices and remove them from the lastHeartbeatTime map
setInterval(() => {
const now = Date.now();
lastHeartbeatTime.forEach((heartbeatTime, device) => {
if (now - heartbeatTime > heartbeatTimeout) {
lastHeartbeatTime.delete(device);
}
});
}, 1000);
// Function to load registered devices from the text file and populate the `registeredDevices` map
function loadRegisteredDevicesFromFile() {
const filePath = path.join(__dirname, 'registered_devices.txt');
fs.readFile(filePath, 'utf8', (err, data) => {
if (!err) {
try {
const devices = JSON.parse(data);
devices.forEach((device) => {
registeredDevices.set(device.hostName, device);
});
console.log('Registered devices loaded successfully.');
} catch (error) {
console.error('Error parsing registered devices file:', error.message);
}
} else {
console.error('Error reading registered devices file:', err.message);
}
});
}
// Function to save registered devices to the text file
function saveRegisteredDevicesToFile() {
const devices = Array.from(registeredDevices.values());
const filePath = path.join(__dirname, 'registered_devices.txt');
fs.writeFile(filePath, JSON.stringify(devices, null, 2), (err) => {
if (err) {
console.error('Error saving registered devices to file:', err.message);
} else {
console.log('Registered devices saved successfully.');
}
});
}
// Add a new endpoint to fetch the server version
app.get('/getServerVersion', (req, res) => {
const serverVersion = "1.0.3"; // Replace this with your actual server version
res.send(serverVersion);
});