Make db location configurable #1177

Make db location configurable #1177
pull/1199/head
ShahanaFarooqui 1 year ago
parent ceceab04af
commit 8e65dd2c9e

1
.github/README.md vendored

@ -83,6 +83,7 @@ Example RTL-Config.json:
"multiPass": "password",
"port": "3000",
"defaultNodeIndex": 1,
"dbDirectoryPath": "<Complete path of the folder where rtl's database file should be saved>",
"SSO": {
"rtlSSO": 0,
"rtlCookiePath": "",

@ -9,6 +9,7 @@ parameters have `default` values for initial setup and can be updated after RTL
"port": "<port number for the rtl node server, default '3000', Required>",
"host": "<host for the rtl node server, default 'all IPs', Optional>",
"defaultNodeIndex": <Default index to load when rtl server starts, default 1, Optional>,
"dbDirectoryPath": "<Complete path of the folder where rtl database file should be saved, defults to RTL root, Optional>",
"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, Required>,
"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 (Optional)>",
@ -51,6 +52,7 @@ If the environment variables are set, it will take precedence over the parameter
<br />
PORT (port number for the rtl node server, default 3000, Required)<br />
HOST (host for the rtl node server, default localhost, Optional)<br />
DB_DIRECTORY_PATH (Path for the folder where rtl database file should be saved, default RTL root directory, Optional)
APP_PASSWORD (Plaintext password to be provided by the parent container, NOT suggested for standalone RTL applications, to be used by Umbrel) (Optional)<br />
LN_IMPLEMENTATION (LND/CLN/ECL. Default 'LND', Required)<br />
LN_SERVER_URL (LN server URL for LNP REST APIs, default https://127.0.0.1:8080) (Required)<br />

@ -65,6 +65,7 @@ Ensure that the follow values are correct per your config:
"multiPass": <password required for accessing RTL>,
"port": "3000",
"defaultNodeIndex": 1,
"dbDirectoryPath": "<Complete path of the folder where rtl's database file should be saved>",
"SSO": {
"rtlSSO": 0,
"rtlCookiePath": "",

@ -60,6 +60,7 @@ Ensure that the follow values are correct per your config:
"multiPass": <password required for accessing RTL>,
"port": "3000",
"defaultNodeIndex": 1,
"dbDirectoryPath": "<Complete path of the folder where rtl's database file should be saved>",
"SSO": {
"rtlSSO": 0,
"rtlCookiePath": "",

@ -20,16 +20,17 @@ This step is only required to configure the nodes, which will be remotely connec
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 `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. `swapMacaroonPath` should be set to the local path of the folder containing `loop.macaroon` file for loop.
9. `boltzMacaroonPath` should be set to the local path of the folder containing `admin.macaroon` file for boltz swaps.
10. `lnServerUrl` must be set to the service url for LND/Core Lightining REST APIs for each node, with the unique ip address of the node hosting LND/Core Lightning e.g. https://192.168.0.1:8080 OR https://192.168.0.1:3001. In this case the ip address of the node hosting LND/Core Lightning is '192.168.0.1'
11. `swapServerUrl` must be set to the swap service url. e.g. https://127.0.0.1:8081.
12. `boltzServerUrl` must be set to the boltz service url. e.g. https://127.0.0.1:9003.
13. `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.
14. `lnApiPassword` is mandatory in the ln implementation is ECL and configPath is missing. It is used to provide password for API authentication. It will be ignored in other ln implementations.
5. `dbDirectoryPath` should be set to the folder where RTL's database will be saved.
6. `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.
7. `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`).
8. `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.
9. `swapMacaroonPath` should be set to the local path of the folder containing `loop.macaroon` file for loop.
10. `boltzMacaroonPath` should be set to the local path of the folder containing `admin.macaroon` file for boltz swaps.
11. `lnServerUrl` must be set to the service url for LND/Core Lightining REST APIs for each node, with the unique ip address of the node hosting LND/Core Lightning e.g. https://192.168.0.1:8080 OR https://192.168.0.1:3001. In this case the ip address of the node hosting LND/Core Lightning is '192.168.0.1'
12. `swapServerUrl` must be set to the swap service url. e.g. https://127.0.0.1:8081.
13. `boltzServerUrl` must be set to the boltz service url. e.g. https://127.0.0.1:9003.
14. `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.
15. `lnApiPassword` is mandatory in the ln implementation is ECL and configPath is missing. It is used to provide password for API authentication. It will be ignored in other ln implementations.
#### 3. Restart RTL

@ -15,6 +15,7 @@ If your running RTL and LND on different devices on your local LAN, certain conf
"multiPass": "<password in plain text, Default 'password'>",
"port": "3000",
"defaultNodeIndex": 1,
"dbDirectoryPath": "<Complete path of the folder where rtl's database file should be saved>",
"SSO": {
"rtlSSO": 0,
"rtlCookiePath": "",

@ -2,6 +2,7 @@
"multiPass": "password",
"port": "3000",
"defaultNodeIndex": 1,
"dbDirectoryPath": "C:\\Users\\xyz\\RTL",
"SSO": {
"rtlSSO": 0,
"rtlCookiePath": "",

@ -12,7 +12,6 @@ import clnRoutes from '../routes/cln/index.js';
import eclRoutes from '../routes/eclair/index.js';
import { Common } from './common.js';
import { Logger } from './logger.js';
import { Config } from './config.js';
import { CLWSClient } from '../controllers/cln/webSocketClient.js';
import { ECLWSClient } from '../controllers/eclair/webSocketClient.js';
import { LNDWSClient } from '../controllers/lnd/webSocketClient.js';
@ -22,15 +21,11 @@ export class ExpressApplication {
this.app = express();
this.logger = Logger;
this.common = Common;
this.config = Config;
this.eclWsClient = ECLWSClient;
this.clWsClient = CLWSClient;
this.lndWsClient = LNDWSClient;
this.directoryName = dirname(fileURLToPath(import.meta.url));
this.getApp = () => this.app;
this.loadConfiguration = () => {
this.config.setServerConfiguration();
};
this.setCORS = () => { CORS.mount(this.app); };
this.setCSRF = () => { CSRF.mount(this.app); };
this.setApplicationRoutes = () => {
@ -81,7 +76,6 @@ export class ExpressApplication {
this.app.use(cookieParser(this.common.secret_key));
this.app.use(bodyParser.json({ limit: '25mb' }));
this.app.use(bodyParser.urlencoded({ extended: false, limit: '25mb' }));
this.loadConfiguration();
this.setCORS();
this.setCSRF();
this.setApplicationRoutes();

@ -11,7 +11,8 @@ export class CommonService {
this.initSelectedNode = null;
this.rtl_conf_file_path = '';
this.port = 3000;
this.host = null;
this.host = '';
this.db_directory_path = join(dirname(fileURLToPath(import.meta.url)), '..', '..');
this.rtl_pass = '';
this.flg_allow_password_update = true;
this.rtl_secret2fa = '';
@ -320,7 +321,7 @@ export class CommonService {
this.cookie_value = fs.readFileSync(this.rtl_cookie_path, 'utf-8');
}
catch (err) {
this.logger.log({ selectedNode: this.initSelectedNode, level: 'ERROR', fileName: 'Config', msg: 'Something went wrong while reading cookie: \n' + err });
this.logger.log({ selectedNode: this.initSelectedNode, level: 'ERROR', fileName: 'Common', msg: 'Something went wrong while reading cookie: \n' + err });
throw new Error(err);
}
}
@ -332,7 +333,7 @@ export class CommonService {
this.cookie_value = fs.readFileSync(this.rtl_cookie_path, 'utf-8');
}
catch (err) {
this.logger.log({ selectedNode: this.initSelectedNode, level: 'ERROR', fileName: 'Config', msg: 'Something went wrong while reading the cookie: \n' + err });
this.logger.log({ selectedNode: this.initSelectedNode, level: 'ERROR', fileName: 'Common', msg: 'Something went wrong while reading the cookie: \n' + err });
throw new Error(err);
}
}
@ -433,6 +434,7 @@ export class CommonService {
if (selNode && selNode.index) {
this.logger.log({ selectedNode: selNode, level: 'INFO', fileName: 'Config Setup Variable', msg: 'PORT: ' + this.port });
this.logger.log({ selectedNode: selNode, level: 'INFO', fileName: 'Config Setup Variable', msg: 'HOST: ' + this.host });
this.logger.log({ selectedNode: selNode, level: 'INFO', fileName: 'Config Setup Variable', msg: 'DB_DIRECTORY_PATH: ' + this.db_directory_path });
this.logger.log({ selectedNode: selNode, level: 'INFO', fileName: 'Config Setup Variable', msg: 'SSO: ' + this.rtl_sso });
this.logger.log({ selectedNode: selNode, level: 'INFO', fileName: 'Config Setup Variable', msg: 'DEFAULT NODE INDEX: ' + selNode.index });
this.logger.log({ selectedNode: selNode, level: 'INFO', fileName: 'Config Setup Variable', msg: 'INDEX: ' + selNode.index });

@ -20,31 +20,37 @@ export class ConfigService {
let macaroonPath = '';
let configPath = '';
let channelBackupPath = '';
let dbPath = '';
switch (this.platform) {
case 'win32':
macaroonPath = homeDir + '\\AppData\\Local\\Lnd\\data\\chain\\bitcoin\\mainnet';
configPath = homeDir + '\\AppData\\Local\\Lnd\\lnd.conf';
channelBackupPath = homeDir + '\\backup\\node-1';
dbPath = homeDir + '\\database\\node-1';
break;
case 'darwin':
macaroonPath = homeDir + '/Library/Application Support/Lnd/data/chain/bitcoin/mainnet';
configPath = homeDir + '/Library/Application Support/Lnd/lnd.conf';
channelBackupPath = homeDir + '/backup/node-1';
dbPath = homeDir + '/database/node-1';
break;
case 'linux':
macaroonPath = homeDir + '/.lnd/data/chain/bitcoin/mainnet';
configPath = homeDir + '/.lnd/lnd.conf';
channelBackupPath = homeDir + '/backup/node-1';
dbPath = homeDir + '/database/node-1';
break;
default:
macaroonPath = '';
configPath = '';
channelBackupPath = '';
dbPath = '';
break;
}
const configData = {
port: '3000',
defaultNodeIndex: 1,
dbDirectoryPath: dbPath,
SSO: {
rtlSSO: 0,
rtlCookiePath: '',
@ -132,6 +138,7 @@ export class ConfigService {
}
this.common.port = (process?.env?.PORT) ? this.normalizePort(process?.env?.PORT) : (config.port) ? this.normalizePort(config.port) : 3000;
this.common.host = (process?.env?.HOST) ? process?.env?.HOST : (config.host) ? config.host : null;
this.common.db_directory_path = (process?.env?.DB_DIRECTORY_PATH) ? process?.env?.DB_DIRECTORY_PATH : (config.dbDirectoryPath) ? config.dbDirectoryPath : join(dirname(fileURLToPath(import.meta.url)), '..', '..');
if (config.nodes && config.nodes.length > 0) {
config.nodes.forEach((node, idx) => {
this.common.nodes[idx] = {};
@ -347,6 +354,7 @@ export class ConfigService {
throw new Error(err);
}
};
this.setServerConfiguration();
}
}
export const Config = new ConfigService();

@ -1,6 +1,5 @@
import * as fs from 'fs';
import { join, dirname, sep } from 'path';
import { fileURLToPath } from 'url';
import { join, sep } from 'path';
import { Common } from '../utils/common.js';
import { Logger } from '../utils/logger.js';
import { validateDocument, LNDCollection, ECLCollection, CLNCollection } from '../models/database.model.js';
@ -8,7 +7,7 @@ export class DatabaseService {
constructor() {
this.common = Common;
this.logger = Logger;
this.dbDirectory = join(dirname(fileURLToPath(import.meta.url)), '..', '..', 'database');
this.dbDirectory = join(this.common.db_directory_path, 'database');
this.nodeDatabase = {};
}
loadDatabase(session) {

@ -1,8 +1,9 @@
import http from 'http';
import App from './backend/utils/app.js';
import { Logger } from './backend/utils/logger.js';
import { Common } from './backend/utils/common.js';
import { Config } from './backend/utils/config.js'; // Follow sequence to set server configuration in time
import { WSServer } from './backend/utils/webSocketServer.js';
import App from './backend/utils/app.js';
const logger = Logger;
const common = Common;
@ -13,11 +14,11 @@ const onError = (error) => {
if (error.syscall !== 'listen') { throw error; }
switch (error.code) {
case 'EACCES':
logger.log({ level: 'ERROR', fileName: 'RTL', msg: 'http://' + (common.host ? common.host : 'localhost') + ':' + common.port + ' requires elevated privileges' });
logger.log({ level: 'ERROR', fileName: 'RTL', msg: 'http://' + ((common.host && common.host !== '') ? common.host : 'localhost') + ':' + common.port + ' requires elevated privileges' });
process.exit(1);
break;
case 'EADDRINUSE':
logger.log({ level: 'ERROR', fileName: 'RTL', msg: 'http://' + (common.host ? common.host : 'localhost') + ':' + common.port + ' is already in use' });
logger.log({ level: 'ERROR', fileName: 'RTL', msg: 'http://' + ((common.host && common.host !== '') ? common.host : 'localhost') + ':' + common.port + ' is already in use' });
process.exit(1);
break;
case 'ECONNREFUSED':
@ -35,7 +36,7 @@ const onError = (error) => {
};
const onListening = () => {
logger.log({ level: 'INFO', fileName: 'RTL', msg: 'Server is up and running, please open the UI at http://' + (common.host ? common.host : 'localhost') + ':' + common.port + ' or your proxy configured url' });
logger.log({ level: 'INFO', fileName: 'RTL', msg: 'Server is up and running, please open the UI at http://' + ((common.host && common.host !== '') ? common.host : 'localhost') + ':' + common.port + ' or your proxy configured url' });
};
let server = http.createServer(app.getApp());
@ -45,7 +46,7 @@ server.on('listening', onListening);
wsServer.mount(server);
if (common.host) {
if (common.host && common.host !== '') {
server.listen(common.port, common.host);
} else {
server.listen(common.port);

@ -1,7 +1,7 @@
import { Database, DatabaseService } from '../../utils/database.js';
import { Logger, LoggerService } from '../../utils/logger.js';
import { Common, CommonService } from '../../utils/common.js';
import { CollectionsEnum, PageSettings } from '../../models/database.model.js';
import { CollectionsEnum } from '../../models/database.model.js';
const logger: LoggerService = Logger;
const common: CommonService = Common;

@ -13,7 +13,6 @@ import clnRoutes from '../routes/cln/index.js';
import eclRoutes from '../routes/eclair/index.js';
import { Common, CommonService } from './common.js';
import { Logger, LoggerService } from './logger.js';
import { Config, ConfigService } from './config.js';
import { CLWSClient, CLWebSocketClient } from '../controllers/cln/webSocketClient.js';
import { ECLWSClient, ECLWebSocketClient } from '../controllers/eclair/webSocketClient.js';
import { LNDWSClient, LNDWebSocketClient } from '../controllers/lnd/webSocketClient.js';
@ -25,7 +24,6 @@ export class ExpressApplication {
public app = express();
public logger: LoggerService = Logger;
public common: CommonService = Common;
public config: ConfigService = Config;
public eclWsClient: ECLWebSocketClient = ECLWSClient;
public clWsClient: CLWebSocketClient = CLWSClient;
public lndWsClient: LNDWebSocketClient = LNDWSClient;
@ -39,7 +37,6 @@ export class ExpressApplication {
this.app.use(bodyParser.json({ limit: '25mb' }));
this.app.use(bodyParser.urlencoded({ extended: false, limit: '25mb' }));
this.loadConfiguration();
this.setCORS();
this.setCSRF();
this.setApplicationRoutes();
@ -47,10 +44,6 @@ export class ExpressApplication {
public getApp = () => this.app;
public loadConfiguration = () => {
this.config.setServerConfiguration();
};
public setCORS = () => { CORS.mount(this.app); };
public setCSRF = () => { CSRF.mount(this.app); };

@ -13,7 +13,8 @@ export class CommonService {
public initSelectedNode: CommonSelectedNode = null;
public rtl_conf_file_path = '';
public port = 3000;
public host = null;
public host = '';
public db_directory_path = join(dirname(fileURLToPath(import.meta.url)), '..', '..');
public rtl_pass = '';
public flg_allow_password_update = true;
public rtl_secret2fa = '';
@ -334,7 +335,7 @@ export class CommonService {
try {
this.cookie_value = fs.readFileSync(this.rtl_cookie_path, 'utf-8');
} catch (err) {
this.logger.log({ selectedNode: this.initSelectedNode, level: 'ERROR', fileName: 'Config', msg: 'Something went wrong while reading cookie: \n' + err });
this.logger.log({ selectedNode: this.initSelectedNode, level: 'ERROR', fileName: 'Common', msg: 'Something went wrong while reading cookie: \n' + err });
throw new Error(err);
}
} else {
@ -344,7 +345,7 @@ export class CommonService {
fs.writeFileSync(this.rtl_cookie_path, crypto.randomBytes(64).toString('hex'));
this.cookie_value = fs.readFileSync(this.rtl_cookie_path, 'utf-8');
} catch (err) {
this.logger.log({ selectedNode: this.initSelectedNode, level: 'ERROR', fileName: 'Config', msg: 'Something went wrong while reading the cookie: \n' + err });
this.logger.log({ selectedNode: this.initSelectedNode, level: 'ERROR', fileName: 'Common', msg: 'Something went wrong while reading the cookie: \n' + err });
throw new Error(err);
}
}
@ -445,6 +446,7 @@ export class CommonService {
if (selNode && selNode.index) {
this.logger.log({ selectedNode: selNode, level: 'INFO', fileName: 'Config Setup Variable', msg: 'PORT: ' + this.port });
this.logger.log({ selectedNode: selNode, level: 'INFO', fileName: 'Config Setup Variable', msg: 'HOST: ' + this.host });
this.logger.log({ selectedNode: selNode, level: 'INFO', fileName: 'Config Setup Variable', msg: 'DB_DIRECTORY_PATH: ' + this.db_directory_path });
this.logger.log({ selectedNode: selNode, level: 'INFO', fileName: 'Config Setup Variable', msg: 'SSO: ' + this.rtl_sso });
this.logger.log({ selectedNode: selNode, level: 'INFO', fileName: 'Config Setup Variable', msg: 'DEFAULT NODE INDEX: ' + selNode.index });
this.logger.log({ selectedNode: selNode, level: 'INFO', fileName: 'Config Setup Variable', msg: 'INDEX: ' + selNode.index });

@ -17,38 +17,46 @@ export class ConfigService {
private common: CommonService = Common;
private logger: LoggerService = Logger;
constructor() { }
constructor() {
this.setServerConfiguration();
}
private setDefaultConfig = () => {
const homeDir = os.userInfo().homedir;
let macaroonPath = '';
let configPath = '';
let channelBackupPath = '';
let dbPath = '';
switch (this.platform) {
case 'win32':
macaroonPath = homeDir + '\\AppData\\Local\\Lnd\\data\\chain\\bitcoin\\mainnet';
configPath = homeDir + '\\AppData\\Local\\Lnd\\lnd.conf';
channelBackupPath = homeDir + '\\backup\\node-1';
dbPath = homeDir + '\\database\\node-1';
break;
case 'darwin':
macaroonPath = homeDir + '/Library/Application Support/Lnd/data/chain/bitcoin/mainnet';
configPath = homeDir + '/Library/Application Support/Lnd/lnd.conf';
channelBackupPath = homeDir + '/backup/node-1';
dbPath = homeDir + '/database/node-1';
break;
case 'linux':
macaroonPath = homeDir + '/.lnd/data/chain/bitcoin/mainnet';
configPath = homeDir + '/.lnd/lnd.conf';
channelBackupPath = homeDir + '/backup/node-1';
dbPath = homeDir + '/database/node-1';
break;
default:
macaroonPath = '';
configPath = '';
channelBackupPath = '';
dbPath = '';
break;
}
const configData = {
port: '3000',
defaultNodeIndex: 1,
dbDirectoryPath: dbPath,
SSO: {
rtlSSO: 0,
rtlCookiePath: '',
@ -134,6 +142,7 @@ export class ConfigService {
}
this.common.port = (process?.env?.PORT) ? this.normalizePort(process?.env?.PORT) : (config.port) ? this.normalizePort(config.port) : 3000;
this.common.host = (process?.env?.HOST) ? process?.env?.HOST : (config.host) ? config.host : null;
this.common.db_directory_path = (process?.env?.DB_DIRECTORY_PATH) ? process?.env?.DB_DIRECTORY_PATH : (config.dbDirectoryPath) ? config.dbDirectoryPath : join(dirname(fileURLToPath(import.meta.url)), '..', '..');
if (config.nodes && config.nodes.length > 0) {
config.nodes.forEach((node, idx) => {
this.common.nodes[idx] = {};

@ -1,6 +1,5 @@
import * as fs from 'fs';
import { join, dirname, sep } from 'path';
import { fileURLToPath } from 'url';
import { join, sep } from 'path';
import { Common, CommonService } from '../utils/common.js';
import { Logger, LoggerService } from '../utils/logger.js';
import { Collections, CollectionsEnum, validateDocument, LNDCollection, ECLCollection, CLNCollection } from '../models/database.model.js';
@ -10,7 +9,7 @@ export class DatabaseService {
public common: CommonService = Common;
public logger: LoggerService = Logger;
public dbDirectory = join(dirname(fileURLToPath(import.meta.url)), '..', '..', 'database');
public dbDirectory = join(this.common.db_directory_path, 'database');
public nodeDatabase: { id?: { adapter: DatabaseAdapter, data: Collections } } = {};
constructor() { }

Loading…
Cancel
Save