feat: Create migration from old config to new (#424)
* fix: adapt old settings loader to the new schema (filter schema_version) feat: migrate commissions globals feat: migrate locales refactor: generalize the old fields search chore: created functions signatures for all config migrations feat: created wallet migration feat: migrate operator info feat: migrate coin atm radar feat: migrate terms and conditions feat: migrate commissions overrides fix: removed the wallet_COIN_active field (don't exist anymore) chore: moved the config-migration lib to the lib folder feat: migrate cashout configurations feat: migrate notifications globals feat: export migration function feat: migrate most of notifications scoped configs fix: added the missing text property to the terms and conditions migration feat: migrate compliance triggers feat: migrate receipt printing feat: migrate accounts chore: remove test code form module refactor: change some functions naming fix: set default trigger type to 'volume' feat: added threshold days (default 1) to triggers fix: removed strike from the accounts importing refactor: cleaner code on fixed properties feat: avoid repeated crypto/machine pairs on the commissions overrides migrations refactor: make renameAccountFields function internal to the account migration function fix: migrate all crypto scoped commission overrides * fix: return plain objects from functions to make the jsons more readable fix: fix bitgo fields casing fix: improve commissions migration function readability refactor: standard styling * feat: add fallback values to the migration * feat: created db migration for the new config * feat: create migration to move machine names from file to db fix: updates machine names before the config migration fix: load machineLoader fix: create a param to ignore the schema version when loading the latest config using the old loader * refactor: remove unnecessary arguments on createTrigger function fix: check if there's an smsVerificationThreshold configured prior to migrating triggers * fix: migrate triggers with the correct thresholds and verify if they're valid
This commit is contained in:
parent
3c6f547349
commit
ccf7eacfad
8 changed files with 546 additions and 42 deletions
|
|
@ -1,13 +1,14 @@
|
||||||
const _ = require('lodash/fp')
|
const _ = require('lodash/fp')
|
||||||
|
|
||||||
const db = require('../db')
|
const db = require('../db')
|
||||||
|
const configValidate = require('../config-validate')
|
||||||
const config = require('./config')
|
const config = require('./config')
|
||||||
const ph = require('../plugin-helper')
|
const ph = require('../plugin-helper')
|
||||||
|
|
||||||
const schemas = ph.loadSchemas()
|
const schemas = ph.loadSchemas()
|
||||||
|
|
||||||
function fetchAccounts () {
|
function fetchAccounts () {
|
||||||
return db.oneOrNone('select data from user_config where type=$1', ['accounts'])
|
return db.oneOrNone('select data from user_config where type=$1 and schema_version=$2', ['accounts', configValidate.SETTINGS_LOADER_SCHEMA_VERSION])
|
||||||
.then(row => {
|
.then(row => {
|
||||||
// Hard code this for now
|
// Hard code this for now
|
||||||
const accounts = [{
|
const accounts = [{
|
||||||
|
|
@ -91,7 +92,7 @@ function getAccount (accountCode) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function save (accounts) {
|
function save (accounts) {
|
||||||
return db.none('update user_config set data=$1 where type=$2', [{accounts: accounts}, 'accounts'])
|
return db.none('update user_config set data=$1 where type=$2 and schema_version=$3', [{accounts: accounts}, 'accounts', configValidate.SETTINGS_LOADER_SCHEMA_VERSION])
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateAccounts (newAccount, accounts) {
|
function updateAccounts (newAccount, accounts) {
|
||||||
|
|
|
||||||
|
|
@ -17,10 +17,10 @@ function fetchSchema () {
|
||||||
}
|
}
|
||||||
|
|
||||||
function fetchConfig () {
|
function fetchConfig () {
|
||||||
const sql = `select data from user_config where type=$1
|
const sql = `select data from user_config where type=$1 and schema_version=$2
|
||||||
order by id desc limit 1`
|
order by id desc limit 1`
|
||||||
|
|
||||||
return db.oneOrNone(sql, ['config'])
|
return db.oneOrNone(sql, ['config', configValidate.SETTINGS_LOADER_SCHEMA_VERSION])
|
||||||
.then(row => row ? row.data.config : [])
|
.then(row => row ? row.data.config : [])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
458
lib/config-migration.js
Normal file
458
lib/config-migration.js
Normal file
|
|
@ -0,0 +1,458 @@
|
||||||
|
const _ = require('lodash/fp')
|
||||||
|
const uuid = require('uuid')
|
||||||
|
const { COINS } = require('../lib/new-admin/config/coins')
|
||||||
|
const { scopedValue } = require('./config-manager')
|
||||||
|
|
||||||
|
const GLOBAL = 'global'
|
||||||
|
const ALL_CRYPTOS = _.values(COINS).sort()
|
||||||
|
const ALL_MACHINES = 'ALL_MACHINES'
|
||||||
|
|
||||||
|
const GLOBAL_SCOPE = {
|
||||||
|
crypto: ALL_CRYPTOS,
|
||||||
|
machine: GLOBAL
|
||||||
|
}
|
||||||
|
|
||||||
|
function getConfigFields (codes, config) {
|
||||||
|
const stringfiedGlobalScope = JSON.stringify(GLOBAL_SCOPE)
|
||||||
|
|
||||||
|
const fields = config
|
||||||
|
.filter(i => codes.includes(i.fieldLocator.code))
|
||||||
|
.map(f => {
|
||||||
|
const crypto = Array.isArray(f.fieldLocator.fieldScope.crypto)
|
||||||
|
? f.fieldLocator.fieldScope.crypto.sort()
|
||||||
|
: f.fieldLocator.fieldScope.crypto === GLOBAL
|
||||||
|
? ALL_CRYPTOS
|
||||||
|
: [f.fieldLocator.fieldScope.crypto]
|
||||||
|
const machine = f.fieldLocator.fieldScope.machine
|
||||||
|
|
||||||
|
return {
|
||||||
|
code: f.fieldLocator.code,
|
||||||
|
scope: {
|
||||||
|
crypto,
|
||||||
|
machine
|
||||||
|
},
|
||||||
|
value: f.fieldValue.value
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.filter(f => f.value != null)
|
||||||
|
|
||||||
|
const grouped = _.chain(fields)
|
||||||
|
.groupBy(f => JSON.stringify(f.scope))
|
||||||
|
.value()
|
||||||
|
|
||||||
|
return {
|
||||||
|
global: grouped[stringfiedGlobalScope] || [],
|
||||||
|
scoped:
|
||||||
|
_.entries(
|
||||||
|
_.chain(grouped)
|
||||||
|
.omit([stringfiedGlobalScope])
|
||||||
|
.value()
|
||||||
|
).map(f => {
|
||||||
|
const fallbackValues =
|
||||||
|
_.difference(codes, f[1].map(v => v.code))
|
||||||
|
.map(v => ({
|
||||||
|
code: v,
|
||||||
|
scope: JSON.parse(f[0]),
|
||||||
|
value: scopedValue(f[0].crypto, f[0].machine, v, config)
|
||||||
|
}))
|
||||||
|
.filter(f => f.value != null)
|
||||||
|
|
||||||
|
return {
|
||||||
|
scope: JSON.parse(f[0]),
|
||||||
|
values: f[1].concat(fallbackValues)
|
||||||
|
}
|
||||||
|
}) || []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function migrateCommissions (config) {
|
||||||
|
const areArraysEquals = (arr1, arr2) => _.isEmpty(_.xor(arr1, arr2))
|
||||||
|
const getMachine = _.get('scope.machine')
|
||||||
|
const getCrypto = _.get('scope.crypto')
|
||||||
|
const flattenCoins = _.compose(_.flatten, _.map(getCrypto))
|
||||||
|
const diffAllCryptos = _.compose(_.difference(ALL_CRYPTOS))
|
||||||
|
|
||||||
|
const codes = {
|
||||||
|
minimumTx: 'minimumTx',
|
||||||
|
cashInFee: 'fixedFee',
|
||||||
|
cashInCommission: 'cashIn',
|
||||||
|
cashOutCommission: 'cashOut'
|
||||||
|
}
|
||||||
|
|
||||||
|
const { global, scoped } = getConfigFields(_.keys(codes), config)
|
||||||
|
|
||||||
|
const machineAndCryptoScoped = scoped.filter(
|
||||||
|
f => f.scope.machine !== GLOBAL_SCOPE.machine && f.scope.crypto.length === 1
|
||||||
|
)
|
||||||
|
const cryptoScoped = scoped.filter(
|
||||||
|
f =>
|
||||||
|
f.scope.machine === GLOBAL_SCOPE.machine &&
|
||||||
|
!areArraysEquals(f.scope.crypto, GLOBAL_SCOPE.crypto)
|
||||||
|
)
|
||||||
|
const machineScoped = scoped.filter(
|
||||||
|
f =>
|
||||||
|
f.scope.machine !== GLOBAL_SCOPE.machine &&
|
||||||
|
areArraysEquals(f.scope.crypto, GLOBAL_SCOPE.crypto)
|
||||||
|
)
|
||||||
|
|
||||||
|
const withCryptoScoped = machineAndCryptoScoped.concat(cryptoScoped)
|
||||||
|
|
||||||
|
const filteredMachineScoped = _.map(it => {
|
||||||
|
const filterByMachine = _.filter(_.includes(getMachine(it)))
|
||||||
|
const unrepeatedCryptos = _.compose(
|
||||||
|
diffAllCryptos,
|
||||||
|
flattenCoins,
|
||||||
|
filterByMachine
|
||||||
|
)(withCryptoScoped)
|
||||||
|
|
||||||
|
return _.set('scope.crypto', unrepeatedCryptos)(it)
|
||||||
|
})(machineScoped)
|
||||||
|
|
||||||
|
const allCommissionsOverrides = withCryptoScoped.concat(filteredMachineScoped)
|
||||||
|
|
||||||
|
return {
|
||||||
|
..._.fromPairs(global.map(f => [`commissions_${codes[f.code]}`, f.value])),
|
||||||
|
...(allCommissionsOverrides.length > 0 && {
|
||||||
|
commissions_overrides: allCommissionsOverrides.map(s => ({
|
||||||
|
..._.fromPairs(s.values.map(f => [codes[f.code], f.value])),
|
||||||
|
machine: s.scope.machine === GLOBAL ? ALL_MACHINES : s.scope.machine,
|
||||||
|
cryptoCurrencies: s.scope.crypto,
|
||||||
|
id: uuid.v4()
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function migrateLocales (config) {
|
||||||
|
const codes = {
|
||||||
|
country: 'country',
|
||||||
|
fiatCurrency: 'fiatCurrency',
|
||||||
|
machineLanguages: 'languages',
|
||||||
|
cryptoCurrencies: 'cryptoCurrencies'
|
||||||
|
}
|
||||||
|
|
||||||
|
const { global, scoped } = getConfigFields(_.keys(codes), config)
|
||||||
|
|
||||||
|
return {
|
||||||
|
..._.fromPairs(global.map(f => [`locale_${codes[f.code]}`, f.value])),
|
||||||
|
...(scoped.length > 0 && {
|
||||||
|
locale_overrides: scoped.map(s => ({
|
||||||
|
..._.fromPairs(s.values.map(f => [codes[f.code], f.value])),
|
||||||
|
machine: s.scope.machine,
|
||||||
|
id: uuid.v4()
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO new-admin: virtualCashOutDenomination
|
||||||
|
function migrateCashOut (config) {
|
||||||
|
const globalCodes = {
|
||||||
|
fudgeFactorActive: 'fudgeFactorActive'
|
||||||
|
}
|
||||||
|
|
||||||
|
const scopedCodes = {
|
||||||
|
cashOutEnabled: 'active',
|
||||||
|
topCashOutDenomination: 'top',
|
||||||
|
bottomCashOutDenomination: 'bottom',
|
||||||
|
zeroConfLimit: 'zeroConfLimit'
|
||||||
|
}
|
||||||
|
|
||||||
|
const { global } = getConfigFields(_.keys(globalCodes), config)
|
||||||
|
const { scoped } = getConfigFields(_.keys(scopedCodes), config)
|
||||||
|
|
||||||
|
return {
|
||||||
|
..._.fromPairs(
|
||||||
|
global.map(f => [`cashout_${globalCodes[f.code]}`, f.value])
|
||||||
|
),
|
||||||
|
..._.fromPairs(
|
||||||
|
_.flatten(
|
||||||
|
scoped.map(s => {
|
||||||
|
const fields = s.values.map(f => [
|
||||||
|
`cashout_${f.scope.machine}_${scopedCodes[f.code]}`,
|
||||||
|
f.value
|
||||||
|
])
|
||||||
|
|
||||||
|
fields.push([`cashout_${s.scope.machine}_id`, s.scope.machine])
|
||||||
|
|
||||||
|
return fields
|
||||||
|
})
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function migrateNotifications (config) {
|
||||||
|
const globalCodes = {
|
||||||
|
notificationsEmailEnabled: 'email_active',
|
||||||
|
notificationsSMSEnabled: 'sms_active',
|
||||||
|
cashOutCassette1AlertThreshold: 'fiatBalanceCassette1',
|
||||||
|
cashOutCassette2AlertThreshold: 'fiatBalanceCassette2',
|
||||||
|
cryptoAlertThreshold: 'cryptoLowBalance'
|
||||||
|
}
|
||||||
|
|
||||||
|
const machineScopedCodes = {
|
||||||
|
cashOutCassette1AlertThreshold: 'cassette1',
|
||||||
|
cashOutCassette2AlertThreshold: 'cassette2'
|
||||||
|
}
|
||||||
|
|
||||||
|
const cryptoScopedCodes = {
|
||||||
|
cryptoAlertThreshold: 'lowBalance'
|
||||||
|
}
|
||||||
|
|
||||||
|
const { global } = getConfigFields(_.keys(globalCodes), config)
|
||||||
|
const machineScoped = getConfigFields(
|
||||||
|
_.keys(machineScopedCodes),
|
||||||
|
config
|
||||||
|
).scoped.filter(f => f.scope.crypto === GLOBAL && f.scope.machine !== GLOBAL)
|
||||||
|
const cryptoScoped = getConfigFields(
|
||||||
|
_.keys(cryptoScopedCodes),
|
||||||
|
config
|
||||||
|
).scoped.filter(f => f.scope.crypto !== GLOBAL && f.scope.machine === GLOBAL)
|
||||||
|
|
||||||
|
return {
|
||||||
|
..._.fromPairs(
|
||||||
|
global.map(f => [`notifications_${globalCodes[f.code]}`, f.value])
|
||||||
|
),
|
||||||
|
notifications_email_balance: true,
|
||||||
|
notifications_email_transactions: true,
|
||||||
|
notifications_email_compliance: true,
|
||||||
|
notifications_email_errors: true,
|
||||||
|
notifications_sms_balance: true,
|
||||||
|
notifications_sms_transactions: true,
|
||||||
|
notifications_sms_compliance: true,
|
||||||
|
notifications_sms_errors: true,
|
||||||
|
...(machineScoped.length > 0 && {
|
||||||
|
notifications_fiatBalanceOverrides: machineScoped.map(s => ({
|
||||||
|
..._.fromPairs(
|
||||||
|
s.values.map(f => [machineScopedCodes[f.code], f.value])
|
||||||
|
),
|
||||||
|
machine: s.scope.machine,
|
||||||
|
id: uuid.v4()
|
||||||
|
}))
|
||||||
|
}),
|
||||||
|
...(cryptoScoped.length > 0 && {
|
||||||
|
notifications_cryptoBalanceOverrides: cryptoScoped.map(s => ({
|
||||||
|
..._.fromPairs(s.values.map(f => [cryptoScopedCodes[f.code], f.value])),
|
||||||
|
cryptoCurrency: s.scope.crypto,
|
||||||
|
id: uuid.v4()
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function migrateWallet (config) {
|
||||||
|
const codes = {
|
||||||
|
ticker: 'ticker',
|
||||||
|
wallet: 'wallet',
|
||||||
|
exchange: 'exchange',
|
||||||
|
zeroConf: 'zeroConf'
|
||||||
|
}
|
||||||
|
|
||||||
|
const { scoped } = getConfigFields(_.keys(codes), config)
|
||||||
|
|
||||||
|
return {
|
||||||
|
...(scoped.length > 0 &&
|
||||||
|
_.fromPairs(
|
||||||
|
_.flatten(
|
||||||
|
scoped.map(s =>
|
||||||
|
s.values.map(f => [
|
||||||
|
`wallets_${f.scope.crypto}_${codes[f.code]}`,
|
||||||
|
f.value
|
||||||
|
])
|
||||||
|
)
|
||||||
|
)
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function migrateOperatorInfo (config) {
|
||||||
|
const codes = {
|
||||||
|
operatorInfoActive: 'active',
|
||||||
|
operatorInfoEmail: 'email',
|
||||||
|
operatorInfoName: 'name',
|
||||||
|
operatorInfoPhone: 'phone',
|
||||||
|
operatorInfoWebsite: 'website',
|
||||||
|
operatorInfoCompanyNumber: 'companyNumber'
|
||||||
|
}
|
||||||
|
|
||||||
|
const { global } = getConfigFields(_.keys(codes), config)
|
||||||
|
|
||||||
|
return {
|
||||||
|
..._.fromPairs(global.map(f => [`operatorInfo_${codes[f.code]}`, f.value]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function migrateReceiptPrinting (config) {
|
||||||
|
const codes = {
|
||||||
|
receiptPrintingActive: 'active'
|
||||||
|
}
|
||||||
|
|
||||||
|
const { global } = getConfigFields(_.keys(codes), config)
|
||||||
|
|
||||||
|
return {
|
||||||
|
..._.fromPairs(global.map(f => [`receipt_${codes[f.code]}`, f.value])),
|
||||||
|
receipt_operatorWebsite: true,
|
||||||
|
receipt_operatorEmail: true,
|
||||||
|
receipt_operatorPhone: true,
|
||||||
|
receipt_companyRegistration: true,
|
||||||
|
receipt_machineLocation: true,
|
||||||
|
receipt_customerNameOrPhoneNumber: true,
|
||||||
|
receipt_exchangeRate: true,
|
||||||
|
receipt_addressQRCode: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function migrateCoinATMRadar (config) {
|
||||||
|
const codes = ['coinAtmRadarActive', 'coinAtmRadarShowRates']
|
||||||
|
|
||||||
|
const { global } = getConfigFields(codes, config)
|
||||||
|
const coinAtmRadar = _.fromPairs(global.map(f => [f.code, f.value]))
|
||||||
|
|
||||||
|
return {
|
||||||
|
coinAtmRadar_active: coinAtmRadar.coinAtmRadarActive,
|
||||||
|
coinAtmRadar_commissions: coinAtmRadar.coinAtmRadarShowRates,
|
||||||
|
coinAtmRadar_limitsAndVerification: coinAtmRadar.coinAtmRadarShowRates
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function migrateTermsAndConditions (config) {
|
||||||
|
const codes = {
|
||||||
|
termsScreenActive: 'active',
|
||||||
|
termsScreenTitle: 'title',
|
||||||
|
termsScreenText: 'text',
|
||||||
|
termsAcceptButtonText: 'acceptButtonText',
|
||||||
|
termsCancelButtonText: 'cancelButtonText'
|
||||||
|
}
|
||||||
|
|
||||||
|
const { global } = getConfigFields(_.keys(codes), config)
|
||||||
|
|
||||||
|
return {
|
||||||
|
..._.fromPairs(
|
||||||
|
global.map(f => [`termsConditions_${codes[f.code]}`, f.value])
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO new-admin: rejectAddressReuseActive
|
||||||
|
function migrateComplianceTriggers (config) {
|
||||||
|
const triggerTypes = {
|
||||||
|
amount: 'txAmount',
|
||||||
|
velocity: 'txVelocity',
|
||||||
|
volume: 'txVolume',
|
||||||
|
consecutiveDays: 'consecutiveDays'
|
||||||
|
}
|
||||||
|
|
||||||
|
const requirements = {
|
||||||
|
sms: 'sms',
|
||||||
|
idData: 'idData',
|
||||||
|
idPhoto: 'idPhoto',
|
||||||
|
facePhoto: 'facePhoto',
|
||||||
|
sanctions: 'sanctions'
|
||||||
|
}
|
||||||
|
|
||||||
|
function createTrigger (
|
||||||
|
requirement,
|
||||||
|
threshold
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
id: uuid.v4(),
|
||||||
|
cashDirection: 'both',
|
||||||
|
threshold,
|
||||||
|
thresholdDays: 1,
|
||||||
|
triggerType: triggerTypes.volume,
|
||||||
|
requirement
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const codes = [
|
||||||
|
'smsVerificationActive',
|
||||||
|
'smsVerificationThreshold',
|
||||||
|
'idCardDataVerificationActive',
|
||||||
|
'idCardDataVerificationThreshold',
|
||||||
|
'idCardPhotoVerificationActive',
|
||||||
|
'idCardPhotoVerificationThreshold',
|
||||||
|
'frontCameraVerificationActive',
|
||||||
|
'frontCameraVerificationThreshold',
|
||||||
|
'sanctionsVerificationActive',
|
||||||
|
'sanctionsVerificationThreshold'
|
||||||
|
]
|
||||||
|
|
||||||
|
const global = _.fromPairs(
|
||||||
|
getConfigFields(codes, config).global.map(f => [f.code, f.value])
|
||||||
|
)
|
||||||
|
|
||||||
|
const triggers = []
|
||||||
|
if (global.smsVerificationActive && _.isNumber(global.smsVerificationThreshold)) {
|
||||||
|
triggers.push(
|
||||||
|
createTrigger(requirements.sms, global.smsVerificationThreshold)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (global.idCardDataVerificationActive && _.isNumber(global.idCardDataVerificationThreshold)) {
|
||||||
|
triggers.push(
|
||||||
|
createTrigger(requirements.idData, global.idCardDataVerificationThreshold)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (global.idCardPhotoVerificationActive && _.isNumber(global.idCardPhotoVerificationThreshold)) {
|
||||||
|
triggers.push(
|
||||||
|
createTrigger(requirements.idPhoto, global.idCardPhotoVerificationThreshold)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (global.frontCameraVerificationActive && _.isNumber(global.frontCameraVerificationThreshold)) {
|
||||||
|
triggers.push(
|
||||||
|
createTrigger(requirements.facePhoto, global.frontCameraVerificationThreshold)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (global.sanctionsVerificationActive && _.isNumber(global.sanctionsVerificationThreshold)) {
|
||||||
|
triggers.push(
|
||||||
|
createTrigger(requirements.sanctions, global.sanctionsVerificationThreshold)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
triggers
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function migrateConfig (config) {
|
||||||
|
return {
|
||||||
|
...migrateCommissions(config),
|
||||||
|
...migrateLocales(config),
|
||||||
|
...migrateCashOut(config),
|
||||||
|
...migrateNotifications(config),
|
||||||
|
...migrateWallet(config),
|
||||||
|
...migrateOperatorInfo(config),
|
||||||
|
...migrateReceiptPrinting(config),
|
||||||
|
...migrateCoinATMRadar(config),
|
||||||
|
...migrateTermsAndConditions(config),
|
||||||
|
...migrateComplianceTriggers(config)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function migrateAccounts (accounts) {
|
||||||
|
const accountArray = [
|
||||||
|
'bitgo',
|
||||||
|
'bitstamp',
|
||||||
|
'blockcypher',
|
||||||
|
'infura',
|
||||||
|
'itbit',
|
||||||
|
'kraken',
|
||||||
|
'mailgun',
|
||||||
|
'twilio'
|
||||||
|
]
|
||||||
|
|
||||||
|
return _.pick(accountArray)(accounts)
|
||||||
|
}
|
||||||
|
|
||||||
|
function migrate (config, accounts) {
|
||||||
|
return {
|
||||||
|
config: migrateConfig(config),
|
||||||
|
accounts: migrateAccounts(accounts)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
migrateConfig,
|
||||||
|
migrateAccounts,
|
||||||
|
migrate
|
||||||
|
}
|
||||||
|
|
@ -7,6 +7,8 @@ const schema = require('../lamassu-schema.json')
|
||||||
|
|
||||||
const REMOVED_FIELDS = ['crossRefVerificationActive', 'crossRefVerificationThreshold']
|
const REMOVED_FIELDS = ['crossRefVerificationActive', 'crossRefVerificationThreshold']
|
||||||
|
|
||||||
|
const SETTINGS_LOADER_SCHEMA_VERSION = 1
|
||||||
|
|
||||||
function allScopes (cryptoScopes, machineScopes) {
|
function allScopes (cryptoScopes, machineScopes) {
|
||||||
const scopes = []
|
const scopes = []
|
||||||
cryptoScopes.forEach(c => {
|
cryptoScopes.forEach(c => {
|
||||||
|
|
@ -181,4 +183,9 @@ function validate (config) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {validate, ensureConstraints, validateRequires}
|
module.exports = {
|
||||||
|
SETTINGS_LOADER_SCHEMA_VERSION,
|
||||||
|
validate,
|
||||||
|
ensureConstraints,
|
||||||
|
validateRequires
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -54,8 +54,8 @@ function load (versionId) {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadLatest () {
|
function loadLatest (filterSchemaVersion = true) {
|
||||||
return Promise.all([loadLatestConfig(), loadAccounts()])
|
return Promise.all([loadLatestConfig(filterSchemaVersion), loadAccounts(filterSchemaVersion)])
|
||||||
.then(([config, accounts]) => ({
|
.then(([config, accounts]) => ({
|
||||||
config,
|
config,
|
||||||
accounts
|
accounts
|
||||||
|
|
@ -67,10 +67,10 @@ function loadConfig (versionId) {
|
||||||
|
|
||||||
const sql = `select data
|
const sql = `select data
|
||||||
from user_config
|
from user_config
|
||||||
where id=$1 and type=$2
|
where id=$1 and type=$2 and schema_version=$3
|
||||||
and valid`
|
and valid`
|
||||||
|
|
||||||
return db.one(sql, [versionId, 'config'])
|
return db.one(sql, [versionId, 'config', configValidate.SETTINGS_LOADER_SCHEMA_VERSION])
|
||||||
.then(row => row.data.config)
|
.then(row => row.data.config)
|
||||||
.then(configValidate.validate)
|
.then(configValidate.validate)
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
|
|
@ -82,17 +82,17 @@ function loadConfig (versionId) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadLatestConfig () {
|
function loadLatestConfig (filterSchemaVersion = true) {
|
||||||
if (argv.fixture) return loadFixture()
|
if (argv.fixture) return loadFixture()
|
||||||
|
|
||||||
const sql = `select id, valid, data
|
const sql = `select id, valid, data
|
||||||
from user_config
|
from user_config
|
||||||
where type=$1
|
where type=$1 ${filterSchemaVersion ? 'and schema_version=$2' : ''}
|
||||||
and valid
|
and valid
|
||||||
order by id desc
|
order by id desc
|
||||||
limit 1`
|
limit 1`
|
||||||
|
|
||||||
return db.one(sql, ['config'])
|
return db.one(sql, ['config', configValidate.SETTINGS_LOADER_SCHEMA_VERSION])
|
||||||
.then(row => row.data.config)
|
.then(row => row.data.config)
|
||||||
.then(configValidate.validate)
|
.then(configValidate.validate)
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
|
|
@ -109,19 +109,19 @@ function loadRecentConfig () {
|
||||||
|
|
||||||
const sql = `select id, data
|
const sql = `select id, data
|
||||||
from user_config
|
from user_config
|
||||||
where type=$1
|
where type=$1 and schema_version=$2
|
||||||
order by id desc
|
order by id desc
|
||||||
limit 1`
|
limit 1`
|
||||||
|
|
||||||
return db.one(sql, ['config'])
|
return db.one(sql, ['config', configValidate.SETTINGS_LOADER_SCHEMA_VERSION])
|
||||||
.then(row => row.data.config)
|
.then(row => row.data.config)
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadAccounts () {
|
function loadAccounts (filterSchemaVersion = true) {
|
||||||
const toFields = fieldArr => _.fromPairs(_.map(r => [r.code, r.value], fieldArr))
|
const toFields = fieldArr => _.fromPairs(_.map(r => [r.code, r.value], fieldArr))
|
||||||
const toPairs = r => [r.code, toFields(r.fields)]
|
const toPairs = r => [r.code, toFields(r.fields)]
|
||||||
|
|
||||||
return db.oneOrNone('select data from user_config where type=$1', 'accounts')
|
return db.oneOrNone(`select data from user_config where type=$1 ${filterSchemaVersion ? 'and schema_version=$2' : ''}`, ['accounts', configValidate.SETTINGS_LOADER_SCHEMA_VERSION])
|
||||||
.then(function (data) {
|
.then(function (data) {
|
||||||
if (!data) return {}
|
if (!data) return {}
|
||||||
return _.fromPairs(_.map(toPairs, data.data.accounts))
|
return _.fromPairs(_.map(toPairs, data.data.accounts))
|
||||||
|
|
|
||||||
38
migrations/1599523522436-migrate-config.js
Normal file
38
migrations/1599523522436-migrate-config.js
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
const db = require('./db')
|
||||||
|
const settingsLoader = require('../lib/settings-loader')
|
||||||
|
const machineLoader = require('../lib/machine-loader')
|
||||||
|
const { saveConfig, saveAccounts } = require('../lib/new-settings-loader')
|
||||||
|
const { migrate } = require('../lib/config-migration')
|
||||||
|
|
||||||
|
module.exports.up = function (next) {
|
||||||
|
function migrateConfig(settings) {
|
||||||
|
return migrate(settings.config, settings.accounts)
|
||||||
|
.then(newSettings => Promise.all([
|
||||||
|
saveConfig(newSettings.config),
|
||||||
|
saveAccounts(newSettings.accounts)
|
||||||
|
]))
|
||||||
|
.then(() => next())
|
||||||
|
}
|
||||||
|
|
||||||
|
settingsLoader.loadLatest(false)
|
||||||
|
.then(async settings => ({
|
||||||
|
settings,
|
||||||
|
machines: await machineLoader.getMachineNames(settings.config)
|
||||||
|
}))
|
||||||
|
.then(({ settings, machines }) => {
|
||||||
|
const sql = machines
|
||||||
|
? machines.map(m => `update devices set name = '${m.name}' where device_id = '${m.deviceId}'`)
|
||||||
|
: []
|
||||||
|
return db.multi(sql, () => migrateConfig(settings))
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
if (err.message = 'lamassu-server is not configured')
|
||||||
|
next()
|
||||||
|
|
||||||
|
console.log(err.message)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports.down = function (next) {
|
||||||
|
next()
|
||||||
|
}
|
||||||
|
|
@ -9,7 +9,7 @@ module.exports = {migrateNames}
|
||||||
function migrateNames () {
|
function migrateNames () {
|
||||||
const cs = new pgp.helpers.ColumnSet(['?device_id', 'name'], {table: 'devices'})
|
const cs = new pgp.helpers.ColumnSet(['?device_id', 'name'], {table: 'devices'})
|
||||||
|
|
||||||
return settingsLoader.loadLatest()
|
return settingsLoader.loadLatest(false)
|
||||||
.then(r => machineLoader.getMachineNames(r.config))
|
.then(r => machineLoader.getMachineNames(r.config))
|
||||||
.then(_.map(r => ({device_id: r.deviceId, name: r.name})))
|
.then(_.map(r => ({device_id: r.deviceId, name: r.name})))
|
||||||
.then(data => pgp.helpers.update(data, cs) + ' WHERE t.device_id=v.device_id')
|
.then(data => pgp.helpers.update(data, cs) + ' WHERE t.device_id=v.device_id')
|
||||||
|
|
|
||||||
|
|
@ -30,52 +30,52 @@ export default {
|
||||||
face: true
|
face: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: 'btcWalletId',
|
code: 'BTCWalletId',
|
||||||
display: 'BTC Wallet ID',
|
display: 'BTC Wallet ID',
|
||||||
component: TextInput
|
component: TextInput
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: 'btcWalletPassphrase',
|
code: 'BTCWalletPassphrase',
|
||||||
display: 'BTC Wallet Passphrase',
|
display: 'BTC Wallet Passphrase',
|
||||||
component: SecretInput
|
component: SecretInput
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: 'ltcWalletId',
|
code: 'LTCWalletId',
|
||||||
display: 'LTC Wallet ID',
|
display: 'LTC Wallet ID',
|
||||||
component: TextInput
|
component: TextInput
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: 'ltcWalletPassphrase',
|
code: 'LTCWalletPassphrase',
|
||||||
display: 'LTC Wallet Passphrase',
|
display: 'LTC Wallet Passphrase',
|
||||||
component: SecretInput
|
component: SecretInput
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: 'zecWalletId',
|
code: 'ZECWalletId',
|
||||||
display: 'ZEC Wallet ID',
|
display: 'ZEC Wallet ID',
|
||||||
component: TextInput
|
component: TextInput
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: 'zecWalletPassphrase',
|
code: 'ZECWalletPassphrase',
|
||||||
display: 'ZEC Wallet Passphrase',
|
display: 'ZEC Wallet Passphrase',
|
||||||
component: SecretInput
|
component: SecretInput
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: 'bchWalletId',
|
code: 'BCHWalletId',
|
||||||
display: 'BCH Wallet ID',
|
display: 'BCH Wallet ID',
|
||||||
component: TextInput
|
component: TextInput
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: 'bchWalletPassphrase',
|
code: 'BCHWalletPassphrase',
|
||||||
display: 'BCH Wallet Passphrase',
|
display: 'BCH Wallet Passphrase',
|
||||||
component: SecretInput
|
component: SecretInput
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: 'dashWalletId',
|
code: 'DASHWalletId',
|
||||||
display: 'DASH Wallet ID',
|
display: 'DASH Wallet ID',
|
||||||
component: TextInput
|
component: TextInput
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: 'dashWalletPassphrase',
|
code: 'DASHWalletPassphrase',
|
||||||
display: 'DASH Wallet Passphrase',
|
display: 'DASH Wallet Passphrase',
|
||||||
component: SecretInput
|
component: SecretInput
|
||||||
}
|
}
|
||||||
|
|
@ -84,38 +84,38 @@ export default {
|
||||||
token: Yup.string()
|
token: Yup.string()
|
||||||
.max(100, 'Too long')
|
.max(100, 'Too long')
|
||||||
.required('Required'),
|
.required('Required'),
|
||||||
btcWalletId: Yup.string().max(100, 'Too long'),
|
BTCWalletId: Yup.string().max(100, 'Too long'),
|
||||||
btcWalletPassphrase: Yup.string()
|
BTCWalletPassphrase: Yup.string()
|
||||||
.max(100, 'Too long')
|
.max(100, 'Too long')
|
||||||
.when('btcWalletId', {
|
.when('BTCWalletId', {
|
||||||
is: isDefined,
|
is: isDefined,
|
||||||
then: Yup.string().required()
|
then: Yup.string().required()
|
||||||
}),
|
}),
|
||||||
ltcWalletId: Yup.string().max(100, 'Too long'),
|
LTCWalletId: Yup.string().max(100, 'Too long'),
|
||||||
ltcWalletPassphrase: Yup.string()
|
LTCWalletPassphrase: Yup.string()
|
||||||
.max(100, 'Too long')
|
.max(100, 'Too long')
|
||||||
.when('ltcWalletId', {
|
.when('LTCWalletId', {
|
||||||
is: isDefined,
|
is: isDefined,
|
||||||
then: Yup.string().required()
|
then: Yup.string().required()
|
||||||
}),
|
}),
|
||||||
zecWalletId: Yup.string().max(100, 'Too long'),
|
ZECWalletId: Yup.string().max(100, 'Too long'),
|
||||||
zecWalletPassphrase: Yup.string()
|
ZECWalletPassphrase: Yup.string()
|
||||||
.max(100, 'Too long')
|
.max(100, 'Too long')
|
||||||
.when('zecWalletId', {
|
.when('ZECWalletId', {
|
||||||
is: isDefined,
|
is: isDefined,
|
||||||
then: Yup.string().required()
|
then: Yup.string().required()
|
||||||
}),
|
}),
|
||||||
bchWalletId: Yup.string().max(100, 'Too long'),
|
BCHWalletId: Yup.string().max(100, 'Too long'),
|
||||||
bchWalletPassphrase: Yup.string()
|
BCHWalletPassphrase: Yup.string()
|
||||||
.max(100, 'Too long')
|
.max(100, 'Too long')
|
||||||
.when('bchWalletId', {
|
.when('BCHWalletId', {
|
||||||
is: isDefined,
|
is: isDefined,
|
||||||
then: Yup.string().required()
|
then: Yup.string().required()
|
||||||
}),
|
}),
|
||||||
dashWalletId: Yup.string().max(100, 'Too long'),
|
DASHWalletId: Yup.string().max(100, 'Too long'),
|
||||||
dashWalletPassphrase: Yup.string()
|
DASHWalletPassphrase: Yup.string()
|
||||||
.max(100, 'Too long')
|
.max(100, 'Too long')
|
||||||
.when('dashWalletId', {
|
.when('DASHWalletId', {
|
||||||
is: isDefined,
|
is: isDefined,
|
||||||
then: Yup.string().required()
|
then: Yup.string().required()
|
||||||
}),
|
}),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue