feat: decouple l-s entrypoint

This commit is contained in:
Sérgio Salgado 2022-03-29 15:00:52 +01:00
parent 2a2c1fccc8
commit f4d6b5e454
48 changed files with 411 additions and 232 deletions

1
.env
View file

@ -1 +0,0 @@
LAMASSU_DB=DEV

59
.sample.env Normal file
View file

@ -0,0 +1,59 @@
## Database variables
# Used to describe which database to use. Possible values include: DEV, RELEASE, STRESS_TEST
LAMASSU_DB=
# Postgres related environment variables
POSTGRES_USER=
POSTGRES_PASSWORD=
POSTGRES_HOST=
POSTGRES_PORT=
POSTGRES_DB=
## File paths
# Certificate-related variables
LAMASSU_CA_PATH=
CA_PATH=
CERT_PATH=
KEY_PATH=
# Full path to where the wallet's mnemonic is stored
MNEMONIC_PATH=
MIGRATE_STATE_PATH=
## Directories
BLOCKCHAIN_DIR=
OFAC_DATA_DIR=
ID_PHOTO_CARD_DIR=
FRONT_CAMERA_DIR=
OPERATOR_DATA_DIR=
## URLs
STRIKE_BASE_URL=
COIN_ATM_RADAR_URL=
## OFAC Sources variables
# These variables map to each other, similar to a zip HOF. Entries are separated by commas
# Example:
# OFAC_SOURCES_NAMES=name1,name2
# OFAC_SOURCES_URLS=url1,url2
OFAC_SOURCES_NAMES=
OFAC_SOURCES_URLS=
## Misc
HOSTNAME=
LOG_LEVEL=
LIGHTNING_NETWORK_DAEMON=
## Deprecated or in deprecation
HTTP=
DEV_MODE=
## Uncategorized variables

View file

@ -1,9 +1,13 @@
#!/usr/bin/env node #!/usr/bin/env node
var pgp = require('pg-promise')() const path = require('path')
var psqlUrl = require('../lib/options').postgresql require('dotenv').config({ path: path.resolve(__dirname, '../.env') })
var db = pgp(psqlUrl) var pgp = require('pg-promise')()
const { PSQL_URL } = require('../lib/constants')
var db = pgp(PSQL_URL)
db.manyOrNone(`select * from transactions where incoming=false db.manyOrNone(`select * from transactions where incoming=false
and stage='final_request' and authority='machine'`) and stage='final_request' and authority='machine'`)

View file

@ -2,11 +2,11 @@
'use strict' 'use strict'
const fs = require('fs') const path = require('path')
const options = require('../lib/options-loader')() require('dotenv').config({ path: path.resolve(__dirname, '../.env') })
if (!options.opts.frontCameraDir) { const setEnvVariable = require('../tools/set-env-var')
options.opts.frontCameraDir = '/opt/lamassu-server/frontcamera'
fs.writeFileSync(options.path, JSON.stringify(options.opts, null, '\t'), 'utf8') if (!process.env.FRONT_CAMERA_DIR) {
setEnvVariable('FRONT_CAMERA_DIR', '/opt/lamassu-server/frontcamera')
} }

View file

@ -1,5 +1,7 @@
#!/usr/bin/env node #!/usr/bin/env node
const path = require('path')
require('dotenv').config({ path: path.resolve(__dirname, '../.env') })
const hdkey = require('ethereumjs-wallet/hdkey') const hdkey = require('ethereumjs-wallet/hdkey')
const hkdf = require('futoin-hkdf') const hkdf = require('futoin-hkdf')
const db = require('../lib/db') const db = require('../lib/db')
@ -9,14 +11,14 @@ const mnemonicHelpers = require('../lib/mnemonic-helpers')
const pify = require('pify') const pify = require('pify')
const fs = pify(require('fs')) const fs = pify(require('fs'))
const options = require('../lib/options') const MNEMONIC_PATH = process.env.MNEMONIC_PATH
const defaultPrefixPath = "m/44'/60'/1'/0'" const defaultPrefixPath = "m/44'/60'/1'/0'"
const paymentPrefixPath = "m/44'/60'/0'/0'" const paymentPrefixPath = "m/44'/60'/0'/0'"
const address = process.argv[2] const address = process.argv[2]
if (!options || !options.mnemonicPath) { if (!MNEMONIC_PATH) {
console.error(`Unable to fetch mnemonic from your account!`) console.error(`Unable to fetch mnemonic from your account!`)
process.exit(1) process.exit(1)
} }
@ -47,7 +49,7 @@ function searchForHdIndex (address) {
} }
function fetchMnemonic () { function fetchMnemonic () {
return fs.readFile(options.mnemonicPath, 'utf8') return fs.readFile(MNEMONIC_PATH, 'utf8')
.then(mnemonic => computeSeed(mnemonic)) .then(mnemonic => computeSeed(mnemonic))
} }

View file

