Documentation Updated

Documentation Updated
pull/260/head
Shahana Farooqui 4 years ago
parent 505ba27013
commit 9c9d4f08a0

5
.gitignore vendored

@ -37,12 +37,9 @@ testem.log
.DS_Store
Thumbs.db
RTL.conf
/logs/*
/cookies/*
RTL-Multi-Node-Conf.json
RTL-Multi-Node-Conf-1.json
RTL-Config.json
/backup/*
cookies
sample-RTL-SSO.conf
.env

@ -71,46 +71,53 @@ $ git pull
$ npm install --only=prod
```
### <a name="prep"></a>Prep for Execution
RTL requires its own config file `RTL.conf`, to start the server and provide user authentication on the app.
RTL requires its own config file `RTL-Config.json`, to start the server and provide user authentication on the app.
*Advanced users can refer to [this page](docs/Multi-Node-setup.md), for config settings required to manage multiple nodes*
* Rename `sample-RTL.conf` file to `RTL.conf`.
* Rename `sample-RTL-Config.json` file to `RTL-Config.json`.
* Locate the complete path of the readable macroon file (admin.macroon) on your node and the lnd.conf file.
* Modify the `RTL.conf` file per the example file below
* Modify the `RTL-Config.json` file per the example file below
Example RTL.conf:
Example RTL-Config.json:
```
[Authentication]
macaroonPath=C:\Users\<user>\AppData\Local\Lnd\data\chain\bitcoin\testnet
nodeAuthType=CUSTOM
lndConfigPath=C:\Users\<user>\AppData\Local\Lnd\lnd.conf
rtlPass=***
[SSO]
rtlSSO=0
rtlCookiePath=C:\RTL\cookies\auth.cookie
logoutRedirectLink=/login
[Settings]
userPersona=OPERATOR
themeMode=DAY
themeColor=PURPLE
channelBackupPath=C:\Users\shaha\backup\node-0
bitcoindConfigPath=C:/Bitcoin/bitcoin.conf
enableLogging=true
port=3000
lndServerUrl=https://192.168.1.16:8080/v1
fiatConversion=false
{
"multiPass": "password",
"port": "3000",
"defaultNodeIndex": 1,
"SSO": {
"rtlSSO": 0,
"rtlCookiePath": "",
"logoutRedirectLink": ""
},
"nodes": [
{
"index": 1,
"lnNode": "LND Testnet",
"lnImplementation": "LND",
"Authentication": {
"macaroonPath": "<Complete path of the folder containing admin.macaroon for the node # 1>",
"configPath": "<Optional:Path of the lnd.conf if present locally or empty>"
},
"Settings": {
"userPersona": "OPERATOR",
"themeMode": "DAY",
"themeColor": "PURPLE",
"channelBackupPath": "C:\\RTL\\backup\\node-1",
"bitcoindConfigPath": "<Optional: path of bitcoind.conf path if available locally>",
"enableLogging": true,
"fiatConversion": false,
"lnServerUrl": "<Service url for LND REST APIs for node # 1 e.g. https://192.168.0.1:8080/v1"
}
}
]
}
```
For details on all the configuration options refer to [this page](./docs/Application_configurations).
#### User Authentication on RTL
RTL requires the user to be authenticated by the application first, before allowing access to LND functions.
There are two options to configure authentication on RTL, depending on the `nodeAuthtype` value provided in RTL.conf.
* Option 1: `nodeAuthType=DEFAULT`; Password provided in lnd.conf for the rpc setting for bitcoind will be used for authentication.
* Option 2: `nodeAuthType=CUSTOM`; Specific password must be provided in RTL.conf (in plain text) for authentication. Password should be set with `rtlPass=<user defined>` in the [Authentication] section of RTL.conf
Specific password must be provided in RTL-Config.json (in plain text) for authentication. Password should be set with `multiPass:<user defined>` in the `Authentication` section of RTL-Config.json. Default initial password is `password`.
### <a name="start"></a>Start the Server
Run the following command:

@ -12,5 +12,5 @@
<link rel="stylesheet" href="styles.90ee7bcb73e8367b2a29.css"></head>
<body>
<rtl-app></rtl-app>
<script src="runtime.381542d227df565e3542.js" defer></script><script src="polyfills-es5.37b2eeccc22c1df73ce7.js" nomodule defer></script><script src="polyfills.f1c3d2a0bcdfc4e93ca8.js" defer></script><script src="main.2881f7333c1176eec0b8.js" defer></script></body>
<script src="runtime.381542d227df565e3542.js" defer></script><script src="polyfills-es5.37b2eeccc22c1df73ce7.js" nomodule defer></script><script src="polyfills.f1c3d2a0bcdfc4e93ca8.js" defer></script><script src="main.5b4359fe9f0cfb1a3504.js" defer></script></body>
</html>

File diff suppressed because one or more lines are too long

@ -3,9 +3,7 @@ var crypto = require('crypto');
var path = require('path');
var common = {};
common.multi_node_setup = false;
common.rtl_conf_file_path = '';
common.node_auth_type = 'DEFAULT';
common.rtl_pass = '';
common.rtl_sso = 0;
common.port = 3000;

@ -3,11 +3,8 @@ var fs = require('fs');
var platform = require('os').platform();
var crypto = require('crypto');
var hash = crypto.createHash('sha256');
var clArgs = require('optimist').argv;
var ini = require('ini');
var common = require('./common');
var path = require('path');
var upperCase = require('upper-case');
var logger = require('./controllers/logger');
var connect = {};
var errMsg = '';
@ -41,28 +38,35 @@ connect.setDefaultConfig = () => {
break;
}
return {
multiPass: "password",
port: "3000",
defaultNodeIndex: 1,
SSO: {
rtlSSO: 0,
rtlCookiePath: "",
logoutRedirectLink: ""
},
Authentication: {
macaroonPath: macaroonPath,
configPath: configPath,
nodeAuthType:"CUSTOM",
rtlPass:"password"
},
Settings: {
port: "3000",
lnImplementation: "LND",
userPersona: 'MERCHANT',
themeMode: "DAY",
themeColor: "PURPLE",
enableLogging: false,
lnServerUrl: "https://localhost:8080/v1",
fiatConversion: false
}
};
nodes: [
{
index: 1,
lnNode: "LND Node 1",
lnImplementation: "LND",
Authentication: {
macaroonPath: macaroonPath,
configPath: configPath,
},
Settings: {
userPersona: 'MERCHANT',
themeMode: "DAY",
themeColor: "PURPLE",
channelBackupPath: channelBackupPath,
enableLogging: false,
lnServerUrl: "https://localhost:8080/v1",
fiatConversion: false
}
}
]
}
}
connect.normalizePort = val => {
@ -92,208 +96,31 @@ connect.setMacaroonPath = (clArgs, config) => {
}
}
connect.convertCustomToHash = (nodeSetupType) => {
common.rtl_conf_file_path = (process.env.RTL_CONFIG_PATH) ? process.env.RTL_CONFIG_PATH.substring(0, process.env.RTL_CONFIG_PATH.length - 9) : path.normalize(__dirname);
if(nodeSetupType === 'SINGLE') {
try {
RTLConfFile = common.rtl_conf_file_path + '/RTL.conf';
var config = ini.parse(fs.readFileSync(RTLConfFile, 'utf-8'));
const authTemp = config.Authentication;
authTemp.rtlPassHashed = hash.update(authTemp.rtlPass).digest('hex');
delete authTemp.rtlPass;
delete config.Authentication;
fs.writeFileSync(RTLConfFile, ini.stringify(config));
fs.appendFileSync(RTLConfFile, ini.stringify(authTemp, { section: 'Authentication' }));
console.log('Please note that RTL has hashed the plaintext password into its corresponding hash.');
return authTemp.rtlPassHashed;
} catch (err) {
errMsg = errMsg + '\nrtlPass hash conversion failed!';
}
}
if(nodeSetupType === 'MULTI') {
try {
RTLConfFile = common.rtl_conf_file_path + '/RTL-Multi-Node-Conf.json';
var config = JSON.parse(fs.readFileSync(RTLConfFile, 'utf-8'));
config.multiPassHashed = hash.update(config.multiPass).digest('hex');
delete config.multiPass;
fs.writeFileSync(RTLConfFile, JSON.stringify(config, null, 2), 'utf-8');
console.log('Please note that RTL has encrypted the plaintext password into its corresponding hash.');
return config.multiPassHashed;
} catch (err) {
errMsg = errMsg + '\nmultiPass hash conversion failed!';
}
}
}
connect.validateSingleNodeConfig = (config) => {
connect.setSSOParams(config);
if(process.env.LN_IMPLEMENTATION) {
common.nodes[0].ln_implementation = process.env.LN_IMPLEMENTATION;
} else if (config.Settings.lnImplementation && config.Settings.lnImplementation !== '') {
common.nodes[0].ln_implementation = config.Settings.lnImplementation;
} else {
common.nodes[0].ln_implementation = 'LND';
}
if(!+common.rtl_sso) {
if(process.env.NODE_AUTH_TYPE) {
common.node_auth_type = process.env.NODE_AUTH_TYPE;
} else {
if(config.Authentication.nodeAuthType === '' || undefined === config.Authentication.nodeAuthType) {
errMsg = errMsg + '\nPlease set Node Auth Type through environment or RTL.conf!';
} else {
common.node_auth_type = config.Authentication.nodeAuthType;
}
}
if (process.env.RTL_PASS) {
common.rtl_pass = hash.update(process.env.RTL_PASS).digest('hex');
} else if (config.Authentication.rtlPassHashed !== '' && config.Authentication.rtlPassHashed) {
common.rtl_pass = config.Authentication.rtlPassHashed;
} else if (config.Authentication.rtlPass !== '' && config.Authentication.rtlPass) {
common.rtl_pass = connect.convertCustomToHash('SINGLE');
}
if (upperCase(common.node_auth_type) === 'CUSTOM' && (common.rtl_pass === '' || undefined === common.rtl_pass)) {
errMsg = errMsg + '\nCustom Node Authentication can be set with RTL password only. Please set RTL Password through environment or RTL.conf';
}
if(process.env.LND_CONFIG_PATH) {
common.nodes[0].config_path = process.env.LND_CONFIG_PATH;
} else if (process.env.CONFIG_PATH) {
common.nodes[0].config_path = process.env.CONFIG_PATH;
} else {
if(config.Authentication.lndConfigPath !== '' && config.Authentication.lndConfigPath) {
common.nodes[0].config_path = config.Authentication.lndConfigPath;
} else if(config.Authentication.configPath && config.Authentication.configPath.trim() !== '') {
common.nodes[0].config_path = config.Authentication.configPath;
} else {
if(upperCase(common.node_auth_type) === 'DEFAULT') {
errMsg = errMsg + '\nDefault Node Authentication can be set with LND Config Path only. Please set LND Config Path through environment or RTL.conf!';
}
}
}
}
if(common.nodes[0].macaroon_path === '' || undefined === common.nodes[0].macaroon_path) {
errMsg = 'Please set macaroon path through environment or RTL.conf!';
}
if(process.env.LND_SERVER_URL) {
common.nodes[0].ln_server_url = process.env.LND_SERVER_URL;
} else if(process.env.LN_SERVER_URL) {
common.nodes[0].ln_server_url = process.env.LN_SERVER_URL;
} else {
if(
(config.Authentication.lndServerUrl === '' || undefined === config.Authentication.lndServerUrl)
&& (config.Settings.lndServerUrl === '' || undefined === config.Settings.lndServerUrl)
&& (config.Settings.lnServerUrl === '' || undefined === config.Settings.lnServerUrl)
) {
errMsg = errMsg + '\nPlease set Server URL through environment or RTL.conf!';
} else {
if (config.Settings.lndServerUrl !== '' && config.Settings.lndServerUrl) {
common.nodes[0].ln_server_url = config.Settings.lndServerUrl;
} else if (config.Authentication.lndServerUrl !== '' && config.Authentication.lndServerUrl) {
common.nodes[0].ln_server_url = config.Authentication.lndServerUrl;
} else if (config.Settings.lnServerUrl !== '' && config.Settings.lnServerUrl) {
common.nodes[0].ln_server_url = config.Settings.lnServerUrl;
}
}
}
if(process.env.BITCOIND_CONFIG_PATH) {
common.nodes[0].bitcoind_config_path = process.env.BITCOIND_CONFIG_PATH;
} else {
if(config.Settings.bitcoindConfigPath !== '' && config.Settings.bitcoindConfigPath) {
common.nodes[0].bitcoind_config_path = config.Settings.bitcoindConfigPath;
} else if(config.Authentication.bitcoindConfigPath !== '' && config.Authentication.bitcoindConfigPath) {
common.nodes[0].bitcoind_config_path = config.Authentication.bitcoindConfigPath;
}
}
if(common.ln_implementation === 'LND') {
if(process.env.CHANNEL_BACKUP_PATH) {
common.nodes[0].channel_backup_path = process.env.CHANNEL_BACKUP_PATH;
} else {
if(config.Settings.channelBackupPath !== '' && config.Settings.channelBackupPath) {
common.nodes[0].channel_backup_path = config.Settings.channelBackupPath;
} else {
common.nodes[0].channel_backup_path = common.rtl_conf_file_path + common.path_separator + 'backup';
}
try {
connect.createDirectory(common.nodes[0].channel_backup_path);
let exists = fs.existsSync(common.nodes[0].channel_backup_path + common.path_separator + 'channel-all.bak');
if (!exists) {
try {
var createStream = fs.createWriteStream(common.nodes[0].channel_backup_path + common.path_separator + 'channel-all.bak');
createStream.end();
} catch (err) {
console.error('Something went wrong while creating backup file: \n' + err);
}
}
} catch (err) {
console.error('Something went wrong while creating backup file: \n' + err);
}
}
}
if (config.Settings.enableLogging) {
common.nodes[0].enable_logging = config.Settings.enableLogging;
} else if (config.Authentication.enableLogging) {
common.nodes[0].enable_logging = config.Authentication.enableLogging;
}
if (common.nodes[0].enable_logging) {
common.nodes[0].log_file = common.rtl_conf_file_path + '/logs/RTL.log';
let exists = fs.existsSync(common.nodes[0].log_file);
if (exists) {
fs.writeFile(common.nodes[0].log_file, '', () => { });
} else {
try {
var dirname = path.dirname(common.nodes[0].log_file);
connect.createDirectory(dirname);
var createStream = fs.createWriteStream(common.nodes[0].log_file);
createStream.end();
}
catch (err) {
console.error('Something went wrong while creating log file: \n' + err);
}
}
}
if (config.Settings.fiatConversion) {
common.nodes[0].fiat_conversion = config.Settings.fiatConversion;
} else {
common.nodes[0].fiat_conversion = false;
}
if (config.Settings.fiatConversion && config.Settings.currencyUnit) {
common.nodes[0].currency_unit = config.Settings.currencyUnit;
}
if (process.env.PORT) {
common.port = connect.normalizePort(process.env.PORT);
} else if (config.Settings.port) {
common.port = connect.normalizePort(config.Settings.port);
}
if (errMsg !== '') {
throw new Error(errMsg);
connect.convertCustomToHash = () => {
common.rtl_conf_file_path = process.env.RTL_CONFIG_PATH ? process.env.RTL_CONFIG_PATH : path.normalize(__dirname);
try {
RTLConfFile = common.rtl_conf_file_path + common.path_separator + 'RTL-Config.json';
var config = JSON.parse(fs.readFileSync(RTLConfFile, 'utf-8'));
config.multiPassHashed = hash.update(config.multiPass).digest('hex');
delete config.multiPass;
fs.writeFileSync(RTLConfFile, JSON.stringify(config, null, 2), 'utf-8');
console.log('Please note that, RTL has encrypted the plaintext password into its corresponding hash.');
return config.multiPassHashed;
} catch (err) {
errMsg = errMsg + '\nPassword hashing failed!';
}
}
connect.validateMultiNodeConfig = (config) => {
connect.validateNodeConfig = (config) => {
if(!+config.SSO.rtlSSO) {
common.node_auth_type = 'CUSTOM';
if (process.env.RTL_PASS) {
common.rtl_pass = hash.update(process.env.RTL_PASS).digest('hex');
} else if (config.multiPassHashed !== '' && config.multiPassHashed) {
common.rtl_pass = config.multiPassHashed;
} else if (config.multiPass !== '' && config.multiPass) {
common.rtl_pass = connect.convertCustomToHash('MULTI');
common.rtl_pass = connect.convertCustomToHash();
} else {
errMsg = errMsg + '\nMulti Node Authentication can be set with multiPass only. Please set MultiPass in RTL-Multi-Node-Conf.json';
errMsg = errMsg + '\nNode Authentication can be set with multiPass only. Please set multiPass in RTL-Config.json';
}
}
common.port = (config.port) ? connect.normalizePort(config.port) : 3000;
@ -301,7 +128,7 @@ connect.validateMultiNodeConfig = (config) => {
config.nodes.forEach((node, idx) => {
common.nodes[idx] = {};
if(node.Authentication.macaroonPath === '' || undefined === node.Authentication.macaroonPath) {
errMsg = 'Please set macaroon path for node index ' + node.index + ' in RTL-Multi-Node-Conf.json!';
errMsg = 'Please set macaroon path for node index ' + node.index + ' in RTL-Config.json!';
} else {
common.nodes[idx].macaroon_path = node.Authentication.macaroonPath;
}
@ -310,7 +137,7 @@ connect.validateMultiNodeConfig = (config) => {
(node.Settings.lndServerUrl === '' || undefined === node.Settings.lndServerUrl)
&& (node.Settings.lnServerUrl === '' || undefined === node.Settings.lnServerUrl)
) {
errMsg = errMsg + '\nPlease set server URL for node index ' + node.index + ' in RTL-Multi-Node-Conf.json!';
errMsg = errMsg + '\nPlease set server URL for node index ' + node.index + ' in RTL-Config.json!';
} else {
common.nodes[idx].ln_server_url = node.Settings.lndServerUrl ? node.Settings.lndServerUrl : node.Settings.lnServerUrl;
}
@ -457,29 +284,20 @@ connect.refreshCookie = (cookieFile) => {
}
connect.logEnvVariables = () => {
if (common.multi_node_setup && common.nodes && common.nodes.length > 0) {
if (common.nodes && common.nodes.length > 0) {
common.nodes.forEach((node, idx) => {
if (!node.enable_logging) { return; }
logger.info({fileName: 'Config Setup Variable', msg: 'PORT: ' + common.port, node});
logger.info({fileName: 'Config Setup Variable', msg: 'DEFAULT NODE INDEX: ' + common.selectedNode.index});
logger.info({fileName: 'Config Setup Variable', msg: 'NODE_SETUP: MULTI', node});
logger.info({fileName: 'Config Setup Variable', msg: 'SSO: ' + common.rtl_sso, node});
logger.info({fileName: 'Config Setup Variable', msg: 'LOGOUT REDIRECT LINK: ' + common.logout_redirect_link + '\r\n', node});
logger.info({fileName: 'Config Setup Variable', msg: 'INDEX: ' + node.index, node});
logger.info({fileName: 'Config Setup Variable', msg: 'LN NODE: ' + node.ln_node, node});
logger.info({fileName: 'Config Setup Variable', msg: 'LN IMPLEMENTATION: ' + node.ln_implementation, node});
logger.info({fileName: 'Config Setup Variable', msg: 'PORT: ' + common.port, node});
logger.info({fileName: 'Config Setup Variable', msg: 'FIAT CONVERSION: ' + node.fiatConversion, node});
logger.info({fileName: 'Config Setup Variable', msg: 'CURRENCY UNIT: ' + node.currency_unit, node});
logger.info({fileName: 'Config Setup Variable', msg: 'LND SERVER URL: ' + node.ln_server_url, node});
logger.info({fileName: 'Config Setup Variable', msg: 'LN SERVER URL: ' + node.ln_server_url, node});
});
} else {
if (!common.nodes[0].enable_logging) { return; }
logger.info({fileName: 'Config Setup Variable', msg: 'NODE_SETUP: SINGLE'});
logger.info({fileName: 'Config Setup Variable', msg: 'PORT: ' + common.port});
logger.info({fileName: 'Config Setup Variable', msg: 'LN IMPLEMENTATION: ' + common.nodes[0].ln_implementation});
logger.info({fileName: 'Config Setup Variable', msg: 'LN SERVER URL: ' + common.nodes[0].ln_server_url});
logger.info({fileName: 'Config Setup Variable', msg: 'SSO: ' + common.rtl_sso});
logger.info({fileName: 'Config Setup Variable', msg: 'LOGOUT REDIRECT LINK: ' + common.logout_redirect_link});
}
}
@ -514,43 +332,6 @@ connect.getAllNodeAllChannelBackup = (node) => {
})
};
connect.setSingleNodeConfiguration = (singleNodeFilePath) => {
const exists = fs.existsSync(singleNodeFilePath);
if (exists) {
var config = ini.parse(fs.readFileSync(singleNodeFilePath, 'utf-8'));
connect.setMacaroonPath(clArgs, config);
connect.validateSingleNodeConfig(config);
connect.setSelectedNode(config);
connect.logEnvVariables();
} else {
try {
fs.writeFileSync(singleNodeFilePath, ini.stringify(connect.setDefaultConfig()));
var config = ini.parse(fs.readFileSync(singleNodeFilePath, 'utf-8'));
connect.setMacaroonPath(clArgs, config);
connect.validateSingleNodeConfig(config);
connect.setSelectedNode(config);
connect.logEnvVariables();
}
catch(err) {
console.error('Something went wrong while configuring the single node server: \n' + err);
throw new Error(err);
}
}
}
connect.setMultiNodeConfiguration = (multiNodeFilePath) => {
try {
var config = JSON.parse(fs.readFileSync(multiNodeFilePath, 'utf-8'));
connect.validateMultiNodeConfig(config);
connect.setSelectedNode(config);
connect.logEnvVariables();
}
catch(err) {
console.error('Something went wrong while configuring the multi node server: \n' + err);
throw new Error(err);
}
}
connect.setSelectedNode = (config) => {
if(config.defaultNodeIndex) {
common.selectedNode = common.findNode(config.defaultNodeIndex);
@ -560,17 +341,19 @@ connect.setSelectedNode = (config) => {
}
connect.setServerConfiguration = () => {
common.rtl_conf_file_path = (process.env.RTL_CONFIG_PATH) ? process.env.RTL_CONFIG_PATH.substring(0, process.env.RTL_CONFIG_PATH.length - 9) : path.normalize(__dirname);
singleNodeConfFile = common.rtl_conf_file_path + '/RTL.conf';
multiNodeConfFile = common.rtl_conf_file_path + '/RTL-Multi-Node-Conf.json';
const singleNodeExists = fs.existsSync(singleNodeConfFile);
const multiNodeExists = fs.existsSync(multiNodeConfFile);
if ((!multiNodeExists && singleNodeExists) || (!multiNodeExists && !singleNodeExists)) {
common.multi_node_setup = false;
connect.setSingleNodeConfiguration(singleNodeConfFile);
} else if ((multiNodeExists && singleNodeExists) || (multiNodeExists && !singleNodeExists)) {
common.multi_node_setup = true;
connect.setMultiNodeConfiguration(multiNodeConfFile);
try {
common.rtl_conf_file_path = (process.env.RTL_CONFIG_PATH) ? process.env.RTL_CONFIG_PATH : path.normalize(__dirname);
confFileFullPath = common.rtl_conf_file_path + common.path_separator + 'RTL-Config.json';
if (!fs.existsSync(confFileFullPath)) {
fs.writeFileSync(confFileFullPath, JSON.stringify(connect.setDefaultConfig()));
}
var config = JSON.parse(fs.readFileSync(confFileFullPath, 'utf-8'));
connect.validateNodeConfig(config);
connect.setSelectedNode(config);
connect.logEnvVariables();
} catch(err) {
console.error('Something went wrong while configuring the node server: \n' + err);
throw new Error(err);
}
}

@ -14,170 +14,97 @@ exports.updateSelectedNode = (req, res, next) => {
};
exports.getRTLConfig = (req, res, next) => {
if(!common.multi_node_setup) {
var RTLConfFile = common.rtl_conf_file_path + '/RTL.conf';
logger.info({fileName: 'RTLConf', msg: 'Getting RTL Config'});
fs.readFile(RTLConfFile, 'utf8', function(err, data) {
if (err) {
logger.error({fileName: 'RTLConf', lineNum: 19, msg: 'Getting RTL Config Failed!'});
var confFile = common.rtl_conf_file_path + common.path_separator + 'RTL-Config.json';
logger.info({fileName: 'RTLConf', msg: 'Getting Node Config'});
fs.readFile(confFile, 'utf8', function(err, data) {
if (err) {
if (err.code === 'ENOENT') {
logger.error({fileName: 'RTLConf', lineNum: 46, msg: 'Node config does not exist!'});
res.status(200).json({ defaultNodeIndex: 0, selectedNodeIndex: 0, sso: {}, nodes: [] });
} else {
logger.error({fileName: 'RTLConf', lineNum: 49, msg: 'Getting Node Config Failed!'});
res.status(500).json({
message: "Reading RTL Config Failed!",
message: "Reading Node Config Failed!",
error: err
});
} else {
const jsonConfig = ini.parse(data);
const sso = { rtlSSO: common.rtl_sso, logoutRedirectLink: common.logout_redirect_link };
const authentication = {
nodeAuthType: common.node_auth_type,
configPath: common.nodes[0].config_path,
bitcoindConfigPath: common.nodes[0].bitcoind_config_path
};
jsonConfig.Settings.lnImplementation = (common.nodes[0].ln_implementation) ? common.nodes[0].ln_implementation : (jsonConfig.Settings.lnImplementation ? jsonConfig.Settings.lnImplementation : 'LND');
jsonConfig.Settings.channelBackupPath = common.nodes[0].channel_backup_path ? common.nodes[0].channel_backup_path : (jsonConfig.Settings.channelBackupPath) ? jsonConfig.Settings.channelBackupPath : common.nodes[0].channel_backup_path;
jsonConfig.Settings.flgSidenavOpened = (jsonConfig.Settings.flgSidenavOpened) ? jsonConfig.Settings.flgSidenavOpened : true;
jsonConfig.Settings.flgSidenavPinned = (jsonConfig.Settings.flgSidenavPinned) ? jsonConfig.Settings.flgSidenavPinned : true;
jsonConfig.Settings.menu = (jsonConfig.Settings.menu) ? jsonConfig.Settings.menu : 'VERTICAL';
jsonConfig.Settings.menuType = (jsonConfig.Settings.menuType) ? jsonConfig.Settings.menuType : 'REGULAR';
jsonConfig.Settings.fontSize = (jsonConfig.Settings.fontSize) ? jsonConfig.Settings.fontSize : 'MEDIUM';
jsonConfig.Settings.themeMode = (jsonConfig.Settings.themeMode) ? jsonConfig.Settings.themeMode : 'DAY';
jsonConfig.Settings.themeColor = (jsonConfig.Settings.themeColor) ? jsonConfig.Settings.themeColor : 'PURPLE';
jsonConfig.Settings.satsToBTC = (jsonConfig.Settings.satsToBTC) ? jsonConfig.Settings.satsToBTC : false;
res.status(200).json({ defaultNodeIndex: 0, selectedNodeIndex: common.selectedNode.index, sso: sso, nodes: [{
index: common.nodes[0].index,
lnNode: 'SingleNode',
lnImplementation: jsonConfig.Settings.lnImplementation,
settings: jsonConfig.Settings,
authentication: authentication}] });
}
});
} else {
var RTLMultiNodeConfFile = common.rtl_conf_file_path + '/RTL-Multi-Node-Conf.json';
logger.info({fileName: 'RTLConf', msg: 'Getting Multi Node Config'});
fs.readFile(RTLMultiNodeConfFile, 'utf8', function(err, data) {
if (err) {
if (err.code === 'ENOENT') {
logger.error({fileName: 'RTLConf', lineNum: 46, msg: 'Multi Node Config does not exist!'});
res.status(200).json({ defaultNodeIndex: 0, selectedNodeIndex: 0, sso: {}, nodes: [] });
} else {
logger.error({fileName: 'RTLConf', lineNum: 49, msg: 'Getting Multi Node Config Failed!'});
res.status(500).json({
message: "Reading Multi Node Config Failed!",
error: err
});
}
} else {
const multiNodeConfig = JSON.parse(data);
const sso = { rtlSSO: common.rtl_sso, logoutRedirectLink: common.logout_redirect_link };
var nodesArr = [];
if (multiNodeConfig.nodes && multiNodeConfig.nodes.length > 0) {
multiNodeConfig.nodes.forEach((node, i) => {
const authentication = {};
authentication.nodeAuthType = 'CUSTOM';
if(node.Authentication && node.Authentication.lndConfigPath) {
authentication.configPath = node.Authentication.lndConfigPath;
} else if(node.Authentication && node.Authentication.configPath) {
authentication.configPath = node.Authentication.configPath;
} else {
authentication.configPath = '';
}
if(node.Settings.bitcoindConfigPath) {
authentication.bitcoindConfigPath = node.Settings.bitcoindConfigPath;
}
node.Settings.channelBackupPath = (node.Settings.channelBackupPath) ? node.Settings.channelBackupPath : common.nodes[i].channel_backup_path;
node.Settings.flgSidenavOpened = (node.Settings.flgSidenavOpened) ? node.Settings.flgSidenavOpened : true;
node.Settings.flgSidenavPinned = (node.Settings.flgSidenavPinned) ? node.Settings.flgSidenavPinned : true;
node.Settings.menu = (node.Settings.menu) ? node.Settings.menu : 'VERTICAL';
node.Settings.menuType = (node.Settings.menuType) ? node.Settings.menuType : 'REGULAR';
node.Settings.fontSize = (node.Settings.fontSize) ? node.Settings.fontSize : 'MEDIUM';
node.Settings.themeMode = (node.Settings.themeMode) ? node.Settings.themeMode : 'DAY';
node.Settings.themeColor = (node.Settings.themeColor) ? node.Settings.themeColor : 'PURPLE';
node.Settings.satsToBTC = (node.Settings.satsToBTC) ? node.Settings.satsToBTC : false;
nodesArr.push({
index: node.index,
lnNode: node.lnNode,
lnImplementation: node.lnImplementation,
settings: node.Settings,
authentication: authentication})
});
}
res.status(200).json({ defaultNodeIndex: multiNodeConfig.defaultNodeIndex, selectedNodeIndex: common.selectedNode.index, sso: sso, nodes: nodesArr });
} else {
const nodeConfData = JSON.parse(data);
const sso = { rtlSSO: common.rtl_sso, logoutRedirectLink: common.logout_redirect_link };
var nodesArr = [];
if (nodeConfData.nodes && nodeConfData.nodes.length > 0) {
nodeConfData.nodes.forEach((node, i) => {
const authentication = {};
if(node.Authentication && node.Authentication.lndConfigPath) {
authentication.configPath = node.Authentication.lndConfigPath;
} else if(node.Authentication && node.Authentication.configPath) {
authentication.configPath = node.Authentication.configPath;
} else {
authentication.configPath = '';
}
if(node.Settings.bitcoindConfigPath) {
authentication.bitcoindConfigPath = node.Settings.bitcoindConfigPath;
}
node.Settings.channelBackupPath = (node.Settings.channelBackupPath) ? node.Settings.channelBackupPath : common.nodes[i].channel_backup_path;
node.Settings.flgSidenavOpened = (node.Settings.flgSidenavOpened) ? node.Settings.flgSidenavOpened : true;
node.Settings.flgSidenavPinned = (node.Settings.flgSidenavPinned) ? node.Settings.flgSidenavPinned : true;
node.Settings.menu = (node.Settings.menu) ? node.Settings.menu : 'VERTICAL';
node.Settings.menuType = (node.Settings.menuType) ? node.Settings.menuType : 'REGULAR';
node.Settings.fontSize = (node.Settings.fontSize) ? node.Settings.fontSize : 'MEDIUM';
node.Settings.themeMode = (node.Settings.themeMode) ? node.Settings.themeMode : 'DAY';
node.Settings.themeColor = (node.Settings.themeColor) ? node.Settings.themeColor : 'PURPLE';
node.Settings.satsToBTC = (node.Settings.satsToBTC) ? node.Settings.satsToBTC : false;
nodesArr.push({
index: node.index,
lnNode: node.lnNode,
lnImplementation: node.lnImplementation,
settings: node.Settings,
authentication: authentication})
});
}
});
}
res.status(200).json({ defaultNodeIndex: nodeConfData.defaultNodeIndex, selectedNodeIndex: common.selectedNode.index, sso: sso, nodes: nodesArr });
}
});
};
exports.updateUISettings = (req, res, next) => {
var RTLConfFile = '';
if(common.multi_node_setup) {
RTLConfFile = common.rtl_conf_file_path + '/RTL-Multi-Node-Conf.json';
var config = JSON.parse(fs.readFileSync(RTLConfFile, 'utf-8'));
config.nodes.find(node => {
if(node.index == common.selectedNode.index) {
node.Settings.userPersona = req.body.updatedSettings.userPersona;
node.Settings.themeMode = req.body.updatedSettings.themeMode;
node.Settings.themeColor = req.body.updatedSettings.themeColor;
node.Settings.fiatConversion = req.body.updatedSettings.fiatConversion;
if(req.body.updatedSettings.fiatConversion) {
node.Settings.currencyUnit = req.body.updatedSettings.currencyUnit ? req.body.updatedSettings.currencyUnit : 'USD';
} else {
delete node.Settings.currencyUnit;
}
node.Settings.flgSidenavOpened = true; // req.body.updatedSettings.flgSidenavOpened;
node.Settings.flgSidenavPinned = true; // req.body.updatedSettings.flgSidenavPinned;
node.Settings.menu = 'VERTICAL'; // req.body.updatedSettings.menu;
node.Settings.menuType = 'REGULAR'; // req.body.updatedSettings.menuType;
node.Settings.fontSize = 'MEDIUM'; // req.body.updatedSettings.fontSize;
node.Settings.satsToBTC = false; // req.body.updatedSettings.satsToBTC;
}
});
try {
fs.writeFileSync(RTLConfFile, JSON.stringify(config, null, 2), 'utf-8');
logger.info({fileName: 'RTLConf', msg: 'Updating Application Node Settings Succesful!'});
res.status(201).json({message: 'Application Node Settings Updated Successfully'});
}
catch (err) {
logger.error({fileName: 'Conf', lineNum: 102, msg: 'Updating Application Node Settings Failed!'});
res.status(500).json({
message: "Updating Application Node Settings Failed!",
error: 'Updating Application Node Settings Failed!'
});
}
} else {
RTLConfFile = common.rtl_conf_file_path + '/RTL.conf';
var config = ini.parse(fs.readFileSync(RTLConfFile, 'utf-8'));
const settingsTemp = config.Settings;
settingsTemp.userPersona = req.body.updatedSettings.userPersona;
settingsTemp.themeMode = req.body.updatedSettings.themeMode;
settingsTemp.themeColor = req.body.updatedSettings.themeColor;
settingsTemp.fiatConversion = req.body.updatedSettings.fiatConversion;
if(req.body.updatedSettings.fiatConversion) {
settingsTemp.currencyUnit = req.body.updatedSettings.currencyUnit ? req.body.updatedSettings.currencyUnit : 'USD';
}
settingsTemp.flgSidenavOpened = true; // req.body.updatedSettings.flgSidenavOpened;
settingsTemp.flgSidenavPinned = true; // req.body.updatedSettings.flgSidenavPinned;
settingsTemp.menu = 'VERTICAL'; // req.body.updatedSettings.menu;
settingsTemp.menuType = 'REGULAR'; // req.body.updatedSettings.menuType;
settingsTemp.fontSize = 'MEDIUM'; // req.body.updatedSettings.fontSize;
settingsTemp.satsToBTC = false; // req.body.updatedSettings.satsToBTC;
delete config.Settings;
fs.writeFileSync(RTLConfFile, ini.stringify(config));
fs.appendFile(RTLConfFile, ini.stringify(settingsTemp, { section: 'Settings' }), function(err) {
if (err) {
logger.error({fileName: 'Conf', lineNum: 122, msg:'Updating Application Node Settings Failed!'});
res.status(500).json({
message: "Updating Application Node Settings Failed!",
error: 'Updating Application Node Settings Failed!'
});
var RTLConfFile = common.rtl_conf_file_path + common.path_separator + 'RTL-Config.json';
var config = JSON.parse(fs.readFileSync(RTLConfFile, 'utf-8'));
config.nodes.find(node => {
if(node.index == common.selectedNode.index) {
node.Settings.userPersona = req.body.updatedSettings.userPersona;
node.Settings.themeMode = req.body.updatedSettings.themeMode;
node.Settings.themeColor = req.body.updatedSettings.themeColor;
node.Settings.fiatConversion = req.body.updatedSettings.fiatConversion;
if(req.body.updatedSettings.fiatConversion) {
node.Settings.currencyUnit = req.body.updatedSettings.currencyUnit ? req.body.updatedSettings.currencyUnit : 'USD';
} else {
logger.info({fileName: 'RTLConf', msg: 'Updating Application Node Settings Succesful!'});
res.status(201).json({message: 'Application Node Settings Updated Successfully'});
delete node.Settings.currencyUnit;
}
node.Settings.flgSidenavOpened = true; // req.body.updatedSettings.flgSidenavOpened;
node.Settings.flgSidenavPinned = true; // req.body.updatedSettings.flgSidenavPinned;
node.Settings.menu = 'VERTICAL'; // req.body.updatedSettings.menu;
node.Settings.menuType = 'REGULAR'; // req.body.updatedSettings.menuType;
node.Settings.fontSize = 'MEDIUM'; // req.body.updatedSettings.fontSize;
node.Settings.satsToBTC = false; // req.body.updatedSettings.satsToBTC;
}
});
try {
fs.writeFileSync(RTLConfFile, JSON.stringify(config, null, 2), 'utf-8');
logger.info({fileName: 'RTLConf', msg: 'Updating Application Node Settings Succesful!'});
res.status(201).json({message: 'Application Node Settings Updated Successfully'});
}
catch (err) {
logger.error({fileName: 'Conf', lineNum: 102, msg: 'Updating Application Node Settings Failed!'});
res.status(500).json({
message: "Updating Application Node Settings Failed!",
error: 'Updating Application Node Settings Failed!'
});
}
};
exports.updateDefaultNode = (req, res, next) => {
RTLConfFile = common.rtl_conf_file_path + '/RTL-Multi-Node-Conf.json';
RTLConfFile = common.rtl_conf_file_path + common.path_separator + 'RTL-Config.json';
var config = JSON.parse(fs.readFileSync(RTLConfFile, 'utf-8'));
config.defaultNodeIndex = req.body.defaultNodeIndex;
try {
@ -207,8 +134,8 @@ exports.getConfig = (req, res, next) => {
confFile = common.selectedNode.bitcoind_config_path;
break;
case 'rtl':
JSONFormat = (common.multi_node_setup) ? true : false;
confFile = (common.multi_node_setup) ? common.rtl_conf_file_path + '/RTL-Multi-Node-Conf.json' : common.rtl_conf_file_path + '/RTL.conf';
JSONFormat = true;
confFile = common.rtl_conf_file_path + common.path_separator + 'RTL-Config.json';
break;
default:
JSONFormat = false;
@ -225,9 +152,6 @@ exports.getConfig = (req, res, next) => {
});
} else {
const jsonConfig = (JSONFormat) ? JSON.parse(data) : ini.parse(data);
if (jsonConfig.Authentication && jsonConfig.Authentication.rtlPass) {
jsonConfig.Authentication.rtlPass = jsonConfig.Authentication.rtlPass.replace(/./g, '*');
}
if (jsonConfig.Bitcoind && jsonConfig.Bitcoind['bitcoind.rpcpass']) {
jsonConfig.Bitcoind['bitcoind.rpcpass'] = jsonConfig.Bitcoind['bitcoind.rpcpass'].replace(/./g, '*');
}

@ -1,13 +1,8 @@
var ini = require('ini');
var fs = require('fs');
var common = require('../common');
var connect = require('../connect');
const jwt = require("jsonwebtoken");
var upperCase = require('upper-case');
var crypto = require('crypto');
var hash = crypto.createHash('sha256');
var logger = require('./logger');
var rpcPass = '';
exports.authenticateUser = (req, res, next) => {
if(+common.rtl_sso) {
@ -29,76 +24,19 @@ exports.authenticateUser = (req, res, next) => {
}
} else {
const password = req.body.authenticationValue;
if (common.multi_node_setup) {
if (common.rtl_pass === password) {
var rpcUser = 'Multi_Node_User';
const token = jwt.sign(
{ user: rpcUser, configPath: common.nodes[0].config_path, macaroonPath: common.nodes[0].macaroon_path },
common.secret_key
);
res.status(200).json({ token: token });
} else {
logger.error({fileName: 'Authenticate', lineNum: 38, msg: 'Password Validation Failed!'});
res.status(401).json({
message: "Authentication Failed!",
error: "Password Validation Failed!"
});
}
if (common.rtl_pass === password) {
var rpcUser = 'Node_User';
const token = jwt.sign(
{ user: rpcUser, configPath: common.nodes[0].config_path, macaroonPath: common.nodes[0].macaroon_path },
common.secret_key
);
res.status(200).json({ token: token });
} else {
if(upperCase(common.node_auth_type) === 'CUSTOM') {
if (common.rtl_pass === password) {
var rpcUser = 'Single_Node_User';
const token = jwt.sign(
{ user: rpcUser, configPath: common.nodes[0].config_path, macaroonPath: common.nodes[0].macaroon_path },
common.secret_key
);
res.status(200).json({ token: token });
} else {
logger.error({fileName: 'Authenticate', lineNum: 54, msg: 'Password Validation Failed!'});
res.status(401).json({
message: "Authentication Failed!",
error: "Password Validation Failed!"
});
}
} else {
fs.readFile(common.nodes[0].config_path, 'utf8', function (err, data) {
if (err) {
logger.error({fileName: 'Authenticate', lineNum: 60, msg: 'LND Config Reading Failed!'});
err.description = 'You might be connecting RTL remotely to your LND node OR You might be missing rpcpass in your lnd.conf.';
err.description = err.description + ' If the former modify the RTL.conf for remote setting.';
err.description = err.description + ' If the later modify the lnd.conf to include rpcpass';
res.status(500).json({
message: "LND Config Reading Failed!",
error: err
});
} else {
const jsonLNDConfig = ini.parse(data);
if (rpcPass === '') {
if (undefined !== jsonLNDConfig.Bitcoind && undefined !== jsonLNDConfig.Bitcoind['bitcoind.rpcpass']) {
rpcPass = jsonLNDConfig.Bitcoind['bitcoind.rpcpass'];
} else if (undefined !== jsonLNDConfig['bitcoind.rpcpass']) {
rpcPass = jsonLNDConfig['bitcoind.rpcpass'];
}
rpcPass = hash.update(rpcPass).digest('hex');
}
if (rpcPass === password) {
var rpcUser = (undefined !== jsonLNDConfig.Bitcoind && undefined !== jsonLNDConfig.Bitcoind['bitcoind.rpcuser']) ? jsonLNDConfig.Bitcoind['bitcoind.rpcuser'] : '';
rpcUser = (rpcUser === '' && undefined !== jsonLNDConfig['bitcoind.rpcuser']) ? jsonLNDConfig['bitcoind.rpcuser'] : '';
const token = jwt.sign(
{ user: rpcUser, configPath: common.nodes[0].config_path, macaroonPath: common.nodes[0].macaroon_path },
common.secret_key
);
res.status(200).json({ token: token });
} else {
logger.error({fileName: 'Authenticate', lineNum: 89, msg: 'Password Validation Failed!'});
res.status(401).json({
message: "Authentication Failed!",
error: "Password Validation Failed!"
});
}
}
});
}
logger.error({fileName: 'Authenticate', lineNum: 38, msg: 'Password Validation Failed!'});
res.status(401).json({
message: "Authentication Failed!",
error: "Password Validation Failed!"
});
}
}
};

@ -1,18 +1,13 @@
var request = require('request-promise');
var common = require('../../common');
var logger = require('../logger');
var connect = require('../../connect');
var options = {};
exports.getInfo = (req, res, next) => {
common.setOptions();
options = common.getOptions();
options.url = common.getSelLNServerUrl() + '/getinfo';
if(common.multi_node_setup) {
logger.info({fileName:'GetInfo', msg: 'Selected Node: ' + JSON.stringify(common.selectedNode.ln_node)});
} else {
logger.info({fileName:'GetInfo', msg: 'Single Node Setup!'});
}
logger.info({fileName:'GetInfo', msg: 'Selected Node: ' + JSON.stringify(common.selectedNode.ln_node)});
logger.info({fileName: 'GetInfo', msg: 'Calling getinfo from c-lightning server url: ' + options.url});
request(options).then((body) => {
logger.info({fileName: 'GetInfo', msg: JSON.stringify(body)});

@ -8,11 +8,7 @@ exports.getInfo = (req, res, next) => {
common.setOptions();
options = common.getOptions();
options.url = common.getSelLNServerUrl() + '/getinfo';
if(common.multi_node_setup) {
logger.info({fileName:'GetInfo', msg: 'Selected Node: ' + JSON.stringify(common.selectedNode.ln_node)});
} else {
logger.info({fileName:'GetInfo', msg: 'Single Node Setup!'});
}
logger.info({fileName:'GetInfo', msg: 'Selected Node: ' + JSON.stringify(common.selectedNode.ln_node)});
if (!options.headers || !options.headers['Grpc-Metadata-macaroon']) {
logger.error({fileName: 'GetInfo', lineNum: 17, msg: 'Get info failed due to bad or missing macaroon!'});
res.status(502).json({

@ -1,68 +1,53 @@
RTL allows the user to configure and control specific application parameters for app customization and integration.
The parameters can be configured via RTL.conf file or through environment variables defined at the OS level.
The parameters can be configured via RTL-Config.json file or through environment variables defined at the OS level.
#### RTL.conf
[Authentication]
;Path for the folder containing 'admin.macaroon' file
macaroonPath=<>
;For stand alone RTL authentication. Allowed values - CUSTOM, DEFAULT
nodeAuthType=<>
;Full path of the lnd.conf file including the file name
lndConfigPath=<>
;For 'nodeAuthType=CUSTOM', the password in plain text
rtlPass=<>
[Settings]
;Set by RTL
userPersona=OPERATOR
;Set by RTL
themeMode=DAY
;Set by RTL
themeColor=PURPLE
;Full path of the bitcoin.conf file including the file name
bitcoindConfigPath=<>
;parameter to turn RTL logging off/on. Allowed values - true, false
enableLogging=<>
;port number for the rtl node server, default 3000
port=3000
;<LND server URL for REST APIs.
;Default is 'https://localhost:8080/v1'
lndServerUrl=https://localhost:8080/v1
;Channel backup folder
channelBackupPath=<>
;Set by RTL
fiatConversion=false
;Set by RTL, dafault 'USD' If fiatConversion is true
currencyUnit=USD
[SSO]
;Single Sign On control
;Allowed values - 1,0
;1-single sign on via an external cookie
;0-stand alone RTL authentication
rtlSSO=0
;Required if 'rtlSSO=1'
;Full path of the cookie file including the file name
;The application url needs to pass the value from this cookie file as query param 'access-key'
;for the SSO authentication to work
rtlCookiePath=<>
;Required if 'rtlSSO=1'
;URL to re-direct to after logout/timeout from RTL
logoutRedirectLink=/login
#### RTL-Config.json
{
"multiPass": "<The password in plain text, default 'password'>",
"port": "<port number for the rtl node server, default '3000'>",
"defaultNodeIndex": <Default index to load when rtl server starts, default 1>,
"SSO": {
"rtlSSO": <parameter to turn SSO off/on. Allowed values - 1 (single sign on via an external cookie), 0 (stand alone RTL authentication), default 0>,
"rtlCookiePath": "<Full path of the cookie file including the file name. The application url needs to pass the value from this cookie file as query param 'access-key' for the SSO authentication to work, Required if SSO=1 else empty>",
"logoutRedirectLink": "<URL to re-direct to after logout/timeout from RTL, Required if SSO=1 else empty>"
},
"nodes": [
{
"index": <Incrimental Node indices starting from 1>,
"lnNode": "<Node name to uniquely identify the node in the UI, Default 'LND Node 1'>",
"lnImplementation": "<LNP implementation, Allowed values LND/CLT. Default 'LND'>",
"Authentication": {
"macaroonPath": "<Path for the folder containing 'admin.macaroon' file>",
"configPath": "<Optional:Full path of the lnd.conf file including the file name, if present locally or empty>"
},
"Settings": {
"userPersona": "<User persona to tailor the data on UI. Allowed values MERCHANT, OPERATOR. Default MERCHANT>",
"themeMode": "<Theme modes, Allowed values DAY, NIGHT. Default DAY>",
"themeColor": "<Theme colors, Allowed values PURPLE, TEAL, INDIGO, PINK. Default PURPLE>",
"channelBackupPath": "<Optional: Path to save channel backup file. Only for LND implementation, Default <RTL root>\backup\node-1>",
"bitcoindConfigPath": "<Optional: path of bitcoind.conf path if available locally>",
"enableLogging": <parameter to turn RTL logging off/on. Allowed values - true, false, default false>,
"fiatConversion": <parameter to turn fiat conversion off/on. Allowed values - true, false, default false>,
"currencyUnit": "<Optional: Fiat current Unit for currency conversion, default 'USD' If fiatConversion is true>",
"lnServerUrl": "<Service url for LND/CLightning REST APIs for the node, e.g. https://192.168.0.1:8080/v1 OR https://192.168.0.1:3001/v1. Default 'https://localhost:8080/v1'"
}
}
]
}
#### Environment variables
;The environment variable can also be used for all of the above configurations except the UI settings.
;If the environment variables are set, it will take precedence over the parameters in the RTL.conf file.
;If the environment variables are set, it will take precedence over the parameters in the RTL-Config.json file.
PORT (port number for the rtl node server, default 3000)
NODE_AUTH_TYPE (For stand alone RTL authentication allowed values - CUSTOM, DEFAULT)
NODE_AUTH_TYPE (For stand alone RTL authentication allowed value - CUSTOM)
RTL_PASS (Password for RTL custom authentication)
LN_IMPLEMENTATION (LND, CLT. Default 'LND')
LND_SERVER_URL (LND server URL for REST APIs, default https://localhost:8080/v1) OR LN_SERVER_URL (LN server URL for LNP REST APIs)
LND_CONFIG_PATH (Full path of the lnd.conf file including the file name) OR CONFIG_PATH (Full path of the LNP .conf file including the file name)
LN_SERVER_URL (LND server URL for REST APIs, default https://localhost:8080/v1) OR LN_SERVER_URL (LN server URL for LNP REST APIs)
LN_CONFIG_PATH (Full path of the lnd.conf file including the file name) OR CONFIG_PATH (Full path of the LNP .conf file including the file name)
MACAROON_PATH (Path for the folder containing 'admin.macaroon' file)
RTL_SSO (1 - single sign on via an external cookie, 0 - stand alone RTL authentication)
RTL_COOKIE_PATH (Full path of the cookie file including the file name)
LOGOUT_REDIRECT_LINK (URL to re-direct to after logout/timeout from RTL)
RTL_CONFIG_PATH (Full path of the RTL.conf file including the file name)
RTL_CONFIG_PATH (Full path of the RTL-Config.json file including the file name)
BITCOIND_CONFIG_PATH (Full path of the bitcoind.conf file including the file name)
CHANNEL_BACKUP_PATH (folder location for saving the channel backup files, valid for LND implementation only)

@ -45,8 +45,8 @@ $ git pull
$ npm install --only=prod
```
### <a name="prep"></a>Prep for Execution
RTL requires its own config file `RTL-Multi-Node-Conf.json`, to start the server and provide user authentication on the app.
* Rename `sample-RTL-Multi-Node-Conf.json` file to `RTL-Multi-Node-Conf.json`.
RTL requires its own config file `RTL-Config.json`, to start the server and provide user authentication on the app.
* Rename `sample-RTL-Config.json` file to `RTL-Config.json`.
* Locate the complete path of the readable `access.macaroon` from `cl-rest` on your node.
* Modify the RTL conf file per the example file below
@ -84,7 +84,7 @@ Ensure that the follow values are correct per your config:
"lnServerUrl": "https://<cl-rest api server ip address>:3001/v1"
}
}
],
],
"multiPass": <password required for accessing RTL>
}
```

@ -4,7 +4,7 @@ Static Channel Backup APIs of LND has been leveraged to provide channel backup f
#### Backup folder location
Default location: If no folder location is specified in the RTL conf files (single or mult-node setup), RTL will create a folder `backup` on the RTL root. If multiple nodes are being managed via RTL, multiple node sub-folders will be created in the `backup` folder.
User defined: User can specify the folder where channel backups should be created, by setting a config variable `channelBackupPath` in the `RTL.conf` or `RTL-Multi-Node-Conf.json` files. Please ensure that RTL has the permission to write in the specified folder location.
User defined: User can specify the folder where channel backups should be created, by setting a config variable `channelBackupPath` in the `RTL-Config.json` file. Please ensure that RTL has the permission to write in the specified folder location.
Environment variable: Channel backup folder location can also be controlled via an environment variable `CHANNEL_BACKUP_PATH`

@ -4,7 +4,7 @@ Caution: This feature is for advanced users, running multiple nodes.
A single server instance of RTL can now be used to connect with multiple nodes on the same network. Multi-Node configuration requires the following:
1. In case of LND node implementation, update lnd.conf for the node to enable remote connections and restart LND
2. Configure 'RTL-Multi-Node-Conf.json' with individual entries for each node
2. Configure 'RTL-Config.json' with individual entries for each node
3. Restart RTL
4. Run RTL and switch nodes live via dropdown on the menubar
@ -15,18 +15,16 @@ This step is only required to configure the nodes, which will be remotely connec
3. Add this setting your lnd.conf file under the [Application Options] section: `restlisten=<ip address of the device running LND>:8080`
4. Restart LND
#### 2. Configure 'RTL-Multi-Node-Conf.json'
1. Rename the `sample-RTL-Multi-Node-Conf.json` on the root RTL location to `RTL-Multi-Node-Conf.json`
#### 2. Configure 'RTL-Config.json'
1. Rename the `sample-RTL-Config.json` on the root RTL location to `RTL-Config.json`
2. Set `multiPass` to the preferred password. This password will be used to authenticate the user for RTL. Once authenticated, the user will be able to access all the nodes configured in the json file
3. Set the `port` to the preferred port number over which to run RTL
4. Set the `defaultNodeIndex` to configure the default start up node at server restart
5. `SSO` section can be used for single-sign-on from applications like BTCPayserver. If using RTL as a stand-alone app to connect with the nodes, keep the `rtlSSO=0` and ignore the rest of `SSO` section.
6. `nodes` section is a json array, with each element of the array representing the specific parameters for the LND node to connect with. `index` must be a number and start with 1. This number must be unique for each node in the array. For each element, two items need to be configured for each node on the network (`macaroonPath` and `lndServerUrl`/`lnServerUrl`).
6. `nodes` section is a json array, with each element of the array representing the specific parameters for the LND node to connect with. `index` must be a number and start with 1. This number must be unique for each node in the array. For each element, two items need to be configured for each node on the network (`macaroonPath` and `lnServerUrl`).
7. `macaroonPath` should be set to the local path of the folder containing `admin.macaroon` file for each node. Each node must have a different folder for the `admin.macaroon` on the RTL server.
8. `lndServerUrl` must be set to the service url for LND REST APIs for each node, with the unique ip address of the node hosting lnd e.g. https://192.168.0.1:8080/v1. In this case the ip address of the node hosting lnd is '192.168.0.1'
OR
`lnServerUrl` must be set to the service url for C Lightining REST APIs for each node, with the unique ip address of the node hosting clightning e.g. https://192.168.0.2:3001/v1. In this case the ip address of the node hosting clightning is '192.168.0.2'
9. `lndConfigPath`(for LND)/`configPath`(for CLT) and `bitcoindConfigPath` are optional parameters which can be set only if the RTL is running locally on the same node. Else it can be set to "" or removed from the conf file all together.
8. `lnServerUrl` must be set to the service url for LND/C Lightining REST APIs for each node, with the unique ip address of the node hosting lnd/clightning e.g. https://192.168.0.1:8080/v1 OR https://192.168.0.1:3001/v1. In this case the ip address of the node hosting lnd/clightning is '192.168.0.1'
9. `configPath` and `bitcoindConfigPath` are optional parameters which can be set only if the RTL is running locally on the same node. Else it can be set to "" or removed from the conf file all together.
#### 3. Restart RTL

@ -9,14 +9,39 @@ If your running RTL and LND on different devices on your local LAN, certain conf
2. `admin.macaroon` file must be transferred to the device on which you need to run RTL
3. Add to your lnd.conf file under the [Application Options] section: `restlisten=<ip address of the device running LND>:8080`
4. Restart LND
5. Make the following changes to the RTL.conf file
5. Make the following changes to the RTL-Config.json file
```
[Authentication]
macaroonPath=<Path of the folder containing 'admin.macaroon' on the device running RTL>
nodeAuthType=CUSTOM
rtlPass=<password in plain text>
[Settings]
lndServerUrl=https://<ip-address-of-device-running-lnd>:8080/v1
{
"multiPass": "<password in plain text, Default 'password'>",
"port": "3000",
"defaultNodeIndex": 1,
"SSO": {
"rtlSSO": 0,
"rtlCookiePath": "",
"logoutRedirectLink": ""
},
"nodes": [
{
"index": 1,
"lnNode": "LND Testnet",
"lnImplementation": "LND",
"Authentication": {
"macaroonPath": "<Path of the folder containing 'admin.macaroon' on the device running RTL>",
"configPath": "<Optional:Path of the lnd.conf if present locally or empty>"
},
"Settings": {
"userPersona": "OPERATOR",
"themeMode": "DAY",
"themeColor": "PURPLE",
"channelBackupPath": "<RTL Root path + \backup\node-1>",
"bitcoindConfigPath": "<Optional: path of bitcoind.conf path if available locally>",
"enableLogging": false,
"fiatConversion": false,
"lnServerUrl": "<https://<ip-address-of-device-running-lnd>:8080/v1; e.g. https://192.168.0.1:8080/v1>"
}
}
]
}
```
6. Restart RTL
7. Access RTL by opening your browser at the following address: http://localhost:3000

10
package-lock.json generated

@ -7509,7 +7509,8 @@
"ini": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
"integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw=="
"integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
"dev": true
},
"inquirer": {
"version": "6.5.1",
@ -9750,7 +9751,8 @@
"minimist": {
"version": "0.0.10",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz",
"integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8="
"integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=",
"dev": true
},
"minipass": {
"version": "2.9.0",
@ -10489,6 +10491,7 @@
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
"integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=",
"dev": true,
"requires": {
"minimist": "~0.0.1",
"wordwrap": "~0.0.2"
@ -15826,7 +15829,8 @@
"wordwrap": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz",
"integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc="
"integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=",
"dev": true
},
"worker-farm": {
"version": "1.7.0",

@ -39,11 +39,9 @@
"cookie-parser": "^1.4.4",
"express": "^4.17.1",
"hammerjs": "^2.0.8",
"ini": "^1.3.5",
"jsonwebtoken": "^8.5.1",
"material-design-icons": "^3.0.1",
"ngx-perfect-scrollbar": "^8.0.0",
"optimist": "^0.6.1",
"request": "^2.88.0",
"request-promise": "^4.2.5",
"roboto-fontface": "^0.10.0",

@ -0,0 +1,30 @@
{
"multiPass": "password",
"port": "3000",
"defaultNodeIndex": 1,
"SSO": {
"rtlSSO": 0,
"rtlCookiePath": "",
"logoutRedirectLink": ""
},
"nodes": [
{
"index": 1,
"lnNode": "LND Node 1",
"lnImplementation": "LND",
"Authentication": {
"macaroonPath": "C:\\Users\\shaha\\AppData\\Local\\Lnd\\data\\chain\\bitcoin\\mainnet",
"configPath": "C:\\Users\\shaha\\AppData\\Local\\Lnd\\lnd.conf"
},
"Settings": {
"userPersona": "MERCHANT",
"themeMode": "DAY",
"themeColor": "PURPLE",
"channelBackupPath": "C:\\Users\\shaha\\backup\\node-1",
"enableLogging": false,
"lnServerUrl": "https://localhost:8080/v1",
"fiatConversion": false
}
}
]
}

@ -1,50 +0,0 @@
{
"multiPass": "password",
"port": "3000",
"defaultNodeIndex": 1,
"SSO": {
"rtlSSO": 0,
"rtlCookiePath": "",
"logoutRedirectLink": ""
},
"nodes": [
{
"index": 1,
"lnNode": "LND Testnet",
"lnImplementation": "LND",
"Authentication": {
"macaroonPath": "<Complete path of the folder containing admin.macaroon for the node # 1>",
"lndConfigPath": "<Optional:Path of the lnd.conf if present locally or empty>"
},
"Settings": {
"userPersona": "OPERATOR",
"themeMode": "DAY",
"themeColor": "PURPLE",
"channelBackupPath": "C:\\RTL\\backup\\node-1",
"bitcoindConfigPath": "<Optional: path of bitcoind.conf path if available locally>",
"enableLogging": true,
"currencyUnit": "USD",
"fiatConversion": true,
"lndServerUrl": "<Service url for LND REST APIs for node # 1 e.g. https://192.168.0.1:8080/v1"
}
},
{
"index": 2,
"lnNode": "C Lighting Testnet",
"lnImplementation": "CLT",
"Authentication": {
"macaroonPath": "<Complete path of the folder containing access.macaroon of CL REST for the node # 2>"
},
"Settings": {
"userPersona": "MERCHANT",
"themeMode": "NIGHT",
"themeColor": "TEAL",
"channelBackupPath": "C:\\RTL\\backup\\node-2",
"bitcoindConfigPath": "",
"enableLogging": true,
"fiatConversion": false,
"lnServerUrl": "<Service url for C Lightning REST APIs for node # 2 e.g. https://192.168.0.2:3001/v1"
}
}
]
}

@ -1,22 +0,0 @@
[Authentication]
macaroonPath=
nodeAuthType=DEFAULT
lndConfigPath=
rtlPass=
[Settings]
userPersona=OPERATOR
themeMode=DAY
themeColor=PURPLE
channelBackupPath=C:\RTL\backup
bitcoindConfigPath=
enableLogging=true
port=3000
lndServerUrl=https://localhost:8080/v1
currencyUnit=GBP
fiatConversion=true
[SSO]
rtlSSO=0
rtlCookiePath=
logoutRedirectLink=/login

@ -9,7 +9,6 @@
<form (ngSubmit)="onSignin()" #signinForm="ngForm" fxLayout="column" fxLayout.gt-sm="row wrap" fxLayoutAlign="start" fxLayoutAlign.gt-sm="space-between">
<mat-form-field fxFlex="100" fxLayoutAlign="start">
<input matInput placeholder="Password" type="password" id="password" name="password" [(ngModel)]="password" tabindex="1" required>
<mat-hint>{{hintStr}}</mat-hint>
<mat-error *ngIf="!password">Password is required.</mat-error>
</mat-form-field>
<div fxLayout="row" fxFlex="100" fxFlex.gt-sm="30" fxLayoutAlign="space-between stretch" class="mt-2">

@ -20,10 +20,8 @@ export class SigninComponent implements OnInit, OnDestroy {
public faUnlockAlt = faUnlockAlt;
public selNode: ConfigSettingsNode;
public password = '';
public nodeAuthType = '';
public rtlSSO = 0;
public rtlCookiePath = '';
public hintStr = '';
public accessKey = '';
private unsub: Array<Subject<void>> = [new Subject(), new Subject(), new Subject()];
@ -38,13 +36,7 @@ export class SigninComponent implements OnInit, OnDestroy {
this.logger.error(effectsErr);
});
this.selNode = rtlStore.selNode;
this.nodeAuthType = this.selNode.authentication.nodeAuthType;
this.logger.info(rtlStore);
if (this.nodeAuthType.toUpperCase() === 'DEFAULT') {
this.hintStr = 'Enter RPC password';
} else {
this.hintStr = ''; // Do not remove, initial passowrd 'DEFAULT' is initilizing its value
}
});
}

@ -30,7 +30,6 @@ export class Settings {
export class Authentication {
constructor(
public nodeAuthType?: string,
public configPath?: string,
public bitcoindConfigPath?: string
) { }

@ -14,7 +14,7 @@ export interface RootState {
}
const initNodeSettings = { userPersona: 'OPERATOR', flgSidenavOpened: true, flgSidenavPinned: true, menu: 'VERTICAL', menuType: 'REGULAR', fontSize: 'MEDIUM', themeMode: 'DAY', themeColor: 'PURPLE', satsToBTC: false, channelBackupPath: '', selCurrencyUnit: 'USD', fiatConversion: false, currencyUnits: ['Sats', 'BTC', 'USD'] };
const initNodeAuthentication = { nodeAuthType: 'CUSTOM', configPath: '', bitcoindConfigPath: '' };
const initNodeAuthentication = { configPath: '', bitcoindConfigPath: '' };
const initRootState: RootState = {
effectErrorsRoot: [],

Loading…
Cancel
Save