Commit 6cd19a9c944a38c94a13aba9136c64eb7107379d
1 parent
3f614b14ac
Exists in
master
Typo
Showing 2 changed files with 4 additions and 1 deletions Inline Diff
lib/apiserver/index.js
1 | "use strict"; | 1 | "use strict"; |
2 | 2 | ||
3 | /** | 3 | /** |
4 | * Modul untuk menerima callback dari modem handler jika ada SMS masuk. | 4 | * Modul untuk menerima callback dari modem handler jika ada SMS masuk. |
5 | */ | 5 | */ |
6 | 6 | ||
7 | 7 | ||
8 | const express = require('express'); | 8 | const express = require('express'); |
9 | const moment = require('moment'); | 9 | const moment = require('moment'); |
10 | 10 | ||
11 | const messagingService = require('komodo-center-messaging-client-lib'); | 11 | const messagingService = require('komodo-center-messaging-client-lib'); |
12 | 12 | ||
13 | const config = require('komodo-sdk/config'); | 13 | const config = require('komodo-sdk/config'); |
14 | const logger = require('komodo-sdk/logger'); | 14 | const logger = require('komodo-sdk/logger'); |
15 | 15 | ||
16 | const transport = require('../transport'); | 16 | const transport = require('../transport'); |
17 | const partnerLastSeen = require('../partner-last-seen'); | 17 | const partnerLastSeen = require('../partner-last-seen'); |
18 | const history = require('../history'); | 18 | const history = require('../history'); |
19 | // const modems = require('../modems2'); | 19 | // const modems = require('../modems2'); |
20 | 20 | ||
21 | const routerModems = require('./router-modems'); | 21 | const routerModems = require('./router-modems'); |
22 | 22 | ||
23 | if (config.handler_callback_server) { | 23 | if (config.handler_callback_server) { |
24 | logger.warn('Deprecated config.handler_callback_server. Please migrate it to config.apiserver!'); | 24 | logger.warn('Deprecated config.handler_callback_server. Please migrate it to config.apiserver!'); |
25 | } | 25 | } |
26 | 26 | ||
27 | const app = express(); | 27 | const app = express(); |
28 | messagingService.setTransport(transport); | 28 | messagingService.setTransport(transport); |
29 | 29 | ||
30 | function apikeyChecker(req, res, next) { | 30 | function apikeyChecker(req, res, next) { |
31 | res.locals.has_valid_apikey = req.params.apikey === (config.apiserver.apikey || config.handler_callback_server.apikey); | 31 | res.locals.has_valid_apikey = req.params.apikey === ((config.apiserver && config.apiserver.apikey ? config.apiserver.apikey : null) || config.handler_callback_server.apikey); |
32 | if (res.locals.has_valid_apikey) { | 32 | if (res.locals.has_valid_apikey) { |
33 | next(); | 33 | next(); |
34 | } | 34 | } |
35 | else { | 35 | else { |
36 | res.end('APISERVER: Invalid apikey'); | 36 | res.end('APISERVER: Invalid apikey'); |
37 | } | 37 | } |
38 | } | 38 | } |
39 | 39 | ||
40 | function onIncomingSms(req, res) { | 40 | function onIncomingSms(req, res) { |
41 | res.end('OK'); | 41 | res.end('OK'); |
42 | 42 | ||
43 | if (!req.query.number) return; | 43 | if (!req.query.number) return; |
44 | const numberWithSuffix = req.query.number.replace(/^\+/, '') + (config.number_suffix || ''); | 44 | const numberWithSuffix = req.query.number.replace(/^\+/, '') + (config.number_suffix || ''); |
45 | 45 | ||
46 | partnerLastSeen.set(req.query.number, req.query.modem_imsi); | 46 | partnerLastSeen.set(req.query.number, req.query.modem_imsi); |
47 | 47 | ||
48 | history.push({ | 48 | history.push({ |
49 | ts: req.query.ts || moment().format('YYYY-MM-DD HH:mm:ss'), | 49 | ts: req.query.ts || moment().format('YYYY-MM-DD HH:mm:ss'), |
50 | modem: { | 50 | modem: { |
51 | name: req.query.modem, | 51 | name: req.query.modem, |
52 | imsi: req.query.modem_imsi, | 52 | imsi: req.query.modem_imsi, |
53 | msisdn: req.query.modem_msisdn, | 53 | msisdn: req.query.modem_msisdn, |
54 | }, | 54 | }, |
55 | direction: 'INCOMING', | 55 | direction: 'INCOMING', |
56 | partner: req.query.number, | 56 | partner: req.query.number, |
57 | message: req.query.msg, | 57 | message: req.query.msg, |
58 | }); | 58 | }); |
59 | 59 | ||
60 | /* | 60 | /* |
61 | modems.set({ | 61 | modems.set({ |
62 | name: req.query.modem, | 62 | name: req.query.modem, |
63 | device: req.query.modem_device, | 63 | device: req.query.modem_device, |
64 | imsi: req.query.modem_imsi, | 64 | imsi: req.query.modem_imsi, |
65 | msisdn: req.query.modem_msisdn, | 65 | msisdn: req.query.modem_msisdn, |
66 | networkId: req.query.modem.network_id, | 66 | networkId: req.query.modem.network_id, |
67 | networkName: req.query.modem_network_name, | 67 | networkName: req.query.modem_network_name, |
68 | signalStrength: req.query.modem_signal_strength, | 68 | signalStrength: req.query.modem_signal_strength, |
69 | uptime: req.query.uptime, | 69 | uptime: req.query.uptime, |
70 | reportIp: req.query.report_ip || req.ip, | 70 | reportIp: req.query.report_ip || req.ip, |
71 | reportPort: req.query.report_port, | 71 | reportPort: req.query.report_port, |
72 | reportApikey: req.query.report_apikey, | 72 | reportApikey: req.query.report_apikey, |
73 | reportPathSms: req.query.report_path_sms || '/sms', | 73 | reportPathSms: req.query.report_path_sms || '/sms', |
74 | }); | 74 | }); |
75 | */ | 75 | */ |
76 | 76 | ||
77 | logger.info('APISERVER: Incoming SMS', { modem: req.query.modem, from: req.query.number, from_with_suffix: numberWithSuffix, msg: req.query.msg }); | 77 | logger.info('APISERVER: Incoming SMS', { modem: req.query.modem, from: req.query.number, from_with_suffix: numberWithSuffix, msg: req.query.msg }); |
78 | messagingService.onIncomingMessage({ | 78 | messagingService.onIncomingMessage({ |
79 | me: req.query.modem, | 79 | me: req.query.modem, |
80 | partner: numberWithSuffix, | 80 | partner: numberWithSuffix, |
81 | partner_raw: req.query.number, | 81 | partner_raw: req.query.number, |
82 | msg: req.query.msg, | 82 | msg: req.query.msg, |
83 | origin_label: `IMSI_${req.query.modem_imsi || 'UNKNOWN'}`, | 83 | origin_label: `IMSI_${req.query.modem_imsi || 'UNKNOWN'}`, |
84 | origin_transport: 'SMS', | 84 | origin_transport: 'SMS', |
85 | origin_partner: req.query.number, | 85 | origin_partner: req.query.number, |
86 | do_not_forward_to_core: req.query.number.indexOf('+') !== 0, | 86 | do_not_forward_to_core: req.query.number.indexOf('+') !== 0, |
87 | }); | 87 | }); |
88 | } | 88 | } |
89 | 89 | ||
90 | async function pageHistory(req, res) { | 90 | async function pageHistory(req, res) { |
91 | res.json(await history.dump()); | 91 | res.json(await history.dump()); |
92 | } | 92 | } |
93 | 93 | ||
94 | app.use(function(req, res, next) { | 94 | app.use(function(req, res, next) { |
95 | if ( | 95 | if ( |
96 | req && req.path && typeof req.path === 'string' | 96 | req && req.path && typeof req.path === 'string' |
97 | && ( | 97 | && ( |
98 | req.path.search(/\/modems$/) >= 0 | 98 | req.path.search(/\/modems$/) >= 0 |
99 | || req.path.search(/\/modems\/set$/) >= 0 | 99 | || req.path.search(/\/modems\/set$/) >= 0 |
100 | ) | 100 | ) |
101 | ) { | 101 | ) { |
102 | next(); | 102 | next(); |
103 | return; | 103 | return; |
104 | } | 104 | } |
105 | 105 | ||
106 | logger.verbose('APISERVER: Incoming http request', { ip: req.ip, path: req.path, url: req.url }); | 106 | logger.verbose('APISERVER: Incoming http request', { ip: req.ip, path: req.path, url: req.url }); |
107 | next(); | 107 | next(); |
108 | }) | 108 | }) |
109 | 109 | ||
110 | app.use('/apikey/:apikey', apikeyChecker); | 110 | app.use('/apikey/:apikey', apikeyChecker); |
111 | app.get('/apikey/:apikey/on-sms', onIncomingSms); | 111 | app.get('/apikey/:apikey/on-sms', onIncomingSms); |
112 | app.get('/apikey/:apikey/inbox', onIncomingSms); | 112 | app.get('/apikey/:apikey/inbox', onIncomingSms); |
113 | app.get('/apikey/:apikey/on-sms/inbox', onIncomingSms); | 113 | app.get('/apikey/:apikey/on-sms/inbox', onIncomingSms); |
114 | app.get('/apikey/:apikey/history', pageHistory); | 114 | app.get('/apikey/:apikey/history', pageHistory); |
115 | app.use('/apikey/:apikey/modems', routerModems); | 115 | app.use('/apikey/:apikey/modems', routerModems); |
116 | 116 | ||
117 | const listenPort = (config && config.apiserver && config.apiserver.listen_port ? config.apiserver.listen_port : null) | 117 | const listenPort = (config && config.apiserver && config.apiserver.listen_port ? config.apiserver.listen_port : null) |
118 | || (config && config.handler_callback_server ? config.handler_callback_server.listen_port : null); | 118 | || (config && config.handler_callback_server ? config.handler_callback_server.listen_port : null); |
119 | 119 | ||
120 | if (listenPort) { | 120 | if (listenPort) { |
121 | app.listen(listenPort, () => { | 121 | app.listen(listenPort, () => { |
122 | logger.info('HTTP Handler Callback server listening on port ' + listenPort); | 122 | logger.info('HTTP Handler Callback server listening on port ' + listenPort); |
123 | }); | 123 | }); |
124 | } else { | 124 | } else { |
125 | logger.warn('Undefined config.apiserver.listen_port for APISERVER. Not listening for command.'); | 125 | logger.warn('Undefined config.apiserver.listen_port for APISERVER. Not listening for command.'); |
126 | } | 126 | } |
127 | 127 |
lib/transport.js
1 | "use strict"; | 1 | "use strict"; |
2 | 2 | ||
3 | const MAX_SMS_LENGTH = 160; | 3 | const MAX_SMS_LENGTH = 160; |
4 | 4 | ||
5 | const url = require('url'); | 5 | const url = require('url'); |
6 | const request = require('request'); | 6 | const request = require('request'); |
7 | const uuidv4 = require('uuid/v4'); | 7 | const uuidv4 = require('uuid/v4'); |
8 | const moment = require('moment'); | 8 | const moment = require('moment'); |
9 | 9 | ||
10 | const config = require('komodo-sdk/config'); | 10 | const config = require('komodo-sdk/config'); |
11 | const logger = require('komodo-sdk/logger'); | 11 | const logger = require('komodo-sdk/logger'); |
12 | 12 | ||
13 | const messagingService = require('komodo-center-messaging-client-lib'); | 13 | const messagingService = require('komodo-center-messaging-client-lib'); |
14 | 14 | ||
15 | const modemSelect = require('./modemSelect'); | 15 | const modemSelect = require('./modemSelect'); |
16 | const modems = require('./modems2'); | 16 | const modems = require('./modems2'); |
17 | const partnerLastSeen = require('./partner-last-seen'); | 17 | const partnerLastSeen = require('./partner-last-seen'); |
18 | const history = require('./history'); | 18 | const history = require('./history'); |
19 | const prefixes = require('./prefixes'); | ||
19 | 20 | ||
20 | function _send(destinationNumber, msg, handlerIMSI) { | 21 | function _send(destinationNumber, msg, handlerIMSI) { |
21 | 22 | ||
22 | if (msg.length > 160) { | 23 | if (msg.length > 160) { |
23 | logger.info('Splitting message'); | 24 | logger.info('Splitting message'); |
24 | 25 | ||
25 | const newMsg = msg.slice(0, MAX_SMS_LENGTH); | 26 | const newMsg = msg.slice(0, MAX_SMS_LENGTH); |
26 | const remainingMsg = msg.slice(MAX_SMS_LENGTH); | 27 | const remainingMsg = msg.slice(MAX_SMS_LENGTH); |
27 | 28 | ||
28 | _send(destinationNumber, newMsg, handlerIMSI); | 29 | _send(destinationNumber, newMsg, handlerIMSI); |
29 | setTimeout(() => { | 30 | setTimeout(() => { |
30 | _send(destinationNumber, remainingMsg, handlerIMSI); | 31 | _send(destinationNumber, remainingMsg, handlerIMSI); |
31 | }, 1000); | 32 | }, 1000); |
32 | 33 | ||
33 | return; | 34 | return; |
34 | } | 35 | } |
35 | 36 | ||
36 | const modem = modems.get('imsi', handlerIMSI); | 37 | const modem = modems.get('imsi', handlerIMSI); |
37 | if (!modem) { | 38 | if (!modem) { |
38 | logger.warn('Not knowing modem to use. Ignoring message', { destination_number: destinationNumber, msg: msg, modem_imsi: handlerIMSI }); | 39 | logger.warn('Not knowing modem to use. Ignoring message', { destination_number: destinationNumber, msg: msg, modem_imsi: handlerIMSI }); |
39 | return; | 40 | return; |
40 | } | 41 | } |
41 | 42 | ||
42 | if (!modem.reportIp || !modem.reportPort || !modem.reportApikey) { | 43 | if (!modem.reportIp || !modem.reportPort || !modem.reportApikey) { |
43 | logger.warn('Invalid modem configuration', { modem }); | 44 | logger.warn('Invalid modem configuration', { modem }); |
44 | return; | 45 | return; |
45 | } | 46 | } |
46 | 47 | ||
47 | const reqId = uuidv4(); | 48 | const reqId = uuidv4(); |
48 | 49 | ||
49 | history.push({ | 50 | history.push({ |
50 | ts: moment().format('YYYY-MM-DD HH:mm:ss'), | 51 | ts: moment().format('YYYY-MM-DD HH:mm:ss'), |
51 | modem: { | 52 | modem: { |
52 | name: modem.name, | 53 | name: modem.name, |
53 | imsi: modem.imsi, | 54 | imsi: modem.imsi, |
54 | msisdn: modem.msisdn, | 55 | msisdn: modem.msisdn, |
55 | }, | 56 | }, |
56 | direction: 'OUTGOING', | 57 | direction: 'OUTGOING', |
57 | partner: destinationNumber, | 58 | partner: destinationNumber, |
58 | message: msg, | 59 | message: msg, |
59 | }); | 60 | }); |
60 | 61 | ||
61 | logger.verbose('TRANSPORT: saving outgoing message'); | 62 | logger.verbose('TRANSPORT: saving outgoing message'); |
62 | messagingService.onIncomingMessage({ | 63 | messagingService.onIncomingMessage({ |
63 | me: modem.name, | 64 | me: modem.name, |
64 | partner: destinationNumber, | 65 | partner: destinationNumber, |
65 | partner_raw: `+${destinationNumber}`.replace(/^\++/, '+'), | 66 | partner_raw: `+${destinationNumber}`.replace(/^\++/, '+'), |
66 | msg: msg, | 67 | msg: msg, |
67 | origin_label: `IMSI_${modem.imsi || 'UNKNOWN'}`, | 68 | origin_label: `IMSI_${modem.imsi || 'UNKNOWN'}`, |
68 | origin_transport: 'SMS', | 69 | origin_transport: 'SMS', |
69 | origin_partner: destinationNumber, | 70 | origin_partner: destinationNumber, |
70 | do_not_forward_to_core: true, | 71 | do_not_forward_to_core: true, |
71 | is_outgoing: true, | 72 | is_outgoing: true, |
72 | }); | 73 | }); |
73 | 74 | ||
74 | const requestOptions = { | 75 | const requestOptions = { |
75 | url: url.format({ | 76 | url: url.format({ |
76 | protocol: 'http', | 77 | protocol: 'http', |
77 | hostname: modem.reportIp, | 78 | hostname: modem.reportIp, |
78 | port: modem.reportPort, | 79 | port: modem.reportPort, |
79 | pathname: modem.reportPathSms || '/sms', | 80 | pathname: modem.reportPathSms || '/sms', |
80 | }), | 81 | }), |
81 | qs: { | 82 | qs: { |
82 | msg: msg, | 83 | msg: msg, |
83 | number: destinationNumber, | 84 | number: destinationNumber, |
84 | reqid: reqId, | 85 | reqid: reqId, |
85 | apikey: modem.reportApikey, | 86 | apikey: modem.reportApikey, |
86 | } | 87 | } |
87 | } | 88 | } |
88 | 89 | ||
89 | logger.info('Sending message to modem handler', { req_id: reqId, destination_number: destinationNumber, msg: msg, msg_length: msg.length, modem_name: modem.name, modem_imsi: modem.imsi }); | 90 | logger.info('Sending message to modem handler', { req_id: reqId, destination_number: destinationNumber, msg: msg, msg_length: msg.length, modem_name: modem.name, modem_imsi: modem.imsi }); |
90 | request(requestOptions, function(err, res, body) { | 91 | request(requestOptions, function(err, res, body) { |
91 | if (err) { | 92 | if (err) { |
92 | logger.warn('Error requesting to modem handler. ' + err.toString(), { req_id: reqId, modem_name: modem.name, modem_imsi: modem.imsi }); | 93 | logger.warn('Error requesting to modem handler. ' + err.toString(), { req_id: reqId, modem_name: modem.name, modem_imsi: modem.imsi }); |
93 | 94 | ||
94 | } | 95 | } |
95 | else if (res.statusCode != 200) { | 96 | else if (res.statusCode != 200) { |
96 | logger.warn('Modem handler not responding with HTTP status code 200.', { http_status_code: res.statusCode, req_id: reqId, modem_name: modem.name, modem_imsi: modem.imsi }); | 97 | logger.warn('Modem handler not responding with HTTP status code 200.', { http_status_code: res.statusCode, req_id: reqId, modem_name: modem.name, modem_imsi: modem.imsi }); |
97 | } | 98 | } |
98 | else { | 99 | else { |
99 | logger.verbose('Message sent to handler', { req_id: reqId, modem_name: modem.name, modem_imsi: modem.imsi, response_body: body }); | 100 | logger.verbose('Message sent to handler', { req_id: reqId, modem_name: modem.name, modem_imsi: modem.imsi, response_body: body }); |
100 | } | 101 | } |
101 | }) | 102 | }) |
102 | 103 | ||
103 | } | 104 | } |
104 | 105 | ||
105 | async function send(partner, msg) { | 106 | async function send(partner, msg) { |
106 | if (!partner) return; | 107 | if (!partner) return; |
107 | 108 | ||
108 | if (typeof msg !== 'string') { | 109 | if (typeof msg !== 'string') { |
109 | logger.warn('Message to send is not a string, ignoring message'); | 110 | logger.warn('Message to send is not a string, ignoring message'); |
110 | return; | 111 | return; |
111 | } | 112 | } |
112 | 113 | ||
113 | msg = msg.trim(); | 114 | msg = msg.trim(); |
114 | if (!msg) return; | 115 | if (!msg) return; |
115 | 116 | ||
116 | const destinationNumber = modemSelect.removeSuffixFromNumber(partner, config); | 117 | const destinationNumber = modemSelect.removeSuffixFromNumber(partner, config); |
118 | const prefixName = await prefixes(destinationNumber); | ||
119 | logger.verbose('Destination number prefix lookup', {partner: destinationNumber, prefix: prefixName}); | ||
117 | 120 | ||
118 | // logger.verbose('Choosing handler name', { partner, destinationNumber, msg, origin }); | 121 | // logger.verbose('Choosing handler name', { partner, destinationNumber, msg, origin }); |
119 | const handlerIMSI = await partnerLastSeen.get(destinationNumber) ; | 122 | const handlerIMSI = await partnerLastSeen.get(destinationNumber) ; |
120 | 123 | ||
121 | if (!handlerIMSI) { | 124 | if (!handlerIMSI) { |
122 | logger.warn(`Unknown handler for sending message to partner`, { partner, destinationNumber }); | 125 | logger.warn(`Unknown handler for sending message to partner`, { partner, destinationNumber }); |
123 | return; | 126 | return; |
124 | } | 127 | } |
125 | 128 | ||
126 | _send(destinationNumber, msg, handlerIMSI); | 129 | _send(destinationNumber, msg, handlerIMSI); |
127 | } | 130 | } |
128 | 131 | ||
129 | exports.send = send; | 132 | exports.send = send; |