@ -1,10 +1,16 @@
#!/usr/bin/env node #!/usr/bin/env node
const FileStore = require('migrate/lib/file-store') const FileStore = require('migrate/lib/file-store')
const _ = require('lodash/fp')
const path = require('path')
require('dotenv').config({ path: path.resolve(__dirname, '../.env') })
const db = require('../lib/db') const db = require('../lib/db')
const migrate = require('../lib/migrate') const migrate = require('../lib/migrate')
const options = require('../lib/options')
const { asyncLocalStorage, defaultStore } = require('../lib/async-storage') const { asyncLocalStorage, defaultStore } = require('../lib/async-storage')
const MIGRATE_STATE_PATH = process.env.MIGRATE_STATE_PATH
const createMigration = `CREATE TABLE IF NOT EXISTS migrations ( const createMigration = `CREATE TABLE IF NOT EXISTS migrations (
id serial PRIMARY KEY, id serial PRIMARY KEY,
data json NOT NULL data json NOT NULL
@ -14,7 +20,7 @@ const select = 'select * from migrations limit 1'
const getMigrateFile = () => { const getMigrateFile = () => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
new FileStore(options.migrateStatePath).load((err, store) => { new FileStore(MIGRATE_STATE_PATH).load((err, store) => {
if (err) return reject(err) if (err) return reject(err)
return resolve(store) return resolve(store)
}) })
@ -26,7 +32,7 @@ asyncLocalStorage.run(store, () => {
db.none(createMigration) db.none(createMigration)
.then(() => Promise.all([db.oneOrNone(select), getMigrateFile()])) .then(() => Promise.all([db.oneOrNone(select), getMigrateFile()]))
.then(([qResult, migrateFile]) => { .then(([qResult, migrateFile]) => {
process.env.SKIP_SERVER_LOGS = !(qResult && qResult.data.migrations.find(({ title }) => title === '1572524820075-server-support-logs.js')) process.env.SKIP_SERVER_LOGS = !(qResult && _.find(({ title }) => title === '1572524820075-server-support-logs.js', qResult.data.migrations ?? []))
if (!qResult && migrateFile) { if (!qResult && migrateFile) {
return db.none('insert into migrations (id, data) values (1, $1)', [migrateFile]) return db.none('insert into migrations (id, data) values (1, $1)', [migrateFile])
} }

View file

@ -1,8 +1,10 @@
#!/usr/bin/env node #!/usr/bin/env node
const fs = require('fs') const fs = require('fs')
const path = require('path')
require('dotenv').config({ path: path.resolve(__dirname, '../.env') })
const options = require('../lib/options') const MNEMONIC_PATH = process.env.MNEMONIC_PATH
const mnemonic = fs.readFileSync(options.mnemonicPath, 'utf8').trim() const mnemonic = fs.readFileSync(MNEMONIC_PATH, 'utf8').trim()
console.log(mnemonic) console.log(mnemonic)

View file

@ -2,22 +2,12 @@
'use strict' 'use strict'
const fs = require('fs') const setEnvVariable = require('../tools/set-env-var')
const ofacSources = [ const path = require('path')
{ require('dotenv').config({ path: path.resolve(__dirname, '../.env') })
name: 'sdn_advanced',
url: 'https://www.treasury.gov/ofac/downloads/sanctions/1.0/sdn_advanced.xml'
},
{
name: 'cons_advanced',
url: 'https://www.treasury.gov/ofac/downloads/sanctions/1.0/cons_advanced.xml'
}
]
const options = require('../lib/options-loader')() if (!process.env.OFAC_SOURCES_NAMES && !process.env.OFAC_SOURCES_URLS) {
setEnvVariable('OFAC_SOURCES_NAMES', 'sdn_advanced,cons_advanced')
if (!options.opts.ofacSources) { setEnvVariable('OFAC_SOURCES_URLS', 'https://www.treasury.gov/ofac/downloads/sanctions/1.0/sdn_advanced.xml,https://www.treasury.gov/ofac/downloads/sanctions/1.0/cons_advanced.xml')
options.opts.ofacSources = ofacSources
fs.writeFileSync(options.path, JSON.stringify(options.opts, null, ' '), 'utf8')
} }

View file

@ -3,10 +3,14 @@
const fs = require('fs') const fs = require('fs')
const hkdf = require('futoin-hkdf') const hkdf = require('futoin-hkdf')
const options = require('../lib/options') const path = require('path')
require('dotenv').config({ path: path.resolve(__dirname, '../.env') })
const mnemonicHelpers = require('../lib/mnemonic-helpers') const mnemonicHelpers = require('../lib/mnemonic-helpers')
const mnemonic = fs.readFileSync(options.mnemonicPath, 'utf8').trim() const MNEMONIC_PATH = process.env.MNEMONIC_PATH
const mnemonic = fs.readFileSync(MNEMONIC_PATH, 'utf8').trim()
const masterSeed = mnemonicHelpers.toEntropyBuffer(mnemonic) const masterSeed = mnemonicHelpers.toEntropyBuffer(mnemonic)
console.log(hkdf(masterSeed, 16, { salt: 'lamassu-server-salt', info: 'operator-id' }).toString('hex')) console.log(hkdf(masterSeed, 16, { salt: 'lamassu-server-salt', info: 'operator-id' }).toString('hex'))

View file

@ -1,16 +1,17 @@
#!/usr/bin/env node #!/usr/bin/env node
const path = require('path')
require('dotenv').config({ path: path.resolve(__dirname, '../.env') })
const { asyncLocalStorage, defaultStore } = require('../lib/async-storage') const { asyncLocalStorage, defaultStore } = require('../lib/async-storage')
const userManagement = require('../lib/new-admin/graphql/modules/userManagement') const userManagement = require('../lib/new-admin/graphql/modules/userManagement')
const authErrors = require('../lib/new-admin/graphql/errors/authentication') const authErrors = require('../lib/new-admin/graphql/errors/authentication')
const options = require('../lib/options')
const name = process.argv[2] const name = process.argv[2]
const role = process.argv[3] const role = process.argv[3]
const domain = options.hostname const domain = process.env.HOSTNAME
if (!domain) { if (!domain) {
console.error('No hostname configured in lamassu.json') console.error('No hostname configured in the environment')
process.exit(1) process.exit(1)
} }

View file

@ -6,25 +6,26 @@ const fs = require('fs')
const path = require('path') const path = require('path')
const os = require('os') const os = require('os')
const mnemonicHelpers = require('../lib/mnemonic-helpers') const mnemonicHelpers = require('../lib/mnemonic-helpers')
const options = require('../lib/options-loader')() const setEnvVariable = require('../tools/set-env-var')
if (!options.opts.mnemonicPath && options.opts.seedPath) { const path = require('path')
const seed = fs.readFileSync(options.opts.seedPath, 'utf8').trim() require('dotenv').config({ path: path.resolve(__dirname, '../.env') })
if (!process.env.MNEMONIC_PATH && process.env.SEED_PATH) {
const seed = fs.readFileSync(process.env.SEED_PATH, 'utf8').trim()
const mnemonic = mnemonicHelpers.fromSeed(seed) const mnemonic = mnemonicHelpers.fromSeed(seed)
if (process.argv[2] === '--prod') { if (process.argv[2] === '--prod') {
options.opts.mnemonicPath = path.resolve('/etc', 'lamassu', 'mnemonics', 'mnemonic.txt') setEnvVariable('MNEMONIC_PATH', path.resolve('/etc', 'lamassu', 'mnemonics', 'mnemonic.txt'))
} else { } else {
options.opts.mnemonicPath = path.resolve(os.homedir(), '.lamassu', 'mnemonics', 'mnemonic.txt') setEnvVariable('MNEMONIC_PATH', path.resolve(os.homedir(), '.lamassu', 'mnemonics', 'mnemonic.txt'))
} }
if (!fs.existsSync(path.dirname(options.opts.mnemonicPath))) { if (!fs.existsSync(path.dirname(process.env.MNEMONIC_PATH))) {
fs.mkdirSync(path.dirname(options.opts.mnemonicPath)) fs.mkdirSync(path.dirname(process.env.MNEMONIC_PATH))
} }
if (!fs.existsSync(options.opts.mnemonicPath)) { if (!fs.existsSync(process.env.MNEMONIC_PATH)) {
fs.writeFileSync(options.opts.mnemonicPath, mnemonic, 'utf8') fs.writeFileSync(process.env.MNEMONIC_PATH, mnemonic, 'utf8')
} }
fs.writeFileSync(options.path, JSON.stringify(options.opts, null, '\t'), 'utf8')
} }

View file

@ -3,9 +3,13 @@
'use strict' 'use strict'
const pgp = require('pg-promise')() const pgp = require('pg-promise')()
const psqlUrl = require('../lib/options').postgresql
const db = pgp(psqlUrl) const path = require('path')
require('dotenv').config({ path: path.resolve(__dirname, '../.env') })
const { PSQL_URL } = require('../lib/constants')
const db = pgp(PSQL_URL)
db.many('select data from user_config', 'exchanges') db.many('select data from user_config', 'exchanges')
.then(rows => { .then(rows => {

View file

@ -1,13 +1,15 @@
#!/usr/bin/env node #!/usr/bin/env node
const path = require('path')
require('dotenv').config({ path: path.resolve(__dirname, '../.env') })
const login = require('../lib/admin/login') const login = require('../lib/admin/login')
const options = require('../lib/options')
const name = process.argv[2] const name = process.argv[2]
const domain = options.hostname const domain = process.env.HOSTNAME
if (!domain) { if (!domain) {
console.error('No hostname configured in lamassu.json') console.error('No hostname configured in the environment')
process.exit(1) process.exit(1)
} }

View file

@ -2,7 +2,7 @@ require('es6-promise').polyfill()
var notifier = require('../lib/notifier') var notifier = require('../lib/notifier')
var db = require('../lib/postgresql_interface') var db = require('../lib/postgresql_interface')
var psqlUrl = require('../lib/options').postgres const { PSQL_URL } = require('../lib/constants')
function getBalances () { function getBalances () {
return [ return [
@ -11,7 +11,7 @@ function getBalances () {
] ]
} }
db.init(psqlUrl) db.init(PSQL_URL)
notifier.init(db, getBalances, {lowBalanceThreshold: 10}) notifier.init(db, getBalances, {lowBalanceThreshold: 10})
console.log('DEBUG0') console.log('DEBUG0')
notifier.checkStatus() notifier.checkStatus()

View file

@ -6,21 +6,21 @@ const fs = require('fs')
const path = require('path') const path = require('path')
const os = require('os') const os = require('os')
const bip39 = require('bip39') const bip39 = require('bip39')
const options = require('../lib/options-loader')() const setEnvVariable = require('../tools/set-env-var')
if (options.opts.mnemonicPath && !options.opts.seedPath) { require('dotenv').config({ path: path.resolve(__dirname, '../.env') })
const mnemonic = fs.readFileSync(options.opts.mnemonicPath, 'utf8')
if (process.env.MNEMONIC_PATH && !process.env.SEED_PATH) {
const mnemonic = fs.readFileSync(process.env.MNEMONIC_PATH, 'utf8')
const seed = bip39.mnemonicToEntropy(mnemonic.split('\n').join(' ').trim()).toString('hex') const seed = bip39.mnemonicToEntropy(mnemonic.split('\n').join(' ').trim()).toString('hex')
options.opts.seedPath = path.resolve(os.homedir(), '.lamassu', 'seeds', 'seed.txt') setEnvVariable('SEED_PATH', path.resolve(os.homedir(), '.lamassu', 'seeds', 'seed.txt'))
if (!fs.existsSync(path.dirname(options.opts.seedPath))) { if (!fs.existsSync(path.dirname(process.env.SEED_PATH))) {
fs.mkdirSync(path.dirname(options.opts.seedPath)) fs.mkdirSync(path.dirname(process.env.SEED_PATH))
} }
if (!fs.existsSync(options.opts.seedPath)) { if (!fs.existsSync(process.env.SEED_PATH)) {
fs.writeFileSync(options.opts.seedPath, seed, 'utf8') fs.writeFileSync(process.env.SEED_PATH, seed, 'utf8')
} }
fs.writeFileSync(options.path, JSON.stringify(options.opts, null, '\t'), 'utf8')
} }

View file

@ -20,7 +20,6 @@ const _ = require('lodash/fp')
const machineLoader = require('../machine-loader') const machineLoader = require('../machine-loader')
const T = require('../time') const T = require('../time')
const logger = require('../logger') const logger = require('../logger')
const options = require('../options')
const accounts = require('./accounts') const accounts = require('./accounts')
const config = require('./config') const config = require('./config')
@ -35,18 +34,20 @@ const supportServer = require('./admin-support')
const NEVER = new Date(Date.now() + 100 * T.years) const NEVER = new Date(Date.now() + 100 * T.years)
const REAUTHENTICATE_INTERVAL = T.minute const REAUTHENTICATE_INTERVAL = T.minute
const idPhotoCardBasedir = _.get('idPhotoCardDir', options)
const frontCameraBasedir = _.get('frontCameraDir', options) const HOSTNAME = process.env.HOSTNAME
const operatorDataBasedir = _.get('operatorDataDir', options) const KEY_PATH = process.env.KEY_PATH
const CERT_PATH = process.env.CERT_PATH
const ID_PHOTO_CARD_DIR = process.env.ID_PHOTO_CARD_DIR
const FRONT_CAMERA_DIR = process.env.FRONT_CAMERA_DIR
const OPERATOR_DATA_DIR = process.env.OPERATOR_DATA_DIR
const devMode = argv.dev const devMode = argv.dev
const version = require('../../package.json').version const version = require('../../package.json').version
logger.info('Version: %s', version) logger.info('Version: %s', version)
const hostname = options.hostname if (!HOSTNAME) {
if (!hostname) {
logger.error('no hostname specified.') logger.error('no hostname specified.')
process.exit(1) process.exit(1)
} }
@ -82,7 +83,7 @@ app.get('/api/totem', (req, res) => {
if (!name) return res.status(400).send('Name is required') if (!name) return res.status(400).send('Name is required')
return pairing.totem(hostname, name) return pairing.totem(HOSTNAME, name)
.then(totem => res.send(totem)) .then(totem => res.send(totem))
}) })
@ -222,27 +223,27 @@ app.use((err, req, res, next) => {
}) })
const certOptions = { const certOptions = {
key: fs.readFileSync(options.keyPath), key: fs.readFileSync(KEY_PATH),
cert: fs.readFileSync(options.certPath) cert: fs.readFileSync(CERT_PATH)
} }
app.use(serveStatic(path.resolve(__dirname, 'public'))) app.use(serveStatic(path.resolve(__dirname, 'public')))
if (!fs.existsSync(idPhotoCardBasedir)) { if (!fs.existsSync(ID_PHOTO_CARD_DIR)) {
makeDir.sync(idPhotoCardBasedir) makeDir.sync(ID_PHOTO_CARD_DIR)
} }
if (!fs.existsSync(frontCameraBasedir)) { if (!fs.existsSync(FRONT_CAMERA_DIR)) {
makeDir.sync(frontCameraBasedir) makeDir.sync(FRONT_CAMERA_DIR)
} }
if (!fs.existsSync(operatorDataBasedir)) { if (!fs.existsSync(OPERATOR_DATA_DIR)) {
makeDir.sync(operatorDataBasedir) makeDir.sync(OPERATOR_DATA_DIR)
} }
app.use('/id-card-photo', serveStatic(idPhotoCardBasedir, {index: false})) app.use('/id-card-photo', serveStatic(ID_PHOTO_CARD_DIR, {index: false}))
app.use('/front-camera-photo', serveStatic(frontCameraBasedir, {index: false})) app.use('/front-camera-photo', serveStatic(FRONT_CAMERA_DIR, {index: false}))
app.use('/operator-data', serveStatic(operatorDataBasedir, {index: false})) app.use('/operator-data', serveStatic(OPERATOR_DATA_DIR, {index: false}))
function register (req, res, next) { function register (req, res, next) {
const otp = req.query.otp const otp = req.query.otp
@ -259,7 +260,7 @@ function register (req, res, next) {
const cookieOpts = { const cookieOpts = {
httpOnly: true, httpOnly: true,
secure: true, secure: true,
domain: hostname, domain: HOSTNAME,
sameSite: true, sameSite: true,
expires: NEVER expires: NEVER
} }

View file

@ -9,7 +9,9 @@ const _ = require('lodash/fp')
const serveStatic = require('serve-static') const serveStatic = require('serve-static')
const path = require('path') const path = require('path')
const options = require('../options') const KEY_PATH = process.env.KEY_PATH
const CERT_PATH = process.env.CERT_PATH
const LAMASSU_CA_PATH = process.env.LAMASSU_CA_PATH
app.use(morgan('dev')) app.use(morgan('dev'))
app.use(helmet({noCache: true})) app.use(helmet({noCache: true}))
@ -20,9 +22,9 @@ app.use(serveStatic(path.resolve(__dirname, '..', '..', 'public'), {
})) }))
const certOptions = { const certOptions = {
key: fs.readFileSync(options.keyPath), key: fs.readFileSync(KEY_PATH),
cert: fs.readFileSync(options.certPath), cert: fs.readFileSync(CERT_PATH),
ca: [fs.readFileSync(options.lamassuCaPath)], ca: [fs.readFileSync(LAMASSU_CA_PATH)],
requestCert: true, requestCert: true,
rejectUnauthorized: true rejectUnauthorized: true
} }

View file

@ -4,19 +4,18 @@ const readFile = pify(fs.readFile)
const crypto = require('crypto') const crypto = require('crypto')
const baseX = require('base-x') const baseX = require('base-x')
const options = require('../options')
const db = require('../db') const db = require('../db')
const pairing = require('../pairing') const pairing = require('../pairing')
const ALPHA_BASE = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:' const ALPHA_BASE = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:'
const bsAlpha = baseX(ALPHA_BASE) const bsAlpha = baseX(ALPHA_BASE)
const CA_PATH = process.env.CA_PATH
const unpair = pairing.unpair const unpair = pairing.unpair
function totem (hostname, name) { function totem (hostname, name) {
const caPath = options.caPath return readFile(CA_PATH)
return readFile(caPath)
.then(data => { .then(data => {
const caHash = crypto.createHash('sha256').update(data).digest() const caHash = crypto.createHash('sha256').update(data).digest()
const token = crypto.randomBytes(32) const token = crypto.randomBytes(32)

View file

@ -1,8 +1,11 @@
const fs = require('fs') const fs = require('fs')
const path = require('path')
const http = require('http') const http = require('http')
const https = require('https') const https = require('https')
const argv = require('minimist')(process.argv.slice(2)) const argv = require('minimist')(process.argv.slice(2))
require('dotenv').config({ path: path.resolve(__dirname, '../.env') })
const { asyncLocalStorage, defaultStore } = require('./async-storage') const { asyncLocalStorage, defaultStore } = require('./async-storage')
const routes = require('./routes') const routes = require('./routes')
const logger = require('./logger') const logger = require('./logger')
@ -10,11 +13,13 @@ const poller = require('./poller')
const settingsLoader = require('./new-settings-loader') const settingsLoader = require('./new-settings-loader')
const configManager = require('./new-config-manager') const configManager = require('./new-config-manager')
const complianceTriggers = require('./compliance-triggers') const complianceTriggers = require('./compliance-triggers')
const options = require('./options')
const ofac = require('./ofac/index') const ofac = require('./ofac/index')
const ofacUpdate = require('./ofac/update') const ofacUpdate = require('./ofac/update')
const devMode = argv.dev || options.http const KEY_PATH = process.env.KEY_PATH
const CERT_PATH = process.env.CERT_PATH
const devMode = argv.dev || process.env.HTTP
const version = require('../package.json').version const version = require('../package.json').version
logger.info('Version: %s', version) logger.info('Version: %s', version)
@ -66,8 +71,8 @@ function startServer (settings) {
.then(() => { .then(() => {
poller.setup(['public']) poller.setup(['public'])
const httpsServerOptions = { const httpsServerOptions = {
key: fs.readFileSync(options.keyPath), key: fs.readFileSync(KEY_PATH),
cert: fs.readFileSync(options.certPath), cert: fs.readFileSync(CERT_PATH),
requestCert: true, requestCert: true,
rejectUnauthorized: false rejectUnauthorized: false
} }
@ -80,7 +85,7 @@ function startServer (settings) {
const localPort = 3030 const localPort = 3030
const localServer = http.createServer(routes.localApp) const localServer = http.createServer(routes.localApp)
if (options.devMode) logger.info('In dev mode') if (devMode) logger.info('In dev mode')
server.listen(port, () => { server.listen(port, () => {
logger.info('lamassu-server listening on port ' + logger.info('lamassu-server listening on port ' +

View file

@ -1,10 +1,10 @@
const fs = require('fs') const fs = require('fs')
const options = require('../options')
const common = require('./common') const common = require('./common')
const MOUNT_POINT = options.blockchainDir const BLOCKCHAIN_DIR = process.env.BLOCKCHAIN_DIR
const MOUNT_POINT = BLOCKCHAIN_DIR
module.exports = {prepareVolume} module.exports = {prepareVolume}

View file

@ -8,7 +8,6 @@ const inquirer = require('inquirer')
const _ = require('lodash/fp') const _ = require('lodash/fp')
const { utils: coinUtils } = require('@lamassu/coins') const { utils: coinUtils } = require('@lamassu/coins')
const options = require('../options')
const settingsLoader = require('../new-settings-loader') const settingsLoader = require('../new-settings-loader')
const wallet = require('../wallet') const wallet = require('../wallet')
@ -29,10 +28,12 @@ const PLUGINS = {
ZEC: require('./zcash.js') ZEC: require('./zcash.js')
} }
const BLOCKCHAIN_DIR = process.env.BLOCKCHAIN_DIR
module.exports = {run} module.exports = {run}
function installedVolumeFilePath (crypto) { function installedVolumeFilePath (crypto) {
return path.resolve(coinUtils.cryptoDir(crypto, options.blockchainDir), '.installed') return path.resolve(coinUtils.cryptoDir(crypto, BLOCKCHAIN_DIR), '.installed')
} }
function isInstalledVolume (crypto) { function isInstalledVolume (crypto) {
@ -63,7 +64,7 @@ function processCryptos (codes) {
common.es('sudo supervisorctl reread') common.es('sudo supervisorctl reread')
common.es('sudo supervisorctl update') common.es('sudo supervisorctl update')
const blockchainDir = options.blockchainDir const blockchainDir = BLOCKCHAIN_DIR
const backupDir = path.resolve(os.homedir(), 'backups') const backupDir = path.resolve(os.homedir(), 'backups')
const rsyncCmd = `( \ const rsyncCmd = `( \
(crontab -l 2>/dev/null || echo -n "") | grep -v "@daily rsync ".*"wallet.dat"; \ (crontab -l 2>/dev/null || echo -n "") | grep -v "@daily rsync ".*"wallet.dat"; \
@ -83,7 +84,7 @@ function processCryptos (codes) {
function setupCrypto (crypto) { function setupCrypto (crypto) {
logger.info(`Installing ${crypto.display}...`) logger.info(`Installing ${crypto.display}...`)
const cryptoDir = coinUtils.cryptoDir(crypto, options.blockchainDir) const cryptoDir = coinUtils.cryptoDir(crypto, BLOCKCHAIN_DIR)
makeDir.sync(cryptoDir) makeDir.sync(cryptoDir)
const cryptoPlugin = plugin(crypto) const cryptoPlugin = plugin(crypto)
const oldDir = process.cwd() const oldDir = process.cwd()

View file

@ -1,13 +1,9 @@
const axios = require('axios') const axios = require('axios')
const _ = require('lodash/fp') const _ = require('lodash/fp')
const pify = require('pify')
const fs = pify(require('fs'))
const db = require('../db') const db = require('../db')
const configManager = require('../new-config-manager') const configManager = require('../new-config-manager')
const complianceTriggers = require('../compliance-triggers') const complianceTriggers = require('../compliance-triggers')
const options = require('../options')
const logger = require('../logger') const logger = require('../logger')
const plugins = require('../plugins') const plugins = require('../plugins')
const { getOperatorId } = require('../operator') const { getOperatorId } = require('../operator')
@ -15,6 +11,8 @@ const { getOperatorId } = require('../operator')
const TIMEOUT = 10000 const TIMEOUT = 10000
const MAX_CONTENT_LENGTH = 2000 const MAX_CONTENT_LENGTH = 2000
const COIN_ATM_RADAR_URL = process.env.COIN_ATM_RADAR_URL
// How long a machine can be down before it's considered offline // How long a machine can be down before it's considered offline
const STALE_INTERVAL = '2 minutes' const STALE_INTERVAL = '2 minutes'
@ -112,7 +110,7 @@ function getMachines (rates, settings) {
} }
function sendRadar (data) { function sendRadar (data) {
const url = _.get(['coinAtmRadar', 'url'], options) const url = COIN_ATM_RADAR_URL
if (_.isEmpty(url)) { if (_.isEmpty(url)) {
return Promise.reject(new Error('Missing coinAtmRadar url!')) return Promise.reject(new Error('Missing coinAtmRadar url!'))

View file

@ -1,5 +1,13 @@
const T = require('./time') const T = require('./time')
const POSTGRES_USER = process.env.POSTGRES_USER
const POSTGRES_PASSWORD = process.env.POSTGRES_PASSWORD
const POSTGRES_HOST = process.env.POSTGRES_HOST
const POSTGRES_PORT = process.env.POSTGRES_PORT
const POSTGRES_DB = process.env.POSTGRES_DB
const PSQL_URL = `postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}`
const anonymousCustomer = { const anonymousCustomer = {
uuid: '47ac1184-8102-11e7-9079-8f13a7117867', uuid: '47ac1184-8102-11e7-9079-8f13a7117867',
name: 'anonymous' name: 'anonymous'
@ -39,5 +47,6 @@ module.exports = {
CASH_OUT_MINIMUM_AMOUNT_OF_CASSETTES, CASH_OUT_MINIMUM_AMOUNT_OF_CASSETTES,
CASH_OUT_MAXIMUM_AMOUNT_OF_CASSETTES, CASH_OUT_MAXIMUM_AMOUNT_OF_CASSETTES,
WALLET_SCORE_THRESHOLD, WALLET_SCORE_THRESHOLD,
RECEIPT RECEIPT,
PSQL_URL
} }

View file

@ -13,20 +13,20 @@ const BN = require('./bn')
const anonymous = require('../lib/constants').anonymousCustomer const anonymous = require('../lib/constants').anonymousCustomer
const complianceOverrides = require('./compliance_overrides') const complianceOverrides = require('./compliance_overrides')
const users = require('./users') const users = require('./users')
const options = require('./options')
const writeFile = util.promisify(fs.writeFile) const writeFile = util.promisify(fs.writeFile)
const notifierQueries = require('./notifier/queries') const notifierQueries = require('./notifier/queries')
const notifierUtils = require('./notifier/utils') const notifierUtils = require('./notifier/utils')
const NUM_RESULTS = 1000 const NUM_RESULTS = 1000
const idPhotoCardBasedir = _.get('idPhotoCardDir', options)
const frontCameraBaseDir = _.get('frontCameraDir', options)
const operatorDataDir = _.get('operatorDataDir', options)
const sms = require('./sms') const sms = require('./sms')
const settingsLoader = require('./new-settings-loader') const settingsLoader = require('./new-settings-loader')
const logger = require('./logger') const logger = require('./logger')
const TX_PASSTHROUGH_ERROR_CODES = ['operatorCancel'] const TX_PASSTHROUGH_ERROR_CODES = ['operatorCancel']
const ID_PHOTO_CARD_DIR = process.env.ID_PHOTO_CARD_DIR
const FRONT_CAMERA_DIR = process.env.FRONT_CAMERA_DIR
const OPERATOR_DATA_DIR = process.env.OPERATOR_DATA_DIR
/** /**
* Add new customer * Add new customer
* *
@ -673,7 +673,7 @@ function updatePhotoCard (id, patch) {
const rpath = _.join(path.sep, _.map(_.wrap(_.join, ''), _.take(3, _.chunk(2, _.split('', hash))))) const rpath = _.join(path.sep, _.map(_.wrap(_.join, ''), _.take(3, _.chunk(2, _.split('', hash)))))
// i.e. ../<lamassu-server-home>/idphotocard/24/0e/85 // i.e. ../<lamassu-server-home>/idphotocard/24/0e/85
const dirname = path.join(idPhotoCardBasedir, rpath) const dirname = path.join(ID_PHOTO_CARD_DIR, rpath)
// create the directory tree if needed // create the directory tree if needed
_.attempt(() => makeDir.sync(dirname)) _.attempt(() => makeDir.sync(dirname))
@ -736,7 +736,7 @@ function updatePhotos (imagesData, id, dir) {
function updateIdCardData (patch, id) { function updateIdCardData (patch, id) {
/* TODO: fetch operator id */ /* TODO: fetch operator id */
const operatorId = 'id-operator' const operatorId = 'id-operator'
const directory = `${operatorDataDir}/${operatorId}/${id}/` const directory = `${OPERATOR_DATA_DIR}/${operatorId}/${id}/`
return Promise.resolve(patch) return Promise.resolve(patch)
.then(patch => { .then(patch => {
@ -755,7 +755,7 @@ function updateTxCustomerPhoto (imageData) {
return Promise.resolve(imageData) return Promise.resolve(imageData)
.then(imageData => { .then(imageData => {
const newPatch = {} const newPatch = {}
const directory = `${operatorDataDir}/customersphotos` const directory = `${OPERATOR_DATA_DIR}/customersphotos`
if (_.isEmpty(imageData)) { if (_.isEmpty(imageData)) {
return return
@ -826,7 +826,7 @@ function updateFrontCamera (id, patch) {
const rpath = _.join(path.sep, _.map(_.wrap(_.join, ''), _.take(3, _.chunk(2, _.split('', hash))))) const rpath = _.join(path.sep, _.map(_.wrap(_.join, ''), _.take(3, _.chunk(2, _.split('', hash)))))
// i.e. ../<lamassu-server-home>/idphotocard/24/0e/85 // i.e. ../<lamassu-server-home>/idphotocard/24/0e/85
const dirname = path.join(frontCameraBaseDir, rpath) const dirname = path.join(FRONT_CAMERA_DIR, rpath)
// create the directory tree if needed // create the directory tree if needed
_.attempt(() => makeDir.sync(dirname)) _.attempt(() => makeDir.sync(dirname))

View file

@ -2,7 +2,7 @@ const Pgp = require('pg-promise')
const uuid = require('uuid') const uuid = require('uuid')
const _ = require('lodash/fp') const _ = require('lodash/fp')
const psqlUrl = require('../lib/options').postgresql const { PSQL_URL } = require('./constants')
const logger = require('./logger') const logger = require('./logger')
const eventBus = require('./event-bus') const eventBus = require('./event-bus')
const { asyncLocalStorage, defaultStore } = require('./async-storage') const { asyncLocalStorage, defaultStore } = require('./async-storage')
@ -87,7 +87,7 @@ const pgp = Pgp({
} }
}) })
const db = stripDefaultDbFuncs(pgp(psqlUrl)) const db = stripDefaultDbFuncs(pgp(PSQL_URL))
eventBus.subscribe('log', args => { eventBus.subscribe('log', args => {
if (process.env.SKIP_SERVER_LOGS) return if (process.env.SKIP_SERVER_LOGS) return

View file

@ -1,9 +1,11 @@
const winston = require('winston') const winston = require('winston')
const Postgres = require('./pg-transport') const Postgres = require('./pg-transport')
const options = require('./options') const { PSQL_URL } = require('./constants')
const LOG_LEVEL = process.env.LOG_LEVEL
const logger = new winston.Logger({ const logger = new winston.Logger({
level: options.logLevel, level: LOG_LEVEL,
transports: [ transports: [
new (winston.transports.Console)({ new (winston.transports.Console)({
timestamp: true, timestamp: true,
@ -12,7 +14,7 @@ const logger = new winston.Logger({
humanReadableUnhandledException: true humanReadableUnhandledException: true
}), }),
new Postgres({ new Postgres({
connectionString: options.postgresql, connectionString: PSQL_URL,
tableName: 'server_logs', tableName: 'server_logs',
handleExceptions: true, handleExceptions: true,
humanReadableUnhandledException: true humanReadableUnhandledException: true

View file

@ -8,13 +8,13 @@ const cors = require('cors')
const helmet = require('helmet') const helmet = require('helmet')
const nocache = require('nocache') const nocache = require('nocache')
const cookieParser = require('cookie-parser') const cookieParser = require('cookie-parser')
const { ApolloServer, AuthenticationError } = require('apollo-server-express')
const { graphqlUploadExpress } = require('graphql-upload') const { graphqlUploadExpress } = require('graphql-upload')
const { ApolloServer } = require('apollo-server-express')
const _ = require('lodash/fp') const _ = require('lodash/fp')
require('dotenv').config({ path: path.resolve(__dirname, '../../.env') })
const { asyncLocalStorage, defaultStore } = require('../async-storage') const { asyncLocalStorage, defaultStore } = require('../async-storage')
const options = require('../options')
const users = require('../users')
const logger = require('../logger') const logger = require('../logger')
const { AuthDirective } = require('./graphql/directives') const { AuthDirective } = require('./graphql/directives')
@ -25,13 +25,16 @@ const { USER_SESSIONS_CLEAR_INTERVAL } = require('../constants')
const { session, cleanUserSessions, buildApolloContext } = require('./middlewares') const { session, cleanUserSessions, buildApolloContext } = require('./middlewares')
const devMode = require('minimist')(process.argv.slice(2)).dev const devMode = require('minimist')(process.argv.slice(2)).dev
const idPhotoCardBasedir = _.get('idPhotoCardDir', options)
const frontCameraBasedir = _.get('frontCameraDir', options)
const operatorDataBasedir = _.get('operatorDataDir', options)
const hostname = options.hostname const HOSTNAME = process.env.HOSTNAME
if (!hostname) { const KEY_PATH = process.env.KEY_PATH
logger.error('no hostname specified.') const CERT_PATH = process.env.CERT_PATH
const ID_PHOTO_CARD_DIR = process.env.ID_PHOTO_CARD_DIR
const FRONT_CAMERA_DIR = process.env.FRONT_CAMERA_DIR
const OPERATOR_DATA_DIR = process.env.OPERATOR_DATA_DIR
if (!HOSTNAME) {
logger.error('No hostname specified.')
process.exit(1) process.exit(1)
} }
@ -77,16 +80,16 @@ apolloServer.applyMiddleware({
// cors on app for /api/register endpoint. // cors on app for /api/register endpoint.
app.use(cors({ credentials: true, origin: devMode && 'https://localhost:3001' })) app.use(cors({ credentials: true, origin: devMode && 'https://localhost:3001' }))
app.use('/id-card-photo', serveStatic(idPhotoCardBasedir, { index: false })) app.use('/id-card-photo', serveStatic(ID_PHOTO_CARD_DIR, { index: false }))
app.use('/front-camera-photo', serveStatic(frontCameraBasedir, { index: false })) app.use('/front-camera-photo', serveStatic(FRONT_CAMERA_DIR, { index: false }))
app.use('/operator-data', serveStatic(operatorDataBasedir, { index: false })) app.use('/operator-data', serveStatic(OPERATOR_DATA_DIR, { index: false }))
// Everything not on graphql or api/register is redirected to the front-end // Everything not on graphql or api/register is redirected to the front-end
app.get('*', (req, res) => res.sendFile(path.resolve(__dirname, '..', '..', 'public', 'index.html'))) app.get('*', (req, res) => res.sendFile(path.resolve(__dirname, '..', '..', 'public', 'index.html')))
const certOptions = { const certOptions = {
key: fs.readFileSync(options.keyPath), key: fs.readFileSync(KEY_PATH),
cert: fs.readFileSync(options.certPath) cert: fs.readFileSync(CERT_PATH)
} }
function run () { function run () {

View file

@ -1,6 +1,9 @@
const express = require('express') const express = require('express')
const path = require('path')
const { ApolloServer } = require('apollo-server-express') const { ApolloServer } = require('apollo-server-express')
require('dotenv').config({ path: path.resolve(__dirname, '../../.env') })
const { typeDefs, resolvers } = require('./graphql/schema') const { typeDefs, resolvers } = require('./graphql/schema')
const logger = require('../logger') const logger = require('../logger')

View file

@ -3,12 +3,9 @@ const router = express.Router()
const session = require('express-session') const session = require('express-session')
const PgSession = require('connect-pg-simple')(session) const PgSession = require('connect-pg-simple')(session)
const db = require('../../db') const db = require('../../db')
const options = require('../../options')
const { USER_SESSIONS_TABLE_NAME } = require('../../constants') const { USER_SESSIONS_TABLE_NAME } = require('../../constants')
const { getOperatorId } = require('../../operator') const { getOperatorId } = require('../../operator')
const hostname = options.hostname
router.use('*', async (req, res, next) => getOperatorId('authentication').then(({ operatorId }) => session({ router.use('*', async (req, res, next) => getOperatorId('authentication').then(({ operatorId }) => session({
store: new PgSession({ store: new PgSession({
pgPromise: db, pgPromise: db,

View file

@ -5,24 +5,25 @@ const crypto = require('crypto')
const baseX = require('base-x') const baseX = require('base-x')
const { parse, NIL } = require('uuid') const { parse, NIL } = require('uuid')
const options = require('../../options')
const db = require('../../db') const db = require('../../db')
const pairing = require('../../pairing') const pairing = require('../../pairing')
const ALPHA_BASE = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:' const ALPHA_BASE = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:'
const bsAlpha = baseX(ALPHA_BASE) const bsAlpha = baseX(ALPHA_BASE)
const CA_PATH = process.env.CA_PATH
const HOSTNAME = process.env.HOSTNAME
const unpair = pairing.unpair const unpair = pairing.unpair
function totem (name) { function totem (name) {
const caPath = options.caPath return readFile(CA_PATH)
return readFile(caPath)
.then(data => { .then(data => {
const caHash = crypto.createHash('sha256').update(data).digest() const caHash = crypto.createHash('sha256').update(data).digest()
const token = crypto.randomBytes(32) const token = crypto.randomBytes(32)
const hexToken = token.toString('hex') const hexToken = token.toString('hex')
const caHexToken = crypto.createHash('sha256').update(hexToken).digest('hex') const caHexToken = crypto.createHash('sha256').update(hexToken).digest('hex')
const buf = Buffer.concat([caHash, token, Buffer.from(options.hostname)]) const buf = Buffer.concat([caHash, token, Buffer.from(HOSTNAME)])
const sql = 'insert into pairing_tokens (token, name) values ($1, $3), ($2, $3)' const sql = 'insert into pairing_tokens (token, name) values ($1, $3), ($2, $3)'
return db.none(sql, [hexToken, caHexToken, name]) return db.none(sql, [hexToken, caHexToken, name])

View file

@ -4,23 +4,24 @@ const util = require('util')
const loader = require('./loading') const loader = require('./loading')
const matcher = require('./matching') const matcher = require('./matching')
const nameUtils = require('./name-utils') const nameUtils = require('./name-utils')
const options = require('../options')
const _ = require('lodash/fp') const _ = require('lodash/fp')
const logger = require('../logger') const logger = require('../logger')
const debugLog = require('../pp')(__filename) // KOSTIS TODO: remove const debugLog = require('../pp')(__filename) // KOSTIS TODO: remove
const OFAC_DATA_DIR = process.env.OFAC_DATA_DIR
let structs = null let structs = null
const readdir = util.promisify(fs.readdir) const readdir = util.promisify(fs.readdir)
function load () { function load () {
if (!options.ofacDataDir) { if (!OFAC_DATA_DIR) {
const message = 'The ofacDataDir option has not been set in lamassu.json' const message = 'The ofacDataDir option has not been set in the environment'
return Promise.reject(new Error(message)) return Promise.reject(new Error(message))
} }
const ofacSourcesDir = path.join(options.ofacDataDir, 'sources') const ofacSourcesDir = path.join(OFAC_DATA_DIR, 'sources')
return readdir(ofacSourcesDir) return readdir(ofacSourcesDir)
.then(_.flow( .then(_.flow(

View file

@ -4,12 +4,23 @@ const url = require('url')
const fs = require('fs') const fs = require('fs')
const path = require('path') const path = require('path')
const util = require('util') const util = require('util')
const options = require('../options')
const _ = require('lodash/fp') const _ = require('lodash/fp')
const logger = require('../logger') const logger = require('../logger')
const DOWNLOAD_DIR = path.resolve('/tmp') const DOWNLOAD_DIR = path.resolve('/tmp')
const OFAC_DATA_DIR = process.env.OFAC_DATA_DIR
const OFAC_SOURCES_NAMES = process.env.OFAC_SOURCES_NAMES.split(',')
const OFAC_SOURCES_URLS = process.env.OFAC_SOURCES_URLS.split(',')
const ofacSources = _.map(
it => ({
name: it[0],
url: it[1]
}),
_.zip(OFAC_SOURCES_NAMES, OFAC_SOURCES_URLS)
)
function mkdir (path) { function mkdir (path) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
fs.mkdir(path, err => { fs.mkdir(path, err => {
@ -97,14 +108,12 @@ const moveToSourcesDir = (srcFile, ofacSourcesDir) => {
} }
function update () { function update () {
const OFAC_DATA_DIR = options.ofacDataDir
if (!OFAC_DATA_DIR) { if (!OFAC_DATA_DIR) {
throw new Error('ofacDataDir must be defined in lamassu.json') throw new Error('ofacDataDir must be defined in the environment')
} }
if (!options.ofacSources) { if (!ofacSources) {
logger.error('ofacSources must be defined in lamassu.json') logger.error('ofacSources must be defined in the environment')
} }
const OFAC_SOURCES_DIR = path.join(OFAC_DATA_DIR, 'sources') const OFAC_SOURCES_DIR = path.join(OFAC_DATA_DIR, 'sources')
@ -125,7 +134,7 @@ function update () {
return {} return {}
}) })
const promiseNewEtags = Promise.resolve(options.ofacSources || []) const promiseNewEtags = Promise.resolve(ofacSources || [])
.then(sources => Promise.all(_.map(promiseGetEtag, sources)) .then(sources => Promise.all(_.map(promiseGetEtag, sources))
.then(etags => _.map( .then(etags => _.map(
([source, etag]) => ({...source, etag}), ([source, etag]) => ({...source, etag}),

View file

@ -4,11 +4,12 @@ const os = require('os')
const argv = require('minimist')(process.argv.slice(2)) const argv = require('minimist')(process.argv.slice(2))
const _ = require('lodash/fp') const _ = require('lodash/fp')
require('dotenv').config() require('dotenv').config({ path: path.resolve(__dirname, '../.env') })
const DATABASE = process.env.LAMASSU_DB ?? 'PROD' const DATABASE = process.env.LAMASSU_DB ?? 'PROD'
const dbMapping = psqlConf => ({ const dbMapping = psqlConf => ({
STRESS_TEST: _.replace('lamassu', 'lamassu_stress', psqlConf), STRESS_TEST: _.replace('lamassu', 'lamassu_stress', psqlConf),
PAZUZ: _.replace('lamassu', 'lamassu_pazuz', psqlConf),
RELEASE: _.replace('lamassu', 'lamassu_release', psqlConf), RELEASE: _.replace('lamassu', 'lamassu_release', psqlConf),
DEV: _.replace('lamassu', 'lamassu', psqlConf), DEV: _.replace('lamassu', 'lamassu', psqlConf),
PROD: _.replace('lamassu', 'lamassu', psqlConf) PROD: _.replace('lamassu', 'lamassu', psqlConf)

View file

@ -2,13 +2,14 @@ const fs = require('fs')
const pify = require('pify') const pify = require('pify')
const readFile = pify(fs.readFile) const readFile = pify(fs.readFile)
const db = require('./db') const db = require('./db')
const options = require('./options')
const logger = require('./logger') const logger = require('./logger')
const uuid = require('uuid') const uuid = require('uuid')
const CA_PATH = process.env.CA_PATH
// A machine on an older version (no multicassette code) could be paired with a server with multicassette code. // A machine on an older version (no multicassette code) could be paired with a server with multicassette code.
// This makes sure that the server stores a default value // This makes sure that the server stores a default value
const DEFAULT_NUMBER_OF_CASSETTES = 2 const DEFAULT_NUMBER_OF_CASSETTES = 2
function pullToken (token) { function pullToken (token) {
const sql = `delete from pairing_tokens const sql = `delete from pairing_tokens
@ -58,8 +59,7 @@ function authorizeCaDownload (caToken) {
.then(r => { .then(r => {
if (r.expired) throw new Error('Expired') if (r.expired) throw new Error('Expired')
const caPath = options.caPath return readFile(CA_PATH, {encoding: 'utf8'})
return readFile(caPath, {encoding: 'utf8'})
}) })
} }

View file

@ -6,9 +6,7 @@ const _ = require('lodash/fp')
const request = require('request-promise') const request = require('request-promise')
const { utils: coinUtils } = require('@lamassu/coins') const { utils: coinUtils } = require('@lamassu/coins')
const options = require('../../options') const BLOCKCHAIN_DIR = process.env.BLOCKCHAIN_DIR
const blockchainDir = options.blockchainDir
module.exports = { module.exports = {
fetch, fetchDigest, parseConf, rpcConfig fetch, fetchDigest, parseConf, rpcConfig
@ -108,7 +106,7 @@ function parseConf (confPath) {
function rpcConfig (cryptoRec) { function rpcConfig (cryptoRec) {
try { try {
const configPath = coinUtils.configPath(cryptoRec, blockchainDir) const configPath = coinUtils.configPath(cryptoRec, BLOCKCHAIN_DIR)
const config = parseConf(configPath) const config = parseConf(configPath)
return { return {
username: config.rpcuser, username: config.rpcuser,

View file

@ -1,7 +1,7 @@
const axios = require('axios') const axios = require('axios')
const _ = require('lodash/fp') const _ = require('lodash/fp')
const options = require('../../../options') const STRIKE_BASE_URL = process.env.STRIKE_BASE_URL
module.exports = { module.exports = {
newAddress, newAddress,
@ -9,7 +9,7 @@ module.exports = {
cryptoNetwork cryptoNetwork
} }
axios.defaults.baseURL = _.get('strike.baseUrl', options) axios.defaults.baseURL = STRIKE_BASE_URL
if (_.isEmpty(axios.defaults.baseURL)) { if (_.isEmpty(axios.defaults.baseURL)) {
throw new Error('Missing Strike baseUrl!') throw new Error('Missing Strike baseUrl!')
} }

View file

@ -3,13 +3,14 @@ const lnd = require('lnd-async')
const BN = require('../../../bn') const BN = require('../../../bn')
const E = require('../../../error') const E = require('../../../error')
const { utils: coinUtils } = require('@lamassu/coins') const { utils: coinUtils } = require('@lamassu/coins')
const options = require('../../../options')
const _ = require('lodash/fp') const _ = require('lodash/fp')
const cryptoRec = coinUtils.getCryptoCurrency('BTC') const cryptoRec = coinUtils.getCryptoCurrency('BTC')
const unitScale = cryptoRec.unitScale const unitScale = cryptoRec.unitScale
const LND = process.env.LIGHTNING_NETWORK_DAEMON
module.exports = { module.exports = {
balance, balance,
sendCoins, sendCoins,
@ -20,7 +21,7 @@ module.exports = {
} }
function connect () { function connect () {
return lnd.connect(options.lnd || {}) return lnd.connect(LND || {})
} }
function cryptoNetwork (account, cryptoCode, settings, operatorId) { function cryptoNetwork (account, cryptoCode, settings, operatorId) {

View file

@ -7,14 +7,13 @@ const { default: PQueue } = require('p-queue')
const BN = require('../../../bn') const BN = require('../../../bn')
const E = require('../../../error') const E = require('../../../error')
const { logger } = require('../../../blockchain/common') const { logger } = require('../../../blockchain/common')
const options = require('../../../options')
const jsonRpc = require('../../common/json-rpc') const jsonRpc = require('../../common/json-rpc')
const blockchainDir = options.blockchainDir const BLOCKCHAIN_DIR = process.env.BLOCKCHAIN_DIR
const cryptoRec = utils.getCryptoCurrency(COINS.XMR) const cryptoRec = utils.getCryptoCurrency(COINS.XMR)
const configPath = utils.configPath(cryptoRec, blockchainDir) const configPath = utils.configPath(cryptoRec, BLOCKCHAIN_DIR)
const walletDir = path.resolve(utils.cryptoDir(cryptoRec, blockchainDir), 'wallets') const walletDir = path.resolve(utils.cryptoDir(cryptoRec, BLOCKCHAIN_DIR), 'wallets')
const DIGEST_QUEUE = new PQueue({ const DIGEST_QUEUE = new PQueue({
concurrency: 1, concurrency: 1,

View file

@ -4,11 +4,11 @@ const E = require('../../../error')
const _ = require('lodash/fp') const _ = require('lodash/fp')
const ENV = process.env.NODE_ENV === undefined || process.env.NODE_ENV === 'development' ? 'development' : 'production' const ENV = process.env.NODE_ENV === undefined || process.env.NODE_ENV === 'development' ? 'development' : 'production'
const SUPPORTED_COINS = ['BTC', 'ZEC', 'LTC', 'BCH', 'DASH', 'ETH'] const SUPPORTED_COINS = ['BTC']
const axios = require('axios').create({ const axios = require('axios').create({
// TODO: get rejectUnauthorized true to work // TODO: get rejectUnauthorized true to work
baseURL: ENV === 'development' ? 'https://localhost:5555/api/' : process.env.PAZUZ_API_WALLET_URL, baseURL: `${process.env.WALLET_URL}/api`,
httpsAgent: new https.Agent({ httpsAgent: new https.Agent({
rejectUnauthorized: false rejectUnauthorized: false
}) })
@ -22,9 +22,8 @@ function balance (account, cryptoCode, settings, operatorId) {
return checkCryptoCode(cryptoCode) return checkCryptoCode(cryptoCode)
.then(() => { .then(() => {
return axios.post('/balance', { return axios.post('/balance', {
account,
cryptoCode, cryptoCode,
settings, config: settings.config,
operatorId operatorId
}) })
}) })
@ -39,9 +38,8 @@ function sendCoins (account, tx, settings, operatorId) {
return checkCryptoCode(cryptoCode) return checkCryptoCode(cryptoCode)
.then(() => { .then(() => {
return axios.post('/sendCoins', { return axios.post('/sendCoins', {
account,
tx, tx,
settings, config: settings.config,
operatorId operatorId
}) })
}) })
@ -57,10 +55,9 @@ function sendCoins (account, tx, settings, operatorId) {
function newAddress (account, info, tx, settings, operatorId) { function newAddress (account, info, tx, settings, operatorId) {
return checkCryptoCode(info.cryptoCode) return checkCryptoCode(info.cryptoCode)
.then(() => axios.post('/newAddress', { .then(() => axios.post('/newAddress', {
account,
info, info,
tx, tx,
settings, config: settings.config,
operatorId operatorId
})) }))
.then(({ data }) => { .then(({ data }) => {

View file

@ -6,7 +6,6 @@ const morgan = require('morgan')
const nocache = require('nocache') const nocache = require('nocache')
const logger = require('./logger') const logger = require('./logger')
const options = require('./options')
const authorize = require('./middlewares/authorize') const authorize = require('./middlewares/authorize')
const errorHandler = require('./middlewares/errorHandler') const errorHandler = require('./middlewares/errorHandler')
@ -41,7 +40,7 @@ const configRequiredRoutes = [
'/tx', '/tx',
'/verify_promo_code' '/verify_promo_code'
] ]
const devMode = argv.dev || options.http const devMode = argv.dev || process.env.HTTP
// middleware setup // middleware setup
app.use(compression({ threshold: 500 })) app.use(compression({ threshold: 500 }))

View file

@ -7,7 +7,6 @@ const pify = require('pify')
const fs = pify(require('fs')) const fs = pify(require('fs'))
const mnemonicHelpers = require('./mnemonic-helpers') const mnemonicHelpers = require('./mnemonic-helpers')
const options = require('./options')
const ph = require('./plugin-helper') const ph = require('./plugin-helper')
const layer2 = require('./layer2') const layer2 = require('./layer2')
const httpError = require('./route-helpers').httpError const httpError = require('./route-helpers').httpError
@ -20,6 +19,8 @@ const INSUFFICIENT_FUNDS_CODE = 570
const INSUFFICIENT_FUNDS_NAME = 'InsufficientFunds' const INSUFFICIENT_FUNDS_NAME = 'InsufficientFunds'
const ZERO_CONF_EXPIRATION = 60000 const ZERO_CONF_EXPIRATION = 60000
const MNEMONIC_PATH = process.env.MNEMONIC_PATH
function computeSeed (masterSeed) { function computeSeed (masterSeed) {
return hkdf(masterSeed, 32, { salt: 'lamassu-server-salt', info: 'wallet-seed' }) return hkdf(masterSeed, 32, { salt: 'lamassu-server-salt', info: 'wallet-seed' })
} }
@ -29,7 +30,7 @@ function computeOperatorId (masterSeed) {
} }
function fetchWallet (settings, cryptoCode) { function fetchWallet (settings, cryptoCode) {
return fs.readFile(options.mnemonicPath, 'utf8') return fs.readFile(MNEMONIC_PATH, 'utf8')
.then(mnemonic => { .then(mnemonic => {
const masterSeed = mnemonicHelpers.toEntropyBuffer(mnemonic) const masterSeed = mnemonicHelpers.toEntropyBuffer(mnemonic)
const plugin = configManager.getWalletSettings(cryptoCode, settings.config).wallet const plugin = configManager.getWalletSettings(cryptoCode, settings.config).wallet

View file

@ -5,7 +5,6 @@ const hkdf = require('futoin-hkdf')
const state = require('../lib/middlewares/state') const state = require('../lib/middlewares/state')
const mnemonicHelpers = require('../lib/mnemonic-helpers') const mnemonicHelpers = require('../lib/mnemonic-helpers')
const options = require('../lib/options')
function computeOperatorId (masterSeed) { function computeOperatorId (masterSeed) {
return hkdf(masterSeed, 16, { salt: 'lamassu-server-salt', info: 'operator-id' }).toString('hex') return hkdf(masterSeed, 16, { salt: 'lamassu-server-salt', info: 'operator-id' }).toString('hex')
@ -13,7 +12,7 @@ function computeOperatorId (masterSeed) {
function getMnemonic () { function getMnemonic () {
if (state.mnemonic) return Promise.resolve(state.mnemonic) if (state.mnemonic) return Promise.resolve(state.mnemonic)
return fs.readFile(options.mnemonicPath, 'utf8').then(mnemonic => { return fs.readFile(process.env.MNEMONIC_PATH, 'utf8').then(mnemonic => {
state.mnemonic = mnemonic state.mnemonic = mnemonic
return mnemonic return mnemonic
}) })

View file

@ -2,4 +2,4 @@ SKIP_PREFLIGHT_CHECK=true
HTTPS=true HTTPS=true
REACT_APP_TYPE_CHECK_SANCTUARY=false REACT_APP_TYPE_CHECK_SANCTUARY=false
PORT=3001 PORT=3001
REACT_APP_BUILD_TARGET=LAMASSU REACT_APP_BUILD_TARGET=PAZUZ

View file

@ -170,22 +170,26 @@ const Accounting = () => {
] ]
return ( return (
<> !loading && (
<TitleSection title="Accounting" /> <>
<Assets <TitleSection title="Accounting" />
balance={operatorData.fiatBalances[operatorData.preferredFiatCurrency]} <Assets
hedgingReserve={operatorData.hedgingReserve ?? 0} balance={
currency={operatorData.preferredFiatCurrency} operatorData.fiatBalances[operatorData.preferredFiatCurrency]
/> }
<H4 className={classes.tableTitle}>Fiat balance history</H4> hedgingReserve={operatorData.hedgingReserve ?? 0}
<DataTable currency={operatorData.preferredFiatCurrency}
loading={loading} />
emptyText="No transactions so far" <H4 className={classes.tableTitle}>Fiat balance history</H4>
elements={elements} <DataTable
data={operatorData.fundings ?? []} loading={loading}
rowSize="sm" emptyText="No transactions so far"
/> elements={elements}
</> data={operatorData.fundings ?? []}
rowSize="sm"
/>
</>
)
) )
} }

32
tools/build-dev-env.js Normal file
View file

@ -0,0 +1,32 @@
const fs = require('fs')
const os = require('os')
const path = require('path')
const setEnvVariable = require('./set-env-var')
fs.copyFileSync(path.resolve(__dirname, '../.sample.env'), path.resolve(__dirname, '../.env'))
setEnvVariable('LAMASSU_DB', 'DEV')
setEnvVariable('POSTGRES_USER', 'postgres')
setEnvVariable('POSTGRES_PASSWORD', 'postgres123')
setEnvVariable('POSTGRES_HOST', 'localhost')
setEnvVariable('POSTGRES_PORT', '5432')
setEnvVariable('POSTGRES_DB', 'lamassu')
setEnvVariable('CA_PATH', `${process.env.PWD}/certs/Lamassu_OP_Root_CA.pem`)
setEnvVariable('CERT_PATH', `${process.env.PWD}/certs/Lamassu_OP.pem`)
setEnvVariable('KEY_PATH', `${process.env.PWD}/certs/Lamassu_OP.key`)
setEnvVariable('MNEMONIC_PATH', `${process.env.HOME}/.lamassu/mnemonics/mnemonic.txt`)
setEnvVariable('MIGRATE_STATE_PATH', `${process.env.HOME}/.lamassu/.migrate`)
setEnvVariable('OFAC_DATA_DIR', `${process.env.HOME}/.lamassu/ofac`)
setEnvVariable('ID_PHOTO_CARD_DIR', `${process.env.HOME}/.lamassu/idphotocard`)
setEnvVariable('FRONT_CAMERA_DIR', `${process.env.HOME}/.lamassu/frontcamera`)
setEnvVariable('OPERATOR_DATA_DIR', `${process.env.HOME}/.lamassu/operatordata`)
setEnvVariable('OFAC_SOURCES_NAMES', 'sdn_advanced,cons_advanced')
setEnvVariable('OFAC_SOURCES_URLS', 'https://www.treasury.gov/ofac/downloads/sanctions/1.0/sdn_advanced.xml,https://www.treasury.gov/ofac/downloads/sanctions/1.0/cons_advanced.xml')
setEnvVariable('HOSTNAME', 'localhost')
setEnvVariable('LOG_LEVEL', 'debug')

45
tools/build-prod-env.js Normal file
View file

@ -0,0 +1,45 @@
const fs = require('fs')
const os = require('os')
const path = require('path')
const argv = require('minimist')(process.argv.slice(2))
const _ = require('lodash/fp')
const setEnvVariable = require('./set-env-var')
const requiredParams = ['db-password', 'hostname']
if (!_.isEqual(_.intersection(_.keys(argv), requiredParams), requiredParams)) {
console.error('Usage: node tools/build-prod-env.js --db-password <DB_PASSWORD> --hostname <IP>')
process.exit(2)
}
fs.copyFileSync(path.resolve(__dirname, '../.sample.env'), path.resolve(__dirname, '../.env'))
setEnvVariable('POSTGRES_USER', 'lamassu_pg')
setEnvVariable('POSTGRES_PASSWORD', `${argv['db-password']}`)
setEnvVariable('POSTGRES_HOST', 'localhost')
setEnvVariable('POSTGRES_PORT', '5432')
setEnvVariable('POSTGRES_DB', 'lamassu')
setEnvVariable('LAMASSU_CA_PATH', `/etc/ssl/certs/Lamassu_CA.pem`)
setEnvVariable('CA_PATH', `/etc/ssl/certs/Lamassu_OP_Root_CA.pem`)
setEnvVariable('CERT_PATH', `/etc/ssl/certs/Lamassu_OP.pem`)
setEnvVariable('KEY_PATH', `/etc/ssl/certs/Lamassu_OP.key`)
setEnvVariable('MNEMONIC_PATH', `/etc/lamassu/mnemonics/mnemonic.txt`)
setEnvVariable('MIGRATE_STATE_PATH', `/etc/lamassu/.migrate`)
setEnvVariable('BLOCKCHAIN_DIR', `/mnt/blockchains`)
setEnvVariable('OFAC_DATA_DIR', `/var/lamassu/ofac`)
setEnvVariable('ID_PHOTO_CARD_DIR', `/opt/lamassu-server/idphotocard`)
setEnvVariable('FRONT_CAMERA_DIR', `/opt/lamassu-server/frontcamera`)
setEnvVariable('OPERATOR_DATA_DIR', `/opt/lamassu-server/operatordata`)
setEnvVariable('STRIKE_BASE_URL', `https://api.strike.acinq.co/api/`)
setEnvVariable('COIN_ATM_RADAR_URL', `https://coinatmradar.info/api/lamassu/`)
setEnvVariable('OFAC_SOURCES_NAMES', 'sdn_advanced,cons_advanced')
setEnvVariable('OFAC_SOURCES_URLS', 'https://www.treasury.gov/ofac/downloads/sanctions/1.0/sdn_advanced.xml,https://www.treasury.gov/ofac/downloads/sanctions/1.0/cons_advanced.xml')
setEnvVariable('HOSTNAME', `${argv.hostname}`)
setEnvVariable('LOG_LEVEL', 'info')

View file

@ -91,32 +91,6 @@ rm /tmp/Lamassu_OP.csr.pem
mkdir -p $OFAC_DATA_DIR/sources mkdir -p $OFAC_DATA_DIR/sources
touch $OFAC_DATA_DIR/etags.json touch $OFAC_DATA_DIR/etags.json
cat <<EOF > $CONFIG_DIR/lamassu.json node tools/build-dev-env.js
{
"postgresql": "psql://postgres:$POSTGRES_PASS@localhost/lamassu",
"mnemonicPath": "$MNEMONIC_FILE",
"caPath": "$CA_PATH",
"certPath": "$SERVER_CERT_PATH",
"keyPath": "$SERVER_KEY_PATH",
"hostname": "$DOMAIN",
"logLevel": "debug",
"lamassuCaPath": "$LAMASSU_CA_PATH",
"migrateStatePath": "$MIGRATE_STATE_PATH",
"ofacDataDir": "$OFAC_DATA_DIR",
"ofacSources": [
{
"name": "sdn_advanced",
"url": "https://www.treasury.gov/ofac/downloads/sanctions/1.0/sdn_advanced.xml"
},
{
"name": "cons_advanced",
"url": "https://www.treasury.gov/ofac/downloads/sanctions/1.0/cons_advanced.xml"
}
],
"idPhotoCardDir": "$IDPHOTOCARD_DIR",
"frontCameraDir": "$FRONTCAMERA_DIR",
"operatorDataDir": "$OPERATOR_DIR"
}
EOF
echo "Done." echo "Done."

24
tools/set-env-var.js Normal file
View file

@ -0,0 +1,24 @@
const fs = require('fs')
const os = require('os')
const path = require('path')
const setEnvVariable = (key, value) => {
const ENV_VARIABLES = fs.readFileSync(path.resolve(__dirname, '../.env'), 'utf-8').split(os.EOL)
const target = ENV_VARIABLES.indexOf(ENV_VARIABLES.find(line => line.match(new RegExp(`^${key}=`))))
if (target < 0) {
// The variable doesn't exist, add it
ENV_VARIABLES.push(`${key}=${value}`)
} else {
// .env already has that variable set, or at least has the definition of its key
//
// This is currently circumventing a possible bug on dotenv
// where the variables on this script were showing up as undefined on the first run despite the key existing,
// while on a second run they'd appear as empty string, as intended
ENV_VARIABLES.splice(target, 1, `${key}=${value}`)
}
fs.writeFileSync(path.resolve(__dirname, '../.env'), ENV_VARIABLES.join(os.EOL))
}
module.exports = setEnvVariable