const express = require('express'); const bodyParser = require('body-parser'); const axios = require('axios'); const app = express(); const DEBUG_LOGGING = false; const AUTH_SERVER_URL = 'http://localhost:8000'; const log = (string) => { date = new Date().toISOString().replace('T', ' ').split('.')[0]; console.log(date, '-', string); } // Enums const lockStates = { LOCK_OFF: 0, LOCK_PREARM: 1, LOCK_ARMED: 2, LOCK_ON_PRESSED: 3, LOCK_ON: 4, }; let toolStatus = null; const server = app.listen(8080, () => { log('Lockout socket server listening on port 8080!'); }); const io = require('socket.io')(server); // Express http server stuff: // TODO : remove on prod app.use((req, res, next) => { res.header('Access-Control-Allow-Origin', '*'); res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept'); next(); }); app.use(bodyParser.json()); app.post('/api/lockout/:mac', (req, res) => { if (toolStatus) { const mac = req.params.mac; DEBUG_LOGGING && log(`${mac} - Lock state: ${Object.keys(lockStates)[req.body.lockState]}`); const tmp = Object.entries(toolStatus).find(x => x[1].mac == mac) const toolSlug = tmp ? tmp[0] : null; if (toolSlug) { let clearAction = false; let tool = toolStatus[toolSlug]; tool.lastState = tool.state; switch (req.body.lockState) { case lockStates.LOCK_OFF: tool.state = 'off'; if (tool.action == 'disarm') clearAction = true; break; case lockStates.LOCK_ARMED: tool.state = 'armed'; if (tool.action == 'arm') clearAction = true; break; case lockStates.LOCK_ON: tool.state = 'on'; break; default: break; } // Track in case state is switched from elsewhere if (tool.state != tool.lastState) clearAction = true; if (clearAction) { log(`${mac} - Report | Previous state: ${tool.lastState} | New state: ${tool.state} | Website action: ${tool.action || 'N/A'}`); tool.action = ''; io.sockets.emit('toolStatus', toolStatus); } res.setHeader('Content-Type', 'application/json'); res.send(JSON.stringify({ action: tool.action })); toolStatus[toolSlug] = tool; } else { res.sendStatus(404); } } else { res.sendStatus(503); } }); app.get('*', (req, res) => { res.sendStatus(404); }); // Socket.io websocket stuff: // TODO : remove on prod io.origins('*:*'); io.on('connection', socket => { socket.emit('toolStatus', toolStatus); socket.on('log', string => { log(string); }); socket.on('requestInterlock', data => { const username = data.username; const token = data.token; const toolSlug = data.change.toolSlug; const action = data.change.action; log(`${username} - Request | Tool: ${toolSlug} | Action: ${action}`); axios.get(AUTH_SERVER_URL + '/user/', { headers: {'Authorization': 'Token ' + token}, }) .then(res => { const profile = res.data[0].profile || null; if (profile && profile.authorized_tools.includes(toolSlug)) { toolStatus[toolSlug].action = action; log(`${username} - Allowed | Tool: ${toolSlug} | Action: ${action}`); io.sockets.emit('toolStatus', toolStatus); } else { log(`${username} - DISALLOWED | Tool: ${toolSlug} | Action: ${action}`); } }) .catch(err => log(err.message) ); }); }); setInterval(() => { axios.get(AUTH_SERVER_URL + '/tooldata/') .then(res => { toolStatus = res.data.categories .reduce((a, x) => a.concat(x.tools), []) .reduce((a, x) => ({...a, [x.slug]: a[x.slug] ? { ...a[x.slug], mac: x.mac } : { mac: x.mac, action: '', state: 'off', lastState: 'n/a' }}), toolStatus || {}); io.sockets.emit('toolStatus', toolStatus); }) .catch(err => log(err.message) ); }, 10000);