2022-12-28 02:14:05 +00:00
import * as os from 'os' ;
import * as fs from 'fs' ;
import { join , dirname , sep } from 'path' ;
import { fileURLToPath } from 'url' ;
import * as crypto from 'crypto' ;
import ini from 'ini' ;
import parseHocon from 'hocon-parser' ;
import { Common } from './common.js' ;
import { Logger } from './logger.js' ;
export class ConfigService {
constructor ( ) {
this . platform = os . platform ( ) ;
this . hash = crypto . createHash ( 'sha256' ) ;
this . errMsg = '' ;
this . directoryName = dirname ( fileURLToPath ( import . meta . url ) ) ;
this . common = Common ;
this . logger = Logger ;
this . setDefaultConfig = ( ) => {
const homeDir = os . userInfo ( ) . homedir ;
let macaroonPath = '' ;
let configPath = '' ;
let channelBackupPath = '' ;
2023-02-18 01:33:33 +00:00
let dbPath = '' ;
2022-12-28 02:14:05 +00:00
switch ( this . platform ) {
case 'win32' :
macaroonPath = homeDir + '\\AppData\\Local\\Lnd\\data\\chain\\bitcoin\\mainnet' ;
configPath = homeDir + '\\AppData\\Local\\Lnd\\lnd.conf' ;
channelBackupPath = homeDir + '\\backup\\node-1' ;
2023-02-18 01:33:33 +00:00
dbPath = homeDir + '\\database\\node-1' ;
2022-12-28 02:14:05 +00:00
break ;
case 'darwin' :
macaroonPath = homeDir + '/Library/Application Support/Lnd/data/chain/bitcoin/mainnet' ;
configPath = homeDir + '/Library/Application Support/Lnd/lnd.conf' ;
channelBackupPath = homeDir + '/backup/node-1' ;
2023-02-18 01:33:33 +00:00
dbPath = homeDir + '/database/node-1' ;
2022-12-28 02:14:05 +00:00
break ;
case 'linux' :
macaroonPath = homeDir + '/.lnd/data/chain/bitcoin/mainnet' ;
configPath = homeDir + '/.lnd/lnd.conf' ;
channelBackupPath = homeDir + '/backup/node-1' ;
2023-02-18 01:33:33 +00:00
dbPath = homeDir + '/database/node-1' ;
2022-12-28 02:14:05 +00:00
break ;
default :
macaroonPath = '' ;
configPath = '' ;
channelBackupPath = '' ;
2023-02-18 01:33:33 +00:00
dbPath = '' ;
2022-12-28 02:14:05 +00:00
break ;
}
const configData = {
port : '3000' ,
defaultNodeIndex : 1 ,
2023-02-18 01:33:33 +00:00
dbDirectoryPath : dbPath ,
2022-12-28 02:14:05 +00:00
SSO : {
rtlSSO : 0 ,
rtlCookiePath : '' ,
logoutRedirectLink : ''
} ,
nodes : [
{
index : 1 ,
lnNode : 'Node 1' ,
lnImplementation : 'LND' ,
Authentication : {
macaroonPath : macaroonPath ,
configPath : configPath
} ,
Settings : {
userPersona : 'MERCHANT' ,
themeMode : 'DAY' ,
themeColor : 'PURPLE' ,
channelBackupPath : channelBackupPath ,
logLevel : 'ERROR' ,
lnServerUrl : 'https://127.0.0.1:8080' ,
fiatConversion : false ,
unannouncedChannels : false
}
}
]
} ;
if ( ( process ? . env ? . RTL _SSO && + process ? . env ? . RTL _SSO === 0 ) || configData . SSO . rtlSSO === 0 ) {
configData [ 'multiPass' ] = 'password' ;
}
return configData ;
} ;
this . normalizePort = ( val ) => {
const port = parseInt ( val , 10 ) ;
if ( isNaN ( port ) ) {
return val ;
}
if ( port >= 0 ) {
return port ;
}
return false ;
} ;
this . updateLogByLevel = ( ) => {
let updateLogFlag = false ;
this . common . rtl _conf _file _path = process ? . env ? . RTL _CONFIG _PATH ? process ? . env ? . RTL _CONFIG _PATH : join ( this . directoryName , '../..' ) ;
try {
const RTLConfFile = this . common . rtl _conf _file _path + sep + 'RTL-Config.json' ;
const config = JSON . parse ( fs . readFileSync ( RTLConfFile , 'utf-8' ) ) ;
config . nodes . forEach ( ( node ) => {
if ( node . Settings . hasOwnProperty ( 'enableLogging' ) ) {
updateLogFlag = true ;
node . Settings . logLevel = node . Settings . enableLogging ? 'INFO' : 'ERROR' ;
delete node . Settings . enableLogging ;
}
} ) ;
if ( updateLogFlag ) {
fs . writeFileSync ( RTLConfFile , JSON . stringify ( config , null , 2 ) , 'utf-8' ) ;
}
}
catch ( err ) {
this . errMsg = this . errMsg + '\nLog level update failed!' ;
}
} ;
this . validateNodeConfig = ( config ) => {
if ( ( process ? . env ? . RTL _SSO && + process ? . env ? . RTL _SSO === 0 ) || ( typeof process ? . env ? . RTL _SSO === 'undefined' && + config . SSO . rtlSSO === 0 ) ) {
if ( process ? . env ? . APP _PASSWORD && process ? . env ? . APP _PASSWORD . trim ( ) !== '' ) {
this . common . rtl _pass = this . hash . update ( process ? . env ? . APP _PASSWORD ) . digest ( 'hex' ) ;
this . common . flg _allow _password _update = false ;
}
else if ( config . multiPassHashed && config . multiPassHashed !== '' ) {
this . common . rtl _pass = config . multiPassHashed ;
}
else if ( config . multiPass && config . multiPass !== '' ) {
this . common . rtl _pass = this . common . replacePasswordWithHash ( this . hash . update ( config . multiPass ) . digest ( 'hex' ) ) ;
}
else {
this . errMsg = this . errMsg + '\nNode Authentication can be set with multiPass only. Please set multiPass in RTL-Config.json' ;
}
this . common . rtl _secret2fa = config . secret2fa ;
}
else {
if ( process ? . env ? . APP _PASSWORD && process ? . env ? . APP _PASSWORD . trim ( ) !== '' ) {
this . errMsg = this . errMsg + '\nRTL Password cannot be set with SSO. Please set SSO as 0 or remove password.' ;
}
}
this . common . port = ( process ? . env ? . PORT ) ? this . normalizePort ( process ? . env ? . PORT ) : ( config . port ) ? this . normalizePort ( config . port ) : 3000 ;
this . common . host = ( process ? . env ? . HOST ) ? process ? . env ? . HOST : ( config . host ) ? config . host : null ;
2023-02-18 01:33:33 +00:00
this . common . db _directory _path = ( process ? . env ? . DB _DIRECTORY _PATH ) ? process ? . env ? . DB _DIRECTORY _PATH : ( config . dbDirectoryPath ) ? config . dbDirectoryPath : join ( dirname ( fileURLToPath ( import . meta . url ) ) , '..' , '..' ) ;
2022-12-28 02:14:05 +00:00
if ( config . nodes && config . nodes . length > 0 ) {
config . nodes . forEach ( ( node , idx ) => {
this . common . nodes [ idx ] = { } ;
this . common . nodes [ idx ] . index = node . index ;
this . common . nodes [ idx ] . ln _node = node . lnNode ;
this . common . nodes [ idx ] . ln _implementation = ( process ? . env ? . LN _IMPLEMENTATION ) ? process ? . env ? . LN _IMPLEMENTATION : node . lnImplementation ? node . lnImplementation : 'LND' ;
if ( this . common . nodes [ idx ] . ln _implementation === 'CLT' ) {
this . common . nodes [ idx ] . ln _implementation = 'CLN' ;
}
if ( this . common . nodes [ idx ] . ln _implementation !== 'ECL' && process ? . env ? . MACAROON _PATH && process ? . env ? . MACAROON _PATH . trim ( ) !== '' ) {
this . common . nodes [ idx ] . macaroon _path = process ? . env ? . MACAROON _PATH ;
}
else if ( this . common . nodes [ idx ] . ln _implementation !== 'ECL' && node . Authentication && node . Authentication . macaroonPath && node . Authentication . macaroonPath . trim ( ) !== '' ) {
this . common . nodes [ idx ] . macaroon _path = node . Authentication . macaroonPath ;
}
else if ( this . common . nodes [ idx ] . ln _implementation !== 'ECL' ) {
this . errMsg = 'Please set macaroon path for node index ' + node . index + ' in RTL-Config.json!' ;
}
if ( this . common . nodes [ idx ] . ln _implementation === 'ECL' ) {
if ( process ? . env ? . LN _API _PASSWORD ) {
this . common . nodes [ idx ] . ln _api _password = process ? . env ? . LN _API _PASSWORD ;
}
else if ( node . Authentication && node . Authentication . lnApiPassword ) {
this . common . nodes [ idx ] . ln _api _password = node . Authentication . lnApiPassword ;
}
else {
this . common . nodes [ idx ] . ln _api _password = '' ;
}
}
if ( process ? . env ? . CONFIG _PATH ) {
this . common . nodes [ idx ] . config _path = process ? . env ? . CONFIG _PATH ;
}
else if ( node . Authentication && node . Authentication . configPath ) {
this . common . nodes [ idx ] . config _path = node . Authentication . configPath ;
}
else {
this . common . nodes [ idx ] . config _path = '' ;
}
if ( this . common . nodes [ idx ] . ln _implementation === 'ECL' && this . common . nodes [ idx ] . ln _api _password === '' && this . common . nodes [ idx ] . config _path !== '' ) {
try {
const exists = fs . existsSync ( this . common . nodes [ idx ] . config _path || '' ) ;
if ( exists ) {
try {
const configFile = fs . readFileSync ( ( this . common . nodes [ idx ] . config _path || '' ) , 'utf-8' ) ;
const iniParsed = ini . parse ( configFile ) ;
this . common . nodes [ idx ] . ln _api _password = iniParsed [ 'eclair.api.password' ] ? iniParsed [ 'eclair.api.password' ] : parseHocon ( configFile ) . eclair . api . password ;
}
catch ( err ) {
this . errMsg = this . errMsg + '\nSomething went wrong while reading config file: \n' + err ;
}
}
else {
this . errMsg = this . errMsg + '\nInvalid config path: ' + this . common . nodes [ idx ] . config _path ;
}
}
catch ( err ) {
this . errMsg = this . errMsg + '\nUnable to read config file: \n' + err ;
}
}
if ( this . common . nodes [ idx ] . ln _implementation === 'ECL' && this . common . nodes [ idx ] . ln _api _password === '' ) {
this . errMsg = this . errMsg + '\nPlease set config path Or api password for node index ' + node . index + ' in RTL-Config.json! It is mandatory for Eclair authentication!' ;
}
if ( process ? . env ? . LN _SERVER _URL && process ? . env ? . LN _SERVER _URL . trim ( ) !== '' ) {
this . common . nodes [ idx ] . ln _server _url = process ? . env ? . LN _SERVER _URL . endsWith ( '/v1' ) ? process ? . env ? . LN _SERVER _URL . slice ( 0 , - 3 ) : process ? . env ? . LN _SERVER _URL ;
}
else if ( process ? . env ? . LND _SERVER _URL && process ? . env ? . LND _SERVER _URL . trim ( ) !== '' ) {
this . common . nodes [ idx ] . ln _server _url = process ? . env ? . LND _SERVER _URL . endsWith ( '/v1' ) ? process ? . env ? . LND _SERVER _URL . slice ( 0 , - 3 ) : process ? . env ? . LND _SERVER _URL ;
}
else if ( node . Settings . lnServerUrl && node . Settings . lnServerUrl . trim ( ) !== '' ) {
this . common . nodes [ idx ] . ln _server _url = node . Settings . lnServerUrl . endsWith ( '/v1' ) ? node . Settings . lnServerUrl . slice ( 0 , - 3 ) : node . Settings . lnServerUrl ;
}
else if ( node . Settings . lndServerUrl && node . Settings . lndServerUrl . trim ( ) !== '' ) {
this . common . nodes [ idx ] . ln _server _url = node . Settings . lndServerUrl . endsWith ( '/v1' ) ? node . Settings . lndServerUrl . slice ( 0 , - 3 ) : node . Settings . lndServerUrl ;
}
else {
this . errMsg = this . errMsg + '\nPlease set LN Server URL for node index ' + node . index + ' in RTL-Config.json!' ;
}
this . common . nodes [ idx ] . user _persona = node . Settings . userPersona ? node . Settings . userPersona : 'MERCHANT' ;
this . common . nodes [ idx ] . theme _mode = node . Settings . themeMode ? node . Settings . themeMode : 'DAY' ;
this . common . nodes [ idx ] . theme _color = node . Settings . themeColor ? node . Settings . themeColor : 'PURPLE' ;
this . common . nodes [ idx ] . unannounced _channels = node . Settings . unannouncedChannels ? ! ! node . Settings . unannouncedChannels : false ;
this . common . nodes [ idx ] . log _level = node . Settings . logLevel ? node . Settings . logLevel : 'ERROR' ;
this . common . nodes [ idx ] . fiat _conversion = node . Settings . fiatConversion ? ! ! node . Settings . fiatConversion : false ;
if ( this . common . nodes [ idx ] . fiat _conversion ) {
this . common . nodes [ idx ] . currency _unit = node . Settings . currencyUnit ? node . Settings . currencyUnit : 'USD' ;
}
if ( process ? . env ? . SWAP _SERVER _URL && process ? . env ? . SWAP _SERVER _URL . trim ( ) !== '' ) {
this . common . nodes [ idx ] . swap _server _url = process ? . env ? . SWAP _SERVER _URL . endsWith ( '/v1' ) ? process ? . env ? . SWAP _SERVER _URL . slice ( 0 , - 3 ) : process ? . env ? . SWAP _SERVER _URL ;
this . common . nodes [ idx ] . swap _macaroon _path = process ? . env ? . SWAP _MACAROON _PATH ;
}
else if ( node . Settings . swapServerUrl && node . Settings . swapServerUrl . trim ( ) !== '' ) {
this . common . nodes [ idx ] . swap _server _url = node . Settings . swapServerUrl . endsWith ( '/v1' ) ? node . Settings . swapServerUrl . slice ( 0 , - 3 ) : node . Settings . swapServerUrl ;
this . common . nodes [ idx ] . swap _macaroon _path = node . Authentication . swapMacaroonPath ? node . Authentication . swapMacaroonPath : '' ;
}
else {
this . common . nodes [ idx ] . swap _server _url = '' ;
this . common . nodes [ idx ] . swap _macaroon _path = '' ;
}
if ( process ? . env ? . BOLTZ _SERVER _URL && process ? . env ? . BOLTZ _SERVER _URL . trim ( ) !== '' ) {
this . common . nodes [ idx ] . boltz _server _url = process ? . env ? . BOLTZ _SERVER _URL . endsWith ( '/v1' ) ? process ? . env ? . BOLTZ _SERVER _URL . slice ( 0 , - 3 ) : process ? . env ? . BOLTZ _SERVER _URL ;
this . common . nodes [ idx ] . boltz _macaroon _path = process ? . env ? . BOLTZ _MACAROON _PATH ;
}
else if ( node . Settings . boltzServerUrl && node . Settings . boltzServerUrl . trim ( ) !== '' ) {
this . common . nodes [ idx ] . boltz _server _url = node . Settings . boltzServerUrl . endsWith ( '/v1' ) ? node . Settings . boltzServerUrl . slice ( 0 , - 3 ) : node . Settings . boltzServerUrl ;
this . common . nodes [ idx ] . boltz _macaroon _path = node . Authentication . boltzMacaroonPath ? node . Authentication . boltzMacaroonPath : '' ;
}
else {
this . common . nodes [ idx ] . boltz _server _url = '' ;
this . common . nodes [ idx ] . boltz _macaroon _path = '' ;
}
this . common . nodes [ idx ] . enable _offers = process ? . env ? . ENABLE _OFFERS ? process ? . env ? . ENABLE _OFFERS : ( node . Settings . enableOffers ) ? node . Settings . enableOffers : false ;
this . common . nodes [ idx ] . enable _peerswap = process ? . env ? . ENABLE _PEERSWAP ? process ? . env ? . ENABLE _PEERSWAP : ( node . Settings . enablePeerswap ) ? node . Settings . enablePeerswap : false ;
this . common . nodes [ idx ] . bitcoind _config _path = process ? . env ? . BITCOIND _CONFIG _PATH ? process ? . env ? . BITCOIND _CONFIG _PATH : ( node . Settings . bitcoindConfigPath ) ? node . Settings . bitcoindConfigPath : '' ;
this . common . nodes [ idx ] . channel _backup _path = process ? . env ? . CHANNEL _BACKUP _PATH ? process ? . env ? . CHANNEL _BACKUP _PATH : ( node . Settings . channelBackupPath ) ? node . Settings . channelBackupPath : this . common . rtl _conf _file _path + sep + 'channels-backup' + sep + 'node-' + node . index ;
try {
this . common . createDirectory ( this . common . nodes [ idx ] . channel _backup _path ) ;
const exists = fs . existsSync ( this . common . nodes [ idx ] . channel _backup _path + sep + 'channel-all.bak' ) ;
if ( ! exists ) {
try {
if ( this . common . nodes [ idx ] . ln _implementation === 'LND' ) {
this . common . getAllNodeAllChannelBackup ( this . common . nodes [ idx ] ) ;
}
else {
const createStream = fs . createWriteStream ( this . common . nodes [ idx ] . channel _backup _path + sep + 'channel-all.bak' ) ;
createStream . end ( ) ;
}
}
catch ( err ) {
this . logger . log ( { selectedNode : this . common . initSelectedNode , level : 'ERROR' , fileName : 'Config' , msg : 'Something went wrong while creating backup file: \n' + err } ) ;
}
}
}
catch ( err ) {
this . logger . log ( { selectedNode : this . common . initSelectedNode , level : 'ERROR' , fileName : 'Config' , msg : 'Something went wrong while creating the backup directory: \n' + err } ) ;
}
this . common . nodes [ idx ] . log _file = this . common . rtl _conf _file _path + '/logs/RTL-Node-' + node . index + '.log' ;
this . logger . log ( { selectedNode : this . common . initSelectedNode , level : 'INFO' , fileName : 'Config' , msg : 'Node Config: ' + JSON . stringify ( this . common . nodes [ idx ] ) } ) ;
const log _file = this . common . nodes [ idx ] . log _file ;
if ( fs . existsSync ( log _file || '' ) ) {
fs . writeFile ( ( log _file || '' ) , '' , ( ) => { } ) ;
}
else {
try {
const directoryName = dirname ( log _file || '' ) ;
this . common . createDirectory ( directoryName ) ;
const createStream = fs . createWriteStream ( log _file || '' ) ;
createStream . end ( ) ;
}
catch ( err ) {
this . logger . log ( { selectedNode : this . common . initSelectedNode , level : 'ERROR' , fileName : 'Config' , msg : 'Something went wrong while creating log file ' + log _file + ': \n' + err } ) ;
}
}
} ) ;
}
this . setSSOParams ( config ) ;
if ( this . errMsg && this . errMsg . trim ( ) !== '' ) {
throw new Error ( this . errMsg ) ;
}
} ;
this . setSSOParams = ( config ) => {
if ( process ? . env ? . RTL _SSO ) {
this . common . rtl _sso = + process ? . env ? . RTL _SSO ;
}
else if ( config . SSO && config . SSO . rtlSSO ) {
this . common . rtl _sso = config . SSO . rtlSSO ;
}
if ( process ? . env ? . RTL _COOKIE _PATH ) {
this . common . rtl _cookie _path = process ? . env ? . RTL _COOKIE _PATH ;
}
else if ( config . SSO && config . SSO . rtlCookiePath ) {
this . common . rtl _cookie _path = config . SSO . rtlCookiePath ;
}
else {
this . common . rtl _cookie _path = '' ;
}
if ( process ? . env ? . LOGOUT _REDIRECT _LINK ) {
this . common . logout _redirect _link = process ? . env ? . LOGOUT _REDIRECT _LINK ;
}
else if ( config . SSO && config . SSO . logoutRedirectLink ) {
this . common . logout _redirect _link = config . SSO . logoutRedirectLink ;
}
if ( + this . common . rtl _sso ) {
if ( ! this . common . rtl _cookie _path || this . common . rtl _cookie _path . trim ( ) === '' ) {
this . errMsg = 'Please set rtlCookiePath value for single sign on option!' ;
}
else {
this . common . readCookie ( ) ;
}
}
} ;
this . setSelectedNode = ( config ) => {
if ( config . defaultNodeIndex ) {
this . common . initSelectedNode = this . common . findNode ( config . defaultNodeIndex ) || { } ;
}
else {
this . common . initSelectedNode = this . common . findNode ( this . common . nodes [ 0 ] . index ) || { } ;
}
} ;
this . setServerConfiguration = ( ) => {
try {
this . common . rtl _conf _file _path = ( process ? . env ? . RTL _CONFIG _PATH ) ? process ? . env ? . RTL _CONFIG _PATH : join ( this . directoryName , '../..' ) ;
const confFileFullPath = this . common . rtl _conf _file _path + sep + 'RTL-Config.json' ;
if ( ! fs . existsSync ( confFileFullPath ) ) {
fs . writeFileSync ( confFileFullPath , JSON . stringify ( this . setDefaultConfig ( ) ) ) ;
}
const config = JSON . parse ( fs . readFileSync ( confFileFullPath , 'utf-8' ) ) ;
this . updateLogByLevel ( ) ;
this . validateNodeConfig ( config ) ;
this . setSelectedNode ( config ) ;
}
catch ( err ) {
this . logger . log ( { selectedNode : this . common . initSelectedNode , level : 'ERROR' , fileName : 'Config' , msg : 'Something went wrong while configuring the node server: \n' + err } ) ;
throw new Error ( err ) ;
}
} ;
2023-02-18 01:33:33 +00:00
this . setServerConfiguration ( ) ;
2022-12-28 02:14:05 +00:00
}
}
export const Config = new ConfigService ( ) ;