diff --git a/.gitignore b/.gitignore index 9db5f2a..f3e0e75 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ /node_modules/ /config.json /config.smstools.json +/smsd.conf.tmp /tmp/ /logs/ config_*.json diff --git a/bin/smstools-config-install b/bin/smstools-config-install new file mode 100755 index 0000000..afca8bb --- /dev/null +++ b/bin/smstools-config-install @@ -0,0 +1,14 @@ +#!/bin/bash +set -x + +DIRFILE=`dirname "$0"` +cd "$DIRFILE/.." +echo `pwd` + +TARGETFILE=$1 +if [ -z "$TARGETFILE" ]; then + exit 0 +fi + +sudo install smsd.conf.tmp "$TARGETFILE" +sudo systemctl restart sms3 diff --git a/index.js b/index.js index 3cf6065..1c2d182 100644 --- a/index.js +++ b/index.js @@ -9,7 +9,7 @@ const config = require('komodo-sdk/config'); global.KOMODO_LOG_LABEL = `KOMODO-CENTER@${(config && typeof config.name === 'string') ? config.name.toUpperCase() : 'SMSTOOLS'}`; process.title = global.KOMODO_LOG_LABEL; -require('./lib/smstools-configurator'); +require('./lib/smstools-config'); require('./lib/apiserver'); const messagingClient = require('komodo-center-messaging-client-lib'); diff --git a/lib/apiserver/index.js b/lib/apiserver/index.js index 523f597..4ee5fcd 100644 --- a/lib/apiserver/index.js +++ b/lib/apiserver/index.js @@ -5,10 +5,13 @@ const config = require('komodo-sdk/config'); const logger = require('komodo-sdk/logger'); const routerSmstools = require('./routers/smstools'); +const routerSmstoolsConfig = require('./routers/smstools-config'); const app = express(); app.use('/smstools', routerSmstools); +app.use('/smstools-config', routerSmstoolsConfig); + app.get('/cwd', (req, res) => { res.json({ cwd: process.cwd(), diff --git a/lib/apiserver/routers/smstools-config.js b/lib/apiserver/routers/smstools-config.js new file mode 100644 index 0000000..5eee6ed --- /dev/null +++ b/lib/apiserver/routers/smstools-config.js @@ -0,0 +1,120 @@ +const childProcess = require('child_process'); + +const express = require('express'); +const bodyParser = require('body-parser'); + +const config = require('komodo-sdk/config'); +const smstoolsConfig = require('../../smstools-config'); +const smstoolsConfigCreator = require('../../smstools-config/creator'); +const smstoolsConfigSetter = require('../../smstools-config/setter'); +const smstoolsConfigData = require('../../smstools-config/config-file'); + +const router = express.Router(); +module.exports = router; + +function pageIndex(req, res) { + const configString = smstoolsConfigCreator(); + res.json({ + dirty: smstoolsConfig.config.dirty, + config: smstoolsConfig.config, + configStringLength: configString.length, + configStringNrLines: (configString.match(/\n/g) || []).length, + configString, + }); +} + +function pageGenerate(req, res) { + res.end(smstoolsConfigCreator()); +} + +async function pageModemList(req, res) { + const modems = []; + // eslint-disable-next-line no-restricted-syntax + for (const [key, value] of Object.entries(smstoolsConfigData.modems || {})) { + modems.push({ + label: `${key}: ${value.device}, INCOMING${value.outgoing ? ' and OUTGOING' : ''}`, + value: key, + data: value, + }); + } + + res.json(modems); +} + +async function pageSet(req, res) { + const keyword = (req.params.keyword || req.query.keyword || '').trim(); + const value = (req.query.value || '').trim(); + + if (!keyword) { + res.end('Invalid keyword'); + return; + } + + const result = await smstoolsConfigSetter.set(keyword, value); + res.json({ + dirty: !!smstoolsConfigData.dirty, + result, + }); +} + +async function pageModemSet(req, res) { + if (!req.body) { + res.json({ + status: 'NOT OK', + message: 'Empty body', + }); + return; + } + + const modemName = (req.params.modemName || '').trim(); + if (!modemName) { + res.json({ + status: 'NOT OK', + message: 'Invalid modem name', + }); + return; + } + + const result = await smstoolsConfigSetter.setModem(modemName, req.body); + res.json({ + dirty: !!smstoolsConfigData.dirty, + result, + }); +} + +async function pageModemDelete(req, res) { + const modemName = (req.params.modemName || '').trim(); + if (!modemName) { + res.json({ + status: 'NOT OK', + message: 'Invalid modem name', + }); + return; + } + + const result = await smstoolsConfigSetter.delModem(modemName); + res.json({ + dirty: !!smstoolsConfigData.dirty, + result, + }); +} + +function pageInstallConfig(req, res) { + const fileToExec = `${process.cwd()}/bin/smstools-config-install`; + childProcess.execFile(fileToExec, [config.smstools_config_file], (err, stdout, stderr) => { + res.json({ + err, + stdout, + stderr, + }); + }); +} + +router.get('/', pageIndex); +router.get('/generate', pageGenerate); +router.get('/modems', pageModemList); + +router.get('/set/:keyword', pageSet); +router.post('/modem/set/:modemName', bodyParser.json({ type: '*/json' }), pageModemSet); +router.get('/modem/delete/:modemName', pageModemDelete); +router.get('/install-config', pageInstallConfig); diff --git a/lib/smstools-config/config-file.js b/lib/smstools-config/config-file.js new file mode 100644 index 0000000..9c2216e --- /dev/null +++ b/lib/smstools-config/config-file.js @@ -0,0 +1 @@ +module.exports = require('../../config.smstools.json'); diff --git a/lib/smstools-config/creator.js b/lib/smstools-config/creator.js new file mode 100644 index 0000000..12c0256 --- /dev/null +++ b/lib/smstools-config/creator.js @@ -0,0 +1,67 @@ +const path = require('path'); +const moment = require('moment'); +const config = require('komodo-sdk/config'); +const smstoolsConfig = require('./config-file'); + +function modemNames() { + const modems = []; + + // eslint-disable-next-line no-restricted-syntax + for (const [key, value] of Object.entries(smstoolsConfig.modems || {})) { + if (value && !value.disabled) modems.push(key); + } + + return modems; +} + +function modemEntries() { + const modems = modemNames(); + const retval = []; + + modems.forEach((modemName) => { + const modem = smstoolsConfig.modems[modemName]; + + const modemEntry = ` +[${modemName}] +device = ${modem.device} +outgoing = ${modem.outgoing ? 'yes' : 'no'} + `.trim(); + + retval.push(modemEntry); + }); + + return retval; +} + +module.exports = () => { + const newContent = ` +# Generated by komodo-center-smstools based on config timestamp ${moment(smstoolsConfig.ts).format('YYYY-MM-DD HH:mm:ss')} +# Do not edit this file manually + +devices = ${modemNames().join(',')} +logfile = ${config.smstools_logfile || config.smstools_log_file || '/var/log/smsd/smsd.log'} +loglevel = ${smstoolsConfig.loglevel || 5} +smartlogging = yes +autosplit = ${(config.smstools_autosplit !== undefined && config.smstools_autosplit !== null) ? config.smstools_autosplit : 1} +user = ${smstoolsConfig.user || 'smstools'} +group = ${smstoolsConfig.group || 'smstools'} +eventhandler = ${config.smstools_eventhandler || '/var/lib/smstools/centers/smstools/bin/smstools-eventhandler.js'} +stats_interval = ${config.smstools_stats_interval || 60} +stats = ${path.dirname(config.smstools_status_file) || '/var/log/smsd/smsd_stats'} +sent = /var/spool/sms/sent +${smstoolsConfig.customConfig || ''} + +[default] +incoming = yes +regular_run_interval = 60 +regular_run_cmd = AT+CGSN +regular_run_cmd = AT+CIMI +regular_run_cmd = AT+COPS? +regular_run_statfile = /var/spool/sms/regular_run/modemname + +${modemEntries().join('\n\n')} + +# end of configuration file + `; + return `${newContent.trim()}\n`; +}; diff --git a/lib/smstools-config/index.js b/lib/smstools-config/index.js new file mode 100644 index 0000000..3e4b164 --- /dev/null +++ b/lib/smstools-config/index.js @@ -0,0 +1,14 @@ +const fs = require('fs'); +const logger = require('komodo-sdk/logger'); + + +if (!fs.existsSync('config.smstools.json')) { + logger.info('config.smstools.json does not exists, creating it'); + fs.writeFileSync('config.smstools.json', JSON.stringify({ dirty: true }, 0, 4)); +} + +const smstoolsConfigData = require('./config-file'); + +exports.config = smstoolsConfigData; + +logger.info('Current SMSTOOLS config', { smstoolsConfig: smstoolsConfigData }); diff --git a/lib/smstools-config/setter.js b/lib/smstools-config/setter.js new file mode 100644 index 0000000..ac677fe --- /dev/null +++ b/lib/smstools-config/setter.js @@ -0,0 +1,92 @@ +const fs = require('fs'); +const logger = require('komodo-sdk/logger'); +const smstoolsConfigData = require('./config-file'); +const creator = require('./creator'); + +async function writeConfig() { + const data = smstoolsConfigData; + data.ts = new Date(); + const configFilename = 'config.smstools.json'; + try { + await fs.promises.writeFile(configFilename, JSON.stringify(data || {}, 0, 4)); + } catch (e) { + logger.warn('SMSTOOLS-CONFIG/setter: Exception on writeConfig', { filename: configFilename, e }); + return e; + } + + const newSmsdConfFilename = 'smsd.conf.tmp'; + const newSmsdConf = await creator(); + try { + await fs.promises.writeFile(newSmsdConfFilename, newSmsdConf); + } catch (e) { + logger.warn('SMSTOOLS-CONFIG/setter: Exception on writeConfig', { filename: newSmsdConfFilename, e }); + return e; + } + + return null; +} + +exports.set = async (keyword, value) => { + if (!keyword) return smstoolsConfigData; + smstoolsConfigData[keyword] = value; + smstoolsConfigData.dirty = true; + await writeConfig(); + return smstoolsConfigData; +}; + +function getModemByDevice(device) { + let modemName = null; + if (!device) return modemName; + + // eslint-disable-next-line no-restricted-syntax + for (const [key, value] of Object.entries(smstoolsConfigData.modems || {})) { + if (value.device === device) { + modemName = key; + } + } + + return modemName; +} + + +exports.setModem = async (modemName, data) => { + if (!modemName) return smstoolsConfigData; + + if (data.device) { + const modemWithSameDevice = getModemByDevice(data.device); + if (modemWithSameDevice && modemWithSameDevice !== modemName) { + return smstoolsConfigData; + } + } + + if (!smstoolsConfigData.modems) { + smstoolsConfigData.modems = {}; + } + + if (!smstoolsConfigData.modems[modemName]) { + smstoolsConfigData.modems[modemName] = {}; + } + + smstoolsConfigData.modems[modemName] = data; + smstoolsConfigData.dirty = true; + await writeConfig(); + return smstoolsConfigData; +}; + + +exports.delModem = async (modemName) => { + if (!modemName) return smstoolsConfigData; + + if (!smstoolsConfigData.modems) { + return smstoolsConfigData; + } + + if (!smstoolsConfigData.modems[modemName]) { + return smstoolsConfigData; + } + + delete smstoolsConfigData.modems[modemName]; + smstoolsConfigData.dirty = true; + await writeConfig(); + return smstoolsConfigData; +}; diff --git a/lib/smstools-configurator/index.js b/lib/smstools-configurator/index.js deleted file mode 100644 index 542812e..0000000 --- a/lib/smstools-configurator/index.js +++ /dev/null @@ -1,11 +0,0 @@ -const fs = require('fs'); -const logger = require('komodo-sdk/logger'); - -if (!fs.existsSync('config.smstools.json')) { - logger.info('config.smstools.json does not exists, creating it'); - fs.writeFileSync('config.smstools.json', JSON.stringify({}, 0, 4)); -} - -const smstoolsConfig = require('../../config.smstools.json'); - -logger.info('Our SMSTOOLS config read', { smstoolsConfig }); diff --git a/lib/smstools-status-parsed.js b/lib/smstools-status-parsed.js index 195b88d..7d6b8b4 100644 --- a/lib/smstools-status-parsed.js +++ b/lib/smstools-status-parsed.js @@ -7,7 +7,7 @@ const smstoolsStatus = require('./smstools-status'); const modemInfo = require('./smstools-modem-info'); module.exports = async (statsFilename) => { - const smstoolsLogfile = config.smstools_log_file || '/var/log/smsd/smsd.log'; + const smstoolsLogfile = config.smstools_logfile || config.smstools_log_file || '/var/log/smsd/smsd.log'; logger.verbose('ROUTER-SMSTOOLS-STATUS-PARSED: Executing', { smstoolsLogfile }); const contents = await smstoolsStatus(statsFilename); const contentsSplitted = (contents || '').trim().split('\n'); diff --git a/package.json b/package.json index ae5bade..4ad8188 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "should": "^13.2.3" }, "dependencies": { + "body-parser": "^1.19.0", "express": "^4.17.1", "get-stdin": "^7.0.0", "komodo-center-messaging-client-lib": "git+http://gitlab.kodesumber.com/komodo/komodo-center-messaging-client-lib.git",