v12.0.0 - initial commit
This commit is contained in:
commit
e2c49ea43c
1145 changed files with 97211 additions and 0 deletions
44
packages/server/lib/routes/cashboxRoutes.js
Normal file
44
packages/server/lib/routes/cashboxRoutes.js
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
const express = require('express')
|
||||
const _ = require('lodash/fp')
|
||||
const router = express.Router()
|
||||
|
||||
const cashbox = require('../cashbox-batches')
|
||||
const notifier = require('../notifier')
|
||||
const { getMachine, getMachineName } = require('../machine-loader')
|
||||
const { loadConfig } = require('../new-settings-loader')
|
||||
const { getCashInSettings } = require('../new-config-manager')
|
||||
const { AUTOMATIC } = require('../constants')
|
||||
const logger = require('../logger')
|
||||
|
||||
function cashboxRemoval(req, res, next) {
|
||||
notifier.cashboxNotify(req.deviceId).catch(logger.error)
|
||||
|
||||
return Promise.all([getMachine(req.deviceId), loadConfig()])
|
||||
.then(([machine, config]) => {
|
||||
const cashInSettings = getCashInSettings(config)
|
||||
if (cashInSettings.cashboxReset !== AUTOMATIC) {
|
||||
return Promise.all([
|
||||
cashbox.getMachineUnbatchedBills(req.deviceId),
|
||||
getMachineName(req.deviceId),
|
||||
])
|
||||
}
|
||||
return cashbox
|
||||
.createCashboxBatch(req.deviceId, machine.cashbox)
|
||||
.then(batch =>
|
||||
Promise.all([
|
||||
cashbox.getBatchById(batch.id),
|
||||
getMachineName(batch.device_id),
|
||||
]),
|
||||
)
|
||||
})
|
||||
.then(([batch, machineName]) =>
|
||||
res
|
||||
.status(200)
|
||||
.send({ batch: _.merge(batch, { machineName }), status: 'OK' }),
|
||||
)
|
||||
.catch(next)
|
||||
}
|
||||
|
||||
router.post('/removal', cashboxRemoval)
|
||||
|
||||
module.exports = router
|
||||
451
packages/server/lib/routes/customerRoutes.js
Normal file
451
packages/server/lib/routes/customerRoutes.js
Normal file
|
|
@ -0,0 +1,451 @@
|
|||
const express = require('express')
|
||||
const router = express.Router()
|
||||
const _ = require('lodash/fp')
|
||||
const { zonedTimeToUtc, utcToZonedTime } = require('date-fns-tz/fp')
|
||||
const { add, intervalToDuration } = require('date-fns/fp')
|
||||
const uuid = require('uuid')
|
||||
|
||||
const sms = require('../sms')
|
||||
const BN = require('../bn')
|
||||
const compliance = require('../compliance')
|
||||
const complianceTriggers = require('../compliance-triggers')
|
||||
const configManager = require('../new-config-manager')
|
||||
const customers = require('../customers')
|
||||
const httpError = require('../route-helpers').httpError
|
||||
const notifier = require('../notifier')
|
||||
const respond = require('../respond')
|
||||
const {
|
||||
updateTxCustomerPhoto: txsUpdateTxCustomerPhoto,
|
||||
} = require('../new-admin/services/transactions.js')
|
||||
const machineLoader = require('../machine-loader')
|
||||
const customInfoRequestQueries = require('../new-admin/services/customInfoRequests')
|
||||
const T = require('../time')
|
||||
const plugins = require('../plugins')
|
||||
const Tx = require('../tx')
|
||||
const loyalty = require('../loyalty')
|
||||
const logger = require('../logger')
|
||||
const externalCompliance = require('../compliance-external')
|
||||
const { doesTxReuseAddress } = require('../cash-in/cash-in-tx')
|
||||
const { getTx } = require('../tx')
|
||||
|
||||
function updateCustomerCustomInfoRequest(customerId, patch) {
|
||||
const promise = _.isNil(patch.data)
|
||||
? Promise.resolve(null)
|
||||
: customInfoRequestQueries.setCustomerDataViaMachine(
|
||||
customerId,
|
||||
patch.infoRequestId,
|
||||
patch,
|
||||
)
|
||||
return promise.then(() => customers.getById(customerId))
|
||||
}
|
||||
|
||||
const createPendingManualComplianceNotifs = (settings, customer, deviceId) => {
|
||||
const customInfoRequests = _.reduce(
|
||||
(reqs, req) => _.set(req.info_request_id, req, reqs),
|
||||
{},
|
||||
_.get(['customInfoRequestData'], customer),
|
||||
)
|
||||
|
||||
const isPending = field =>
|
||||
uuid.validate(field)
|
||||
? _.get([field, 'override'], customInfoRequests) === 'automatic'
|
||||
: customer[`${field}At`] &&
|
||||
(!customer[`${field}OverrideAt`] ||
|
||||
customer[`${field}OverrideAt`].getTime() <
|
||||
customer[`${field}At`].getTime())
|
||||
|
||||
const unnestCustomTriggers = triggersAutomation => {
|
||||
const customTriggers = _.fromPairs(
|
||||
_.map(({ id, type }) => [id, type], triggersAutomation.custom),
|
||||
)
|
||||
return _.flow(
|
||||
_.unset('custom'),
|
||||
_.mapKeys(k => (k === 'facephoto' ? 'frontCamera' : k)),
|
||||
_.assign(customTriggers),
|
||||
)(triggersAutomation)
|
||||
}
|
||||
|
||||
const isManual = v => v === 'Manual'
|
||||
|
||||
const hasManualAutomation = triggersAutomation =>
|
||||
_.any(isManual, _.values(triggersAutomation))
|
||||
|
||||
configManager
|
||||
.getTriggersAutomation(
|
||||
customInfoRequestQueries.getCustomInfoRequests(true),
|
||||
settings.config,
|
||||
)
|
||||
.then(triggersAutomation => {
|
||||
triggersAutomation = unnestCustomTriggers(triggersAutomation)
|
||||
if (!hasManualAutomation(triggersAutomation)) return
|
||||
|
||||
const pendingFields = _.filter(
|
||||
field => isManual(triggersAutomation[field]) && isPending(field),
|
||||
_.keys(triggersAutomation),
|
||||
)
|
||||
|
||||
if (!_.isEmpty(pendingFields))
|
||||
notifier.complianceNotify(
|
||||
settings,
|
||||
customer,
|
||||
deviceId,
|
||||
'PENDING_COMPLIANCE',
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
function updateCustomer(req, res, next) {
|
||||
const id = req.params.id
|
||||
const patch = req.body
|
||||
const deviceId = req.deviceId
|
||||
const settings = req.settings
|
||||
|
||||
if (patch.customRequestPatch) {
|
||||
return updateCustomerCustomInfoRequest(id, patch.customRequestPatch)
|
||||
.then(customer => {
|
||||
createPendingManualComplianceNotifs(settings, customer, deviceId)
|
||||
respond(req, res, { customer })
|
||||
})
|
||||
.catch(next)
|
||||
}
|
||||
|
||||
customers
|
||||
.getById(id)
|
||||
.then(customer =>
|
||||
!customer ? Promise.reject(httpError('Not Found', 404)) : {},
|
||||
)
|
||||
.then(_.merge(patch))
|
||||
.then(newPatch => customers.updatePhotoCard(id, newPatch))
|
||||
.then(newPatch => customers.updateFrontCamera(id, newPatch))
|
||||
.then(newPatch => customers.update(id, newPatch, null))
|
||||
.then(customer => {
|
||||
createPendingManualComplianceNotifs(settings, customer, deviceId)
|
||||
respond(req, res, { customer })
|
||||
})
|
||||
.catch(next)
|
||||
}
|
||||
|
||||
function updateIdCardData(req, res, next) {
|
||||
const id = req.params.id
|
||||
const patch = req.body
|
||||
customers
|
||||
.getById(id)
|
||||
.then(customer => {
|
||||
if (!customer) {
|
||||
throw httpError('Not Found', 404)
|
||||
}
|
||||
return customers.updateIdCardData(patch, id).then(() => customer)
|
||||
})
|
||||
.then(customer => respond(req, res, { customer }))
|
||||
.catch(next)
|
||||
}
|
||||
|
||||
function triggerSanctions(req, res, next) {
|
||||
const id = req.params.id
|
||||
|
||||
customers
|
||||
.getById(id)
|
||||
.then(customer => {
|
||||
if (!customer) {
|
||||
throw httpError('Not Found', 404)
|
||||
}
|
||||
return compliance
|
||||
.validationPatch(req.deviceId, customer)
|
||||
.then(patch => customers.update(id, patch))
|
||||
})
|
||||
.then(customer => respond(req, res, { customer }))
|
||||
.catch(next)
|
||||
}
|
||||
|
||||
function triggerBlock(req, res, next) {
|
||||
const id = req.params.id
|
||||
const settings = req.settings
|
||||
|
||||
customers
|
||||
.update(id, { authorizedOverride: 'blocked' })
|
||||
.then(customer => {
|
||||
notifier.complianceNotify(settings, customer, req.deviceId, 'BLOCKED')
|
||||
return respond(req, res, { customer })
|
||||
})
|
||||
.catch(next)
|
||||
}
|
||||
|
||||
function triggerSuspend(req, res, next) {
|
||||
const id = req.params.id
|
||||
const triggerId = req.body.triggerId
|
||||
const settings = req.settings
|
||||
|
||||
const triggers = req.machineSettings.complianceTriggers
|
||||
const getSuspendDays = _.compose(
|
||||
_.get('suspensionDays'),
|
||||
_.find(_.matches({ id: triggerId })),
|
||||
)
|
||||
|
||||
const days = _.includes(triggerId, ['no-ff-camera', 'id-card-photo-disabled'])
|
||||
? 1
|
||||
: getSuspendDays(triggers)
|
||||
|
||||
const suspensionDuration = intervalToDuration({ start: 0, end: T.day * days })
|
||||
|
||||
customers
|
||||
.update(id, { suspendedUntil: add(suspensionDuration, new Date()) })
|
||||
.then(customer => {
|
||||
notifier.complianceNotify(
|
||||
settings,
|
||||
customer,
|
||||
req.deviceId,
|
||||
'SUSPENDED',
|
||||
days,
|
||||
)
|
||||
return respond(req, res, { customer })
|
||||
})
|
||||
.catch(next)
|
||||
}
|
||||
|
||||
function updateTxCustomerPhoto(req, res, next) {
|
||||
const customerId = req.params.id
|
||||
const txId = req.params.txId
|
||||
const tcPhotoData = req.body.tcPhotoData
|
||||
const direction = req.body.direction
|
||||
|
||||
Promise.all([customers.getById(customerId), getTx(txId, direction)])
|
||||
.then(([customer, tx]) => {
|
||||
if (!customer || !tx) return
|
||||
return customers
|
||||
.updateTxCustomerPhoto(tcPhotoData)
|
||||
.then(newPatch =>
|
||||
txsUpdateTxCustomerPhoto(customerId, txId, direction, newPatch),
|
||||
)
|
||||
})
|
||||
.then(() => respond(req, res, {}))
|
||||
.catch(next)
|
||||
}
|
||||
|
||||
function buildSms(config, data, receiptOptions) {
|
||||
return getTx(data.session, data.txClass).then(tx => {
|
||||
return Promise.all([
|
||||
customers.getCustomerById(tx.customer_id),
|
||||
machineLoader.getMachine(tx.device_id, config),
|
||||
]).then(([customer, deviceConfig]) => {
|
||||
const formattedTx = _.mapKeys(_.camelCase)(tx)
|
||||
const localeConfig = configManager.getLocale(formattedTx.deviceId, config)
|
||||
const timezone = localeConfig.timezone
|
||||
|
||||
const cashInCommission = new BN(1).plus(
|
||||
new BN(formattedTx.commissionPercentage),
|
||||
)
|
||||
|
||||
const rate = new BN(formattedTx.rawTickerPrice)
|
||||
.multipliedBy(cashInCommission)
|
||||
.decimalPlaces(2)
|
||||
const date = utcToZonedTime(
|
||||
timezone,
|
||||
zonedTimeToUtc(process.env.TZ, new Date()),
|
||||
)
|
||||
const dateString = `${date.toISOString().replace('T', ' ').slice(0, 19)}`
|
||||
|
||||
const data = {
|
||||
operatorInfo: configManager.getOperatorInfo(config),
|
||||
location: deviceConfig.machineLocation,
|
||||
customerName: customer.name,
|
||||
customerPhone: customer.phone,
|
||||
session: formattedTx.id,
|
||||
time: dateString,
|
||||
direction: formattedTx.txClass === 'cashIn' ? 'Cash-in' : 'Cash-out',
|
||||
fiat: `${formattedTx.fiat.toString()} ${formattedTx.fiatCode}`,
|
||||
crypto: `${sms.toCryptoUnits(BN(formattedTx.cryptoAtoms), formattedTx.cryptoCode)} ${formattedTx.cryptoCode}`,
|
||||
rate: `1 ${formattedTx.cryptoCode} = ${rate} ${formattedTx.fiatCode}`,
|
||||
address: formattedTx.toAddress,
|
||||
txId: formattedTx.txHash,
|
||||
}
|
||||
|
||||
return sms.formatSmsReceipt(data, receiptOptions)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function sendSmsReceipt(req, res, next) {
|
||||
const receiptOptions = _.omit(
|
||||
['active', 'sms'],
|
||||
configManager.getReceipt(req.settings.config),
|
||||
)
|
||||
buildSms(req.settings.config, req.body.data, receiptOptions).then(
|
||||
smsRequest => {
|
||||
sms
|
||||
.sendMessage(req.settings, smsRequest)
|
||||
.then(() => respond(req, res, {}))
|
||||
.catch(next)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
function getExternalComplianceLink(req, res, next) {
|
||||
const customerId = req.query.customer
|
||||
const triggerId = req.query.trigger
|
||||
const isRetry = req.query.isRetry
|
||||
if (_.isNil(customerId) || _.isNil(triggerId))
|
||||
return next(httpError('Not Found', 404))
|
||||
|
||||
const settings = req.settings
|
||||
const triggers = req.machineSettings.complianceTriggers
|
||||
const trigger = _.find(it => it.id === triggerId)(triggers)
|
||||
const externalService = trigger.externalService
|
||||
|
||||
if (isRetry) {
|
||||
return externalCompliance
|
||||
.createLink(settings.accounts, externalService, customerId)
|
||||
.then(url => respond(req, res, { url }))
|
||||
}
|
||||
|
||||
return externalCompliance
|
||||
.createApplicant(settings.accounts, externalService, customerId)
|
||||
.then(applicant =>
|
||||
customers.addExternalCompliance(
|
||||
customerId,
|
||||
externalService,
|
||||
applicant.id,
|
||||
),
|
||||
)
|
||||
.then(() =>
|
||||
externalCompliance.createLink(
|
||||
settings.accounts,
|
||||
externalService,
|
||||
customerId,
|
||||
),
|
||||
)
|
||||
.then(url => respond(req, res, { url }))
|
||||
}
|
||||
|
||||
function addOrUpdateCustomer({
|
||||
customerData,
|
||||
deviceId,
|
||||
config,
|
||||
isEmailAuth,
|
||||
cryptoCode,
|
||||
triggers,
|
||||
}) {
|
||||
const maxDaysThreshold = complianceTriggers.maxDaysThreshold(triggers)
|
||||
|
||||
const customerKey = isEmailAuth ? customerData.email : customerData.phone
|
||||
const getFunc = isEmailAuth ? customers.getWithEmail : customers.get
|
||||
const addFunction = isEmailAuth ? customers.addWithEmail : customers.add
|
||||
|
||||
return getFunc(customerKey)
|
||||
.then(customer => {
|
||||
if (customer) return customer
|
||||
|
||||
return addFunction(customerData)
|
||||
})
|
||||
.then(customer => customers.getById(customer.id))
|
||||
.then(customer => {
|
||||
customers.updateLastAuthAttempt(customer.id, deviceId).catch(() => {
|
||||
logger.info(
|
||||
'failure updating last auth attempt for customer ',
|
||||
customer.id,
|
||||
)
|
||||
})
|
||||
return customer
|
||||
})
|
||||
.then(customer => {
|
||||
return Tx.customerHistory(customer.id, maxDaysThreshold).then(result => {
|
||||
customer.txHistory = result
|
||||
return customer
|
||||
})
|
||||
})
|
||||
.then(customer => {
|
||||
return loyalty
|
||||
.getCustomerActiveIndividualDiscount(customer.id)
|
||||
.then(discount => ({ ...customer, discount }))
|
||||
})
|
||||
.then(customer => {
|
||||
const enableLastUsedAddress = !!configManager.getWalletSettings(
|
||||
cryptoCode,
|
||||
config,
|
||||
).enableLastUsedAddress
|
||||
if (!cryptoCode || !enableLastUsedAddress) return customer
|
||||
return customers
|
||||
.getLastUsedAddress(customer.id, cryptoCode)
|
||||
.then(lastUsedAddress => {
|
||||
return { ...customer, lastUsedAddress }
|
||||
})
|
||||
})
|
||||
.then(customer => {
|
||||
const { rejectAddressReuse } = configManager.getCompliance(config)
|
||||
if (!rejectAddressReuse || !customer.lastUsedAddress) return customer
|
||||
|
||||
return doesTxReuseAddress({
|
||||
toAddress: customer.lastUsedAddress,
|
||||
customerId: customer.id,
|
||||
}).then(isReused => {
|
||||
const newAddress = isReused ? null : customer.lastUsedAddress
|
||||
return { ...customer, lastUsedAddress: newAddress }
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function getOrAddCustomerPhone(req, res, next) {
|
||||
const deviceId = req.deviceId
|
||||
const customerData = req.body
|
||||
|
||||
const pi = plugins(req.settings, deviceId)
|
||||
const phone = req.body.phone
|
||||
const cryptoCode = req.query.cryptoCode
|
||||
|
||||
return pi
|
||||
.getPhoneCode(phone)
|
||||
.then(code => {
|
||||
return addOrUpdateCustomer({
|
||||
customerData,
|
||||
deviceId,
|
||||
config: req.settings.config,
|
||||
isEmailAuth: false,
|
||||
cryptoCode,
|
||||
triggers: req.machineSettings.complianceTriggers,
|
||||
}).then(customer => respond(req, res, { code, customer }))
|
||||
})
|
||||
.catch(err => {
|
||||
if (err.name === 'BadNumberError') throw httpError('Bad number', 401)
|
||||
throw err
|
||||
})
|
||||
.catch(next)
|
||||
}
|
||||
|
||||
function getOrAddCustomerEmail(req, res, next) {
|
||||
const deviceId = req.deviceId
|
||||
const customerData = req.body
|
||||
const cryptoCode = req.query.cryptoCode
|
||||
|
||||
const pi = plugins(req.settings, req.deviceId)
|
||||
const email = req.body.email
|
||||
|
||||
return pi
|
||||
.getEmailCode(email)
|
||||
.then(code => {
|
||||
return addOrUpdateCustomer({
|
||||
customerData,
|
||||
deviceId,
|
||||
config: req.settings.config,
|
||||
isEmailAuth: true,
|
||||
cryptoCode,
|
||||
triggers: req.machineSettings.complianceTriggers,
|
||||
}).then(customer => respond(req, res, { code, customer }))
|
||||
})
|
||||
.catch(err => {
|
||||
if (err.name === 'BadNumberError') throw httpError('Bad number', 401)
|
||||
throw err
|
||||
})
|
||||
.catch(next)
|
||||
}
|
||||
|
||||
router.patch('/:id', updateCustomer)
|
||||
router.patch('/:id/sanctions', triggerSanctions)
|
||||
router.patch('/:id/block', triggerBlock)
|
||||
router.patch('/:id/suspend', triggerSuspend)
|
||||
router.patch('/:id/photos/idcarddata', updateIdCardData)
|
||||
router.patch('/:id/:txId/photos/customerphoto', updateTxCustomerPhoto)
|
||||
router.post('/:id/smsreceipt', sendSmsReceipt)
|
||||
router.get('/external', getExternalComplianceLink)
|
||||
router.post('/phone_code', getOrAddCustomerPhone)
|
||||
router.post('/email_code', getOrAddCustomerEmail)
|
||||
|
||||
module.exports = router
|
||||
14
packages/server/lib/routes/diagnosticsRoutes.js
Normal file
14
packages/server/lib/routes/diagnosticsRoutes.js
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
const express = require('express')
|
||||
const router = express.Router()
|
||||
|
||||
const { updateDiagnostics } = require('../machine-loader')
|
||||
|
||||
function diagnostics(req, res, next) {
|
||||
return updateDiagnostics(req.deviceId, req.body)
|
||||
.then(() => res.status(200).send({ status: 'OK' }))
|
||||
.catch(next)
|
||||
}
|
||||
|
||||
router.post('/', diagnostics)
|
||||
|
||||
module.exports = router
|
||||
14
packages/server/lib/routes/failedQRScans.js
Normal file
14
packages/server/lib/routes/failedQRScans.js
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
const express = require('express')
|
||||
const router = express.Router()
|
||||
|
||||
const { updateFailedQRScans } = require('../machine-loader')
|
||||
|
||||
function failedQRScans(req, res, next) {
|
||||
return updateFailedQRScans(req.deviceId, req.body)
|
||||
.then(() => res.status(200).send({ status: 'OK' }))
|
||||
.catch(next)
|
||||
}
|
||||
|
||||
router.post('/', failedQRScans)
|
||||
|
||||
module.exports = router
|
||||
37
packages/server/lib/routes/logsRoutes.js
Normal file
37
packages/server/lib/routes/logsRoutes.js
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
const express = require('express')
|
||||
const router = express.Router()
|
||||
|
||||
const state = require('../middlewares/state')
|
||||
const logs = require('../logs')
|
||||
|
||||
const THROTTLE_LOGS_QUERY = 30 * 1000
|
||||
|
||||
function getLastSeen(req, res, next) {
|
||||
const deviceId = req.deviceId
|
||||
const timestamp = Date.now()
|
||||
const shouldTrigger =
|
||||
!state.canGetLastSeenMap[deviceId] ||
|
||||
timestamp - state.canGetLastSeenMap[deviceId] >= THROTTLE_LOGS_QUERY
|
||||
|
||||
if (shouldTrigger) {
|
||||
state.canGetLastSeenMap[deviceId] = timestamp
|
||||
return logs
|
||||
.getLastSeen(deviceId)
|
||||
.then(r => res.json(r))
|
||||
.catch(next)
|
||||
}
|
||||
|
||||
return res.status(408).json({})
|
||||
}
|
||||
|
||||
function updateLogs(req, res, next) {
|
||||
return logs
|
||||
.update(req.deviceId, req.body.logs)
|
||||
.then(success => res.json({ success }))
|
||||
.catch(next)
|
||||
}
|
||||
|
||||
router.get('/', getLastSeen)
|
||||
router.post('/', updateLogs)
|
||||
|
||||
module.exports = router
|
||||
28
packages/server/lib/routes/pairingRoutes.js
Normal file
28
packages/server/lib/routes/pairingRoutes.js
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
const express = require('express')
|
||||
const router = express.Router()
|
||||
|
||||
const ca = require('../middlewares/ca')
|
||||
const httpError = require('../route-helpers').httpError
|
||||
const pairing = require('../pairing')
|
||||
const populateDeviceId = require('../middlewares/populateDeviceId')
|
||||
|
||||
function pair(req, res, next) {
|
||||
const token = req.query.token
|
||||
const deviceId = req.deviceId
|
||||
const model = req.query.model
|
||||
const numOfCassettes = req.query.numOfCassettes
|
||||
const numOfRecyclers = req.query.numOfRecyclers
|
||||
|
||||
return pairing
|
||||
.pair(token, deviceId, model, numOfCassettes, numOfRecyclers)
|
||||
.then(isValid => {
|
||||
if (isValid) return res.json({ status: 'paired' })
|
||||
throw httpError('Pairing failed')
|
||||
})
|
||||
.catch(next)
|
||||
}
|
||||
|
||||
router.post('/pair', populateDeviceId, pair)
|
||||
router.get('/ca', ca)
|
||||
|
||||
module.exports = router
|
||||
24
packages/server/lib/routes/performanceRoutes.js
Normal file
24
packages/server/lib/routes/performanceRoutes.js
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
const express = require('express')
|
||||
const router = express.Router()
|
||||
|
||||
const {
|
||||
updateNetworkHeartbeat,
|
||||
updateNetworkPerformance,
|
||||
} = require('../machine-loader')
|
||||
|
||||
function networkHeartbeat(req, res, next) {
|
||||
return updateNetworkHeartbeat(req.deviceId, req.body)
|
||||
.then(() => res.status(200).send({ status: 'OK' }))
|
||||
.catch(next)
|
||||
}
|
||||
|
||||
function networkPerformance(req, res, next) {
|
||||
return updateNetworkPerformance(req.deviceId, req.body)
|
||||
.then(() => res.status(200).send({ status: 'OK' }))
|
||||
.catch(next)
|
||||
}
|
||||
|
||||
router.post('/heartbeat', networkHeartbeat)
|
||||
router.post('/performance', networkPerformance)
|
||||
|
||||
module.exports = router
|
||||
20
packages/server/lib/routes/probeLnRoutes.js
Normal file
20
packages/server/lib/routes/probeLnRoutes.js
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
const express = require('express')
|
||||
const router = express.Router()
|
||||
|
||||
const plugins = require('../plugins')
|
||||
const settingsLoader = require('../new-settings-loader')
|
||||
|
||||
function probe(req, res, next) {
|
||||
// TODO: why req.settings is undefined?
|
||||
settingsLoader.load().then(settings => {
|
||||
const pi = plugins(settings, req.deviceId)
|
||||
return pi
|
||||
.probeLN('LN', req.body.address)
|
||||
.then(r => res.status(200).send({ hardLimits: r }))
|
||||
.catch(next)
|
||||
})
|
||||
}
|
||||
|
||||
router.get('/', probe)
|
||||
|
||||
module.exports = router
|
||||
16
packages/server/lib/routes/stateRoutes.js
Normal file
16
packages/server/lib/routes/stateRoutes.js
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
const express = require('express')
|
||||
const router = express.Router()
|
||||
|
||||
const helpers = require('../route-helpers')
|
||||
const respond = require('../respond')
|
||||
|
||||
function stateChange(req, res, next) {
|
||||
helpers
|
||||
.stateChange(req.deviceId, req.deviceTime, req.body)
|
||||
.then(() => respond(req, res))
|
||||
.catch(next)
|
||||
}
|
||||
|
||||
router.post('/', stateChange)
|
||||
|
||||
module.exports = router
|
||||
32
packages/server/lib/routes/termsAndConditionsRoutes.js
Normal file
32
packages/server/lib/routes/termsAndConditionsRoutes.js
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
const express = require('express')
|
||||
const nmd = require('nano-markdown')
|
||||
|
||||
const router = express.Router()
|
||||
|
||||
const configManager = require('../new-config-manager')
|
||||
const { load } = require('../new-settings-loader')
|
||||
|
||||
const createTerms = terms =>
|
||||
terms.active && terms.text
|
||||
? {
|
||||
delay: terms.delay,
|
||||
active: terms.active,
|
||||
tcPhoto: terms.tcPhoto,
|
||||
title: terms.title,
|
||||
text: nmd(terms.text),
|
||||
accept: terms.acceptButtonText,
|
||||
cancel: terms.cancelButtonText,
|
||||
}
|
||||
: null
|
||||
|
||||
function getTermsConditions(req, res, next) {
|
||||
const { config } = req.settings
|
||||
const terms = configManager.getTermsConditions(config)
|
||||
return load()
|
||||
.then(({ version }) => res.json({ terms: createTerms(terms), version }))
|
||||
.catch(next)
|
||||
}
|
||||
|
||||
router.get('/', getTermsConditions)
|
||||
|
||||
module.exports = router
|
||||
89
packages/server/lib/routes/txRoutes.js
Normal file
89
packages/server/lib/routes/txRoutes.js
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
const express = require('express')
|
||||
const router = express.Router()
|
||||
const _ = require('lodash/fp')
|
||||
|
||||
const dbErrorCodes = require('../db-error-codes')
|
||||
const E = require('../error')
|
||||
const helpers = require('../route-helpers')
|
||||
const httpError = require('../route-helpers').httpError
|
||||
const logger = require('../logger')
|
||||
const plugins = require('../plugins')
|
||||
const Tx = require('../tx')
|
||||
|
||||
function postTx(req, res, next) {
|
||||
const pi = plugins(req.settings, req.deviceId)
|
||||
|
||||
return Tx.post(_.set('deviceId', req.deviceId, req.body), pi)
|
||||
.then(tx => {
|
||||
if (tx.errorCode) {
|
||||
logger.error(tx.error)
|
||||
switch (tx.errorCode) {
|
||||
case 'InsufficientFundsError':
|
||||
throw httpError(tx.error, 570)
|
||||
case 'scoreThresholdReached':
|
||||
throw httpError(tx.error, 571)
|
||||
case 'walletScoringError':
|
||||
throw httpError(tx.error, 572)
|
||||
default:
|
||||
throw httpError(tx.error, 500)
|
||||
}
|
||||
}
|
||||
|
||||
return res.json(tx)
|
||||
})
|
||||
.catch(err => {
|
||||
// 204 so that l-m can ignore the error
|
||||
// this is fine because the request is polled and will be retried if needed.
|
||||
if (err.code === dbErrorCodes.SERIALIZATION_FAILURE) {
|
||||
logger.warn('Harmless DB conflict, the query will be retried.')
|
||||
return res.status(204).json({})
|
||||
}
|
||||
if (err instanceof E.StaleTxError)
|
||||
return res.status(409).json({ errorType: 'stale' })
|
||||
if (err instanceof E.RatchetError)
|
||||
return res.status(409).json({ errorType: 'ratchet' })
|
||||
|
||||
throw err
|
||||
})
|
||||
.catch(next)
|
||||
}
|
||||
|
||||
function getTx(req, res, next) {
|
||||
if (req.query.status) {
|
||||
return helpers
|
||||
.fetchStatusTx(req.params.id, req.query.status)
|
||||
.then(r => res.json(r))
|
||||
.catch(err => {
|
||||
if (err.name === 'HTTPError') {
|
||||
return res.status(err.code).send(err.message)
|
||||
}
|
||||
next(err)
|
||||
})
|
||||
}
|
||||
|
||||
return next(httpError('Not Found', 404))
|
||||
}
|
||||
|
||||
function getPhoneOrEmailTx(req, res, next) {
|
||||
if (req.query.phone) {
|
||||
return helpers
|
||||
.fetchPhoneTx(req.query.phone)
|
||||
.then(r => res.json(r))
|
||||
.catch(next)
|
||||
}
|
||||
|
||||
if (req.query.email) {
|
||||
return helpers
|
||||
.fetchEmailTx(req.query.email)
|
||||
.then(r => res.json(r))
|
||||
.catch(next)
|
||||
}
|
||||
|
||||
return next(httpError('Not Found', 404))
|
||||
}
|
||||
|
||||
router.post('/', postTx)
|
||||
router.get('/:id', getTx)
|
||||
router.get('/', getPhoneOrEmailTx)
|
||||
|
||||
module.exports = router
|
||||
26
packages/server/lib/routes/unitsRoutes.js
Normal file
26
packages/server/lib/routes/unitsRoutes.js
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
const express = require('express')
|
||||
const { emptyMachineUnits, refillMachineUnits } = require('../machine-loader')
|
||||
const router = express.Router()
|
||||
|
||||
const emptyUnitUpdateCounts = (req, res, next) => {
|
||||
const deviceId = req.deviceId
|
||||
const { units: newUnits, fiatCode } = req.body
|
||||
|
||||
return emptyMachineUnits({ deviceId, newUnits: newUnits, fiatCode })
|
||||
.then(() => res.status(200).send({ status: 'OK' }))
|
||||
.catch(next)
|
||||
}
|
||||
|
||||
const refillUnitUpdateCounts = (req, res, next) => {
|
||||
const deviceId = req.deviceId
|
||||
const { units: newUnits } = req.body
|
||||
|
||||
return refillMachineUnits({ deviceId, newUnits: newUnits })
|
||||
.then(() => res.status(200).send({ status: 'OK' }))
|
||||
.catch(next)
|
||||
}
|
||||
|
||||
router.post('/empty', emptyUnitUpdateCounts)
|
||||
router.post('/refill', refillUnitUpdateCounts)
|
||||
|
||||
module.exports = router
|
||||
46
packages/server/lib/routes/verifyPromoCodeRoutes.js
Normal file
46
packages/server/lib/routes/verifyPromoCodeRoutes.js
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
const express = require('express')
|
||||
const router = express.Router()
|
||||
|
||||
const BN = require('../bn')
|
||||
const commissionMath = require('../commission-math')
|
||||
const configManager = require('../new-config-manager')
|
||||
const loyalty = require('../loyalty')
|
||||
const respond = require('../respond')
|
||||
|
||||
function verifyPromoCode(req, res, next) {
|
||||
loyalty
|
||||
.getPromoCode(req.body.codeInput)
|
||||
.then(promoCode => {
|
||||
if (!promoCode) return next()
|
||||
|
||||
const transaction = req.body.tx
|
||||
const commissions = configManager.getCommissions(
|
||||
transaction.cryptoCode,
|
||||
req.deviceId,
|
||||
req.settings.config,
|
||||
)
|
||||
const tickerRate = new BN(transaction.rawTickerPrice)
|
||||
const discount = commissionMath.getDiscountRate(
|
||||
promoCode.discount,
|
||||
commissions[transaction.direction],
|
||||
)
|
||||
const rates = {
|
||||
[transaction.cryptoCode]: {
|
||||
[transaction.direction]:
|
||||
transaction.direction === 'cashIn'
|
||||
? tickerRate.times(discount).decimalPlaces(5)
|
||||
: tickerRate.div(discount).decimalPlaces(5),
|
||||
},
|
||||
}
|
||||
|
||||
respond(req, res, {
|
||||
promoCode: promoCode,
|
||||
newRates: rates,
|
||||
})
|
||||
})
|
||||
.catch(next)
|
||||
}
|
||||
|
||||
router.post('/', verifyPromoCode)
|
||||
|
||||
module.exports = router
|
||||
16
packages/server/lib/routes/verifyTxRoutes.js
Normal file
16
packages/server/lib/routes/verifyTxRoutes.js
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
const express = require('express')
|
||||
const router = express.Router()
|
||||
|
||||
const plugins = require('../plugins')
|
||||
const respond = require('../respond')
|
||||
|
||||
function verifyTx(req, res, next) {
|
||||
const pi = plugins(req.settings, req.deviceId)
|
||||
pi.verifyTransaction(req.body)
|
||||
.then(idResult => respond(req, res, idResult))
|
||||
.catch(next)
|
||||
}
|
||||
|
||||
router.post('/', verifyTx)
|
||||
|
||||
module.exports = router
|
||||
16
packages/server/lib/routes/verifyUserRoutes.js
Normal file
16
packages/server/lib/routes/verifyUserRoutes.js
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
const express = require('express')
|
||||
const router = express.Router()
|
||||
|
||||
const plugins = require('../plugins')
|
||||
const respond = require('../respond')
|
||||
|
||||
function verifyUser(req, res, next) {
|
||||
const pi = plugins(req.settings, req.deviceId)
|
||||
pi.verifyUser(req.body)
|
||||
.then(idResult => respond(req, res, idResult))
|
||||
.catch(next)
|
||||
}
|
||||
|
||||
router.post('/', verifyUser)
|
||||
|
||||
module.exports = router
|
||||
Loading…
Add table
Add a link
Reference in a new issue