Commit fbea5c907f98e4ce84155312d80a9681db8d873f

Authored by Adhidarma Hadiwinoto
1 parent 18c6f924d1
Exists in master

api near complete

Showing 12 changed files with 315 additions and 13 deletions Inline Diff

1 /node_modules/ 1 /node_modules/
2 /config.json 2 /config.json
3 /config.smstools.json 3 /config.smstools.json
4 /smsd.conf.tmp
4 /tmp/ 5 /tmp/
5 /logs/ 6 /logs/
6 config_*.json 7 config_*.json
7 pid.txt 8 pid.txt
8 9
bin/smstools-config-install
File was created 1 #!/bin/bash
2 set -x
3
4 DIRFILE=`dirname "$0"`
5 cd "$DIRFILE/.."
6 echo `pwd`
7
8 TARGETFILE=$1
9 if [ -z "$TARGETFILE" ]; then
10 exit 0
11 fi
12
13 sudo install smsd.conf.tmp "$TARGETFILE"
14 sudo systemctl restart sms3
15
1 process.chdir(__dirname); 1 process.chdir(__dirname);
2 2
3 const fs = require('fs'); 3 const fs = require('fs');
4 4
5 fs.writeFileSync('pid.txt', process.pid); 5 fs.writeFileSync('pid.txt', process.pid);
6 6
7 const config = require('komodo-sdk/config'); 7 const config = require('komodo-sdk/config');
8 8
9 global.KOMODO_LOG_LABEL = `KOMODO-CENTER@${(config && typeof config.name === 'string') ? config.name.toUpperCase() : 'SMSTOOLS'}`; 9 global.KOMODO_LOG_LABEL = `KOMODO-CENTER@${(config && typeof config.name === 'string') ? config.name.toUpperCase() : 'SMSTOOLS'}`;
10 process.title = global.KOMODO_LOG_LABEL; 10 process.title = global.KOMODO_LOG_LABEL;
11 11
12 require('./lib/smstools-configurator'); 12 require('./lib/smstools-config');
13 require('./lib/apiserver'); 13 require('./lib/apiserver');
14 14
15 const messagingClient = require('komodo-center-messaging-client-lib'); 15 const messagingClient = require('komodo-center-messaging-client-lib');
16 const transport = require('./lib/transport'); 16 const transport = require('./lib/transport');
17 17
18 messagingClient.setTransport(transport); 18 messagingClient.setTransport(transport);
19 19
lib/apiserver/index.js
1 const process = require('process'); 1 const process = require('process');
2 const express = require('express'); 2 const express = require('express');
3 3
4 const config = require('komodo-sdk/config'); 4 const config = require('komodo-sdk/config');
5 const logger = require('komodo-sdk/logger'); 5 const logger = require('komodo-sdk/logger');
6 6
7 const routerSmstools = require('./routers/smstools'); 7 const routerSmstools = require('./routers/smstools');
8 const routerSmstoolsConfig = require('./routers/smstools-config');
8 9
9 const app = express(); 10 const app = express();
10 11
11 app.use('/smstools', routerSmstools); 12 app.use('/smstools', routerSmstools);
13 app.use('/smstools-config', routerSmstoolsConfig);
14
12 app.get('/cwd', (req, res) => { 15 app.get('/cwd', (req, res) => {
13 res.json({ 16 res.json({
14 cwd: process.cwd(), 17 cwd: process.cwd(),
15 }); 18 });
16 }); 19 });
17 20
18 const listenPort = (config.apiserver && config.apiserver.listen_port) || 17243; 21 const listenPort = (config.apiserver && config.apiserver.listen_port) || 17243;
19 app.listen(listenPort, () => { 22 app.listen(listenPort, () => {
20 logger.info(`APISERVER listen on port ${listenPort}`); 23 logger.info(`APISERVER listen on port ${listenPort}`);
21 }); 24 });
22 25
lib/apiserver/routers/smstools-config.js
File was created 1 const childProcess = require('child_process');
2
3 const express = require('express');
4 const bodyParser = require('body-parser');
5
6 const config = require('komodo-sdk/config');
7 const smstoolsConfig = require('../../smstools-config');
8 const smstoolsConfigCreator = require('../../smstools-config/creator');
9 const smstoolsConfigSetter = require('../../smstools-config/setter');
10 const smstoolsConfigData = require('../../smstools-config/config-file');
11
12 const router = express.Router();
13 module.exports = router;
14
15 function pageIndex(req, res) {
16 const configString = smstoolsConfigCreator();
17 res.json({
18 dirty: smstoolsConfig.config.dirty,
19 config: smstoolsConfig.config,
20 configStringLength: configString.length,
21 configStringNrLines: (configString.match(/\n/g) || []).length,
22 configString,
23 });
24 }
25
26 function pageGenerate(req, res) {
27 res.end(smstoolsConfigCreator());
28 }
29
30 async function pageModemList(req, res) {
31 const modems = [];
32 // eslint-disable-next-line no-restricted-syntax
33 for (const [key, value] of Object.entries(smstoolsConfigData.modems || {})) {
34 modems.push({
35 label: `${key}: ${value.device}, INCOMING${value.outgoing ? ' and OUTGOING' : ''}`,
36 value: key,
37 data: value,
38 });
39 }
40
41 res.json(modems);
42 }
43
44 async function pageSet(req, res) {
45 const keyword = (req.params.keyword || req.query.keyword || '').trim();
46 const value = (req.query.value || '').trim();
47
48 if (!keyword) {
49 res.end('Invalid keyword');
50 return;
51 }
52
53 const result = await smstoolsConfigSetter.set(keyword, value);
54 res.json({
55 dirty: !!smstoolsConfigData.dirty,
56 result,
57 });
58 }
59
60 async function pageModemSet(req, res) {
61 if (!req.body) {
62 res.json({
63 status: 'NOT OK',
64 message: 'Empty body',
65 });
66 return;
67 }
68
69 const modemName = (req.params.modemName || '').trim();
70 if (!modemName) {
71 res.json({
72 status: 'NOT OK',
73 message: 'Invalid modem name',
74 });
75 return;
76 }
77
78 const result = await smstoolsConfigSetter.setModem(modemName, req.body);
79 res.json({
80 dirty: !!smstoolsConfigData.dirty,
81 result,
82 });
83 }
84
85 async function pageModemDelete(req, res) {
86 const modemName = (req.params.modemName || '').trim();
87 if (!modemName) {
88 res.json({
89 status: 'NOT OK',
90 message: 'Invalid modem name',
91 });
92 return;
93 }
94
95 const result = await smstoolsConfigSetter.delModem(modemName);
96 res.json({
97 dirty: !!smstoolsConfigData.dirty,
98 result,
99 });
100 }
101
102 function pageInstallConfig(req, res) {
103 const fileToExec = `${process.cwd()}/bin/smstools-config-install`;
104 childProcess.execFile(fileToExec, [config.smstools_config_file], (err, stdout, stderr) => {
105 res.json({
106 err,
107 stdout,
108 stderr,
109 });
110 });
111 }
112
113 router.get('/', pageIndex);
114 router.get('/generate', pageGenerate);
115 router.get('/modems', pageModemList);
116
117 router.get('/set/:keyword', pageSet);
118 router.post('/modem/set/:modemName', bodyParser.json({ type: '*/json' }), pageModemSet);
119 router.get('/modem/delete/:modemName', pageModemDelete);
120 router.get('/install-config', pageInstallConfig);
121
lib/smstools-config/config-file.js
File was created 1 module.exports = require('../../config.smstools.json');
2
lib/smstools-config/creator.js
File was created 1 const path = require('path');
2 const moment = require('moment');
3 const config = require('komodo-sdk/config');
4 const smstoolsConfig = require('./config-file');
5
6 function modemNames() {
7 const modems = [];
8
9 // eslint-disable-next-line no-restricted-syntax
10 for (const [key, value] of Object.entries(smstoolsConfig.modems || {})) {
11 if (value && !value.disabled) modems.push(key);
12 }
13
14 return modems;
15 }
16
17 function modemEntries() {
18 const modems = modemNames();
19 const retval = [];
20
21 modems.forEach((modemName) => {
22 const modem = smstoolsConfig.modems[modemName];
23
24 const modemEntry = `
25 [${modemName}]
26 device = ${modem.device}
27 outgoing = ${modem.outgoing ? 'yes' : 'no'}
28 `.trim();
29
30 retval.push(modemEntry);
31 });
32
33 return retval;
34 }
35
36 module.exports = () => {
37 const newContent = `
38 # Generated by komodo-center-smstools based on config timestamp ${moment(smstoolsConfig.ts).format('YYYY-MM-DD HH:mm:ss')}
39 # Do not edit this file manually
40
41 devices = ${modemNames().join(',')}
42 logfile = ${config.smstools_logfile || config.smstools_log_file || '/var/log/smsd/smsd.log'}
43 loglevel = ${smstoolsConfig.loglevel || 5}
44 smartlogging = yes
45 autosplit = ${(config.smstools_autosplit !== undefined && config.smstools_autosplit !== null) ? config.smstools_autosplit : 1}
46 user = ${smstoolsConfig.user || 'smstools'}
47 group = ${smstoolsConfig.group || 'smstools'}
48 eventhandler = ${config.smstools_eventhandler || '/var/lib/smstools/centers/smstools/bin/smstools-eventhandler.js'}
49 stats_interval = ${config.smstools_stats_interval || 60}
50 stats = ${path.dirname(config.smstools_status_file) || '/var/log/smsd/smsd_stats'}
51 sent = /var/spool/sms/sent
52 ${smstoolsConfig.customConfig || ''}
53
54 [default]
55 incoming = yes
56 regular_run_interval = 60
57 regular_run_cmd = AT+CGSN
58 regular_run_cmd = AT+CIMI
59 regular_run_cmd = AT+COPS?
60 regular_run_statfile = /var/spool/sms/regular_run/modemname
61
62 ${modemEntries().join('\n\n')}
63
64 # end of configuration file
65 `;
66 return `${newContent.trim()}\n`;
67 };
68
lib/smstools-config/index.js
File was created 1 const fs = require('fs');
2 const logger = require('komodo-sdk/logger');
3
4
5 if (!fs.existsSync('config.smstools.json')) {
6 logger.info('config.smstools.json does not exists, creating it');
7 fs.writeFileSync('config.smstools.json', JSON.stringify({ dirty: true }, 0, 4));
8 }
9
10 const smstoolsConfigData = require('./config-file');
11
12 exports.config = smstoolsConfigData;
13
14 logger.info('Current SMSTOOLS config', { smstoolsConfig: smstoolsConfigData });
15
lib/smstools-config/setter.js
File was created 1 const fs = require('fs');
2 const logger = require('komodo-sdk/logger');
3 const smstoolsConfigData = require('./config-file');
4 const creator = require('./creator');
5
6 async function writeConfig() {
7 const data = smstoolsConfigData;
8 data.ts = new Date();
9 const configFilename = 'config.smstools.json';
10 try {
11 await fs.promises.writeFile(configFilename, JSON.stringify(data || {}, 0, 4));
12 } catch (e) {
13 logger.warn('SMSTOOLS-CONFIG/setter: Exception on writeConfig', { filename: configFilename, e });
14 return e;
15 }
16
17 const newSmsdConfFilename = 'smsd.conf.tmp';
18 const newSmsdConf = await creator();
19 try {
20 await fs.promises.writeFile(newSmsdConfFilename, newSmsdConf);
21 } catch (e) {
22 logger.warn('SMSTOOLS-CONFIG/setter: Exception on writeConfig', { filename: newSmsdConfFilename, e });
23 return e;
24 }
25
26 return null;
27 }
28
29 exports.set = async (keyword, value) => {
30 if (!keyword) return smstoolsConfigData;
31 smstoolsConfigData[keyword] = value;
32 smstoolsConfigData.dirty = true;
33 await writeConfig();
34 return smstoolsConfigData;
35 };
36
37 function getModemByDevice(device) {
38 let modemName = null;
39 if (!device) return modemName;
40
41 // eslint-disable-next-line no-restricted-syntax
42 for (const [key, value] of Object.entries(smstoolsConfigData.modems || {})) {
43 if (value.device === device) {
44 modemName = key;
45 }
46 }
47
48 return modemName;
49 }
50
51
52 exports.setModem = async (modemName, data) => {
53 if (!modemName) return smstoolsConfigData;
54
55 if (data.device) {
56 const modemWithSameDevice = getModemByDevice(data.device);
57 if (modemWithSameDevice && modemWithSameDevice !== modemName) {
58 return smstoolsConfigData;
59 }
60 }
61
62 if (!smstoolsConfigData.modems) {
63 smstoolsConfigData.modems = {};
64 }
65
66 if (!smstoolsConfigData.modems[modemName]) {
67 smstoolsConfigData.modems[modemName] = {};
68 }
69
70 smstoolsConfigData.modems[modemName] = data;
71 smstoolsConfigData.dirty = true;
72 await writeConfig();
73 return smstoolsConfigData;
74 };
75
76
77 exports.delModem = async (modemName) => {
78 if (!modemName) return smstoolsConfigData;
79
80 if (!smstoolsConfigData.modems) {
81 return smstoolsConfigData;
82 }
83
84 if (!smstoolsConfigData.modems[modemName]) {
85 return smstoolsConfigData;
86 }
87
88 delete smstoolsConfigData.modems[modemName];
89 smstoolsConfigData.dirty = true;
90 await writeConfig();
91 return smstoolsConfigData;
92 };
93
lib/smstools-configurator/index.js
1 const fs = require('fs'); File was deleted
2 const logger = require('komodo-sdk/logger');
3
4 if (!fs.existsSync('config.smstools.json')) {
5 logger.info('config.smstools.json does not exists, creating it');
6 fs.writeFileSync('config.smstools.json', JSON.stringify({}, 0, 4));
7 }
8
9 const smstoolsConfig = require('../../config.smstools.json');
10
11 logger.info('Our SMSTOOLS config read', { smstoolsConfig });
12 1 const fs = require('fs');
lib/smstools-status-parsed.js
1 /* eslint-disable guard-for-in */ 1 /* eslint-disable guard-for-in */
2 const moment = require('moment'); 2 const moment = require('moment');
3 const config = require('komodo-sdk/config'); 3 const config = require('komodo-sdk/config');
4 const logger = require('komodo-sdk/logger'); 4 const logger = require('komodo-sdk/logger');
5 5
6 const smstoolsStatus = require('./smstools-status'); 6 const smstoolsStatus = require('./smstools-status');
7 const modemInfo = require('./smstools-modem-info'); 7 const modemInfo = require('./smstools-modem-info');
8 8
9 module.exports = async (statsFilename) => { 9 module.exports = async (statsFilename) => {
10 const smstoolsLogfile = config.smstools_log_file || '/var/log/smsd/smsd.log'; 10 const smstoolsLogfile = config.smstools_logfile || config.smstools_log_file || '/var/log/smsd/smsd.log';
11 logger.verbose('ROUTER-SMSTOOLS-STATUS-PARSED: Executing', { smstoolsLogfile }); 11 logger.verbose('ROUTER-SMSTOOLS-STATUS-PARSED: Executing', { smstoolsLogfile });
12 const contents = await smstoolsStatus(statsFilename); 12 const contents = await smstoolsStatus(statsFilename);
13 const contentsSplitted = (contents || '').trim().split('\n'); 13 const contentsSplitted = (contents || '').trim().split('\n');
14 14
15 const lines = []; 15 const lines = [];
16 // eslint-disable-next-line no-restricted-syntax 16 // eslint-disable-next-line no-restricted-syntax
17 for (const i in contentsSplitted) { 17 for (const i in contentsSplitted) {
18 const line = contentsSplitted[i]; 18 const line = contentsSplitted[i];
19 const cols = line.split(/[:,]\t+/); 19 const cols = line.split(/[:,]\t+/);
20 const [keyword, tsRaw, status, succedeed, failed, received, signal] = cols; 20 const [keyword, tsRaw, status, succedeed, failed, received, signal] = cols;
21 21
22 const ts = (tsRaw && moment(tsRaw, 'YY-MM-DD HH:mm:ss')) || null; 22 const ts = (tsRaw && moment(tsRaw, 'YY-MM-DD HH:mm:ss')) || null;
23 23
24 if (keyword === 'Status') { 24 if (keyword === 'Status') {
25 lines.push({ 25 lines.push({
26 keyword, 26 keyword,
27 line, 27 line,
28 }); 28 });
29 } else { 29 } else {
30 const regularRunResultFile = config.smstools_regular_run_result_file || '/var/spool/sms/regular_run/<MODEMNAME>'; 30 const regularRunResultFile = config.smstools_regular_run_result_file || '/var/spool/sms/regular_run/<MODEMNAME>';
31 31
32 // eslint-disable-next-line no-await-in-loop 32 // eslint-disable-next-line no-await-in-loop
33 const { imsi, imei, cops } = (await modemInfo.get(keyword, regularRunResultFile)) || {}; 33 const { imsi, imei, cops } = (await modemInfo.get(keyword, regularRunResultFile)) || {};
34 34
35 lines.push({ 35 lines.push({
36 keyword, 36 keyword,
37 line, 37 line,
38 ts, 38 ts,
39 tsHuman: moment(ts).format('YYYY-MM-DD HH:mm:ss'), 39 tsHuman: moment(ts).format('YYYY-MM-DD HH:mm:ss'),
40 tsRaw, 40 tsRaw,
41 status, 41 status,
42 succedeed, 42 succedeed,
43 failed, 43 failed,
44 received, 44 received,
45 signal, 45 signal,
46 imsi, 46 imsi,
47 imei, 47 imei,
48 cops, 48 cops,
49 }); 49 });
50 } 50 }
51 } 51 }
52 52
53 return lines; 53 return lines;
54 }; 54 };
55 55
1 { 1 {
2 "name": "komodo-center-smstools", 2 "name": "komodo-center-smstools",
3 "version": "0.9.1", 3 "version": "0.9.1",
4 "description": "Komodo center for SMSTOOLS (smsd)", 4 "description": "Komodo center for SMSTOOLS (smsd)",
5 "main": "index.js", 5 "main": "index.js",
6 "scripts": { 6 "scripts": {
7 "test": "mocha", 7 "test": "mocha",
8 "postversion": "git push && git push --tags" 8 "postversion": "git push && git push --tags"
9 }, 9 },
10 "repository": { 10 "repository": {
11 "type": "git", 11 "type": "git",
12 "url": "git@gitlab.kodesumber.com:komodo/komodo-center-smstools.git" 12 "url": "git@gitlab.kodesumber.com:komodo/komodo-center-smstools.git"
13 }, 13 },
14 "keywords": [ 14 "keywords": [
15 "komodo", 15 "komodo",
16 "ppob", 16 "ppob",
17 "tektrans", 17 "tektrans",
18 "sms", 18 "sms",
19 "smstools", 19 "smstools",
20 "smsd" 20 "smsd"
21 ], 21 ],
22 "author": "Adhidarma Hadiwinoto <me@adhisimon.org>", 22 "author": "Adhidarma Hadiwinoto <me@adhisimon.org>",
23 "license": "ISC", 23 "license": "ISC",
24 "devDependencies": { 24 "devDependencies": {
25 "eslint": "^6.7.1", 25 "eslint": "^6.7.1",
26 "eslint-config-airbnb-base": "^14.0.0", 26 "eslint-config-airbnb-base": "^14.0.0",
27 "eslint-plugin-import": "^2.18.2", 27 "eslint-plugin-import": "^2.18.2",
28 "mocha": "^6.2.2", 28 "mocha": "^6.2.2",
29 "should": "^13.2.3" 29 "should": "^13.2.3"
30 }, 30 },
31 "dependencies": { 31 "dependencies": {
32 "body-parser": "^1.19.0",
32 "express": "^4.17.1", 33 "express": "^4.17.1",
33 "get-stdin": "^7.0.0", 34 "get-stdin": "^7.0.0",
34 "komodo-center-messaging-client-lib": "git+http://gitlab.kodesumber.com/komodo/komodo-center-messaging-client-lib.git", 35 "komodo-center-messaging-client-lib": "git+http://gitlab.kodesumber.com/komodo/komodo-center-messaging-client-lib.git",
35 "komodo-sdk": "git+http://gitlab.kodesumber.com/komodo/komodo-sdk.git", 36 "komodo-sdk": "git+http://gitlab.kodesumber.com/komodo/komodo-sdk.git",
36 "mailparser": "^2.7.6", 37 "mailparser": "^2.7.6",
37 "moment": "^2.24.0", 38 "moment": "^2.24.0",
38 "request": "^2.88.0", 39 "request": "^2.88.0",
39 "uniqid": "^5.2.0", 40 "uniqid": "^5.2.0",
40 "winston": "^3.2.1", 41 "winston": "^3.2.1",
41 "yargs": "^15.0.2" 42 "yargs": "^15.0.2"
42 } 43 }
43 } 44 }
44 45