2024-06-10 12:40:37 -07:00
import jwt from 'jsonwebtoken' ;
2021-12-29 18:08:41 -05:00
import * as fs from 'fs' ;
import { sep } from 'path' ;
import ini from 'ini' ;
import parseHocon from 'hocon-parser' ;
import request from 'request-promise' ;
import { Database } from '../../utils/database.js' ;
import { Logger } from '../../utils/logger.js' ;
import { Common } from '../../utils/common.js' ;
import { WSServer } from '../../utils/webSocketServer.js' ;
2024-06-10 12:40:37 -07:00
import { Authentication , SSO } from '../../models/config.model.js' ;
2021-12-29 18:08:41 -05:00
const options = { url : '' } ;
const logger = Logger ;
const common = Common ;
const wsServer = WSServer ;
const databaseService = Database ;
2024-06-10 12:40:37 -07:00
// Set local block explorer URL after first API call
// if the selected node block explorer has working REST API suite
// otherwise set it to mempool.space
let blockExplorerUrl = '' ;
export const getExplorerFeesRecommended = ( req , res , next ) => {
logger . log ( { selectedNode : req . session . selectedNode , level : 'INFO' , fileName : 'RTLConf' , msg : 'Getting Recommended Fee Rates..' } ) ;
options . url = ( blockExplorerUrl === '' ) ?
req . session . selectedNode . settings . blockExplorerUrl + '/api/v1/fees/recommended' :
blockExplorerUrl + '/api/v1/fees/recommended' ;
request ( options ) . then ( ( body ) => {
blockExplorerUrl = req . session . selectedNode . settings . blockExplorerUrl ;
logger . log ( { selectedNode : req . session . selectedNode , level : 'INFO' , fileName : 'RTLConf' , msg : 'Recommended Fee Rates Received' , data : body } ) ;
res . status ( 200 ) . json ( JSON . parse ( body ) ) ;
} ) . catch ( ( errRes ) => {
blockExplorerUrl = 'https://mempool.space' ;
options . url = blockExplorerUrl + '/api/v1/fees/recommended' ;
return request ( options ) . then ( ( body ) => {
logger . log ( { selectedNode : req . session . selectedNode , level : 'INFO' , fileName : 'RTLConf' , msg : 'Recommended Fee Rates Received' , data : body } ) ;
res . status ( 200 ) . json ( JSON . parse ( body ) ) ;
} ) . catch ( ( errRes ) => {
const errMsg = 'Get Recommended Fee Rates Error' ;
const err = common . handleError ( { statusCode : 500 , message : errMsg , error : errRes } , 'RTLConf' , errMsg , req . session . selectedNode ) ;
return res . status ( err . statusCode ) . json ( { message : err . error , error : err . error } ) ;
} ) ;
} ) ;
2021-12-29 18:08:41 -05:00
} ;
2024-06-10 12:40:37 -07:00
export const getExplorerTransaction = ( req , res , next ) => {
logger . log ( { selectedNode : req . session . selectedNode , level : 'INFO' , fileName : 'RTLConf' , msg : 'Getting Transaction From Block Explorer..' } ) ;
options . url = ( blockExplorerUrl === '' ) ?
req . session . selectedNode . settings . blockExplorerUrl + '/api/tx/' + req . params . txid :
blockExplorerUrl + '/api/tx/' + req . params . txid ;
request ( options ) . then ( ( body ) => {
blockExplorerUrl = req . session . selectedNode . settings . blockExplorerUrl ;
logger . log ( { selectedNode : req . session . selectedNode , level : 'INFO' , fileName : 'RTLConf' , msg : 'Transaction From Block Explorer Received' , data : body } ) ;
res . status ( 200 ) . json ( JSON . parse ( body ) ) ;
} ) . catch ( ( errRes ) => {
blockExplorerUrl = 'https://mempool.space' ;
options . url = blockExplorerUrl + '/api/tx/' + req . params . txid ;
return request ( options ) . then ( ( body ) => {
logger . log ( { selectedNode : req . session . selectedNode , level : 'INFO' , fileName : 'RTLConf' , msg : 'Transaction From Block Explorer Received' , data : body } ) ;
res . status ( 200 ) . json ( JSON . parse ( body ) ) ;
} ) . catch ( ( errRes ) => {
const errMsg = 'Get Transaction From Block Explorer Error' ;
const err = common . handleError ( { statusCode : 500 , message : errMsg , error : errRes } , 'RTLConf' , errMsg , req . session . selectedNode ) ;
return res . status ( err . statusCode ) . json ( { message : err . error , error : err . error } ) ;
} ) ;
} ) ;
} ;
export const getCurrencyRates = ( req , res , next ) => {
logger . log ( { selectedNode : req . session . selectedNode , level : 'INFO' , fileName : 'RTLConf' , msg : 'Getting Currency Rates..' } ) ;
options . url = 'https://blockchain.info/ticker' ;
request ( options ) . then ( ( body ) => {
logger . log ( { selectedNode : req . session . selectedNode , level : 'INFO' , fileName : 'RTLConf' , msg : 'Currency Rates Received' , data : body } ) ;
res . status ( 200 ) . json ( JSON . parse ( body ) ) ;
} ) . catch ( ( errRes ) => {
const errMsg = 'Get Rates Error' ;
const err = common . handleError ( { statusCode : 500 , message : errMsg , error : errRes } , 'RTLConf' , errMsg , req . session . selectedNode ) ;
return res . status ( err . statusCode ) . json ( { message : err . error , error : err . error } ) ;
} ) ;
} ;
export const getFile = ( req , res , next ) => {
logger . log ( { selectedNode : req . session . selectedNode , level : 'INFO' , fileName : 'RTLConf' , msg : 'Getting File..' } ) ;
const file = req . query . path ? req . query . path : ( req . session . selectedNode . settings . channelBackupPath + sep + 'channel-' + req . query . channel ? . replace ( ':' , '-' ) + '.bak' ) ;
logger . log ( { selectedNode : req . session . selectedNode , level : 'DEBUG' , fileName : 'RTLConf' , msg : 'Channel Point' , data : req . query . channel } ) ;
logger . log ( { selectedNode : req . session . selectedNode , level : 'DEBUG' , fileName : 'RTLConf' , msg : 'File Path' , data : file } ) ;
fs . readFile ( file , 'utf8' , ( errRes , data ) => {
2021-12-29 18:08:41 -05:00
if ( errRes ) {
2024-06-10 12:40:37 -07:00
if ( errRes . code && errRes . code === 'ENOENT' ) {
errRes . code = 'File Not Found!' ;
2021-12-29 18:08:41 -05:00
}
2024-06-10 12:40:37 -07:00
const errMsg = 'Reading File Error' ;
const err = common . handleError ( { statusCode : 500 , message : errMsg , error : errRes } , 'RTLConf' , errMsg , req . session . selectedNode ) ;
return res . status ( err . statusCode ) . json ( { message : err . error , error : err . error } ) ;
2021-12-29 18:08:41 -05:00
}
else {
2024-06-10 12:40:37 -07:00
logger . log ( { selectedNode : req . session . selectedNode , level : 'INFO' , fileName : 'RTLConf' , msg : 'File Data Received' , data : data } ) ;
res . status ( 200 ) . json ( data ) ;
2021-12-29 18:08:41 -05:00
}
} ) ;
} ;
2024-06-10 12:40:37 -07:00
export const getApplicationSettings = ( req , res , next ) => {
2021-12-29 18:08:41 -05:00
logger . log ( { selectedNode : req . session . selectedNode , level : 'INFO' , fileName : 'RTLConf' , msg : 'Getting RTL Configuration..' } ) ;
2024-06-10 12:40:37 -07:00
const confFile = common . appConfig . rtlConfFilePath + sep + 'RTL-Config.json' ;
2021-12-29 18:08:41 -05:00
fs . readFile ( confFile , 'utf8' , ( errRes , data ) => {
if ( errRes ) {
2024-06-10 12:40:37 -07:00
const errMsg = 'Get Node Config Error' ;
const err = common . handleError ( { statusCode : 500 , message : errMsg , error : errRes } , 'RTLConf' , errMsg , req . session . selectedNode ) ;
return res . status ( err . statusCode ) . json ( { message : err . error , error : err . error } ) ;
2021-12-29 18:08:41 -05:00
}
else {
2024-06-10 12:40:37 -07:00
const appConfData = common . removeSecureData ( JSON . parse ( data ) ) ;
appConfData . allowPasswordUpdate = common . appConfig . allowPasswordUpdate ;
appConfData . enable2FA = common . appConfig . enable2FA ;
appConfData . selectedNodeIndex = ( req . session . selectedNode && req . session . selectedNode . index ? req . session . selectedNode . index : common . selectedNode . index ) ;
common . appConfig . selectedNodeIndex = appConfData . selectedNodeIndex ;
const token = req . headers . authorization ? req . headers . authorization . split ( ' ' ) [ 1 ] : '' ;
jwt . verify ( token , common . secret _key , ( err , user ) => {
if ( err ) {
// Delete unnecessary data for initial response (without security token)
const selNodeIdx = appConfData . nodes . findIndex ( ( node ) => node . index === appConfData . selectedNodeIndex ) || 0 ;
appConfData . SSO = new SSO ( ) ;
appConfData . secret2FA = '' ;
appConfData . dbDirectoryPath = '' ;
appConfData . nodes [ selNodeIdx ] . authentication = new Authentication ( ) ;
delete appConfData . nodes [ selNodeIdx ] . settings . bitcoindConfigPath ;
delete appConfData . nodes [ selNodeIdx ] . settings . lnServerUrl ;
delete appConfData . nodes [ selNodeIdx ] . settings . swapServerUrl ;
delete appConfData . nodes [ selNodeIdx ] . settings . boltzServerUrl ;
delete appConfData . nodes [ selNodeIdx ] . settings . enableOffers ;
delete appConfData . nodes [ selNodeIdx ] . settings . enablePeerswap ;
delete appConfData . nodes [ selNodeIdx ] . settings . channelBackupPath ;
appConfData . nodes = [ appConfData . nodes [ selNodeIdx ] ] ;
}
logger . log ( { selectedNode : req . session . selectedNode , level : 'INFO' , fileName : 'RTLConf' , msg : 'RTL Configuration Received' , data : appConfData } ) ;
res . status ( 200 ) . json ( appConfData ) ;
} ) ;
2021-12-29 18:08:41 -05:00
}
} ) ;
} ;
2024-06-10 12:40:37 -07:00
export const updateSelectedNode = ( req , res , next ) => {
logger . log ( { selectedNode : req . session . selectedNode , level : 'INFO' , fileName : 'RTLConf' , msg : 'Updating Selected Node..' } ) ;
const selNodeIndex = req . params . currNodeIndex ? + req . params . currNodeIndex : common . selectedNode ? + common . selectedNode . index : 1 ;
req . session . selectedNode = common . findNode ( selNodeIndex ) ;
common . selectedNode = req . session . selectedNode ;
if ( req . headers && req . headers . authorization && req . headers . authorization !== '' ) {
wsServer . updateLNWSClientDetails ( req . session . id , + req . session . selectedNode . index , + req . params . prevNodeIndex ) ;
if ( req . params . prevNodeIndex !== '-1' ) {
databaseService . unloadDatabase ( req . params . prevNodeIndex , req . session . id ) ;
2021-12-29 18:08:41 -05:00
}
2024-06-10 12:40:37 -07:00
if ( req . params . currNodeIndex !== '-1' ) {
databaseService . loadDatabase ( req . session ) ;
2021-12-29 18:08:41 -05:00
}
}
2024-06-10 12:40:37 -07:00
blockExplorerUrl = '' ;
logger . log ( { selectedNode : req . session . selectedNode , level : 'INFO' , fileName : 'RTLConf' , msg : 'Selected Node Updated To ' + req . session . selectedNode . lnNode || '' } ) ;
res . status ( 200 ) . json ( common . removeAuthSecureData ( JSON . parse ( JSON . stringify ( req . session . selectedNode ) ) ) ) ;
2021-12-29 18:08:41 -05:00
} ;
export const getConfig = ( req , res , next ) => {
logger . log ( { selectedNode : req . session . selectedNode , level : 'INFO' , fileName : 'RTLConf' , msg : 'Reading Configuration File..' } ) ;
let confFile = '' ;
let fileFormat = 'INI' ;
switch ( req . params . nodeType ) {
case 'ln' :
2024-06-10 12:40:37 -07:00
confFile = req . session . selectedNode . authentication . configPath ;
2021-12-29 18:08:41 -05:00
break ;
case 'bitcoind' :
2024-06-10 12:40:37 -07:00
confFile = req . session . selectedNode . settings . bitcoindConfigPath ;
2021-12-29 18:08:41 -05:00
break ;
case 'rtl' :
fileFormat = 'JSON' ;
2024-06-10 12:40:37 -07:00
confFile = common . appConfig . rtlConfFilePath + sep + 'RTL-Config.json' ;
2021-12-29 18:08:41 -05:00
break ;
default :
confFile = '' ;
break ;
}
2022-01-16 15:55:50 -05:00
logger . log ( { selectedNode : req . session . selectedNode , level : 'DEBUG' , fileName : 'RTLConf' , msg : 'Node Type' , data : req . params . nodeType } ) ;
logger . log ( { selectedNode : req . session . selectedNode , level : 'DEBUG' , fileName : 'RTLConf' , msg : 'File Path' , data : confFile } ) ;
2021-12-29 18:08:41 -05:00
fs . readFile ( confFile , 'utf8' , ( errRes , data ) => {
if ( errRes ) {
const errMsg = 'Reading Config Error' ;
const err = common . handleError ( { statusCode : 500 , message : errMsg , error : errRes } , 'RTLConf' , errMsg , req . session . selectedNode ) ;
return res . status ( err . statusCode ) . json ( { message : err . error , error : err . error } ) ;
}
else {
let jsonConfig = { } ;
if ( fileFormat === 'JSON' ) {
jsonConfig = JSON . parse ( data ) ;
}
else {
fileFormat = 'INI' ;
2022-11-23 18:20:23 -08:00
data = data ? . replace ( 'color=#' , 'color=' ) ;
2021-12-29 18:08:41 -05:00
jsonConfig = ini . parse ( data ) ;
2022-01-16 15:55:50 -05:00
if ( jsonConfig [ 'Application Options' ] && jsonConfig [ 'Application Options' ] . color ) {
jsonConfig [ 'Application Options' ] . color = '#' + jsonConfig [ 'Application Options' ] . color ;
}
2024-06-10 12:40:37 -07:00
if ( req . params . nodeType === 'ln' && req . session . selectedNode . lnImplementation === 'ECL' && ! jsonConfig [ 'eclair.api.password' ] ) {
2021-12-29 18:08:41 -05:00
fileFormat = 'HOCON' ;
jsonConfig = parseHocon ( data ) ;
}
}
2024-06-10 12:40:37 -07:00
jsonConfig = common . maskPasswords ( jsonConfig ) ;
2022-11-23 18:20:23 -08:00
const responseJSON = ( fileFormat === 'JSON' ) ? jsonConfig : ini . stringify ( jsonConfig ) ? . replace ( 'color=\\#' , 'color=#' ) ;
2022-01-16 15:55:50 -05:00
logger . log ( { selectedNode : req . session . selectedNode , level : 'INFO' , fileName : 'RTLConf' , msg : 'Configuration File Data Received' , data : responseJSON } ) ;
2021-12-29 18:08:41 -05:00
res . status ( 200 ) . json ( { format : fileFormat , data : responseJSON } ) ;
}
} ) ;
} ;
2024-06-10 12:40:37 -07:00
export const updateNodeSettings = ( req , res , next ) => {
logger . log ( { selectedNode : req . session . selectedNode , level : 'INFO' , fileName : 'RTLConf' , msg : 'Updating Node Settings..' } ) ;
const RTLConfFile = common . appConfig . rtlConfFilePath + sep + 'RTL-Config.json' ;
const config = JSON . parse ( fs . readFileSync ( RTLConfFile , 'utf-8' ) ) ;
const node = config . nodes . find ( ( node ) => ( node . index === req . session . selectedNode . index ) ) ;
if ( node && node . settings ) {
node . settings = req . body . settings ;
if ( req . body . authentication . boltzMacaroonPath ) {
node . authentication . boltzMacaroonPath = req . body . authentication . boltzMacaroonPath ;
2021-12-29 18:08:41 -05:00
}
else {
2024-06-10 12:40:37 -07:00
delete node . authentication . boltzMacaroonPath ;
2021-12-29 18:08:41 -05:00
}
2024-06-10 12:40:37 -07:00
if ( req . body . authentication . swapMacaroonPath ) {
node . authentication . swapMacaroonPath = req . body . authentication . swapMacaroonPath ;
}
else {
delete node . authentication . swapMacaroonPath ;
}
}
2021-12-29 18:08:41 -05:00
try {
fs . writeFileSync ( RTLConfFile , JSON . stringify ( config , null , 2 ) , 'utf-8' ) ;
2024-06-10 12:40:37 -07:00
const selectedNode = common . findNode ( req . session . selectedNode . index ) ;
if ( selectedNode && selectedNode . settings ) {
selectedNode . settings = req . body . settings ;
selectedNode . authentication . boltzMacaroonPath = req . body . authentication . boltzMacaroonPath ;
selectedNode . authentication . swapMacaroonPath = req . body . authentication . swapMacaroonPath ;
common . replaceNode ( req , selectedNode ) ;
}
let responseNode = JSON . parse ( JSON . stringify ( common . selectedNode ) ) ;
responseNode = common . removeAuthSecureData ( responseNode ) ;
logger . log ( { selectedNode : req . session . selectedNode , level : 'INFO' , fileName : 'RTLConf' , msg : 'Node Settings Updated' , data : responseNode } ) ;
res . status ( 201 ) . json ( responseNode ) ;
2021-12-29 18:08:41 -05:00
}
catch ( errRes ) {
2024-06-10 12:40:37 -07:00
const errMsg = 'Update Node Settings Error' ;
2021-12-29 18:08:41 -05:00
const err = common . handleError ( { statusCode : 500 , message : errMsg , error : errRes } , 'RTLConf' , errMsg , req . session . selectedNode ) ;
return res . status ( err . statusCode ) . json ( { message : err . error , error : err . error } ) ;
}
} ;
2024-06-10 12:40:37 -07:00
export const updateApplicationSettings = ( req , res , next ) => {
logger . log ( { selectedNode : req . session . selectedNode , level : 'INFO' , fileName : 'RTLConf' , msg : 'Updating Application Settings..' } ) ;
const RTLConfFile = common . appConfig . rtlConfFilePath + sep + 'RTL-Config.json' ;
2021-12-29 18:08:41 -05:00
try {
2024-06-10 12:40:37 -07:00
const config = common . addSecureData ( req . body ) ;
common . appConfig = JSON . parse ( JSON . stringify ( config ) ) ;
delete config . selectedNodeIndex ;
delete config . enable2FA ;
delete config . allowPasswordUpdate ;
delete config . rtlConfFilePath ;
delete config . rtlPass ;
2021-12-29 18:08:41 -05:00
fs . writeFileSync ( RTLConfFile , JSON . stringify ( config , null , 2 ) , 'utf-8' ) ;
2024-06-10 12:40:37 -07:00
const newConfig = JSON . parse ( JSON . stringify ( common . appConfig ) ) ;
logger . log ( { selectedNode : req . session . selectedNode , level : 'INFO' , fileName : 'RTLConf' , msg : 'Application Settings Updated' , data : common . maskPasswords ( newConfig ) } ) ;
res . status ( 201 ) . json ( common . removeSecureData ( newConfig ) ) ;
2021-12-29 18:08:41 -05:00
}
catch ( errRes ) {
2024-06-10 12:40:37 -07:00
const errMsg = 'Update Default Node Error' ;
2021-12-29 18:08:41 -05:00
const err = common . handleError ( { statusCode : 500 , message : errMsg , error : errRes } , 'RTLConf' , errMsg , req . session . selectedNode ) ;
return res . status ( err . statusCode ) . json ( { message : err . error , error : err . error } ) ;
}
} ;