diff --git a/app.js b/app.js index ec7aa10..1eafeb1 100644 --- a/app.js +++ b/app.js @@ -10,6 +10,8 @@ const https = require('https'); const certificate = fs.readFileSync(process.env.CLIENT_CRT || 'cert/client.crt', 'utf8'); const privateKey = fs.readFileSync(process.env.CLIENT_KEY || 'cert/client.key', 'utf8'); +const firmwares = './resources/firmwares/' + const credentials = { key: privateKey, cert: certificate, @@ -18,6 +20,7 @@ const credentials = { }; const dbcontroller = require('./db'); +const {firmware} = require('./services'); const routes = require('./routes') const app = express(); @@ -39,6 +42,12 @@ console.log(' T E C H N O L O G I E S'); console.log(''); console.log(''); +firmware.init(firmwares) + .then((err) => { + if (err) { console.log('Unable to initialize firmware service')} + else { console.log ('Firmware service initialized') } + }) + ///// Startup MongoDB Client dbcontroller.init() .then((err) => { @@ -66,6 +75,10 @@ dbcontroller.init() console.log('HTTPS Port: ' + https_port); console.log('-------------------------------------------------------'); }); + + ///// Set keep-alive timeout + httpServer.keepAliveTimeout = 120*1000; + httpsServer.keepAliveTimeout = 120*1000; } }) @@ -81,7 +94,7 @@ app.use(function (req, res, next) { }); app.use(function (req, res, next) { - if (!req.body.length) { + if (!req.body) { // No body ////////// console.log('Got empty frame'); @@ -100,6 +113,9 @@ app.use(function (req, res, next) { cbor_length = req.body.length; ///// Decode CBOR body cbor.decodeFirst(req.body, (err, decoded) => { + ///// Check for error + if (err) { res.sendStatus(418); return; } + ///// Assign decoded data req.body = decoded; json_length = JSON.stringify(decoded).length @@ -129,7 +145,19 @@ app.use(function (req, res, next) { ///////////////////////// // Redefine send function res.send = function (body) { - if (req.is('application/cbor') || req.acceptsEncodings("application/cbor") || req.acceptsEncodings("cbor")) { + if (Buffer.isBuffer(body)) { + // Octet/Stream frame + ///////////////////// + + ///// Set header + res.set('Content-Type', 'application/octet-stream'); + + // Log statistics + console.log('Stream:'); + console.log(body.toString('hex')) + console.log(body.length + ' Bytes'); + + } else if (req.is('application/cbor') || req.acceptsEncodings("application/cbor") || req.acceptsEncodings("cbor")) { // CBOR Frame ///////////// console.log('Send CBOR frame'); diff --git a/controllers/api.controller.js b/controllers/api.controller.js new file mode 100644 index 0000000..759ba39 --- /dev/null +++ b/controllers/api.controller.js @@ -0,0 +1,278 @@ +const services = require('../services') + +const { device } = services + +const getPrograms = async (req, res, next) => { + ///// Emit event to server + try { + const programs = await device.getPrograms(req.params.msn); + res.send(programs); + } catch (error) { + res.status(500).send({message: error.message || "unknown_error"}); + } +} + +const postPrograms = async (req, res, next) => { + ///// Emit event to server + try { + const programs = await device.postPrograms(req.params.msn, req.body.programs, req.body.timestamp); + res.send(programs); + } catch (error) { + res.status(500).send({message: error.message || "unknown_error"}); + } +} + +const putPrograms = async (req, res, next) => { + try { + var programs = await device.postPrograms(req.params.msn, req.body.programs, req.body.timestamp); + res.send(programs); + } catch (error) { + res.status(500).send({message: error.message || "unknown_error"}); + } +} + +const deletePrograms = async (req, res, next) => { + ///// Emit event to server + try { + const programs = await device.postPrograms(req.params.msn, [], req.body.timestamp); + res.send(programs); + } catch (error) { + res.status(500).send({message: error.message || "unknown_error"}); + } +} + +const getConfiguration = async (req, res, next) => { + ///// Emit event to server + try { + const configuration = await device.getConfiguration(req.params.msn); + res.send(configuration); + } catch (error) { + res.status(500).send({message: error.message || "unknown_error"}); + } +} + +const postConfiguration = async (req, res, next) => { + ///// Emit event to server + try { + const configuration = await device.postConfiguration(req.params.msn, req.body.configuration, req.body.timestamp); + res.send(configuration); + } catch (error) { + res.status(500).send({message: error.message || "unknown_error"}); + } +} + +const putConfiguration = async (req, res, next) => { + try { + var configuration = await device.postConfiguration(req.params.msn, req.body.configuration, req.body.timestamp); + res.send(configuration); + } catch (error) { + res.status(500).send({message: error.message || "unknown_error"}); + } +} + +const deleteConfiguration = async (req, res, next) => { + ///// Emit event to server + try { + const configuration = await device.postConfiguration(req.params.msn, [], req.body.timestamp); + res.send(configuration); + } catch (error) { + res.status(500).send({message: error.message || "unknown_error"}); + } +} + +const getSlots = async (req, res, next) => { + ///// Emit event to server + try { + const slots = await device.getSlots(req.params.msn); + res.send(slots); + } catch (error) { + res.status(500).send({message: error.message || "unknown_error"}); + } +} + +const postSlots = async (req, res, next) => { + ///// Emit event to server + try { + const slots = await device.postSlots(req.params.msn, req.body.slots, req.body.timestamp); + res.send(slots); + } catch (error) { + res.status(500).send({message: error.message || "unknown_error"}); + } +} + +const putSlots = async (req, res, next) => { + try { + var slots = await device.postSlots(req.params.msn, req.body.slots, req.body.timestamp); + res.send(slots); + } catch (error) { + res.status(500).send({message: error.message || "unknown_error"}); + } +} + +const deleteSlots = async (req, res, next) => { + ///// Emit event to server + try { + const slots = await device.postSlots(req.params.msn, [], req.body.timestamp); + res.send(slots); + } catch (error) { + res.status(500).send({message: error.message || "unknown_error"}); + } +} + +const getManualCommand = async (req, res, next) => { + ///// Emit event to server + try { + const manualCommand = await device.getManualCommand(req.params.msn); + res.send(manualCommand); + } catch (error) { + res.status(500).send({message: error.message || "unknown_error"}); + } +} + +const postManualCommand = async (req, res, next) => { + ///// Emit event to server + try { + const manualCommand = await device.postManualCommand(req.params.msn, req.body.manualCommand); + res.send(manualCommand); + } catch (error) { + res.status(500).send({message: error.message || "unknown_error"}); + } +} + +const putManualCommand = async (req, res, next) => { + try { + var manualCommand = await device.postManualCommand(req.params.msn, req.body.manualCommand); + res.send(manualCommand); + } catch (error) { + res.status(500).send({message: error.message || "unknown_error"}); + } +} + +const deleteManualCommand = async (req, res, next) => { + ///// Emit event to server + try { + const manualCommand = await device.postManualCommand(req.params.msn, null); + res.send(manualCommand); + } catch (error) { + res.status(500).send({message: error.message || "unknown_error"}); + } +} + + +const getStatusCommand = async (req, res, next) => { + ///// Emit event to server + try { + const statusCommand = await device.getStatusCommand(req.params.msn); + res.send(statusCommand); + } catch (error) { + res.status(500).send({message: error.message || "unknown_error"}); + } +} + +const postStatusCommand = async (req, res, next) => { + ///// Emit event to server + try { + const statusCommand = await device.postStatusCommand(req.params.msn, req.body.statusCommand); + res.send(statusCommand); + } catch (error) { + res.status(500).send({message: error.message || "unknown_error"}); + } +} + +const putStatusCommand = async (req, res, next) => { + try { + var statusCommand = await device.postStatusCommand(req.params.msn, req.body.statusCommand); + res.send(statusCommand); + } catch (error) { + res.status(500).send({message: error.message || "unknown_error"}); + } +} + +const deleteStatusCommand = async (req, res, next) => { + ///// Emit event to server + try { + const statusCommand = await device.postStatusCommand(req.params.msn, null); + res.send(statusCommand); + } catch (error) { + res.status(500).send({message: error.message || "unknown_error"}); + } +} + + +const getAcknowledgedAlerts = async (req, res, next) => { + ///// Emit event to server + try { + const acknowledgedAlerts = await device.getAcknowledgedAlerts(req.params.msn); + res.send(acknowledgedAlerts); + } catch (error) { + res.status(500).send({message: error.message || "unknown_error"}); + } +} + +const postAcknowledgedAlerts = async (req, res, next) => { + ///// Emit event to server + try { + const acknowledgedAlerts = await device.postAcknowledgedAlerts(req.params.msn, req.body.acknowledgedAlerts); + res.send(acknowledgedAlerts); + } catch (error) { + res.status(500).send({message: error.message || "unknown_error"}); + } +} + +const putAcknowledgedAlerts = async (req, res, next) => { + try { + var acknowledgedAlerts = await device.postAcknowledgedAlerts(req.params.msn, req.body.acknowledgedAlerts); + res.send(acknowledgedAlerts); + } catch (error) { + res.status(500).send({message: error.message || "unknown_error"}); + } +} + +const deleteAcknowledgedAlerts = async (req, res, next) => { + ///// Emit event to server + try { + const acknowledgedAlerts = await device.postAcknowledgedAlerts(req.params.msn, null); + res.send(acknowledged); + } catch (error) { + res.status(500).send({message: error.message || "unknown_error"}); + } +} + +const ping = async (req, res, next) => { + ///// Emit event to server + res.sendStatus(200); +} + +module.exports = { + getPrograms, + postPrograms, + putPrograms, + deletePrograms, + + getConfiguration, + postConfiguration, + putConfiguration, + deleteConfiguration, + + getSlots, + postSlots, + putSlots, + deleteSlots, + + getManualCommand, + postManualCommand, + putManualCommand, + deleteManualCommand, + + getStatusCommand, + postStatusCommand, + putStatusCommand, + deleteStatusCommand, + + getAcknowledgedAlerts, + postAcknowledgedAlerts, + putAcknowledgedAlerts, + deleteAcknowledgedAlerts, + + ping, +} \ No newline at end of file diff --git a/controllers/firmware.controller.js b/controllers/firmware.controller.js index 1212c56..c994655 100644 --- a/controllers/firmware.controller.js +++ b/controllers/firmware.controller.js @@ -3,10 +3,15 @@ const services = require('../services') const { firmware } = services const postLookFirmware = async (req, res, next) => { - const { serialNumber, hash, version, type, hardwareIndex, hardwareVersion } = req.body + const { serialNumber, firmwareHash, firmwareVersion, moduleType, hardwareIndex, hardwareVersion } = req.body try { - const obj = await firmware.lookFirmware(serialNumber, hash, version, type, hardwareIndex, hardwareVersion) - res.send(obj) + const obj = await firmware.lookFirmware(serialNumber, firmwareHash, firmwareVersion, moduleType, hardwareIndex, hardwareVersion) + + if (!obj) { res.sendStatus(200); } + else { res.send({ + firmwareHash: obj.firmwareHash, + firmwareSize: obj.firmwareSize, + })} } catch (e) { console.log(e.message) res.sendStatus(500) @@ -14,10 +19,12 @@ const postLookFirmware = async (req, res, next) => { } const postSyncFirmware = async (req, res, next) => { - const { serialNumber, hash, version, type, hardwareIndex, hardwareVersion } = req.body + const { serialNumber, firmwareHash, moduleType, position, length } = req.body try { - const obj = await firmware.syncFirmware(serialNumber, hash, version, type, hardwareIndex, hardwareVersion) - res.send(obj) + const buffer = await firmware.syncFirmware(serialNumber, firmwareHash, moduleType, position, length) + + if (!buffer) { res.sendStatus(400); } + else { res.send(buffer) } } catch (e) { console.log(e.message) res.sendStatus(500) diff --git a/controllers/grtd.controller.js b/controllers/grtd.controller.js index bfa7caf..e5ba312 100644 --- a/controllers/grtd.controller.js +++ b/controllers/grtd.controller.js @@ -1,67 +1,240 @@ const services = require('../services') +const util = require('../util') const { device } = services +const getModuleMsn = (req) => { + return req.query.msn || req.query.id || req.body.module +} + +const getRelayMsn = (req) => { + return req.body.relayMsn +} + const postGetRequestToDo = async (req, res, next) => { ///// Emit event to server try { - await device.postRequestToDo(req.body.relayMsn, req.body); - await device.getRequestToDo(req.body.relayMsn); - var response = { todo: [{ serialNumber: req.body.relayMsn, configuration: 1 }] }; + await device.postRequestToDo(getRelayMsn(req), req.body); + const todo = await device.getRequestToDo(getRelayMsn(req)); + var response = { todo }; console.log(req.body); res.send(response); } catch (error) { - res.send(500); + res.sendStatus(500); } } const getModuleConfiguration = async (req, res, next) => { - ///// Emit event to server - var response = { - "name": "LRBST-R-TARACE", - "inventory": [ - "7400040670330001" - ], - "wakeUpTimes": [ - 60, - 480, - 540, - 600, - 660, - 720, - 780, - 840, - 900, - 960, - 1020, - 1080 - ] - }; + try { + ///// Emit event to server + var response = await device.getConfiguration(getModuleMsn(req)) - res.send(response); + res.send(response.configuration); + } catch (error) { + res.sendStatus(500); + } } const setModuleConfiguration = async (req, res, next) => { + try { + ///// Emit event to server + await device.postConfiguration(getModuleMsn(req), req.body, util.dateToTimestamp(req.body.flashTimeStamp)) -} - -const getModulePrograms = async (req, res, next) => { - -} - -const setModulePrograms = async (req, res, next) => { - + res.sendStatus(200); + } catch (error) { + res.sendStatus(500); + } } const reportModuleDataSent = async (req, res, next) => { - res.send(200); + try { + ///// Emit event to server + await device.postConfiguration(getModuleMsn(req), null, util.dateToTimestamp(req.body.sentAt)) + + res.sendStatus(200); + } catch (error) { + res.sendStatus(500); + } +} + +const getModulePrograms = async (req, res, next) => { + try { + ///// Emit event to server + var response = await device.getPrograms(getModuleMsn(req)) + + res.send(response.programs); + } catch (error) { + res.sendStatus(500); + } +} + +const setModulePrograms = async (req, res, next) => { + try { + ///// Emit event to server + await device.postPrograms(getModuleMsn(req), req.body, util.dateToTimestamp(req.body.flashTimeStamp)) + + res.sendStatus(200); + } catch (error) { + res.sendStatus(500); + } +} + +const reportProgramsDataSent = async (req, res, next) => { + try { + ///// Emit event to server + await device.postPrograms(getModuleMsn(req), null, util.dateToTimestamp(req.body.programmingTimestamp)) + + res.sendStatus(200); + } catch (error) { + res.sendStatus(500); + } +} + +const reportAllModuleProgramsDataSent = async (req, res, next) => { + try { + ///// Emit event to server + await device.postPrograms(getModuleMsn(req), null, util.dateToTimestamp(req.body.sentAt)) + + res.sendStatus(200); + } catch (error) { + res.sendStatus(500); + } +} + +const getModuleSlots = async (req, res, next) => { + try { + ///// Emit event to server + var response = await device.getSlots(getModuleMsn(req)) + + res.send(response.slots); + } catch (error) { + res.sendStatus(500); + } +} + +const setModuleSlots = async (req, res, next) => { + try { + ///// Emit event to server + await device.postSlots(getModuleMsn(req), req.body, util.dateToTimestamp(req.body.flashTimeStamp)) + + res.sendStatus(200); + } catch (error) { + res.sendStatus(500); + } +} + +const reportSlotsDataSent = async (req, res, next) => { + try { + ///// Emit event to server + await device.postSlots(getModuleMsn(req), null, util.dateToTimestamp(req.body.programmingTimestamp)) + + res.sendStatus(200); + } catch (error) { + res.sendStatus(500); + } +} + +const reportAllModuleSlotsDataSent = async (req, res, next) => { + try { + ///// Emit event to server + await device.postSlots(getModuleMsn(req), null, util.dateToTimestamp(req.body.sentAt)) + + res.sendStatus(200); + } catch (error) { + res.sendStatus(500); + } +} + +const getManualCommand = async (req, res, next) => { + try { + ///// Emit event to server + var response = await device.getManualCommand(getModuleMsn(req)) + + res.send(response.manualCommand); + } catch (error) { + res.sendStatus(500); + } +} + +const reportManualCommandSent = async (req, res, next) => { + try { + ///// Emit event to server + await device.postManualCommand(getModuleMsn(req), null) + + res.sendStatus(200); + } catch (error) { + res.sendStatus(500); + } +} + +const getStatusCommand = async (req, res, next) => { + try { + ///// Emit event to server + var response = await device.getStatusCommand(getModuleMsn(req)) + + res.send(response.statusCommand); + } catch (error) { + res.sendStatus(500); + } +} + +const reportStatusCommandSent = async (req, res, next) => { + try { + ///// Emit event to server + await device.postStatusCommand(getModuleMsn(req), null) + + res.sendStatus(200); + } catch (error) { + res.sendStatus(500); + } +} + +const getAcknowledgeAlerts = async (req, res, next) => { + try { + ///// Emit event to server + var response = await device.getAcknowledgedAlerts(getModuleMsn(req)) + + res.send(response.acknowledgedAlerts); + } catch (error) { + res.sendStatus(500); + } + +} + +const reportAcknowledgeAlerts = async (req, res, next) => { + try { + ///// Emit event to server + await device.postAcknowledgedAlerts(getModuleMsn(req), null) + + res.sendStatus(200); + } catch (error) { + res.sendStatus(500); + } } module.exports = { postGetRequestToDo, + getModuleConfiguration, setModuleConfiguration, + reportModuleDataSent, + getModulePrograms, setModulePrograms, - reportModuleDataSent, + reportProgramsDataSent, + reportAllModuleProgramsDataSent, + + getModuleSlots, + setModuleSlots, + reportSlotsDataSent, + reportAllModuleSlotsDataSent, + + getManualCommand, + reportManualCommandSent, + + getStatusCommand, + reportStatusCommandSent, + + getAcknowledgeAlerts, + reportAcknowledgeAlerts, } \ No newline at end of file diff --git a/controllers/index.js b/controllers/index.js index 3d4a655..e8f014e 100644 --- a/controllers/index.js +++ b/controllers/index.js @@ -1,17 +1,21 @@ const status = require('./status.controller') const data = require('./data.controller') +const senbus_value = require('./senbus_value.controller') const firmware = require('./firmware.controller') const ipx = require('./ipx.controller') const journal = require('./journal.controller') const longpolling = require('./longpolling.controller') const grtd = require('./grtd.controller') +const api = require('./api.controller') module.exports = { status, data, + senbus_value, firmware, ipx, journal, longpolling, grtd, + api, } \ No newline at end of file diff --git a/controllers/longpolling.controller.js b/controllers/longpolling.controller.js index 4547392..0f0fed7 100644 --- a/controllers/longpolling.controller.js +++ b/controllers/longpolling.controller.js @@ -84,10 +84,6 @@ const getModuleRequest = async (req, res, next) => { if (error) { res.send(error); } else { res.send(body); } }); - - req.on("close", function () { - console.log("Request cancelled by client"); - }); } const postModuleRequest = async (req, res, next) => { @@ -96,10 +92,6 @@ const postModuleRequest = async (req, res, next) => { if (error) { res.send(error); } else { res.send(body); } }); - - req.on("close", function () { - console.log("Request cancelled by client"); - }); } module.exports = { diff --git a/controllers/senbus_value.controller.js b/controllers/senbus_value.controller.js new file mode 100644 index 0000000..7ab8492 --- /dev/null +++ b/controllers/senbus_value.controller.js @@ -0,0 +1,16 @@ +const services = require('../services') + +const { senbus_value } = services + +const postSenbusValue = async (req, res, next) => { + try { + res.sendStatus(200) + } catch (e) { + console.log(e.message) + res.sendStatus(500) + } +} + +module.exports = { + postSenbusValue, +} \ No newline at end of file diff --git a/controllers/status.controller.js b/controllers/status.controller.js index b067e99..9128dfb 100644 --- a/controllers/status.controller.js +++ b/controllers/status.controller.js @@ -1,6 +1,6 @@ const services = require('../services') -const { status } = services +const { status, device } = services const postSetStatus = async (req, res, next) => { try { @@ -9,11 +9,13 @@ const postSetStatus = async (req, res, next) => { if (req.body.status) { const values = { inputsAlerts: req.body.status.inputsAlerts || [], msn: req.body.msn, id: req.params.id } code = await status.createAlerts(values) + code = await device.updateStatus(req.body.msn, req.body, req.body.dialogTimestamp) } else if (req.body.radioProducts) { for (const product of req.body.radioProducts) { const values = { inputsAlerts: product.inputsAlerts || [], msn: product.msn, id: req.params.id } code = await status.createAlerts(values) + code = await device.updateStatus(product.msn, product, product.dialogTimestamp) } } res.send(code) diff --git a/db/dbcontroller.js b/db/dbcontroller.js index 17ebfd7..4deebe2 100644 --- a/db/dbcontroller.js +++ b/db/dbcontroller.js @@ -1,17 +1,16 @@ const MongoClient = require('mongodb').MongoClient; const models = require('../models') -const mongodb_hostname = process.env.MONGODB_HOST || 'localhost'; +const mongodb_hostname = process.env.MONGODB_HOST || '0.0.0.0'; const mongodb_port = process.env.MONGODB_PORT || 27017; const mongodb_database = process.env.MONGODB_DATABASE || 'lsp'; const mongodb_url = 'mongodb://' + mongodb_hostname + ':' + mongodb_port; -const client = new MongoClient(mongodb_url, { useNewUrlParser: true, useUnifiedTopology: true }); +const client = new MongoClient(mongodb_url, { useUnifiedTopology: true }); const bucketMaxSize = 4096; var _db; -var _devices = MongoClient(); var _data; var _ipx_data; var _events; @@ -22,8 +21,10 @@ var days = function(date) { } var init = async function () { - client.connect(function (err) { + await client.connect(function (err) { if (err) { + console.log('MongoDB Client error'); + console.log(err); return { error: "Error connecting to DB: " + err } } else { @@ -34,13 +35,14 @@ var init = async function () { _events = _db.collection('events'); _alerts = _db.collection('alerts'); + console.log('MongoDB Client connected'); //////////////////////////// // FLUSH DB BY UNCOMMENTING - // _devices.remove(function(err, objects){}); - // _data.remove(function(err, objects){}); - // _ipx_data.remove(function(err, objects){}); - // _events.remove(function(err, objects){}); - // _alerts.remove(function(err, objects){}); + // _devices.deleteMany(function(err, objects){}); + // _data.deleteMany(function(err, objects){}); + // _ipx_data.deleteMany(function(err, objects){}); + // _events.deleteMany(function(err, objects){}); + // _alerts.deleteMany(function(err, objects){}); return null; } @@ -165,9 +167,104 @@ var addIPXData = async function (relay, device, child, element) { } } +var addDevice = async function (element) { + try { + const model = await models.device.validateAsync(element); + + await _devices.updateOne( + { + msn: model.msn + }, + { + $set: model + }, + { + upsert: true + }) + .then(err => { + return err; + }) + } catch (err) { + return err; + } +} + +var updateDevice = async function (model) { + try { + await _devices.updateOne( + { + msn: model.msn + }, + { + $set: model + }, + { + upsert: true + }) + .then(err => { + return err; + }) + } catch (err) { + return err; + } +} + +var getDevice = async function (msn) { + try { + if (!msn) { throw ("msn_not_provided")} + + return await _devices.findOne( + { + $or : [{relayMsn: msn}, {msn: msn}] + }) + .then(element => { + return element; + }) + } catch (err) { + throw err + } +} + +var getDeviceChildren = async function (msn) { + try { + if (!msn) { throw ("msn_not_provided")} + + return await _devices.findOne( + { + relayMsn: msn + }) + .then(element => { + return element.inventory; + }) + } catch (err) { + throw err + } +} + +var getDeviceParents = async function (msn) { + try { + if (!msn) { throw ("msn_not_provided")} + + return await _devices.find( + { + inventory: { $in: {msn: msn} } + }) + .then(elements => { + return elements; + }) + } catch (err) { + throw err + } +} + exports.init = init exports.addData = addData exports.addEvent = addEvent exports.addAlert = addAlert -exports.addIPXData = addIPXData \ No newline at end of file +exports.addIPXData = addIPXData +exports.addDevice = addDevice +exports.updateDevice = updateDevice +exports.getDevice = getDevice +exports.getDeviceChildren = getDeviceChildren +exports.getDeviceParents = getDeviceParents diff --git a/models/device.model.js b/models/device.model.js new file mode 100644 index 0000000..d56ccc5 --- /dev/null +++ b/models/device.model.js @@ -0,0 +1,44 @@ +const joi = require("joi") + +const DeviceModel = joi.object({ + relayMsn: joi.string().optional(), + msn: joi.string().optional(), + relayName: joi.string().optional(), + relayBattery: joi.number().optional(), + relayBatteryStatus: joi.number().optional(), + relayCSQ: joi.number().optional(), + relayNetwork: joi.number().optional(), + connectionType: joi.string().optional(), + phoneNumber: joi.string().optional(), + iccid: joi.string().optional(), + relayTemperature: joi.number().optional(), + relayConfigurationTimestamp: joi.number().optional(), + vsoft: joi.string().optional(), + inventory: joi.array().items( joi.object({ + msn: joi.string().required(), + configurationTimestamp: joi.number().optional(), + programmingTimestamp: joi.number().optional(), + }).optional() + ).optional(), + + configurationTimestamp: joi.number().optional(), + programmationTimestamp: joi.number().optional(), + + todo: joi.array().items(joi.object({ + msn: joi.string().required(), + }).unknown()).optional(), + + status: joi.object().unknown(), + + programs: joi.object().unknown(), + configuration: joi.object().unknown(), + slots: joi.object().unknown(), + manualCommand: joi.object().unknown(), + statusCommand: joi.object().unknown(), + acknowledgedAlerts: joi.object().unknown(), +}) + +module.exports = DeviceModel + + + diff --git a/models/index.js b/models/index.js index 90c192d..316feef 100644 --- a/models/index.js +++ b/models/index.js @@ -2,10 +2,12 @@ const data = require('./data.model.js'); const event = require('./event.model.js'); const alert = require('./alert.model.js'); const ipxdata = require('./ipxdata.model.js'); +const device = require('./device.model.js'); module.exports = { data, event, alert, - ipxdata + ipxdata, + device } \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 52d0f82..090afbb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,97 +1,172 @@ { "name": "lightweight-solem-platform", "version": "1.0.0", - "lockfileVersion": 1, + "lockfileVersion": 3, "requires": true, - "dependencies": { - "@hapi/hoek": { + "packages": { + "": { + "name": "lightweight-solem-platform", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "assert": "^2.0.0", + "body-parser": "^1.19.0", + "cbor": "^5.1.0", + "cbor-body-parser": "^1.0.3", + "chokidar": "^3.6.0", + "crc": "^4.3.2", + "events": "^3.2.0", + "express": "^4.17.1", + "express-session": "^1.17.2", + "joi": "^17.4.0", + "moment": "^2.29.1", + "mongodb": "^3.6.2", + "oauth2-server": "^3.1.1", + "random-bytes": "^1.0.0", + "underscore": "^1.13.6" + } + }, + "node_modules/@hapi/hoek": { "version": "9.2.0", "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.2.0.tgz", "integrity": "sha512-sqKVVVOe5ivCaXDWivIJYVSaEgdQK9ul7a4Kity5Iw7u9+wBAPbX1RMSnLLmp7O4Vzj0WOWwMAJsTL00xwaNug==" }, - "@hapi/topo": { + "node_modules/@hapi/topo": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", - "requires": { + "dependencies": { "@hapi/hoek": "^9.0.0" } }, - "@sideway/address": { + "node_modules/@sideway/address": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.2.tgz", "integrity": "sha512-idTz8ibqWFrPU8kMirL0CoPH/A29XOzzAzpyN3zQ4kAWnzmNfFmRaoMNN6VI8ske5M73HZyhIaW4OuSFIdM4oA==", - "requires": { + "dependencies": { "@hapi/hoek": "^9.0.0" } }, - "@sideway/formula": { + "node_modules/@sideway/formula": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.0.tgz", "integrity": "sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg==" }, - "@sideway/pinpoint": { + "node_modules/@sideway/pinpoint": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==" }, - "accepts": { + "node_modules/accepts": { "version": "1.3.7", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", - "requires": { + "dependencies": { "mime-types": "~2.1.24", "negotiator": "0.6.2" + }, + "engines": { + "node": ">= 0.6" } }, - "array-filter": { + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/array-filter": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-1.0.0.tgz", "integrity": "sha1-uveeYubvTCpMC4MSMtr/7CUfnYM=" }, - "array-flatten": { + "node_modules/array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, - "assert": { + "node_modules/assert": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/assert/-/assert-2.0.0.tgz", "integrity": "sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A==", - "requires": { + "dependencies": { "es6-object-assign": "^1.1.0", "is-nan": "^1.2.1", "object-is": "^1.0.1", "util": "^0.12.0" } }, - "available-typed-arrays": { + "node_modules/available-typed-arrays": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.2.tgz", "integrity": "sha512-XWX3OX8Onv97LMk/ftVyBibpGwY5a8SmuxZPzeOxqmuEqUCOM9ZE+uIaD1VNJ5QnvU2UQusvmKbuM1FR8QWGfQ==", - "requires": { + "dependencies": { "array-filter": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "bignumber.js": { + "node_modules/basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.1.2" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/bignumber.js": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.1.tgz", - "integrity": "sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA==" + "integrity": "sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA==", + "engines": { + "node": "*" + } }, - "bl": { + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bl": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz", "integrity": "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==", - "requires": { + "dependencies": { "readable-stream": "^2.3.5", "safe-buffer": "^5.1.1" } }, - "body-parser": { + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "license": "MIT" + }, + "node_modules/body-parser": { "version": "1.19.0", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", - "requires": { + "dependencies": { "bytes": "3.1.0", "content-type": "~1.0.4", "debug": "2.6.9", @@ -102,110 +177,222 @@ "qs": "6.7.0", "raw-body": "2.4.0", "type-is": "~1.6.17" + }, + "engines": { + "node": ">= 0.8" } }, - "bson": { + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/bson": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.5.tgz", - "integrity": "sha512-kDuEzldR21lHciPQAIulLs1LZlCXdLziXI6Mb/TDkwXhb//UORJNPXgcRs2CuO4H0DcMkpfT3/ySsP3unoZjBg==" + "integrity": "sha512-kDuEzldR21lHciPQAIulLs1LZlCXdLziXI6Mb/TDkwXhb//UORJNPXgcRs2CuO4H0DcMkpfT3/ySsP3unoZjBg==", + "engines": { + "node": ">=0.6.19" + } }, - "bytes": { + "node_modules/bytes": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "engines": { + "node": ">= 0.8" + } }, - "cbor": { + "node_modules/cbor": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/cbor/-/cbor-5.1.0.tgz", "integrity": "sha512-qzEc7kUShdMbWTaUH7X+aHW8owvBU3FS0dfYR1lGYpoZr0mGJhhojLlZJH653x/DfeMZ56h315FRNBUIG1R7qg==", - "requires": { + "dependencies": { "bignumber.js": "^9.0.0", "nofilter": "^1.0.4" + }, + "engines": { + "node": ">=6.0.0" } }, - "cbor-body-parser": { + "node_modules/cbor-body-parser": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/cbor-body-parser/-/cbor-body-parser-1.0.3.tgz", "integrity": "sha512-RXXRzQ7hckzLg3cCtdbCzxFRNfK8RoHTf7yO/uTEE44tF8DAeBQnSYkjXWNFFXL9byRfjg4XqCaZitcVAcEruA==", - "requires": { + "dependencies": { "body-parser": "^1.19.0", "cbor": "^5.1.0" + }, + "engines": { + "node": ">=6.0.0" } }, - "content-disposition": { + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/co-bluebird": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/co-bluebird/-/co-bluebird-1.1.0.tgz", + "integrity": "sha512-JuoemMXxQjYAxbfRrNpOsLyiwDiY8mXvGqJyYLM7jMySDJtnMklW3V2o8uyubpc1eN2YoRsAdfZ1lfKCd3lsrA==", + "dependencies": { + "bluebird": "^2.10.0", + "co-use": "^1.1.0" + }, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/co-bluebird/node_modules/bluebird": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.11.0.tgz", + "integrity": "sha512-UfFSr22dmHPQqPP9XWHRhq+gWnHCYguQGkXQlbyPtW5qTnhFWA8/iXg765tH0cAjy7l/zPJ1aBTO0g5XgA7kvQ==", + "license": "MIT" + }, + "node_modules/co-use": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/co-use/-/co-use-1.1.0.tgz", + "integrity": "sha512-1lVRtdywv41zQO/xvI2wU8w6oFcUYT6T84YKSxN25KN4N4Kld3scLovt8FjDmD63Cm7HtyRWHjezt+IanXmkyA==", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/content-disposition": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", - "requires": { + "dependencies": { "safe-buffer": "5.1.2" + }, + "engines": { + "node": ">= 0.6" } }, - "content-type": { + "node_modules/content-type": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "engines": { + "node": ">= 0.6" + } }, - "cookie": { + "node_modules/cookie": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", + "engines": { + "node": ">= 0.6" + } }, - "cookie-signature": { + "node_modules/cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" }, - "core-util-is": { + "node_modules/core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, - "debug": { + "node_modules/crc": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/crc/-/crc-4.3.2.tgz", + "integrity": "sha512-uGDHf4KLLh2zsHa8D8hIQ1H/HtFQhyHrc0uhHBcoKGol/Xnb+MPYfUMw7cvON6ze/GUESTudKayDcJC5HnJv1A==", + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "buffer": ">=6.0.3" + }, + "peerDependenciesMeta": { + "buffer": { + "optional": true + } + } + }, + "node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { + "dependencies": { "ms": "2.0.0" } }, - "define-properties": { + "node_modules/define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "requires": { + "dependencies": { "object-keys": "^1.0.12" + }, + "engines": { + "node": ">= 0.4" } }, - "denque": { + "node_modules/denque": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/denque/-/denque-1.4.1.tgz", - "integrity": "sha512-OfzPuSZKGcgr96rf1oODnfjqBFmr1DVoc/TrItj3Ohe0Ah1C5WX5Baquw/9U9KovnQ88EqmJbD66rKYUQYN1tQ==" + "integrity": "sha512-OfzPuSZKGcgr96rf1oODnfjqBFmr1DVoc/TrItj3Ohe0Ah1C5WX5Baquw/9U9KovnQ88EqmJbD66rKYUQYN1tQ==", + "engines": { + "node": ">=0.10" + } }, - "depd": { + "node_modules/depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "engines": { + "node": ">= 0.6" + } }, - "destroy": { + "node_modules/destroy": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, - "ee-first": { + "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, - "encodeurl": { + "node_modules/encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "engines": { + "node": ">= 0.8" + } }, - "es-abstract": { + "node_modules/es-abstract": { "version": "1.18.0-next.1", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", - "requires": { + "dependencies": { "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", "has": "^1.0.3", @@ -218,43 +405,61 @@ "object.assign": "^4.1.1", "string.prototype.trimend": "^1.0.1", "string.prototype.trimstart": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "es-to-primitive": { + "node_modules/es-to-primitive": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "requires": { + "dependencies": { "is-callable": "^1.1.4", "is-date-object": "^1.0.1", "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "es6-object-assign": { + "node_modules/es6-object-assign": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz", "integrity": "sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw=" }, - "escape-html": { + "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" }, - "etag": { + "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "engines": { + "node": ">= 0.6" + } }, - "events": { + "node_modules/events": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/events/-/events-3.2.0.tgz", - "integrity": "sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg==" + "integrity": "sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg==", + "engines": { + "node": ">=0.8.x" + } }, - "express": { + "node_modules/express": { "version": "4.17.1", "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", - "requires": { + "dependencies": { "accepts": "~1.3.7", "array-flatten": "1.1.1", "body-parser": "1.19.0", @@ -285,13 +490,16 @@ "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" } }, - "express-session": { + "node_modules/express-session": { "version": "1.17.2", "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.2.tgz", "integrity": "sha512-mPcYcLA0lvh7D4Oqr5aNJFMtBMKPLl++OKKxkHzZ0U0oDq1rpKBnkR5f5vCHR26VeArlTOEF9td4x5IjICksRQ==", - "requires": { + "dependencies": { "cookie": "0.4.1", "cookie-signature": "1.0.6", "debug": "2.6.9", @@ -301,29 +509,61 @@ "safe-buffer": "5.2.1", "uid-safe": "~2.1.5" }, - "dependencies": { - "cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==" - }, - "depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - } + "engines": { + "node": ">= 0.8.0" } }, - "finalhandler": { + "node_modules/express-session/node_modules/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express-session/node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/express-session/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", - "requires": { + "dependencies": { "debug": "2.6.9", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", @@ -331,161 +571,305 @@ "parseurl": "~1.3.3", "statuses": "~1.5.0", "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" } }, - "foreach": { + "node_modules/foreach": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=" }, - "forwarded": { + "node_modules/forwarded": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", + "engines": { + "node": ">= 0.6" + } }, - "fresh": { + "node_modules/fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "engines": { + "node": ">= 0.6" + } }, - "function-bind": { + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, - "has": { + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "requires": { + "dependencies": { "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" } }, - "has-symbols": { + "node_modules/has-symbols": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "http-errors": { + "node_modules/http-errors": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", - "requires": { + "dependencies": { "depd": "~1.1.2", "inherits": "2.0.3", "setprototypeof": "1.1.1", "statuses": ">= 1.5.0 < 2", "toidentifier": "1.0.0" + }, + "engines": { + "node": ">= 0.6" } }, - "iconv-lite": { + "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { + "dependencies": { "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" } }, - "inherits": { + "node_modules/inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, - "ipaddr.js": { + "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } }, - "is-arguments": { + "node_modules/is-arguments": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", - "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==" + "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==", + "engines": { + "node": ">= 0.4" + } }, - "is-callable": { + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-callable": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", - "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==" + "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "is-date-object": { + "node_modules/is-date-object": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", - "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==" + "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "is-generator-function": { + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-generator": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-generator/-/is-generator-1.0.3.tgz", + "integrity": "sha512-G56jBpbJeg7ds83HW1LuShNs8J73Fv3CPz/bmROHOHlnKkN8sWb9ujiagjmxxMUywftgq48HlBZELKKqFLk0oA==", + "license": "MIT" + }, + "node_modules/is-generator-function": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.7.tgz", - "integrity": "sha512-YZc5EwyO4f2kWCax7oegfuSr9mFz1ZvieNYBEjmukLxgXfBUbxAWGVF7GZf0zidYtoBl3WvC07YK0wT76a+Rtw==" + "integrity": "sha512-YZc5EwyO4f2kWCax7oegfuSr9mFz1ZvieNYBEjmukLxgXfBUbxAWGVF7GZf0zidYtoBl3WvC07YK0wT76a+Rtw==", + "engines": { + "node": ">= 0.4" + } }, - "is-nan": { + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-nan": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.0.tgz", "integrity": "sha512-z7bbREymOqt2CCaZVly8aC4ML3Xhfi0ekuOnjO2L8vKdl+CttdVoGZQhd4adMFAsxQ5VeRVwORs4tU8RH+HFtQ==", - "requires": { + "dependencies": { "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "is-negative-zero": { + "node_modules/is-negative-zero": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz", - "integrity": "sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE=" + "integrity": "sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE=", + "engines": { + "node": ">= 0.4" + } }, - "is-regex": { + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-regex": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", - "requires": { + "dependencies": { "has-symbols": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "is-symbol": { + "node_modules/is-symbol": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", - "requires": { + "dependencies": { "has-symbols": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "is-typed-array": { + "node_modules/is-typed-array": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.3.tgz", "integrity": "sha512-BSYUBOK/HJibQ30wWkWold5txYwMUXQct9YHAQJr8fSwvZoiglcqB0pd7vEN23+Tsi9IUEjztdOSzl4qLVYGTQ==", - "requires": { + "dependencies": { "available-typed-arrays": "^1.0.0", "es-abstract": "^1.17.4", "foreach": "^2.0.5", "has-symbols": "^1.0.1" }, - "dependencies": { - "es-abstract": { - "version": "1.17.7", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", - "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.2", - "is-regex": "^1.1.1", - "object-inspect": "^1.8.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.1", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" - } - } + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "isarray": { + "node_modules/is-typed-array/node_modules/es-abstract": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", + "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", + "dependencies": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, - "joi": { + "node_modules/joi": { "version": "17.4.0", "resolved": "https://registry.npmjs.org/joi/-/joi-17.4.0.tgz", "integrity": "sha512-F4WiW2xaV6wc1jxete70Rw4V/VuMd6IN+a5ilZsxG4uYtUXWu2kq9W5P2dz30e7Gmw8RCbY/u/uk+dMPma9tAg==", - "requires": { + "dependencies": { "@hapi/hoek": "^9.0.0", "@hapi/topo": "^5.0.0", "@sideway/address": "^4.1.0", @@ -493,176 +877,311 @@ "@sideway/pinpoint": "^2.0.0" } }, - "media-typer": { + "node_modules/lodash": { + "version": "4.17.19", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", + "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==", + "license": "MIT" + }, + "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "engines": { + "node": ">= 0.6" + } }, - "memory-pager": { + "node_modules/memory-pager": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", "optional": true }, - "merge-descriptors": { + "node_modules/merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" }, - "methods": { + "node_modules/methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "engines": { + "node": ">= 0.6" + } }, - "mime": { + "node_modules/mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } }, - "mime-db": { + "node_modules/mime-db": { "version": "1.44.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", - "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" + "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==", + "engines": { + "node": ">= 0.6" + } }, - "mime-types": { + "node_modules/mime-types": { "version": "2.1.27", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", - "requires": { + "dependencies": { "mime-db": "1.44.0" + }, + "engines": { + "node": ">= 0.6" } }, - "moment": { + "node_modules/moment": { "version": "2.29.1", "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", - "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==" + "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==", + "engines": { + "node": "*" + } }, - "mongodb": { + "node_modules/mongodb": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.6.2.tgz", "integrity": "sha512-sSZOb04w3HcnrrXC82NEh/YGCmBuRgR+C1hZgmmv4L6dBz4BkRse6Y8/q/neXer9i95fKUBbFi4KgeceXmbsOA==", - "requires": { + "dependencies": { "bl": "^2.2.1", "bson": "^1.1.4", "denque": "^1.4.1", "require_optional": "^1.0.1", - "safe-buffer": "^5.1.2", + "safe-buffer": "^5.1.2" + }, + "engines": { + "node": ">=4" + }, + "optionalDependencies": { "saslprep": "^1.0.0" } }, - "ms": { + "node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, - "negotiator": { + "node_modules/negotiator": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", + "engines": { + "node": ">= 0.6" + } }, - "nofilter": { + "node_modules/nofilter": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/nofilter/-/nofilter-1.0.4.tgz", - "integrity": "sha512-N8lidFp+fCz+TD51+haYdbDGrcBWwuHX40F5+z0qkUjMJ5Tp+rdSuAkMJ9N9eoolDlEVTf6u5icM+cNKkKW2mA==" + "integrity": "sha512-N8lidFp+fCz+TD51+haYdbDGrcBWwuHX40F5+z0qkUjMJ5Tp+rdSuAkMJ9N9eoolDlEVTf6u5icM+cNKkKW2mA==", + "engines": { + "node": ">=8" + } }, - "object-inspect": { + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/oauth2-server": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/oauth2-server/-/oauth2-server-3.1.1.tgz", + "integrity": "sha512-4dv+fE9hrK+xTaCygOLh/kQeFzbFr7UqSyHvBDbrQq8Hg52sAkV2vTsyH3Z42hoeaKpbhM7udhL8Y4GYbl6TGQ==", + "license": "MIT", + "dependencies": { + "basic-auth": "2.0.1", + "bluebird": "3.7.2", + "lodash": "4.17.19", + "promisify-any": "2.0.1", + "statuses": "1.5.0", + "type-is": "1.6.18" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/object-inspect": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", - "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==" + "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "object-is": { + "node_modules/object-is": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.3.tgz", "integrity": "sha512-teyqLvFWzLkq5B9ki8FVWA902UER2qkxmdA4nLf+wjOLAWgxzCWZNCxpDq9MvE8MmhWNr+I8w3BN49Vx36Y6Xg==", - "requires": { + "dependencies": { "define-properties": "^1.1.3", "es-abstract": "^1.18.0-next.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "object-keys": { + "node_modules/object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "engines": { + "node": ">= 0.4" + } }, - "object.assign": { + "node_modules/object.assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.1.tgz", "integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==", - "requires": { + "dependencies": { "define-properties": "^1.1.3", "es-abstract": "^1.18.0-next.0", "has-symbols": "^1.0.1", "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "on-finished": { + "node_modules/on-finished": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "requires": { + "dependencies": { "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" } }, - "on-headers": { + "node_modules/on-headers": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "engines": { + "node": ">= 0.8" + } }, - "parseurl": { + "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } }, - "path-to-regexp": { + "node_modules/path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" }, - "process-nextick-args": { + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, - "proxy-addr": { + "node_modules/promisify-any": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promisify-any/-/promisify-any-2.0.1.tgz", + "integrity": "sha512-pVaGouFbTVxqpVJ+T5A15olNJDASAZHYq5cXz6mWdr6/X34mVWiG9MSdzHTcVBCv4aqBP7wGspi7BUSRbEmhsw==", + "dependencies": { + "bluebird": "^2.10.0", + "co-bluebird": "^1.1.0", + "is-generator": "^1.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/promisify-any/node_modules/bluebird": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.11.0.tgz", + "integrity": "sha512-UfFSr22dmHPQqPP9XWHRhq+gWnHCYguQGkXQlbyPtW5qTnhFWA8/iXg765tH0cAjy7l/zPJ1aBTO0g5XgA7kvQ==", + "license": "MIT" + }, + "node_modules/proxy-addr": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", - "requires": { + "dependencies": { "forwarded": "~0.1.2", "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" } }, - "qs": { + "node_modules/qs": { "version": "6.7.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "engines": { + "node": ">=0.6" + } }, - "random-bytes": { + "node_modules/random-bytes": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", - "integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs=" + "integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs=", + "engines": { + "node": ">= 0.8" + } }, - "range-parser": { + "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } }, - "raw-body": { + "node_modules/raw-body": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", - "requires": { + "dependencies": { "bytes": "3.1.0", "http-errors": "1.7.2", "iconv-lite": "0.4.24", "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" } }, - "readable-stream": { + "node_modules/readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { + "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", @@ -672,49 +1191,69 @@ "util-deprecate": "~1.0.1" } }, - "require_optional": { + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/require_optional": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz", "integrity": "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==", - "requires": { + "dependencies": { "resolve-from": "^2.0.0", "semver": "^5.1.0" } }, - "resolve-from": { + "node_modules/resolve-from": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", - "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=" + "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=", + "engines": { + "node": ">=0.10.0" + } }, - "safe-buffer": { + "node_modules/safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, - "safer-buffer": { + "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, - "saslprep": { + "node_modules/saslprep": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz", "integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==", "optional": true, - "requires": { + "dependencies": { "sparse-bitfield": "^3.0.3" + }, + "engines": { + "node": ">=6" } }, - "semver": { + "node_modules/semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "bin": { + "semver": "bin/semver" + } }, - "send": { + "node_modules/send": { "version": "0.17.1", "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", - "requires": { + "dependencies": { "debug": "2.6.9", "depd": "~1.1.2", "destroy": "~1.0.4", @@ -729,142 +1268,191 @@ "range-parser": "~1.2.1", "statuses": "~1.5.0" }, - "dependencies": { - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" - } + "engines": { + "node": ">= 0.8.0" } }, - "serve-static": { + "node_modules/send/node_modules/ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + }, + "node_modules/serve-static": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", - "requires": { + "dependencies": { "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "parseurl": "~1.3.3", "send": "0.17.1" + }, + "engines": { + "node": ">= 0.8.0" } }, - "setprototypeof": { + "node_modules/setprototypeof": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" }, - "sparse-bitfield": { + "node_modules/sparse-bitfield": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", "integrity": "sha1-/0rm5oZWBWuks+eSqzM004JzyhE=", "optional": true, - "requires": { + "dependencies": { "memory-pager": "^1.0.2" } }, - "statuses": { + "node_modules/statuses": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" - }, - "string.prototype.trimend": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", - "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" - }, - "dependencies": { - "es-abstract": { - "version": "1.17.7", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", - "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.2", - "is-regex": "^1.1.1", - "object-inspect": "^1.8.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.1", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" - } - } + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "engines": { + "node": ">= 0.6" } }, - "string.prototype.trimstart": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", - "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" - }, - "dependencies": { - "es-abstract": { - "version": "1.17.7", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", - "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.2", - "is-regex": "^1.1.1", - "object-inspect": "^1.8.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.1", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" - } - } - } - }, - "string_decoder": { + "node_modules/string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { + "dependencies": { "safe-buffer": "~5.1.0" } }, - "toidentifier": { + "node_modules/string.prototype.trimend": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", + "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend/node_modules/es-abstract": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", + "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", + "dependencies": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", + "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart/node_modules/es-abstract": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", + "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", + "dependencies": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", + "engines": { + "node": ">=0.6" + } }, - "type-is": { + "node_modules/type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "requires": { + "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" } }, - "uid-safe": { + "node_modules/uid-safe": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", - "requires": { + "dependencies": { "random-bytes": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" } }, - "unpipe": { + "node_modules/underscore": { + "version": "1.13.6", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", + "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==" + }, + "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "engines": { + "node": ">= 0.8" + } }, - "util": { + "node_modules/util": { "version": "0.12.3", "resolved": "https://registry.npmjs.org/util/-/util-0.12.3.tgz", "integrity": "sha512-I8XkoQwE+fPQEhy9v012V+TSdH2kp9ts29i20TaaDUXsg7x/onePbhFJUExBfv/2ay1ZOp/Vsm3nDlmnFGSAog==", - "requires": { + "dependencies": { "inherits": "^2.0.3", "is-arguments": "^1.0.4", "is-generator-function": "^1.0.7", @@ -873,26 +1461,32 @@ "which-typed-array": "^1.1.2" } }, - "util-deprecate": { + "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, - "utils-merge": { + "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "engines": { + "node": ">= 0.4.0" + } }, - "vary": { + "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "engines": { + "node": ">= 0.8" + } }, - "which-typed-array": { + "node_modules/which-typed-array": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.2.tgz", "integrity": "sha512-KT6okrd1tE6JdZAy3o2VhMoYPh3+J6EMZLyrxBQsZflI1QCZIxMrIYLkosd8Twf+YfknVIHmYQPgJt238p8dnQ==", - "requires": { + "dependencies": { "available-typed-arrays": "^1.0.2", "es-abstract": "^1.17.5", "foreach": "^2.0.5", @@ -900,25 +1494,35 @@ "has-symbols": "^1.0.1", "is-typed-array": "^1.1.3" }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array/node_modules/es-abstract": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", + "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", "dependencies": { - "es-abstract": { - "version": "1.17.7", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", - "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.2", - "is-regex": "^1.1.1", - "object-inspect": "^1.8.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.1", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" - } - } + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } } } diff --git a/package.json b/package.json index 1c03580..dd68273 100644 --- a/package.json +++ b/package.json @@ -15,12 +15,16 @@ "body-parser": "^1.19.0", "cbor": "^5.1.0", "cbor-body-parser": "^1.0.3", + "chokidar": "^3.6.0", + "crc": "^4.3.2", "events": "^3.2.0", "express": "^4.17.1", "express-session": "^1.17.2", "joi": "^17.4.0", "moment": "^2.29.1", "mongodb": "^3.6.2", - "random-bytes": "^1.0.0" + "random-bytes": "^1.0.0", + "underscore": "^1.13.6", + "oauth2-server": "^3.1.1" } } diff --git a/resources/LR-MB_Ind_D-221F61AA.bin b/resources/LR-MB_Ind_D-221F61AA.bin deleted file mode 100644 index a106065..0000000 Binary files a/resources/LR-MB_Ind_D-221F61AA.bin and /dev/null differ diff --git a/resources/firmwares/lrbstcompact_ind_a/LR-BST_Compact_IndA_Release_6_01_67-60A5CE1A.bin b/resources/firmwares/lrbstcompact_ind_a/LR-BST_Compact_IndA_Release_6_01_67-60A5CE1A.bin new file mode 100644 index 0000000..793a294 Binary files /dev/null and b/resources/firmwares/lrbstcompact_ind_a/LR-BST_Compact_IndA_Release_6_01_67-60A5CE1A.bin differ diff --git a/resources/firmwares/lrmb_ind_d/LR-MB_Ind_D-6_2_0-31EE45DD.bin b/resources/firmwares/lrmb_ind_d/LR-MB_Ind_D-6_2_0-31EE45DD.bin new file mode 100644 index 0000000..ef2b633 Binary files /dev/null and b/resources/firmwares/lrmb_ind_d/LR-MB_Ind_D-6_2_0-31EE45DD.bin differ diff --git a/resources/firmwares/lrmb_ind_d/LRMB_IndD_Release_US_6_01_53-0B8D3945.bin b/resources/firmwares/lrmb_ind_d/LRMB_IndD_Release_US_6_01_53-0B8D3945.bin new file mode 100644 index 0000000..1fca19d Binary files /dev/null and b/resources/firmwares/lrmb_ind_d/LRMB_IndD_Release_US_6_01_53-0B8D3945.bin differ diff --git a/resources/firmwares/lrmb_ind_d/LRMB_IndD_Release_US_6_01_54-BBA54BBF.bin b/resources/firmwares/lrmb_ind_d/LRMB_IndD_Release_US_6_01_54-BBA54BBF.bin new file mode 100644 index 0000000..ed76e86 Binary files /dev/null and b/resources/firmwares/lrmb_ind_d/LRMB_IndD_Release_US_6_01_54-BBA54BBF.bin differ diff --git a/resources/firmwares/smartis_ind_c/SMART-IS - Release-1482F7B3.bin b/resources/firmwares/smartis_ind_c/SMART-IS - Release-1482F7B3.bin new file mode 100644 index 0000000..9b6b1fe Binary files /dev/null and b/resources/firmwares/smartis_ind_c/SMART-IS - Release-1482F7B3.bin differ diff --git a/resources/firmwares/smartis_ind_c/SMART-IS-L476 Ind C-26FA9677.bin b/resources/firmwares/smartis_ind_c/SMART-IS-L476 Ind C-26FA9677.bin new file mode 100644 index 0000000..812687d Binary files /dev/null and b/resources/firmwares/smartis_ind_c/SMART-IS-L476 Ind C-26FA9677.bin differ diff --git a/routes/index.js b/routes/index.js index 65a7a5e..8284417 100644 --- a/routes/index.js +++ b/routes/index.js @@ -16,20 +16,64 @@ router.get('/api/module/:id/:route/:module?', controller.longpolling.getModuleRe router.post('/api/module/:id/:route/:module?', controller.longpolling.postModuleRequest); router.post('/v2/setData/:id', controller.data.postSetData); +router.post('/v2/setSenbusValue/:id', controller.senbus_value.postSenbusValue); router.post('/v2/setJournal/:id', controller.journal.postSetJournal); router.post('/v2/ipx-data/:id', controller.ipx.postIpxData); router.post('/v2/setStatus/:id', controller.status.postSetStatus); -router.post("/v2/get/requests/to/do", controller.grtd.postGetRequestToDo); -router.post("/v2/reportModuleDataSent", controller.grtd.reportModuleDataSent); +router.get("/v2/getAcknowledgeAlerts/:id", controller.grtd.getAcknowledgeAlerts); +router.get("/v2/getManualCommand", controller.grtd.getManualCommand); router.get("/v2/getModuleConfiguration", controller.grtd.getModuleConfiguration); -router.post("/v2/setModuleConfiguration", controller.grtd.setModuleConfiguration); router.get("/v2/getModulePrograms", controller.grtd.getModulePrograms); +router.get("/v2/getModuleSlots", controller.grtd.getModuleSlots); +router.get("/v2/getStatusCommand", controller.grtd.getStatusCommand); +router.post("/v2/get/requests/to/do", controller.grtd.postGetRequestToDo); +router.post("/v2/reportAcknowledgeAlerts", controller.grtd.reportAcknowledgeAlerts); +router.post("/v2/reportAllModuleProgramsDataSent", controller.grtd.reportAllModuleProgramsDataSent); +router.post("/v2/reportAllModuleSlotsDataSent", controller.grtd.reportAllModuleSlotsDataSent); +router.post("/v2/reportManualCommandSent", controller.grtd.reportManualCommandSent); +router.post("/v2/reportModuleDataSent", controller.grtd.reportModuleDataSent); +router.post("/v2/reportSlotsDataSent", controller.grtd.reportSlotsDataSent); +router.post("/v2/reportStatusCommandSent", controller.grtd.reportStatusCommandSent); +router.post("/v2/setModuleConfiguration", controller.grtd.setModuleConfiguration); router.post("/v2/setModulePrograms", controller.grtd.setModulePrograms); +router.post("/v2/setModuleSlots", controller.grtd.setModuleSlots); -router.all('/api/*', (req, res) => { res.send(503); }); +router.get("/api/lsp/programs/:msn", controller.api.getPrograms); +router.post("/api/lsp/programs/:msn", controller.api.postPrograms); +router.put("/api/lsp/programs/:msn", controller.api.putPrograms); +router.delete("/api/lsp/programs/:msn", controller.api.deletePrograms); -router.all('*', (req, res) => { res.send(404); }); +router.get("/api/lsp/configuration/:msn", controller.api.getConfiguration); +router.post("/api/lsp/configuration/:msn", controller.api.postConfiguration); +router.put("/api/lsp/configuration/:msn", controller.api.putConfiguration); +router.delete("/api/lsp/configuration/:msn", controller.api.deleteConfiguration); + +router.get("/api/lsp/slots/:msn", controller.api.getSlots); +router.post("/api/lsp/slots/:msn", controller.api.postSlots); +router.put("/api/lsp/slots/:msn", controller.api.putSlots); +router.delete("/api/lsp/slots/:msn", controller.api.deleteSlots); + +router.get("/api/lsp/manual-command/:msn", controller.api.getManualCommand); +router.post("/api/lsp/manual-command/:msn", controller.api.postManualCommand); +router.put("/api/lsp/manual-command/:msn", controller.api.putManualCommand); +router.delete("/api/lsp/manual-command/:msn", controller.api.deleteManualCommand); + +router.get("/api/lsp/status-command/:msn", controller.api.getStatusCommand); +router.post("/api/lsp/status-command/:msn", controller.api.postStatusCommand); +router.put("/api/lsp/status-command/:msn", controller.api.putStatusCommand); +router.delete("/api/lsp/status-command/:msn", controller.api.deleteStatusCommand); + +router.get("/api/lsp/acknowledged-alerts/:msn", controller.api.getAcknowledgedAlerts); +router.post("/api/lsp/acknowledged-alerts/:msn", controller.api.postAcknowledgedAlerts); +router.put("/api/lsp/acknowledged-alerts/:msn", controller.api.putAcknowledgedAlerts); +router.delete("/api/lsp/acknowledged-alerts/:msn", controller.api.deleteAcknowledgedAlerts); + +router.get("/api/ping", controller.api.ping); + +router.all('/api/*', (req, res) => { res.sendStatus(503); }); + +router.all('*', (req, res) => { res.sendStatus(404); }); module.exports = router; diff --git a/services/device.service.js b/services/device.service.js index 1685230..a17b2ea 100644 --- a/services/device.service.js +++ b/services/device.service.js @@ -1,7 +1,14 @@ const dbcontroller = require('../db'); +const util = require('../util'); -const postRequestToDo = async function (data, msn) { +const postRequestToDo = async function (msn, body) { try { + await dbcontroller.addDevice(body, null) + + body.inventory.forEach(async device => { + await dbcontroller.addDevice(device, body.relayMsn) + }); + return { success: true } } catch (e) { throw new Error(e.message) @@ -10,13 +17,315 @@ const postRequestToDo = async function (data, msn) { const getRequestToDo = async function (msn) { try { - return { success: true } + var todos = [] + const children = await dbcontroller.getDeviceChildren(msn) + + for (const child of children) { + ///// Retrieve child + const device = await dbcontroller.getDevice(child.msn) + + var todo = { msn : child.msn } + + ///// Programs + if (device.programs != undefined) { + if (child.programmingTimestamp < device.programs.timestamp) { todo.programs = 1 } + else if (child.programmingTimestamp > device.programs.timestamp) { todo.programs = 2 } + } + + ///// Configuration + if (device.configuration != undefined) { + if (child.configurationTimestamp < device.configuration.timestamp) { todo.configuration = 1 } + else if (child.configurationTimestamp > device.configuration.timestamp) { todo.configuration = 2 } + } + + ///// Slots + if (device.slots != undefined) { + if (child.programmingTimestamp < device.slots.timestamp) { todo.slots = 1 } + else if (child.programmingTimestamp > device.slots.timestamp) { todo.slots = 2 } + } + + ///// Status + if (device.statusCommand != undefined) { todo.status = 1 } + + ///// Manual + if (device.manualCommand != undefined) { todo.manual = 1 } + + ///// Acknowledged + if (device.acknowledgedAlerts != undefined) { todo.acknowledged = 1 } + + ///// Firmware + if (device.firmware != undefined) { device.firmware = 1 } + + ///// Add objet to todos only if something to do + if (Object.keys(todo).length > 1) { todos.push(todo) } + } + + return todos } catch (e) { throw new Error(e.message) } } +const getStatus = async function (msn) { + try { + ///// Retrieve device + const device = await dbcontroller.getDevice(msn) + if (!device) { throw({message: "module_not_found_for_this_identifier"}) } + + return device.status || {} + } catch (e) { + throw new Error(e.message) + } +} + +const updateStatus = async function (msn, status, timestamp) { + try { + ///// Retrieve device + var device = await dbcontroller.getDevice(msn) + if (!device) { await dbcontroller.addDevice({"msn": msn}); } + + device = await dbcontroller.getDevice(msn) + if (!device) { throw({message: "module_not_found_for_this_identifier"}) } + + ///// Set variables + if (status) { + device.status = { timestamp: timestamp || util.unixTimestamp(), status: status } + } else { + device.status = undefined + } + + ///// Update device + await dbcontroller.updateDevice(device); + + return 200; + } catch (e) { + throw new Error(e.message) + } +} + +const getPrograms = async function (msn) { + try { + ///// Retrieve device + const device = await dbcontroller.getDevice(msn) + if (!device) { throw({message: "module_not_found_for_this_identifier"}) } + + return device.programs || {} + } catch (e) { + throw new Error(e.message) + } +} + +const postPrograms = async function (msn, programs, timestamp) { + try { + ///// Retrieve device + var device = await dbcontroller.getDevice(msn) + if (!device) { throw({message: "module_not_found_for_this_identifier"}) } + + ///// Set variables + if (programs) { + device.programs = { timestamp: timestamp || util.unixTimestamp(), programs: programs } + } else { + device.programmingTimestamp = device.programs.timestamp; + // device.programs = undefined + } + + ///// Update device + await dbcontroller.updateDevice(device) + + return device.programs + } catch (e) { + throw new Error(e.message) + } +} + +const getConfiguration = async function (msn) { + try { + ///// Retrieve device + const device = await dbcontroller.getDevice(msn) + if (!device) { throw({message: "module_not_found_for_this_identifier"}) } + + return device.configuration || {} + } catch (e) { + throw new Error(e.message) + } +} + +const postConfiguration = async function (msn, configuration, timestamp) { + try { + ///// Retrieve device + const device = await dbcontroller.getDevice(msn) + if (!device) { throw({message: "module_not_found_for_this_identifier"}) } + + ///// Set variables + if (configuration) { + device.configuration = { timestamp: timestamp || util.unixTimestamp(), configuration: configuration } + } else { + device.configurationTimestamp = device.configuration.timestamp; + // device.configuration = undefined + } + + ///// Update device + await dbcontroller.updateDevice(device) + + return device.configuration + } catch (e) { + throw new Error(e.message) + } +} + +const getSlots = async function (msn) { + try { + ///// Retrieve device + const device = await dbcontroller.getDevice(msn) + if (!device) { throw({message: "module_not_found_for_this_identifier"}) } + + return device.slots || {} + } catch (e) { + throw new Error(e.message) + } +} + +const postSlots = async function (msn, slots, timestamp) { + try { + ///// Retrieve device + const device = await dbcontroller.getDevice(msn) + if (!device) { throw({message: "module_not_found_for_this_identifier"}) } + + ///// Set variables + if (slots) { + device.slots = { timestamp: timestamp || util.unixTimestamp(), slots: slots } + } else { + device.programmingTimestamp = device.slots.timestamp; + // device.slots = undefined + } + + ///// Update device + await dbcontroller.updateDevice(device, msn) + + return device.slots + } catch (e) { + throw new Error(e.message) + } +} + + +const getManualCommand = async function (msn) { + try { + ///// Retrieve device + const device = await dbcontroller.getDevice(msn) + if (!device) { throw({message: "module_not_found_for_this_identifier"}) } + + return device.manualCommand || {} + } catch (e) { + throw new Error(e.message) + } +} + +const postManualCommand = async function (msn, manualCommand) { + try { + ///// Retrieve device + const device = await dbcontroller.getDevice(msn) + if (!device) { throw({message: "module_not_found_for_this_identifier"}) } + + ///// Set variable + if (manualCommand) { + device.manualCommand = { timestamp: util.unixTimestamp(), manualCommand: manualCommand } + } else { + device.manualCommand = undefined + } + + ///// Update device + await dbcontroller.updateDevice(device, msn) + + return device.manualCommand + } catch (e) { + throw new Error(e.message) + } +} + +const getStatusCommand = async function (msn) { + try { + ///// Retrieve device + const device = await dbcontroller.getDevice(msn) + if (!device) { throw({message: "module_not_found_for_this_identifier"}) } + + return device.statusCommand || {} + } catch (e) { + throw new Error(e.message) + } +} + +const postStatusCommand = async function (msn, statusCommand) { + try { + ///// Retrieve device + const device = await dbcontroller.getDevice(msn) + if (!device) { throw({message: "module_not_found_for_this_identifier"}) } + + ///// Set variable + if (statusCommand) { + device.statusCommand = { timestamp: util.unixTimestamp(), statusCommand: statusCommand } + } else { + device.statusCommand = undefined + } + + ///// Update device + await dbcontroller.updateDevice(device, msn) + + return device.statusCommand + } catch (e) { + throw new Error(e.message) + } +} + +const getAcknowledgedAlerts = async function (msn) { + try { + ///// Retrieve device + const device = await dbcontroller.getDevice(msn) + if (!device) { throw({message: "module_not_found_for_this_identifier"}) } + + return device.acknowledgedAlerts || {} + } catch (e) { + throw new Error(e.message) + } +} + +const postAcknowledgedAlerts = async function (msn, acknowledgedAlerts) { + try { + ///// Retrieve device + const device = await dbcontroller.getDevice(msn) + if (!device) { throw({message: "module_not_found_for_this_identifier"}) } + + ///// Set variable + if (acknowledgedAlerts) { + device.acknowledgedAlerts = { timestamp: util.unixTimestamp(), acknowledgedAlerts: acknowledgedAlerts } + } else { + device.acknowledgedAlerts = undefined + } + ///// Update device + await dbcontroller.updateDevice(device, msn) + + return device.acknowledgedAlerts + } catch (e) { + throw new Error(e.message) + } +} + + module.exports = { + getStatus, + updateStatus, postRequestToDo, getRequestToDo, + getPrograms, + postPrograms, + getConfiguration, + postConfiguration, + getSlots, + postSlots, + getManualCommand, + postManualCommand, + getStatusCommand, + postStatusCommand, + getAcknowledgedAlerts, + postAcknowledgedAlerts, } diff --git a/services/firmware.service.js b/services/firmware.service.js index 8ab4a15..2a33861 100644 --- a/services/firmware.service.js +++ b/services/firmware.service.js @@ -1,20 +1,191 @@ -const lookFirmware = (serialNumber, hash, version, type, hardwareIndex, hardwareVersion) => { +var fs = require('fs').promises; +var path = require('path'); +var crc = require('crc'); +var chokidar = require('chokidar'); + +var firmwares = [] + +const parseVersion = (version) => { try { - return {} + const array = version.split('.') + + if (!array || array.length != 3) { throw ({ message: 'invalid_version' }) } + + return { + major: array[0], + minor: array[1], + build: array[2], + } + } catch (e) { return null; } +} + +const compareVersions = (v1, v2) => { + var normalized1 = (v1.major << 16) | (v1.minor << 8) | (v1.build); + var normalized2 = (v2.major << 16) | (v2.minor << 8) | (v2.build); + + return (normalized1 > normalized2) ? -1 : (normalized1 < normalized2) ? 1 : 0; +} + +const validateHardwareVersion = (list, version) => { + for (element of list) { + if (element.minor == version.minor && element.build == version.build) { return true } + } + + return false +} + +const getBinFiles = async (dir) => { + const dirents = await fs.opendir(dir) + var files = [] + + for await (const dirent of dirents) { + const filePath = path.join(dir, dirent.name) + + if (dirent.isDirectory()) { + files = files.concat(await getBinFiles(filePath)) + } else if (path.extname(dirent.name) === '.bin') { + files.push(filePath) + } + } + + return files +} + +const validateFirmware = async (filePath) => { + const fileBuffer = await fs.readFile(filePath); + const stats = await fs.stat(filePath); + const startPattern = Buffer.from('5C2A5C', 'hex'); + const endPattern = Buffer.from('5C2B5C', 'hex'); + var firmware = {} + var offset = 0; + + ///// Check file is bin file + if (path.extname(filePath) != '.bin') { throw ({ message: 'not_a_bin_file' }) } + + ///// Check firmware size + if (stats.size < 18) { throw ({ message: 'invalid_firmware' }) } + + ///// Find patterns first + var startMatch = fileBuffer.indexOf(startPattern, 0) + var endMatch = fileBuffer.indexOf(endPattern, 0) + if (startMatch < 0 || endMatch < 0) { throw ({ message: 'invalid_firmware' }) } + + ///// Version + offset = 0x0000; + firmware.version = { + major: fileBuffer.readUInt8(offset++), + minor: fileBuffer.readUInt8(offset++), + build: fileBuffer.readUInt8(offset++) + } + + ///// Hardware Type + offset = 0x0003; + firmware.hardware_type = fileBuffer.readUInt8(offset++) + + ///// Module Type + offset = 0x0008; + firmware.moduleType = fileBuffer.toString('ascii', offset, Math.min(fileBuffer.indexOf(0x00, offset), offset + 10)).trim(); + + ///// Hardware compatibility + offset = startMatch + 3 + let compatibility = [] + while (offset < endMatch) { + compatibility.push({ + major: fileBuffer.readUInt8(offset++), + minor: fileBuffer.readUInt8(offset++), + build: fileBuffer.readUInt8(offset++) + }) + } + + firmware.compatibility = compatibility + + ///// Hash + firmware.firmwareHash = crc.crc32(fileBuffer).toString(16) + + ///// Size + firmware.firmwareSize = stats.size + + ///// Path + firmware.path = filePath; + + return firmware +} + +const init = async (path) => { + try { + this.path = path; + + var watcher = chokidar.watch(this.path + '/**/*.bin', { ignored: /^\./, persistent: true }); + + watcher + .on('add', async function (path) { + ///// Try validating firmware + try { + firmwares.push(await validateFirmware(path)) + } catch (e) { } + }) + .on('unlink', async function (path) { + ///// Remove firmware from list + firmwares = firmwares.filter((element) => { return element.path != path }) + }) + .on('change', async function (path) { + ///// Remove firmware from list + firmwares = firmwares.filter((element) => { return element.path != path }) + + ///// Try validating firmware + try { + firmwares.push(await validateFirmware(path)) + } catch (e) { } + }) + .on('error', async function (error) { console.error('Error happened', error); }) + + return null + } catch (exception) { + return exception.message + } +} + +const lookFirmware = (serialNumber, firmwareHash, version, moduleType, hardwareIndex, hardwareVersion) => { + try { + ///// Retrieve best firmware + const firmware = firmwares.filter((element) => { + const parsedHardwareVersion = parseVersion(hardwareVersion); + + if (!hardwareVersion) { return false; } + if (!validateHardwareVersion(element.compatibility, parsedHardwareVersion)) { return false; } + if (moduleType && element.moduleType != moduleType) { return false; } + if (firmwareHash && (firmwareHash != 'init' && element.firmwareHash == firmwareHash)) { return false; } + + return true; + }).sort((f1, f2) => { + return compareVersions(f1.version, f2.version); + })[0] + + return firmware } catch (e) { throw new Error(e.message) } } -const syncFirmware = (serialNumber, hash, version, type, hardwareIndex, hardwareVersion) => { +const syncFirmware = async (serialNumber, firmwareHash, moduleType, position, length) => { try { - return {} + ///// Retrieve firmware + const firmware = firmwares.find((element) => { return element.firmwareHash == firmwareHash; }) + + ///// Check module type + if (moduleType && firmware.moduleType != moduleType) { return false; } + + ///// Read file + const fileBuffer = await fs.readFile(firmware.path); + + return fileBuffer.subarray(parseInt(position), parseInt(position) + parseInt(length)); } catch (e) { - throw new Error(e.message) + throw new Error(e.message); } } module.exports = { + init, lookFirmware, syncFirmware, } diff --git a/services/index.js b/services/index.js index 8fcfdb6..15e0a07 100644 --- a/services/index.js +++ b/services/index.js @@ -1,5 +1,6 @@ const status = require('./status.service') const data = require('./data.service') +const senbus_value = require('./senbus_value.service') const firmware = require('./firmware.service') const ipx = require('./ipx.service') const journal = require('./journal.service') @@ -9,6 +10,7 @@ const device = require('./device.service') module.exports = { status, data, + senbus_value, firmware, ipx, journal, diff --git a/services/senbus_value.service.js b/services/senbus_value.service.js new file mode 100644 index 0000000..236c8cb --- /dev/null +++ b/services/senbus_value.service.js @@ -0,0 +1,9 @@ +const dbcontroller = require('../db'); + +const createSenbusValue = async function (data) { + return { success: true } +} + +module.exports = { + createSenbusValue, +} diff --git a/util/index.js b/util/index.js new file mode 100644 index 0000000..fe303ac --- /dev/null +++ b/util/index.js @@ -0,0 +1,3 @@ +const util = require('./util.js') + +module.exports = util \ No newline at end of file diff --git a/util/util.js b/util/util.js new file mode 100644 index 0000000..eb648b2 --- /dev/null +++ b/util/util.js @@ -0,0 +1,34 @@ + + +const dateToTimestamp = function (date) { + var iteration = 0 + + while (1) { + try { + switch (iteration++) { + ///// ISO String + case 0: return Math.floor(Date.parse(date) / 1000); + + ///// Timestamp string + case 2: + if (typeof(date) != 'string') continue; + date = parseInt(date); break; + + ///// Timestamp integer + case 1: + if (typeof(date) != 'integer') continue; + // if (date > 0xFFFFFFFF) return Math.floor(date / 1000) + else return date; + + default: return undefined + } + } catch (e) { continue } + } +} + +const unixTimestamp = function () { return Math.floor(Date.now() / 1000) } + +module.exports = { + dateToTimestamp, + unixTimestamp, +} \ No newline at end of file