2021-12-29 18:08:41 -05:00
import * as fs from 'fs' ;
import { join , dirname , isAbsolute , resolve , sep } from 'path' ;
import { fileURLToPath } from 'url' ;
import * as crypto from 'crypto' ;
import request from 'request-promise' ;
import { Logger , LoggerService } from './logger.js' ;
2024-06-10 12:40:37 -07:00
import { ApplicationConfig , SelectedNode } from '../models/config.model.js' ;
2021-12-29 18:08:41 -05:00
export class CommonService {
public logger : LoggerService = Logger ;
2024-06-10 12:40:37 -07:00
public nodes : SelectedNode [ ] = [ ] ;
public selectedNode : SelectedNode = null ;
public ssoInit = { rtlSso : 0 , rtlCookiePath : '' , logoutRedirectLink : '' , cookieValue : '' } ;
public appConfig : ApplicationConfig = { defaultNodeIndex : 0 , selectedNodeIndex : 0 , rtlConfFilePath : '' , dbDirectoryPath : join ( dirname ( fileURLToPath ( import . meta . url ) ) , '..' , '..' ) , rtlPass : '' , allowPasswordUpdate : true , enable2FA : false , secret2FA : '' , SSO : this.ssoInit , nodes : [ ] } ;
2021-12-29 18:08:41 -05:00
public port = 3000 ;
2023-02-17 17:33:33 -08:00
public host = '' ;
2021-12-29 18:08:41 -05:00
public secret_key = crypto . randomBytes ( 64 ) . toString ( 'hex' ) ;
public read_dummy_data = false ;
public baseHref = '/rtl' ;
private dummy_data_array_from_file = [ ] ;
2022-11-02 15:59:37 -07:00
private MONTHS = [
{ name : 'JAN' , days : 31 } , { name : 'FEB' , days : 28 } , { name : 'MAR' , days : 31 } , { name : 'APR' , days : 30 } , { name : 'MAY' , days : 31 } , { name : 'JUN' , days : 30 } ,
{ name : 'JUL' , days : 31 } , { name : 'AUG' , days : 31 } , { name : 'SEP' , days : 30 } , { name : 'OCT' , days : 31 } , { name : 'NOV' , days : 30 } , { name : 'DEC' , days : 31 }
] ;
2021-12-29 18:08:41 -05:00
2024-06-10 12:40:37 -07:00
constructor ( ) { }
public maskPasswords = ( obj ) = > {
const keys = Object . keys ( obj ) ;
const length = keys . length ;
if ( length !== 0 ) {
for ( let i = 0 ; i < length ; i ++ ) {
if ( typeof obj [ keys [ i ] ] === 'object' ) {
keys [ keys [ i ] ] = this . maskPasswords ( obj [ keys [ i ] ] ) ;
}
if ( typeof keys [ i ] === 'string' &&
( ( keys [ i ] . toLowerCase ( ) . includes ( 'password' ) && keys [ i ] !== 'allowPasswordUpdate' ) || keys [ i ] . toLowerCase ( ) . includes ( 'multipass' ) ||
keys [ i ] . toLowerCase ( ) . includes ( 'rpcpass' ) || keys [ i ] . toLowerCase ( ) . includes ( 'rpcpassword' ) ||
keys [ i ] . toLowerCase ( ) . includes ( 'rpcuser' ) )
) {
obj [ keys [ i ] ] = '*' . repeat ( 20 ) ;
}
}
}
return obj ;
} ;
public removeAuthSecureData = ( node : SelectedNode ) = > {
2024-06-20 13:18:08 -07:00
if ( node . authentication ) {
delete node . authentication . macaroonPath ;
delete node . authentication . runePath ;
delete node . authentication . runeValue ;
delete node . authentication . lnApiPassword ;
delete node . authentication . options ;
}
2024-06-10 12:40:37 -07:00
return node ;
} ;
public removeSecureData = ( config : ApplicationConfig ) = > {
delete config . rtlConfFilePath ;
delete config . rtlPass ;
delete config . multiPass ;
delete config . multiPassHashed ;
delete config . secret2FA ;
config . nodes ? . map ( ( node ) = > this . removeAuthSecureData ( node ) ) ;
return config ;
} ;
public addSecureData = ( config : ApplicationConfig ) = > {
config . rtlConfFilePath = this . appConfig . rtlConfFilePath ;
config . rtlPass = this . appConfig . rtlPass ;
config . multiPassHashed = this . appConfig . multiPassHashed ;
config . SSO . rtlCookiePath = this . appConfig . SSO . rtlCookiePath ;
if ( this . appConfig . multiPass ) {
config . multiPass = this . appConfig . multiPass ;
}
if ( config . secret2FA === this . appConfig . secret2FA ) {
config . secret2FA = this . appConfig . secret2FA ;
}
config . nodes . map ( ( node , i ) = > {
if ( this . appConfig && this . appConfig . nodes && this . appConfig . nodes . length > i && this . appConfig . nodes [ i ] . authentication ) {
if ( this . appConfig . nodes [ i ] . authentication . macaroonPath ) {
node . authentication . macaroonPath = this . appConfig . nodes [ i ] . authentication . macaroonPath ;
}
if ( this . appConfig . nodes [ i ] . authentication . runePath ) {
node . authentication . runePath = this . appConfig . nodes [ i ] . authentication . runePath ;
}
if ( this . appConfig . nodes [ i ] . authentication . lnApiPassword ) {
node . authentication . lnApiPassword = this . appConfig . nodes [ i ] . authentication . lnApiPassword ;
}
}
return node ;
} ) ;
return config ;
} ;
2021-12-29 18:08:41 -05:00
2023-10-02 20:55:16 -07:00
public setSwapServerOptions = ( req ) = > {
2021-12-29 18:08:41 -05:00
const swapOptions = {
2024-06-10 12:40:37 -07:00
baseUrl : req.session.selectedNode.settings.swapServerUrl ,
2023-10-02 20:55:16 -07:00
uri : '' ,
2021-12-29 18:08:41 -05:00
rejectUnauthorized : false ,
json : true ,
headers : { 'Grpc-Metadata-macaroon' : '' }
} ;
2024-06-10 12:40:37 -07:00
if ( req . session . selectedNode . authentication . swapMacaroonPath ) {
2021-12-29 18:08:41 -05:00
try {
2024-06-10 12:40:37 -07:00
swapOptions . headers = { 'Grpc-Metadata-macaroon' : fs . readFileSync ( join ( req . session . selectedNode . authentication . swapMacaroonPath , 'loop.macaroon' ) ) . toString ( 'hex' ) } ;
2021-12-29 18:08:41 -05:00
} catch ( err ) {
2024-06-10 12:40:37 -07:00
this . logger . log ( { selectedNode : this.selectedNode , level : 'ERROR' , fileName : 'Common' , msg : 'Loop macaroon Error' , error : err } ) ;
2021-12-29 18:08:41 -05:00
}
}
2024-06-10 12:40:37 -07:00
this . logger . log ( { selectedNode : this.selectedNode , level : 'INFO' , fileName : 'Common' , msg : 'Swap Options' , data : swapOptions } ) ;
2021-12-29 18:08:41 -05:00
return swapOptions ;
} ;
public getBoltzServerOptions = ( req ) = > {
const boltzOptions = {
2024-06-10 12:40:37 -07:00
url : req.session.selectedNode.settings.boltzServerUrl ,
2021-12-29 18:08:41 -05:00
rejectUnauthorized : false ,
json : true ,
headers : { 'Grpc-Metadata-macaroon' : '' }
} ;
2024-06-10 12:40:37 -07:00
if ( req . session . selectedNode . authentication . boltzMacaroonPath ) {
2021-12-29 18:08:41 -05:00
try {
2024-06-10 12:40:37 -07:00
boltzOptions . headers = { 'Grpc-Metadata-macaroon' : fs . readFileSync ( join ( req . session . selectedNode . authentication . boltzMacaroonPath , 'admin.macaroon' ) ) . toString ( 'hex' ) } ;
2021-12-29 18:08:41 -05:00
} catch ( err ) {
2024-06-10 12:40:37 -07:00
this . logger . log ( { selectedNode : this.selectedNode , level : 'ERROR' , fileName : 'Common' , msg : 'Boltz macaroon Error' , error : err } ) ;
2021-12-29 18:08:41 -05:00
}
}
2024-06-10 12:40:37 -07:00
this . logger . log ( { selectedNode : this.selectedNode , level : 'INFO' , fileName : 'Common' , msg : 'Boltz Options' , data : boltzOptions } ) ;
2021-12-29 18:08:41 -05:00
return boltzOptions ;
} ;
public getOptions = ( req ) = > {
2024-06-10 12:40:37 -07:00
if ( req . session . selectedNode && req . session . selectedNode . authentication . options ) {
req . session . selectedNode . authentication . options . method = ( req . session . selectedNode . lnImplementation && req . session . selectedNode . lnImplementation . toUpperCase ( ) === 'LND' ) ? 'GET' : 'POST' ;
delete req . session . selectedNode . authentication . options . form ;
delete req . session . selectedNode . authentication . options . body ;
req . session . selectedNode . authentication . options . qs = { } ;
return req . session . selectedNode . authentication . options ;
2021-12-29 18:08:41 -05:00
}
2024-06-10 12:40:37 -07:00
return this . handleError ( { statusCode : 401 , message : 'Session expired after a day\'s inactivity' } , 'Session Expired' , 'Session Expiry Error' , this . selectedNode ) ;
2021-12-29 18:08:41 -05:00
} ;
public updateSelectedNodeOptions = ( req ) = > {
if ( ! req . session . selectedNode ) {
req . session . selectedNode = { } ;
}
2024-06-10 12:40:37 -07:00
req . session . selectedNode . authentication . options = {
2021-12-29 18:08:41 -05:00
url : '' ,
rejectUnauthorized : false ,
json : true ,
form : null
} ;
try {
2024-06-10 12:40:37 -07:00
if ( req . session . selectedNode && req . session . selectedNode . lnImplementation ) {
switch ( req . session . selectedNode . lnImplementation . toUpperCase ( ) ) {
2022-05-01 13:35:20 -04:00
case 'CLN' :
2023-12-05 20:32:05 -08:00
try {
2024-06-10 12:40:37 -07:00
if ( ! req . session . selectedNode . authentication . runeValue ) {
req . session . selectedNode . authentication . runeValue = this . getRuneValue ( req . session . selectedNode . authentication . runePath ) ;
2023-12-05 20:32:05 -08:00
}
2024-06-10 12:40:37 -07:00
req . session . selectedNode . authentication . options . headers = { rune : req.session.selectedNode.authentication.runeValue } ;
2023-12-05 20:32:05 -08:00
} catch ( err ) {
throw new Error ( err ) ;
}
2021-12-29 18:08:41 -05:00
break ;
case 'ECL' :
2024-06-10 12:40:37 -07:00
req . session . selectedNode . authentication . options . headers = { authorization : 'Basic ' + Buffer . from ( ':' + req . session . selectedNode . authentication . lnApiPassword ) . toString ( 'base64' ) } ;
2021-12-29 18:08:41 -05:00
break ;
default :
2024-06-10 12:40:37 -07:00
req . session . selectedNode . authentication . options . headers = { 'Grpc-Metadata-macaroon' : fs . readFileSync ( join ( req . session . selectedNode . authentication . macaroonPath , 'admin.macaroon' ) ) . toString ( 'hex' ) } ;
2021-12-29 18:08:41 -05:00
break ;
}
}
if ( req . session . selectedNode ) {
2024-06-10 12:40:37 -07:00
this . logger . log ( { selectedNode : this.selectedNode , level : 'INFO' , fileName : 'Common' , msg : 'Updated Node Options for ' + req . session . selectedNode . lnNode , data : req.session.selectedNode.authentication.options } ) ;
2021-12-29 18:08:41 -05:00
}
2022-01-16 15:55:50 -05:00
return { status : 200 , message : 'Updated Successfully' } ;
2021-12-29 18:08:41 -05:00
} catch ( err ) {
2024-06-10 12:40:37 -07:00
req . session . selectedNode . authentication . options = {
2021-12-29 18:08:41 -05:00
url : '' ,
rejectUnauthorized : false ,
json : true ,
form : null
} ;
2024-06-10 12:40:37 -07:00
this . logger . log ( { selectedNode : this.selectedNode , level : 'ERROR' , fileName : 'Common' , msg : 'Update Selected Node Options Error' , error : err } ) ;
2021-12-29 18:08:41 -05:00
return { status : 502 , message : err } ;
}
} ;
2023-12-05 20:32:05 -08:00
public getRuneValue = ( rune_path ) = > {
const data = fs . readFileSync ( rune_path , 'utf8' ) ;
const pattern = /LIGHTNING_RUNE="(?<runeValue>[^"]+)"/ ;
const match = data . match ( pattern ) ;
if ( match . groups . runeValue ) {
return match . groups . runeValue ;
} else {
throw new Error ( 'Rune not found in the file.' ) ;
}
} ;
2021-12-29 18:08:41 -05:00
public setOptions = ( req ) = > {
2024-06-10 12:40:37 -07:00
if ( this . nodes [ 0 ] . authentication . options && this . nodes [ 0 ] . authentication . options . headers ) { return ; }
2021-12-29 18:08:41 -05:00
if ( this . nodes && this . nodes . length > 0 ) {
this . nodes . forEach ( ( node ) = > {
2024-06-10 12:40:37 -07:00
node . authentication . options = {
2021-12-29 18:08:41 -05:00
url : '' ,
rejectUnauthorized : false ,
json : true ,
form : null
} ;
try {
2024-06-10 12:40:37 -07:00
if ( node . lnImplementation ) {
switch ( node . lnImplementation . toUpperCase ( ) ) {
2022-05-01 13:35:20 -04:00
case 'CLN' :
2023-12-05 20:32:05 -08:00
try {
2024-06-10 12:40:37 -07:00
if ( ! node . authentication . runeValue ) {
node . authentication . runeValue = this . getRuneValue ( node . authentication . runePath ) ;
2023-12-05 20:32:05 -08:00
}
2024-06-10 12:40:37 -07:00
node . authentication . options . headers = { rune : node.authentication.runeValue } ;
2023-12-05 20:32:05 -08:00
} catch ( err ) {
throw new Error ( err ) ;
}
2021-12-29 18:08:41 -05:00
break ;
case 'ECL' :
2024-06-10 12:40:37 -07:00
node . authentication . options . headers = { authorization : 'Basic ' + Buffer . from ( ':' + node . authentication . lnApiPassword ) . toString ( 'base64' ) } ;
2021-12-29 18:08:41 -05:00
break ;
default :
2024-06-10 12:40:37 -07:00
node . authentication . options . headers = { 'Grpc-Metadata-macaroon' : fs . readFileSync ( join ( node . authentication . macaroonPath , 'admin.macaroon' ) ) . toString ( 'hex' ) } ;
2021-12-29 18:08:41 -05:00
break ;
}
}
} catch ( err ) {
2024-06-10 12:40:37 -07:00
this . logger . log ( { selectedNode : this.selectedNode , level : 'ERROR' , fileName : 'Common' , msg : 'Common Set Options Error' , error : err } ) ;
node . authentication . options = {
2021-12-29 18:08:41 -05:00
url : '' ,
rejectUnauthorized : false ,
json : true ,
form : ''
} ;
}
2024-06-10 12:40:37 -07:00
this . logger . log ( { selectedNode : this.selectedNode , level : 'INFO' , fileName : 'Common' , msg : 'Set Node Options for ' + node . lnNode , data : node.authentication.options } ) ;
2021-12-29 18:08:41 -05:00
} ) ;
this . updateSelectedNodeOptions ( req ) ;
}
} ;
public findNode = ( selNodeIndex ) = > this . nodes . find ( ( node ) = > node . index === selNodeIndex ) ;
public replaceNode = ( req , newNode ) = > {
const foundIndex = this . nodes . findIndex ( ( node ) = > node . index === req . session . selectedNode . index ) ;
this . nodes . splice ( foundIndex , 1 , newNode ) ;
req . session . selectedNode = this . findNode ( req . session . selectedNode . index ) ;
} ;
public convertTimeToEpoch = ( timeToConvert : Date ) = > Math . floor ( timeToConvert . getTime ( ) / 1000 ) ;
public convertTimestampToTime = ( num ) = > {
const myDate = new Date ( + num * 1000 ) ;
let days = myDate . getDate ( ) . toString ( ) ;
days = + days < 10 ? '0' + days : days ;
let hours = myDate . getHours ( ) . toString ( ) ;
hours = + hours < 10 ? '0' + hours : hours ;
let minutes = myDate . getMinutes ( ) . toString ( ) ;
minutes = + minutes < 10 ? '0' + minutes : minutes ;
let seconds = myDate . getSeconds ( ) . toString ( ) ;
seconds = + seconds < 10 ? '0' + seconds : seconds ;
return days + '/' + this . MONTHS [ myDate . getMonth ( ) ] . name + '/' + myDate . getFullYear ( ) + ' ' + hours + ':' + minutes + ':' + seconds ;
} ;
public sortAscByKey = ( array , key ) = > array . sort ( ( a , b ) = > {
const x = + a [ key ] ;
const y = + b [ key ] ;
return ( ( x < y ) ? - 1 : ( ( x > y ) ? 1 : 0 ) ) ;
} ) ;
public sortAscByStrKey = ( array , key ) = > array . sort ( ( a , b ) = > {
const x = a [ key ] ? a [ key ] . toUpperCase ( ) : '' ;
const y = b [ key ] ? b [ key ] . toUpperCase ( ) : '' ;
return ( ( x < y ) ? - 1 : ( ( x > y ) ? 1 : 0 ) ) ;
} ) ;
public sortDescByKey = ( array , key ) = > {
const temp = array . sort ( ( a , b ) = > {
const x = + a [ key ] ? + a [ key ] : 0 ;
const y = + b [ key ] ? + b [ key ] : 0 ;
return ( x > y ) ? - 1 : ( ( x < y ) ? 1 : 0 ) ;
} ) ;
return temp ;
} ;
public sortDescByStrKey = ( array , key ) = > {
const temp = array . sort ( ( a , b ) = > {
const x = a [ key ] ? a [ key ] . toUpperCase ( ) : '' ;
const y = b [ key ] ? b [ key ] . toUpperCase ( ) : '' ;
return ( x > y ) ? - 1 : ( ( x < y ) ? 1 : 0 ) ;
} ) ;
return temp ;
} ;
public newestOnTop = ( array , key , value ) = > {
const newlyAddedRecord = array . splice ( array . findIndex ( ( item ) = > item [ key ] === value ) , 1 ) ;
2022-08-17 15:48:04 -07:00
array ? . unshift ( newlyAddedRecord [ 0 ] ) ;
2021-12-29 18:08:41 -05:00
return array ;
} ;
2022-08-17 15:48:04 -07:00
public camelCase = ( str : string ) = > str ? . replace ( /(?:^\w|[A-Z]|\b\w)/g , ( word , index ) = > ( word . toUpperCase ( ) ) ) ? . replace ( /\s+/g , '' ) ? . replace ( /-/g , ' ' ) ;
2022-06-21 20:29:30 -04:00
public titleCase = ( str : string ) = > {
if ( str . indexOf ( '!\n' ) > 0 || str . indexOf ( '.\n' ) > 0 ) {
2022-08-17 15:48:04 -07:00
return str . split ( '\n' ) ? . reduce ( ( accumulator , currentStr ) = > accumulator + currentStr . charAt ( 0 ) . toUpperCase ( ) + currentStr . substring ( 1 ) . toLowerCase ( ) + '\n' , '' ) ;
2022-06-21 20:29:30 -04:00
} else {
if ( str . indexOf ( ' ' ) > 0 ) {
2022-08-17 15:48:04 -07:00
return str . split ( ' ' ) ? . reduce ( ( accumulator , currentStr ) = > accumulator + currentStr . charAt ( 0 ) . toUpperCase ( ) + currentStr . substring ( 1 ) . toLowerCase ( ) + ' ' , '' ) ;
2022-06-21 20:29:30 -04:00
} else {
return str . charAt ( 0 ) . toUpperCase ( ) + str . substring ( 1 ) . toLowerCase ( ) ;
}
}
} ;
2024-06-10 12:40:37 -07:00
public handleError = ( errRes , fileName , errMsg , selectedNode : SelectedNode ) = > {
const err = JSON . parse ( JSON . stringify ( errRes ) ) ;
if ( ! selectedNode ) { selectedNode = this . selectedNode ; }
switch ( selectedNode . lnImplementation ) {
2021-12-29 18:08:41 -05:00
case 'LND' :
if ( err . options && err . options . headers && err . options . headers [ 'Grpc-Metadata-macaroon' ] ) {
delete err . options . headers [ 'Grpc-Metadata-macaroon' ] ;
}
if ( err . response && err . response . request && err . response . request . headers && err . response . request . headers [ 'Grpc-Metadata-macaroon' ] ) {
delete err . response . request . headers [ 'Grpc-Metadata-macaroon' ] ;
}
break ;
2022-05-01 13:35:20 -04:00
case 'CLN' :
2024-06-10 12:40:37 -07:00
if ( err . options && err . options . headers && err . options . headers . rune ) {
delete err . options . headers . rune ;
2021-12-29 18:08:41 -05:00
}
2024-06-10 12:40:37 -07:00
if ( err . response && err . response . request && err . response . request . headers && err . response . request . headers . rune ) {
delete err . response . request . headers . rune ;
2021-12-29 18:08:41 -05:00
}
break ;
case 'ECL' :
if ( err . options && err . options . headers && err . options . headers . authorization ) {
delete err . options . headers . authorization ;
}
if ( err . response && err . response . request && err . response . request . headers && err . response . request . headers . authorization ) {
delete err . response . request . headers . authorization ;
}
break ;
default :
if ( err . options && err . options . headers ) { delete err . options . headers ; }
break ;
}
2023-02-17 14:58:59 -08:00
this . logger . log ( { selectedNode : selectedNode , level : 'ERROR' , fileName : fileName , msg : errMsg , error : ( typeof err === 'object' ? JSON . stringify ( err ) : err ) } ) ;
2022-11-03 14:31:09 -07:00
let newErrorObj = { statusCode : 500 , message : '' , error : '' } ;
2022-11-02 15:59:37 -07:00
if ( err . code && err . code === 'ENOENT' ) {
newErrorObj = {
statusCode : 500 ,
message : 'No such file or directory ' + ( err . path ? err . path : '' ) ,
error : 'No such file or directory ' + ( err . path ? err . path : '' )
} ;
} else {
newErrorObj = {
statusCode : err.statusCode ? err.statusCode : err.status ? err . status : ( err . error && err . error . code && err . error . code === 'ECONNREFUSED' ) ? 503 : 500 ,
message : ( err . error && err . error . message ) ? err.error.message : err.message ? err.message : errMsg ,
error : (
( err . error && err . error . error && err . error . error . error && typeof err . error . error . error === 'string' ) ? err . error . error . error :
( err . error && err . error . error && typeof err . error . error === 'string' ) ? err . error . error :
( err . error && err . error . error && err . error . error . message && typeof err . error . error . message === 'string' ) ? err . error . error . message :
( err . error && err . error . message && typeof err . error . message === 'string' ) ? err . error . message :
( err . error && typeof err . error === 'string' ) ? err . error :
( err . message && typeof err . message === 'string' ) ? err . message : ( typeof err === 'string' ) ? err : 'Unknown Error'
)
} ;
}
2024-06-10 12:40:37 -07:00
if ( selectedNode . lnImplementation === 'ECL' && err . message && err . message . indexOf ( 'Authentication Error' ) < 0 && err . name && err . name === 'StatusCodeError' ) { newErrorObj . statusCode = 500 ; }
2021-12-29 18:08:41 -05:00
return newErrorObj ;
} ;
public getRequestIP = ( req ) = > ( ( typeof req . headers [ 'x-forwarded-for' ] === 'string' && req . headers [ 'x-forwarded-for' ] . split ( ',' ) . shift ( ) ) ||
req . ip ||
req . connection . remoteAddress ||
req . socket . remoteAddress ||
( req . connection . socket ? req.connection.socket.remoteAddress : null ) ) ;
public getDummyData = ( dataKey , lnImplementation ) = > {
2024-06-10 12:40:37 -07:00
const dummyDataFile = this . appConfig . rtlConfFilePath + sep + 'ECLDummyData.log' ;
2021-12-29 18:08:41 -05:00
return new Promise ( ( resolve , reject ) = > {
if ( this . dummy_data_array_from_file . length === 0 ) {
fs . readFile ( dummyDataFile , 'utf8' , ( err , data ) = > {
if ( err ) {
if ( err . code === 'ENOENT' ) {
2024-06-10 12:40:37 -07:00
this . logger . log ( { selectedNode : this.selectedNode , level : 'ERROR' , fileName : 'Common' , msg : 'Dummy data file does not exist' } ) ;
2021-12-29 18:08:41 -05:00
} else {
2024-06-10 12:40:37 -07:00
this . logger . log ( { selectedNode : this.selectedNode , level : 'ERROR' , fileName : 'Common' , msg : 'Getting dummy data failed' } ) ;
2021-12-29 18:08:41 -05:00
}
} else {
this . dummy_data_array_from_file = data . split ( '\n' ) ;
resolve ( this . filterData ( dataKey , lnImplementation ) ) ;
}
} ) ;
} else {
resolve ( this . filterData ( dataKey , lnImplementation ) ) ;
}
} ) ;
} ;
public readCookie = ( ) = > {
2024-06-10 12:40:37 -07:00
const exists = fs . existsSync ( this . appConfig . SSO . rtlCookiePath ) ;
2021-12-29 18:08:41 -05:00
if ( exists ) {
try {
2024-06-10 12:40:37 -07:00
this . appConfig . SSO . cookieValue = fs . readFileSync ( this . appConfig . SSO . rtlCookiePath , 'utf-8' ) ;
2021-12-29 18:08:41 -05:00
} catch ( err ) {
2024-06-10 12:40:37 -07:00
this . logger . log ( { selectedNode : this.selectedNode , level : 'ERROR' , fileName : 'Common' , msg : 'Something went wrong while reading cookie: \n' + err } ) ;
2021-12-29 18:08:41 -05:00
throw new Error ( err ) ;
}
} else {
try {
2024-06-10 12:40:37 -07:00
const directoryName = dirname ( this . appConfig . SSO . rtlCookiePath ) ;
2021-12-29 18:08:41 -05:00
this . createDirectory ( directoryName ) ;
2024-06-10 12:40:37 -07:00
fs . writeFileSync ( this . appConfig . SSO . rtlCookiePath , crypto . randomBytes ( 64 ) . toString ( 'hex' ) ) ;
this . appConfig . SSO . cookieValue = fs . readFileSync ( this . appConfig . SSO . rtlCookiePath , 'utf-8' ) ;
2021-12-29 18:08:41 -05:00
} catch ( err ) {
2024-06-10 12:40:37 -07:00
this . logger . log ( { selectedNode : this.selectedNode , level : 'ERROR' , fileName : 'Common' , msg : 'Something went wrong while reading the cookie: \n' + err } ) ;
2021-12-29 18:08:41 -05:00
throw new Error ( err ) ;
}
}
2022-08-11 02:17:43 -07:00
} ;
2021-12-29 18:08:41 -05:00
public refreshCookie = ( ) = > {
try {
2024-06-10 12:40:37 -07:00
fs . writeFileSync ( this . appConfig . SSO . rtlCookiePath , crypto . randomBytes ( 64 ) . toString ( 'hex' ) ) ;
this . appConfig . SSO . cookieValue = fs . readFileSync ( this . appConfig . SSO . rtlCookiePath , 'utf-8' ) ;
2021-12-29 18:08:41 -05:00
} catch ( err ) {
2024-06-10 12:40:37 -07:00
this . logger . log ( { selectedNode : this.selectedNode , level : 'ERROR' , fileName : 'Common' , msg : 'Something went wrong while refreshing cookie' , error : err } ) ;
2021-12-29 18:08:41 -05:00
throw new Error ( err ) ;
}
2022-08-11 02:17:43 -07:00
} ;
2021-12-29 18:08:41 -05:00
public createDirectory = ( directoryName ) = > {
const initDir = isAbsolute ( directoryName ) ? sep : '' ;
2022-08-17 15:48:04 -07:00
directoryName . split ( sep ) ? . reduce ( ( parentDir , childDir ) = > {
2021-12-29 18:08:41 -05:00
const curDir = resolve ( parentDir , childDir ) ;
try {
if ( ! fs . existsSync ( curDir ) ) {
fs . mkdirSync ( curDir ) ;
}
} catch ( err ) {
if ( err . code !== 'EEXIST' ) {
if ( err . code === 'ENOENT' ) {
throw new Error ( ` ENOENT: No such file or directory, mkdir ' ${ directoryName } '. Ensure that the path separator is ' ${ sep } ' ` ) ;
} else {
throw err ;
}
}
}
return curDir ;
} , initDir ) ;
} ;
public replacePasswordWithHash = ( multiPassHashed ) = > {
2024-06-10 12:40:37 -07:00
this . appConfig . rtlConfFilePath = process . env . RTL_CONFIG_PATH ? process.env.RTL_CONFIG_PATH : join ( dirname ( fileURLToPath ( import . meta . url ) ) , '../..' ) ;
2021-12-29 18:08:41 -05:00
try {
2024-06-10 12:40:37 -07:00
const RTLConfFile = this . appConfig . rtlConfFilePath + sep + 'RTL-Config.json' ;
2021-12-29 18:08:41 -05:00
const config = JSON . parse ( fs . readFileSync ( RTLConfFile , 'utf-8' ) ) ;
config . multiPassHashed = multiPassHashed ;
delete config . multiPass ;
fs . writeFileSync ( RTLConfFile , JSON . stringify ( config , null , 2 ) , 'utf-8' ) ;
2024-06-10 12:40:37 -07:00
this . logger . log ( { selectedNode : this.selectedNode , level : 'INFO' , fileName : 'Common' , msg : 'Please note that, RTL has encrypted the plaintext password into its corresponding hash' } ) ;
2021-12-29 18:08:41 -05:00
return config . multiPassHashed ;
} catch ( err ) {
2024-06-10 12:40:37 -07:00
this . logger . log ( { selectedNode : this.selectedNode , level : 'ERROR' , fileName : 'Common' , msg : 'Password hashing failed' , error : err } ) ;
2021-12-29 18:08:41 -05:00
}
2022-08-11 02:17:43 -07:00
} ;
2021-12-29 18:08:41 -05:00
2024-06-10 12:40:37 -07:00
public getAllNodeAllChannelBackup = ( node : SelectedNode ) = > {
const channel_backup_file = node . settings . channelBackupPath + sep + 'channel-all.bak' ;
2021-12-29 18:08:41 -05:00
const options = {
2024-06-10 12:40:37 -07:00
url : node.settings.lnServerUrl + '/v1/channels/backup' ,
2021-12-29 18:08:41 -05:00
rejectUnauthorized : false ,
json : true ,
2024-06-10 12:40:37 -07:00
headers : { 'Grpc-Metadata-macaroon' : fs . readFileSync ( node . authentication . macaroonPath + '/admin.macaroon' ) . toString ( 'hex' ) }
2021-12-29 18:08:41 -05:00
} ;
2024-06-10 12:40:37 -07:00
this . logger . log ( { selectedNode : this.selectedNode , level : 'INFO' , fileName : 'Common' , msg : 'Getting Channel Backup for Node ' + node . lnNode + '..' } ) ;
2021-12-29 18:08:41 -05:00
request ( options ) . then ( ( body ) = > {
fs . writeFile ( channel_backup_file , JSON . stringify ( body ) , ( err ) = > {
if ( err ) {
2024-06-10 12:40:37 -07:00
if ( node . lnNode ) {
this . logger . log ( { selectedNode : this.selectedNode , level : 'ERROR' , fileName : 'Common' , msg : 'Error in Channel Backup for Node ' + node . lnNode , error : err } ) ;
2021-12-29 18:08:41 -05:00
} else {
2024-06-10 12:40:37 -07:00
this . logger . log ( { selectedNode : this.selectedNode , level : 'ERROR' , fileName : 'Common' , msg : 'Error in Channel Backup for File ' + channel_backup_file , error : err } ) ;
2021-12-29 18:08:41 -05:00
}
} else {
2024-06-10 12:40:37 -07:00
if ( node . lnNode ) {
this . logger . log ( { selectedNode : this.selectedNode , level : 'INFO' , fileName : 'Common' , msg : 'Successful in Channel Backup for Node ' + node . lnNode , data : body } ) ;
2021-12-29 18:08:41 -05:00
} else {
2024-06-10 12:40:37 -07:00
this . logger . log ( { selectedNode : this.selectedNode , level : 'INFO' , fileName : 'Common' , msg : 'Successful in Channel Backup for File ' + channel_backup_file , data : body } ) ;
2021-12-29 18:08:41 -05:00
}
}
} ) ;
} , ( err ) = > {
2024-06-10 12:40:37 -07:00
this . logger . log ( { selectedNode : this.selectedNode , level : 'ERROR' , fileName : 'Common' , msg : 'Error in Channel Backup for Node ' + node . lnNode , error : err } ) ;
2022-01-16 15:55:50 -05:00
fs . writeFile ( channel_backup_file , '' , ( ) = > { } ) ;
2021-12-29 18:08:41 -05:00
} ) ;
} ;
public isVersionCompatible = ( currentVersion , checkVersion ) = > {
2023-12-05 20:32:05 -08:00
if ( currentVersion && currentVersion !== '' ) {
// eslint-disable-next-line prefer-named-capture-group
const pattern = /v?(\d+(\.\d+)*)/ ;
const match = currentVersion . match ( pattern ) ;
if ( match && match . length && match . length > 1 ) {
2024-06-10 12:40:37 -07:00
this . logger . log ( { selectedNode : this.selectedNode , level : 'INFO' , fileName : 'Common' , msg : 'Global Version ' + match [ 1 ] } ) ;
this . logger . log ( { selectedNode : this.selectedNode , level : 'INFO' , fileName : 'Common' , msg : 'Checking Compatiblility with Version ' + checkVersion } ) ;
2023-12-05 20:32:05 -08:00
const currentVersionArr = match [ 1 ] . split ( '.' ) || [ ] ;
currentVersionArr [ 1 ] = currentVersionArr [ 1 ] . substring ( 0 , 2 ) ;
const checkVersionsArr = checkVersion . split ( '.' ) ;
checkVersionsArr [ 1 ] = checkVersionsArr [ 1 ] . substring ( 0 , 2 ) ;
return ( + currentVersionArr [ 0 ] > + checkVersionsArr [ 0 ] ) ||
( + currentVersionArr [ 0 ] === + checkVersionsArr [ 0 ] && + currentVersionArr [ 1 ] > + checkVersionsArr [ 1 ] ) ||
( + currentVersionArr [ 0 ] === + checkVersionsArr [ 0 ] && + currentVersionArr [ 1 ] === + checkVersionsArr [ 1 ] && + currentVersionArr [ 2 ] >= + checkVersionsArr [ 2 ] ) ;
} else {
2024-06-10 12:40:37 -07:00
this . logger . log ( { selectedNode : this.selectedNode , level : 'ERROR' , fileName : 'Common' , msg : 'Invalid Version String ' + currentVersion } ) ;
2023-12-05 20:32:05 -08:00
return false ;
}
2021-12-29 18:08:41 -05:00
}
return false ;
2022-08-11 02:17:43 -07:00
} ;
2021-12-29 18:08:41 -05:00
public getMonthDays = ( selMonth , selYear ) = > ( ( selMonth === 1 && selYear % 4 === 0 ) ? ( this . MONTHS [ selMonth ] . days + 1 ) : this . MONTHS [ selMonth ] . days ) ;
public logEnvVariables = ( req ) = > {
2024-06-10 12:40:37 -07:00
const selNode = < SelectedNode > req . session . selectedNode ;
2021-12-29 18:08:41 -05:00
if ( selNode && selNode . index ) {
2024-06-10 12:40:37 -07:00
this . logger . log ( { selectedNode : selNode , level : 'INFO' , fileName : 'Config Setup:' , msg : JSON.stringify ( this . removeSecureData ( JSON . parse ( JSON . stringify ( this . appConfig ) ) ) ) } ) ;
this . logger . log ( { selectedNode : selNode , level : 'INFO' , fileName : 'Config Setup Variable' , msg : 'SSO: ' + this . appConfig . SSO . rtlSso } ) ;
2021-12-29 18:08:41 -05:00
}
2022-08-11 02:17:43 -07:00
} ;
2021-12-29 18:08:41 -05:00
public filterData = ( dataKey , lnImplementation ) = > {
let search_string = '' ;
if ( lnImplementation === 'ECL' ) {
switch ( dataKey ) {
case 'GetInfo' : search_string = 'INFO: GetInfo => Get Info Response: ' ; break ;
case 'Fees' : search_string = 'INFO: Fees => Fee Response: ' ; break ;
case 'Payments' : search_string = 'INFO: Fees => Payments Response: ' ; break ;
case 'Invoices' : search_string = 'INFO: Invoice => Invoices List Received: ' ; break ;
case 'OnChainBalance' : search_string = 'INFO: Onchain => Balance Received: ' ; break ;
case 'Peers' : search_string = 'INFO: Peers => Peers with Alias: ' ; break ;
case 'Channels' : search_string = 'INFO: Channels => Simplified Channels with Alias: ' ; break ;
default : search_string = 'Random Line' ; break ;
}
2022-05-01 13:35:20 -04:00
} else if ( lnImplementation === 'CLN' ) {
2021-12-29 18:08:41 -05:00
switch ( dataKey ) {
case 'GetInfo' : search_string = 'DEBUG: GetInfo => Node Information. ' ; break ;
case 'Fees' : search_string = 'DEBUG: Fees => Fee Received. ' ; break ;
case 'Payments' : search_string = 'DEBUG: Payments => Payment List Received: ' ; break ;
case 'Invoices' : search_string = 'DEBUG: Invoice => Invoices List Received. ' ; break ;
case 'ChannelBalance' : search_string = 'DEBUG: Channels => Local Remote Balance. ' ; break ;
case 'Peers' : search_string = 'DEBUG: Peers => Peers with Alias: ' ; break ;
case 'Channels' : search_string = 'DEBUG: Channels => List Channels: ' ; break ;
case 'Balance' : search_string = 'DEBUG: Balance => Balance Received. ' ; break ;
case 'ForwardingHistory' : search_string = 'DEBUG: Channels => Forwarding History Received: ' ; break ;
case 'UTXOs' : search_string = 'DEBUG: OnChain => List Funds Received. ' ; break ;
case 'FeeRateperkb' : search_string = 'DEBUG: Network => Network Fee Rates Received for perkb. ' ; break ;
case 'FeeRateperkw' : search_string = 'DEBUG: Network => Network Fee Rates Received for perkw. ' ; break ;
default : search_string = 'Random Line' ; break ;
}
}
const foundDataLine = this . dummy_data_array_from_file . find ( ( dataItem ) = > dataItem . includes ( search_string ) ) ;
const dataStr = foundDataLine ? foundDataLine . substring ( ( foundDataLine . indexOf ( search_string ) ) + search_string . length ) : '{}' ;
return JSON . parse ( dataStr ) ;
2022-08-11 02:17:43 -07:00
} ;
2021-12-29 18:08:41 -05:00
}
export const Common = new CommonService ( ) ;