const db = require('../db') const state = require('./state') const newSettingsLoader = require('../new-settings-loader') const logger = require('../logger') db.connect({ direct: true }) .then(sco => { sco.client.on('notification', data => { const parsedData = JSON.parse(data.payload) return reload(parsedData.operatorId) }) return sco.none('LISTEN $1:name', 'reload') }) .catch(console.error) db.connect({ direct: true }) .then(sco => { sco.client.on('notification', data => { const parsedData = JSON.parse(data.payload) return machineAction(parsedData.action, parsedData.value) }) return sco.none('LISTEN $1:name', 'machineAction') }) .catch(console.error) function machineAction(type, value) { const deviceId = value.deviceId const operatorId = value.operatorId const pid = state.pids?.[operatorId]?.[deviceId]?.pid switch (type) { case 'reboot': logger.debug( `Rebooting machine '${deviceId}' from operator ${operatorId}`, ) state.reboots[operatorId] = { [deviceId]: pid } break case 'shutdown': logger.debug( `Shutting down machine '${deviceId}' from operator ${operatorId}`, ) state.shutdowns[operatorId] = { [deviceId]: pid } break case 'restartServices': logger.debug( `Restarting services of machine '${deviceId}' from operator ${operatorId}`, ) state.restartServicesMap[operatorId] = { [deviceId]: pid } break case 'emptyUnit': logger.debug( `Emptying units from machine '${deviceId}' from operator ${operatorId}`, ) state.emptyUnit[operatorId] = { [deviceId]: pid } break case 'refillUnit': logger.debug( `Refilling recyclers from machine '${deviceId}' from operator ${operatorId}`, ) state.refillUnit[operatorId] = { [deviceId]: pid } break case 'diagnostics': logger.debug( `Running diagnostics on machine '${deviceId}' from operator ${operatorId}`, ) state.diagnostics[operatorId] = { [deviceId]: pid } break default: break } } function reload(operatorId) { state.needsSettingsReload[operatorId] = true } const populateSettings = function (req, res, next) { const { needsSettingsReload, settingsCache } = state const operatorId = res.locals.operatorId const versionId = req.headers['config-version'] try { // Priority of configs to retrieve // 1. Machine is in the middle of a transaction and has the config-version header set, fetch that config from cache or database, depending on whether it exists in cache // 2. The operator settings changed, so we must update the cache // 3. There's a cached config, send the cached value // 4. There's no cached config, cache and send the latest config if (versionId) { const cachedVersionedSettings = settingsCache.get( `${operatorId}-v${versionId}`, ) if (!cachedVersionedSettings) { logger.debug('Fetching a specific config version cached value') return newSettingsLoader .load(versionId) .then(settings => { settingsCache.set(`${operatorId}-v${versionId}`, settings) req.settings = settings }) .then(() => next()) .catch(next) } logger.debug('Fetching a cached specific config version') req.settings = cachedVersionedSettings return next() } const operatorSettings = settingsCache.get(`${operatorId}-latest`) if (!!needsSettingsReload[operatorId] || !operatorSettings) { needsSettingsReload[operatorId] ? logger.debug( 'Fetching and caching a new latest config value, as a reload was requested', ) : logger.debug( "Fetching the latest config version because there's no cached value", ) return newSettingsLoader .loadLatest() .then(settings => { const versionId = settings.version settingsCache.set(`${operatorId}-latest`, settings) settingsCache.set(`${operatorId}-v${versionId}`, settings) if (needsSettingsReload[operatorId]) delete needsSettingsReload[operatorId] req.settings = settings }) .then(() => next()) .catch(next) } logger.debug('Fetching the latest config value from cache') req.settings = operatorSettings return next() } catch (e) { logger.error(e) } } module.exports = populateSettings