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 } from './logger.js' ;
export class CommonService {
constructor ( ) {
this . logger = Logger ;
this . nodes = [ ] ;
2024-06-10 12:40:37 -07:00
this . selectedNode = null ;
this . ssoInit = { rtlSso : 0 , rtlCookiePath : '' , logoutRedirectLink : '' , cookieValue : '' } ;
this . appConfig = { 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
this . port = 3000 ;
2023-02-17 17:33:33 -08:00
this . host = '' ;
2021-12-29 18:08:41 -05:00
this . secret _key = crypto . randomBytes ( 64 ) . toString ( 'hex' ) ;
this . read _dummy _data = false ;
this . baseHref = '/rtl' ;
this . dummy _data _array _from _file = [ ] ;
2022-11-02 15:59:37 -07:00
this . 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 }
] ;
2024-06-10 12:40:37 -07:00
this . 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 ;
} ;
this . removeAuthSecureData = ( node ) => {
delete node . authentication . macaroonPath ;
delete node . authentication . runePath ;
delete node . authentication . runeValue ;
delete node . authentication . lnApiPassword ;
delete node . authentication . options ;
return node ;
} ;
this . removeSecureData = ( config ) => {
delete config . rtlConfFilePath ;
delete config . rtlPass ;
delete config . multiPass ;
delete config . multiPassHashed ;
delete config . secret2FA ;
config . nodes ? . map ( ( node ) => this . removeAuthSecureData ( node ) ) ;
return config ;
} ;
this . addSecureData = ( config ) => {
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 ;
} ;
2023-10-02 20:55:16 -07:00
this . 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 ;
} ;
this . 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 ;
} ;
this . 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
} ;
this . 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
this . 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
this . setOptions = ( req ) => {
2024-06-10 12:40:37 -07:00
if ( this . nodes [ 0 ] . authentication . options && this . nodes [ 0 ] . authentication . options . headers ) {
2021-12-29 18:08:41 -05:00
return ;
}
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 ) ;
}
} ;
this . findNode = ( selNodeIndex ) => this . nodes . find ( ( node ) => node . index === selNodeIndex ) ;
this . 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 ) ;
} ;
this . convertTimeToEpoch = ( timeToConvert ) => Math . floor ( timeToConvert . getTime ( ) / 1000 ) ;
this . 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 ;
} ;
this . sortAscByKey = ( array , key ) => array . sort ( ( a , b ) => {
const x = + a [ key ] ;
const y = + b [ key ] ;
return ( ( x < y ) ? - 1 : ( ( x > y ) ? 1 : 0 ) ) ;
} ) ;
this . 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 ) ) ;
} ) ;
this . 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 ;
} ;
this . 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 ;
} ;
this . newestOnTop = ( array , key , value ) => {
const newlyAddedRecord = array . splice ( array . findIndex ( ( item ) => item [ key ] === value ) , 1 ) ;
2022-11-23 18:20:23 -08:00
array ? . unshift ( newlyAddedRecord [ 0 ] ) ;
2021-12-29 18:08:41 -05:00
return array ;
} ;
2022-11-23 18:20:23 -08:00
this . camelCase = ( str ) => 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
this . titleCase = ( str ) => {
if ( str . indexOf ( '!\n' ) > 0 || str . indexOf ( '.\n' ) > 0 ) {
2022-11-23 18:20:23 -08: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-11-23 18:20:23 -08: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 ( ) ;
}
}
} ;
2021-12-29 18:08:41 -05:00
this . handleError = ( errRes , fileName , errMsg , selectedNode ) => {
2024-06-10 12:40:37 -07:00
const err = JSON . parse ( JSON . stringify ( errRes ) ) ;
2021-12-29 18:08:41 -05:00
if ( ! selectedNode ) {
2024-06-10 12:40:37 -07:00
selectedNode = this . selectedNode ;
2021-12-29 18:08:41 -05:00
}
2024-06-10 12:40:37 -07:00
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' ) {
2022-11-16 13:02:43 -08:00
newErrorObj . statusCode = 500 ;
}
2021-12-29 18:08:41 -05:00
return newErrorObj ;
} ;
this . 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 ) ) ;
this . 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 ) ) ;
}
} ) ;
} ;
this . 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 ) ;
}
}
} ;
this . 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 ) ;
}
} ;
this . createDirectory = ( directoryName ) => {
const initDir = isAbsolute ( directoryName ) ? sep : '' ;
2022-11-23 18:20:23 -08: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 ) ;
} ;
this . 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
}
} ;
this . getAllNodeAllChannelBackup = ( node ) => {
2024-06-10 12:40:37 -07:00
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
} ) ;
} ;
this . 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 ;
} ;
this . getMonthDays = ( selMonth , selYear ) => ( ( selMonth === 1 && selYear % 4 === 0 ) ? ( this . MONTHS [ selMonth ] . days + 1 ) : this . MONTHS [ selMonth ] . days ) ;
this . logEnvVariables = ( req ) => {
const selNode = req . session . selectedNode ;
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
}
} ;
this . 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 ) ;
} ;
}
}
export const Common = new CommonService ( ) ;