const router = require('express').Router(); const request = require('request'); const getProvider = require('../../utils/sms-provider'); const uuidv4 = require('uuid/v4'); const sysError = require('../error'); let idx = 0; async function doSendResponse(res, respondFn, body) { if (typeof respondFn === 'number') res.sendStatus(respondFn); else if (typeof respondFn !== 'function') res.sendStatus(200); else { const payload = await respondFn(body); res.status(200).json(payload); } } router.post('/:provider', async(req, res) => { const provider = req.params.provider; const { retrieveSet, lookupAppByPhoneNumber, logger } = req.app.locals; const setName = `${process.env.JAMBONES_CLUSTER_ID || 'default'}:active-fs`; logger.debug({path: req.path, body: req.body}, 'incomingSMS from carrier'); // search for provider module const arr = getProvider(logger, provider); if (!arr) { logger.info({body: req.body, params: req.params}, `rejecting incomingSms request from unknown provider ${provider}` ); return res.sendStatus(404); } const providerData = arr[1]; if (!providerData || !providerData.module) { logger.info({body: req.body, params: req.params}, `rejecting incomingSms request from badly configured provider ${provider}` ); return res.sendStatus(404); } // load provider module let filterFn, respondFn; try { const { fromProviderFormat, formatProviderResponse } = require(providerData.module); // must at least provide a filter function if (!fromProviderFormat) { logger.info( `missing fromProviderFormat function in module ${providerData.module} for provider ${provider}` ); return res.sendStatus(404); } filterFn = fromProviderFormat; respondFn = formatProviderResponse; } catch (err) { logger.info( err, `failure loading module ${providerData.module} for provider ${provider}` ); return res.sendStatus(500); } try { const fs = await retrieveSet(setName); if (0 === fs.length) { logger.info('No available feature servers to handle createCall API request'); return res .json({ msg: 'no available feature servers at this time' }) .status(480); } const ip = fs[idx++ % fs.length]; const serviceUrl = `http://${ip}:3000/v1/messaging/${provider}`; const messageSid = uuidv4(); const payload = await Promise.resolve(filterFn({messageSid}, req.body)); /** * lookup the application associated with the number in the To field * since there could be multiple Tos, we have to search through (and cc also) */ let app; const to = Array.isArray(payload.to) ? payload.to : [payload.to]; const cc = Array.isArray(payload.cc) ? payload.cc : (payload.cc ? [payload.cc] : []); const dids = to.concat(cc).filter((n) => n.length); for (let did of dids) { const regex = /^\+(\d+)$/; const arr = regex.exec(did); did = arr ? arr[1] : did; const obj = await lookupAppByPhoneNumber(did); logger.info({obj}, `lookup app for phone number ${did}`); if (obj) { logger.info({did, obj}, 'Found app for DID'); app = obj; break; } } if (!app) { logger.info({payload}, 'No application found for incoming SMS'); return res.sendStatus(404); } if (!app.messaging_hook) { logger.info({payload}, `app "${app.name}" found for incoming SMS does not have an associated messaging hook`); return res.sendStatus(404); } payload.app = app; logger.debug({body: req.body, payload}, 'filtered incoming SMS'); logger.info({payload, url: serviceUrl}, `sending incomingSms API request to FS at ${ip}`); request({ url: serviceUrl, method: 'POST', json: true, body: payload, }, async(err, response, body) => { if (err) { logger.error(err, `Error sending incomingSms POST to ${ip}`); return res.sendStatus(500); } if (200 === response.statusCode) { // success logger.info({body}, 'sending response to provider for incomingSMS'); return doSendResponse(res, respondFn, body); } logger.error({statusCode: response.statusCode}, `Non-success response returned by incomingSms ${ip}`); return res.sendStatus(500); }); } catch (err) { sysError(logger, res, err); } }); module.exports = router;