chore: use monorepo organization
This commit is contained in:
parent
deaf7d6ecc
commit
a687827f7e
1099 changed files with 8184 additions and 11535 deletions
204
packages/server/lib/notifier/utils.js
Normal file
204
packages/server/lib/notifier/utils.js
Normal file
|
|
@ -0,0 +1,204 @@
|
|||
const _ = require('lodash/fp')
|
||||
const crypto = require('crypto')
|
||||
const { utils: coinUtils } = require('@lamassu/coins')
|
||||
const prettyMs = require('pretty-ms')
|
||||
|
||||
const {
|
||||
CODES_DISPLAY,
|
||||
NETWORK_DOWN_TIME,
|
||||
PING,
|
||||
ALERT_SEND_INTERVAL
|
||||
} = require('./codes')
|
||||
|
||||
const DETAIL_TEMPLATE = {
|
||||
deviceId: '',
|
||||
cryptoCode: '',
|
||||
code: '',
|
||||
cassette: '',
|
||||
age: '',
|
||||
customerId: '',
|
||||
cryptoAddress: '',
|
||||
direction: '',
|
||||
fiat: '',
|
||||
fiatCode: ''
|
||||
}
|
||||
|
||||
function parseEventNote (event) {
|
||||
return _.set('note', JSON.parse(event.note), event)
|
||||
}
|
||||
|
||||
function checkPing (device) {
|
||||
const age = Date.now() - (new Date(device.lastPing).getTime())
|
||||
if (age > NETWORK_DOWN_TIME) return [{ code: PING, age, machineName: device.name }]
|
||||
return []
|
||||
}
|
||||
|
||||
const getDeviceTime = _.flow(_.get('device_time'), Date.parse)
|
||||
|
||||
const isActive = it => it.active && (it.balance || it.errors)
|
||||
|
||||
const codeDisplay = code => CODES_DISPLAY[code]
|
||||
|
||||
const alertFingerprint = {
|
||||
fingerprint: null,
|
||||
lastAlertTime: null
|
||||
}
|
||||
|
||||
const getAlertFingerprint = () => alertFingerprint.fingerprint
|
||||
|
||||
const getLastAlertTime = () => alertFingerprint.lastAlertTime
|
||||
|
||||
const setAlertFingerprint = (fp, time = Date.now()) => {
|
||||
alertFingerprint.fingerprint = fp
|
||||
alertFingerprint.lastAlertTime = time
|
||||
}
|
||||
|
||||
const shouldNotAlert = currentAlertFingerprint => {
|
||||
return (
|
||||
currentAlertFingerprint === getAlertFingerprint() &&
|
||||
getLastAlertTime() - Date.now() < ALERT_SEND_INTERVAL
|
||||
)
|
||||
}
|
||||
|
||||
function buildAlertFingerprint (alertRec, notifications) {
|
||||
const sms = getAlertTypes(alertRec, notifications.sms)
|
||||
const email = getAlertTypes(alertRec, notifications.email)
|
||||
if (sms.length === 0 && email.length === 0) return null
|
||||
|
||||
const smsTypes = _.map(codeDisplay, _.uniq(_.map('code', sms))).sort()
|
||||
const emailTypes = _.map(codeDisplay, _.uniq(_.map('code', email))).sort()
|
||||
|
||||
const subject = _.concat(smsTypes, emailTypes).join(', ')
|
||||
return crypto.createHash('sha256').update(subject).digest('hex')
|
||||
}
|
||||
|
||||
function sendNoAlerts (plugins, smsEnabled, emailEnabled) {
|
||||
const subject = '[Lamassu] All clear'
|
||||
|
||||
let rec = {}
|
||||
if (smsEnabled) {
|
||||
rec = _.set(['sms', 'body'])(subject)(rec)
|
||||
}
|
||||
|
||||
if (emailEnabled) {
|
||||
rec = _.set(['email', 'subject'])(subject)(rec)
|
||||
rec = _.set(['email', 'body'])('No errors are reported for your machines.')(
|
||||
rec
|
||||
)
|
||||
}
|
||||
|
||||
return plugins.sendMessage(rec)
|
||||
}
|
||||
|
||||
const buildTransactionMessage = (tx, rec, highValueTx, machineName, customer) => {
|
||||
const isCashOut = tx.direction === 'cashOut'
|
||||
const direction = isCashOut ? 'Cash Out' : 'Cash In'
|
||||
const crypto = `${coinUtils.toUnit(tx.cryptoAtoms, tx.cryptoCode)} ${
|
||||
tx.cryptoCode
|
||||
}`
|
||||
const fiat = `${tx.fiat} ${tx.fiatCode}`
|
||||
const customerName = customer.name || customer.id
|
||||
const phone = customer.phone ? `- Phone: ${customer.phone}` : ''
|
||||
|
||||
let status = null
|
||||
if (rec.error) {
|
||||
status = `Error - ${rec.error}`
|
||||
} else {
|
||||
status = !isCashOut
|
||||
? 'Successful'
|
||||
: !rec.isRedemption
|
||||
? 'Successful & awaiting redemption'
|
||||
: 'Successful & dispensed'
|
||||
}
|
||||
|
||||
const body = `
|
||||
- Transaction ID: ${tx.id}
|
||||
- Status: ${status}
|
||||
- Machine name: ${machineName}
|
||||
- ${direction}
|
||||
- ${fiat}
|
||||
- ${crypto}
|
||||
- Customer: ${customerName}
|
||||
${phone}
|
||||
`
|
||||
const smsSubject = `A ${highValueTx ? 'high value ' : ''}${direction.toLowerCase()} transaction just happened at ${machineName} for ${fiat}`
|
||||
const emailSubject = `A ${highValueTx ? 'high value ' : ''}transaction just happened`
|
||||
|
||||
return [{
|
||||
sms: {
|
||||
body: `${smsSubject} – ${status}`
|
||||
},
|
||||
email: {
|
||||
emailSubject,
|
||||
body
|
||||
},
|
||||
webhook: {
|
||||
topic: `New transaction`,
|
||||
content: body
|
||||
}
|
||||
}, highValueTx]
|
||||
}
|
||||
|
||||
function formatCurrency (num = 0, code) {
|
||||
const formattedNumber = Number(num).toLocaleString(undefined, {maximumFractionDigits:2, minimumFractionDigits:2})
|
||||
return `${formattedNumber} ${code}`
|
||||
}
|
||||
|
||||
function formatAge (age, settings) {
|
||||
return prettyMs(age, settings)
|
||||
}
|
||||
|
||||
function buildDetail (obj) {
|
||||
// obj validation
|
||||
const objKeys = _.keys(obj)
|
||||
const detailKeys = _.keys(DETAIL_TEMPLATE)
|
||||
if ((_.difference(objKeys, detailKeys)).length > 0) {
|
||||
return Promise.reject(new Error('Error when building detail object: invalid properties'))
|
||||
}
|
||||
return { ...DETAIL_TEMPLATE, ...obj }
|
||||
}
|
||||
|
||||
function deviceAlerts (config, alertRec, device) {
|
||||
let alerts = []
|
||||
if (config.balance) {
|
||||
alerts = _.concat(alerts, alertRec.devices[device].balanceAlerts)
|
||||
}
|
||||
|
||||
if (config.errors) {
|
||||
alerts = _.concat(alerts, alertRec.devices[device].deviceAlerts)
|
||||
}
|
||||
return alerts
|
||||
}
|
||||
|
||||
function getAlertTypes (alertRec, config) {
|
||||
let alerts = []
|
||||
if (!isActive(config)) return alerts
|
||||
|
||||
if (config.balance) {
|
||||
alerts = _.concat(alerts, alertRec.general)
|
||||
}
|
||||
|
||||
_.forEach(device => {
|
||||
alerts = _.concat(alerts, deviceAlerts(config, alertRec, device))
|
||||
}, _.keys(alertRec.devices))
|
||||
return alerts
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
codeDisplay,
|
||||
parseEventNote,
|
||||
getDeviceTime,
|
||||
checkPing,
|
||||
isActive,
|
||||
getAlertFingerprint,
|
||||
getLastAlertTime,
|
||||
setAlertFingerprint,
|
||||
shouldNotAlert,
|
||||
buildAlertFingerprint,
|
||||
sendNoAlerts,
|
||||
buildTransactionMessage,
|
||||
formatCurrency,
|
||||
formatAge,
|
||||
buildDetail,
|
||||
deviceAlerts
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue