Commit fbea5c907f98e4ce84155312d80a9681db8d873f
1 parent
18c6f924d1
Exists in
master
api near complete
Showing 12 changed files with 315 additions and 13 deletions Inline Diff
- .gitignore
- bin/smstools-config-install
- index.js
- lib/apiserver/index.js
- lib/apiserver/routers/smstools-config.js
- lib/smstools-config/config-file.js
- lib/smstools-config/creator.js
- lib/smstools-config/index.js
- lib/smstools-config/setter.js
- lib/smstools-configurator/index.js
- lib/smstools-status-parsed.js
- package.json
.gitignore
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 |
index.js
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 |
package.json
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 |