2FA Fix
pull/1161/head
ShahanaFarooqui 1 year ago
parent 1e52024fab
commit 5107c300eb

@ -22,7 +22,8 @@
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": [
"zone.js"
"zone.js",
"src/polyfills.ts"
],
"tsConfig": "src/tsconfig.app.json",
"inlineStyleLanguage": "scss",
@ -34,6 +35,7 @@
"node_modules/material-icons/iconfont/material-icons.css",
"node_modules/roboto-fontface/css/roboto/roboto-fontface.css"
],
"scripts": [],
"allowedCommonJsDependencies": [
"buffer",
"rfdc",
@ -41,10 +43,8 @@
"qrcode",
"otplib",
"pdfmake/build/pdfmake",
"pdfmake/build/vfs_fonts",
"clone-deep"
],
"scripts": []
"pdfmake/build/vfs_fonts"
]
},
"configurations": {
"production": {
@ -96,7 +96,8 @@
"options": {
"polyfills": [
"zone.js",
"zone.js/testing"
"zone.js/testing",
"src/polyfills.ts"
],
"tsConfig": "src/tsconfig.spec.json",
"inlineStyleLanguage": "scss",
@ -119,14 +120,8 @@
"src/**/*.html"
]
}
}
}
}
}
},
"cli": {
"schematicCollections": [
"@angular-eslint/schematics"
],
"analytics": false
}
}

@ -1,30 +0,0 @@
import request from 'request-promise';
import { Logger } from '../../utils/logger.js';
import { Common } from '../../utils/common.js';
let options = null;
const logger = Logger;
const common = Common;
export const getBalance = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Balance', msg: 'Getting Balance..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/getBalance';
request(options).then((body) => {
if (!body.totalBalance) {
body.totalBalance = 0;
}
if (!body.confBalance) {
body.confBalance = 0;
}
if (!body.unconfBalance) {
body.unconfBalance = 0;
}
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Balance', msg: 'Balance Received', data: body });
res.status(200).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Balance', 'Get Balance Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};

@ -1,160 +0,0 @@
import request from 'request-promise';
import { Logger } from '../../utils/logger.js';
import { Common } from '../../utils/common.js';
let options = null;
const logger = Logger;
const common = Common;
export const listChannels = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Getting Channels..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/channel/listChannels';
request(options).then((body) => {
body?.map((channel) => {
if (!channel.alias || channel.alias === '') {
channel.alias = channel.id.substring(0, 20);
}
const local = (channel.msatoshi_to_us) ? channel.msatoshi_to_us : 0;
const remote = (channel.msatoshi_to_them) ? channel.msatoshi_to_them : 0;
const total = channel.msatoshi_total ? channel.msatoshi_total : 0;
channel.balancedness = (total === 0) ? 1 : (1 - Math.abs((local - remote) / total)).toFixed(3);
return channel;
});
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Channels List Received', data: body });
res.status(200).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Channels', 'List Channels Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const openChannel = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Opening Channel..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/channel/openChannel';
options.body = req.body;
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Open Channel Options', data: options.body });
request.post(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Channel Opened', data: body });
res.status(201).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Channels', 'Open Channel Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const setChannelFee = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Setting Channel Fee..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/channel/setChannelFee';
options.body = req.body;
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Update Channel Policy Options', data: options.body });
request.post(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Updated Channel Policy', data: body });
res.status(201).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Channels', 'Update Channel Policy Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const closeChannel = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Closing Channel..' });
req.setTimeout(60000 * 10); // timeout 10 mins
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
const unilateralTimeoutQuery = req.query.force ? '?unilateralTimeout=1' : '';
options.url = req.session.selectedNode.ln_server_url + '/v1/channel/closeChannel/' + req.params.channelId + unilateralTimeoutQuery;
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Closing Channel', data: options.url });
request.delete(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Channel Closed', data: body });
res.status(204).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Channels', 'Close Channel Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const getLocalRemoteBalance = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Getting Local & Remote Balances..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/channel/localremotebal';
request(options).then((body) => {
if (!body.localBalance) {
body.localBalance = 0;
}
if (!body.remoteBalance) {
body.remoteBalance = 0;
}
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Local Remote Balance Received', data: body });
res.status(200).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Channels', 'Local Remote Balance Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const listForwards = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Getting Channel List Forwards..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/channel/listForwards?status=' + (req.query.status ? req.query.status : 'settled');
request.get(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Forwarding History Received For Status ' + req.query.status, data: body });
res.status(200).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Channels', 'Forwarding History Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const funderUpdatePolicy = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Getting or Updating Funder Policy..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/channel/funderUpdate';
if (req.body && req.body.policy) {
options.body = req.body;
}
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Funder Update Body', data: options.body });
request.post(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Funder Policy Received', data: body });
body.channel_fee_max_base_msat = (body.channel_fee_max_base_msat && typeof body.channel_fee_max_base_msat === 'string' && body.channel_fee_max_base_msat.includes('msat')) ? +body.channel_fee_max_base_msat?.replace('msat', '') : body.channel_fee_max_base_msat;
body.lease_fee_base_msat = (body.lease_fee_base_msat && typeof body.lease_fee_base_msat === 'string' && body.lease_fee_base_msat.includes('msat')) ? +body.lease_fee_base_msat?.replace('msat', '') : body.channel_fee_max_base_msat;
res.status(200).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Channels', 'Funder Policy Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const listForwardsPaginated = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Getting Paginated List Forwards..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
const { status, maxLen, offset } = req.query;
let queryStr = '?status=' + (status ? status : 'settled');
queryStr = queryStr + '&maxLen=' + (maxLen ? maxLen : '10');
queryStr = queryStr + '&offset=' + (offset ? offset : '0');
options.url = req.session.selectedNode.ln_server_url + '/v1/channel/listForwardsPaginated' + queryStr;
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Paginated Forwarding History url' + options.url });
request.get(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Paginated Forwarding History Received For Status ' + req.query.status, data: body });
res.status(200).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Channels', 'Paginated Forwarding History Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};

@ -1,24 +0,0 @@
import request from 'request-promise';
import { Logger } from '../../utils/logger.js';
import { Common } from '../../utils/common.js';
let options = null;
const logger = Logger;
const common = Common;
export const getFees = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Fees', msg: 'Getting Fees..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/getFees';
request(options).then((body) => {
if (!body.feeCollected) {
body.feeCollected = 0;
}
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Fees', msg: 'Fee Received', data: body });
res.status(200).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Fees', 'Get Fees Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};

@ -1,74 +0,0 @@
import request from 'request-promise';
import { Database } from '../../utils/database.js';
import { Logger } from '../../utils/logger.js';
import { Common } from '../../utils/common.js';
import { CLWSClient } from './webSocketClient.js';
let options = null;
const logger = Logger;
const common = Common;
const clWsClient = CLWSClient;
const databaseService = Database;
export const getInfo = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Getting Core Lightning Node Information..' });
common.logEnvVariables(req);
common.setOptions(req);
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/getinfo';
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Selected Node ' + req.session.selectedNode.ln_node });
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Calling Info from Core Lightning server url ' + options.url });
if (!options.headers || !options.headers.macaroon) {
const errMsg = 'Core lightning get info failed due to bad or missing macaroon!';
const err = common.handleError({ statusCode: 502, message: 'Bad Macaroon', error: errMsg }, 'GetInfo', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
else {
return request(options).then((body) => {
const body_str = (!body) ? '' : JSON.stringify(body);
const search_idx = (!body) ? -1 : body_str.search('Not Found');
if (!body || search_idx > -1 || body.error) {
if (body && !body.error) {
body.error = 'Error From Server!';
}
const err = common.handleError(body, 'GetInfo', 'Get Info Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
else {
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'GetInfo', msg: 'Node Information Before Update', data: body });
body.lnImplementation = 'Core Lightning';
const chainObj = { chain: '', network: '' };
if (body.network.includes('litecoin') || body.network.includes('feathercoin')) {
chainObj.chain = '';
chainObj.network = '';
}
else if (body.network.includes('liquid')) {
chainObj.chain = 'Liquid';
chainObj.network = common.titleCase(body.network);
}
else {
chainObj.chain = 'Bitcoin';
chainObj.network = common.titleCase(body.network);
}
body.chains = [chainObj];
body.uris = [];
if (body.address && body.address.length > 0) {
body.address.forEach((addr) => {
body.uris.push(body.id + '@' + addr.address + ':' + addr.port);
});
}
req.session.selectedNode.api_version = body.api_version || '';
req.session.selectedNode.ln_version = body.version || '';
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Connecting to the Core Lightning\'s Websocket Server.' });
clWsClient.updateSelectedNode(req.session.selectedNode);
databaseService.loadDatabase(req.session);
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Node Information Received', data: body });
return res.status(200).json(body);
}
}).catch((errRes) => {
const err = common.handleError(errRes, 'GetInfo', 'Get Info Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
}
};

@ -1,55 +0,0 @@
import request from 'request-promise';
import { Logger } from '../../utils/logger.js';
import { Common } from '../../utils/common.js';
let options = null;
const logger = Logger;
const common = Common;
export const deleteExpiredInvoice = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoices', msg: 'Deleting Expired Invoices..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
const queryStr = req.query.maxExpiry ? '?maxexpiry=' + req.query.maxExpiry : '';
options.url = req.session.selectedNode.ln_server_url + '/v1/invoice/delExpiredInvoice' + queryStr;
request.delete(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoice', msg: 'Invoices Deleted', data: body });
res.status(204).json({ status: 'Invoice Deleted Successfully' });
}).catch((errRes) => {
const err = common.handleError(errRes, 'Invoice', 'Delete Invoice Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const listInvoices = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoices', msg: 'Getting Invoices..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
const labelQuery = req.query.label ? '?label=' + req.query.label : '';
options.url = req.session.selectedNode.ln_server_url + '/v1/invoice/listInvoices' + labelQuery;
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Invoice', msg: 'Invoices List URL', data: options.url });
request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Invoice', msg: 'Invoices List Received', data: body });
res.status(200).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Invoice', 'List Invoices Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const addInvoice = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoices', msg: 'Creating Invoice..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/invoice/genInvoice';
options.body = req.body;
request.post(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoice', msg: 'Invoice Created', data: body });
res.status(201).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Invoice', 'Add Invoice Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};

@ -1,92 +0,0 @@
import request from 'request-promise';
import { Logger } from '../../utils/logger.js';
import { Common } from '../../utils/common.js';
let options = null;
const logger = Logger;
const common = Common;
export const getRoute = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Network', msg: 'Getting Network Routes..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/network/getRoute/' + req.params.destPubkey + '/' + req.params.amount;
request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Network', msg: 'Network Routes Received', data: body });
res.status(200).json({ routes: body });
}).catch((errRes) => {
const err = common.handleError(errRes, 'Network', 'Query Routes Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const listNode = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Network', msg: 'Node Lookup..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/network/listNode/' + req.params.id;
request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Network', msg: 'Node Lookup Finished', data: body });
res.status(200).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Network', 'Node Lookup Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const listChannel = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Network', msg: 'Channel Lookup..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/network/listChannel/' + req.params.channelShortId;
request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Network', msg: 'Channel Lookup Finished', data: body });
res.status(200).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Network', 'Channel Lookup Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const feeRates = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Network', msg: 'Getting Network Fee Rates..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/network/feeRates/' + req.params.feeRateStyle;
request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Network', msg: 'Network Fee Rates Received for ' + req.params.feeRateStyle, data: body });
res.status(200).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Network', 'Fee Rates Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const listNodes = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Network', msg: 'List Nodes..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
const queryStr = req.query.liquidity_ads ? '?liquidity_ads=' + req.query.liquidity_ads : '';
options.url = req.session.selectedNode.ln_server_url + '/v1/network/listNodes' + queryStr;
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Network', msg: 'List Nodes URL' + options.url });
request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Network', msg: 'List Nodes Finished', data: body });
body.forEach((node) => {
if (node.option_will_fund) {
node.option_will_fund.lease_fee_base_msat = (node.option_will_fund.lease_fee_base_msat && typeof node.option_will_fund.lease_fee_base_msat === 'string' &&
node.option_will_fund.lease_fee_base_msat.includes('msat')) ? node.option_will_fund.lease_fee_base_msat?.replace('msat', '') : node.option_will_fund.lease_fee_base_msat;
node.option_will_fund.channel_fee_max_base_msat = (node.option_will_fund.channel_fee_max_base_msat && typeof node.option_will_fund.channel_fee_max_base_msat === 'string' &&
node.option_will_fund.channel_fee_max_base_msat.includes('msat')) ? node.option_will_fund.channel_fee_max_base_msat?.replace('msat', '') : node.option_will_fund.channel_fee_max_base_msat;
}
return node;
});
res.status(200).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Network', 'Node Lookup Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};

@ -1,99 +0,0 @@
import request from 'request-promise';
import { Logger } from '../../utils/logger.js';
import { Common } from '../../utils/common.js';
import { Database } from '../../utils/database.js';
import { CollectionFieldsEnum, CollectionsEnum } from '../../models/database.model.js';
let options = null;
const logger = Logger;
const common = Common;
const databaseService = Database;
export const listOfferBookmarks = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Getting Offer Bookmarks..' });
databaseService.find(req.session.selectedNode, CollectionsEnum.OFFERS).then((offers) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Offer Bookmarks Received', data: offers });
res.status(200).json(offers);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Offers', 'Offer Bookmarks Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const deleteOfferBookmark = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Deleting Offer Bookmark..' });
databaseService.remove(req.session.selectedNode, CollectionsEnum.OFFERS, CollectionFieldsEnum.BOLT12, req.params.offerStr).then((deleteRes) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Offer Bookmark Deleted', data: deleteRes });
res.status(204).json(req.params.offerStr);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Offers', 'Offer Bookmark Delete Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const listOffers = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Getting Offers..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/offers/listoffers';
if (req.query.offer_id) {
options.url = options.url + '?offer_id=' + req.query.offer_id;
}
if (req.query.active_only) {
options.url = options.url + '?active_only=' + req.query.active_only;
}
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Offers', msg: 'Offers List URL', data: options.url });
request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Offers List Received', data: body });
res.status(200).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Offers', 'List Offers Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const createOffer = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Creating Offer..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/offers/offer';
options.body = req.body;
request.post(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Offer Created', data: body });
res.status(201).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Offer', 'Create Offer Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const fetchOfferInvoice = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Getting Offer Invoice..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/offers/fetchInvoice';
options.body = req.body;
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Offers', msg: 'Offer Invoice Body', data: options.body });
request.post(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Offer Invoice Received', data: body });
res.status(201).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Offers', 'Get Offer Invoice Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const disableOffer = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Disabling Offer..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/offers/disableOffer/' + req.params.offerID;
request.delete(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Offer Disabled', data: body });
res.status(202).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Offers', 'Disable Offer Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};

@ -1,53 +0,0 @@
import request from 'request-promise';
import { Logger } from '../../utils/logger.js';
import { Common } from '../../utils/common.js';
let options = null;
const logger = Logger;
const common = Common;
export const getNewAddress = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'Generating New Address..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/newaddr?addrType=' + req.query.type;
request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'New Address Generated', data: body });
res.status(200).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'OnChain', 'New Address Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const onChainWithdraw = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'Withdrawing from On Chain..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/withdraw';
options.body = req.body;
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'OnChain', msg: 'OnChain Withdraw Options', data: options.body });
request.post(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'Withdraw Finished', data: body });
res.status(201).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'OnChain', 'Withdraw Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const getUTXOs = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'Listing Funds..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/listFunds';
request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'Funds List Received', data: body });
res.status(200).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'OnChain', 'List Funds Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};

@ -1,141 +0,0 @@
import request from 'request-promise';
import { Logger } from '../../utils/logger.js';
import { Common } from '../../utils/common.js';
import { Database } from '../../utils/database.js';
import { CollectionFieldsEnum, CollectionsEnum } from '../../models/database.model.js';
let options = null;
const logger = Logger;
const common = Common;
const databaseService = Database;
function paymentReducer(accumulator, currentPayment) {
const currPayHash = currentPayment.payment_hash;
if (!currentPayment.partid) {
currentPayment.partid = 0;
}
if (!accumulator[currPayHash]) {
accumulator[currPayHash] = [currentPayment];
}
else {
accumulator[currPayHash].push(currentPayment);
}
return accumulator;
}
function summaryReducer(accumulator, mpp) {
if (mpp.status === 'complete') {
accumulator.msatoshi = accumulator.msatoshi + mpp.msatoshi;
accumulator.msatoshi_sent = accumulator.msatoshi_sent + mpp.msatoshi_sent;
accumulator.status = mpp.status;
}
if (mpp.bolt11) {
accumulator.bolt11 = mpp.bolt11;
}
if (mpp.bolt12) {
accumulator.bolt12 = mpp.bolt12;
}
return accumulator;
}
function groupBy(payments) {
const paymentsInGroups = payments?.reduce(paymentReducer, {});
const paymentsGrpArray = Object.keys(paymentsInGroups)?.map((key) => ((paymentsInGroups[key].length && paymentsInGroups[key].length > 1) ? common.sortDescByKey(paymentsInGroups[key], 'partid') : paymentsInGroups[key]));
return paymentsGrpArray?.reduce((acc, curr) => {
let temp = {};
if (curr.length && curr.length === 1) {
temp = JSON.parse(JSON.stringify(curr[0]));
temp.is_group = false;
temp.is_expanded = false;
temp.total_parts = 1;
delete temp.partid;
}
else {
const paySummary = curr?.reduce(summaryReducer, { msatoshi: 0, msatoshi_sent: 0, status: (curr[0] && curr[0].status) ? curr[0].status : 'failed' });
temp = {
is_group: true, is_expanded: false, total_parts: (curr.length ? curr.length : 0), status: paySummary.status, payment_hash: curr[0].payment_hash,
destination: curr[0].destination, msatoshi: paySummary.msatoshi, msatoshi_sent: paySummary.msatoshi_sent, created_at: curr[0].created_at,
mpps: curr
};
if (paySummary.bolt11) {
temp.bolt11 = paySummary.bolt11;
}
if (paySummary.bolt12) {
temp.bolt12 = paySummary.bolt12;
}
}
return acc.concat(temp);
}, []);
}
export const listPayments = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'List Payments..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/pay/listPayments';
request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Payments', msg: 'Payment List Received', data: body.payments });
res.status(200).json(groupBy(body.payments));
}).catch((errRes) => {
const err = common.handleError(errRes, 'Payments', 'List Payments Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const postPayment = (req, res, next) => {
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
if (req.body.paymentType === 'KEYSEND') {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Keysend Payment..' });
options.url = req.session.selectedNode.ln_server_url + '/v1/pay/keysend';
options.body = req.body;
}
else {
if (req.body.paymentType === 'OFFER') {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Sending Offer Payment..' });
options.body = { invoice: req.body.invoice };
}
else {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Sending Invoice Payment..' });
options.body = req.body;
}
options.url = req.session.selectedNode.ln_server_url + '/v1/pay';
}
request.post(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Payment Sent', data: body });
if (req.body.paymentType === 'OFFER') {
if (req.body.saveToDB && req.body.bolt12) {
const offerToUpdate = { bolt12: req.body.bolt12, amountMSat: (req.body.zeroAmtOffer ? 0 : req.body.amount), title: req.body.title, lastUpdatedAt: new Date(Date.now()).getTime() };
if (req.body.vendor) {
offerToUpdate['vendor'] = req.body.vendor;
}
if (req.body.description) {
offerToUpdate['description'] = req.body.description;
}
// eslint-disable-next-line arrow-body-style
return databaseService.validateDocument(CollectionsEnum.OFFERS, offerToUpdate).then((validated) => {
return databaseService.update(req.session.selectedNode, CollectionsEnum.OFFERS, offerToUpdate, CollectionFieldsEnum.BOLT12, req.body.bolt12).then((updatedOffer) => {
logger.log({ level: 'DEBUG', fileName: 'Payments', msg: 'Offer Updated', data: updatedOffer });
return res.status(201).json({ paymentResponse: body, saveToDBResponse: updatedOffer });
}).catch((errDB) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'ERROR', fileName: 'Payments', msg: 'Offer DB update error', error: errDB });
return res.status(201).json({ paymentResponse: body, saveToDBError: errDB });
});
}).catch((errValidation) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'ERROR', fileName: 'Payments', msg: 'Offer DB validation error', error: errValidation });
return res.status(201).json({ paymentResponse: body, saveToDBError: errValidation });
});
}
else {
return res.status(201).json({ paymentResponse: body, saveToDBResponse: 'NA' });
}
}
if (req.body.paymentType === 'INVOICE') {
return res.status(201).json({ paymentResponse: body, saveToDBResponse: 'NA' });
}
if (req.body.paymentType === 'KEYSEND') {
return res.status(201).json(body);
}
}).catch((errRes) => {
const err = common.handleError(errRes, 'Payments', 'Send Payment Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};

@ -1,65 +0,0 @@
import request from 'request-promise';
import { Logger } from '../../utils/logger.js';
import { Common } from '../../utils/common.js';
let options = null;
const logger = Logger;
const common = Common;
export const getPeers = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'List Peers..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/peer/listPeers';
request(options).then((body) => {
body.forEach((peer) => {
if (!peer.alias || peer.alias === '') {
peer.alias = peer.id.substring(0, 20);
}
});
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Peers with Alias Received', data: body });
res.status(200).json(body || []);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Peers', 'List Peers Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const postPeer = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Connecting Peer..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/peer/connect';
options.body = req.body;
request.post(options).then((connectRes) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Peers', msg: 'Peer Connected', data: connectRes });
options.url = req.session.selectedNode.ln_server_url + '/v1/peer/listPeers';
request(options).then((listPeersRes) => {
const peers = listPeersRes ? common.newestOnTop(listPeersRes, 'id', req.body.id) : [];
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Peers List after Connect Received', data: peers });
res.status(201).json(peers);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Peers', 'Connect Peer Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
}).catch((errRes) => {
const err = common.handleError(errRes, 'Peers', 'Connect Peer Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const deletePeer = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Disconnecting Peer..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/peer/disconnect/' + req.params.peerId + '?force=' + req.query.force;
request.delete(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Peer Disconnected', data: body });
res.status(204).json({});
}).catch((errRes) => {
const err = common.handleError(errRes, 'Peers', 'Detach Peer Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};

@ -1,97 +0,0 @@
import request from 'request-promise';
import { Logger } from '../../utils/logger.js';
import { Common } from '../../utils/common.js';
let options = null;
const logger = Logger;
const common = Common;
export const decodePaymentFromPaymentRequest = (selNode, payment) => {
options.url = selNode.ln_server_url + '/v1/utility/decode/' + payment;
return request(options).then((res) => {
logger.log({ selectedNode: selNode, level: 'DEBUG', fileName: 'Payments', msg: 'Payment Decode Received', data: res });
return res;
}).catch((err) => { });
};
export const decodePayments = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Decoding Payments List..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
if (req.body.payments) {
const paymentsArr = req.body.payments.split(',');
return Promise.all(paymentsArr?.map((payment) => decodePaymentFromPaymentRequest(req.session.selectedNode, payment))).
then((values) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Payment List Decoded', data: values });
res.status(200).json(values);
}).
catch((errRes) => {
const err = common.handleError(errRes, 'Payments', 'Decode Payments Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
}
else {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Empty Payment List Decoded' });
return res.status(200).json([]);
}
};
export const decodePayment = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Decoding Payment..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/utility/decode/' + req.params.payReq;
request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Payment Decoded', data: body });
res.status(200).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Payments', 'Decode Payment Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const signMessage = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Message', msg: 'Signing Message..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/utility/signMessage';
options.form = { message: req.body.message };
request.post(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Message', msg: 'Message Signed', data: body });
res.status(201).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Message', 'Sign Message Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const verifyMessage = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Message', msg: 'Verifying Message..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/utility/checkMessage/' + req.body.message + '/' + req.body.signature;
request.get(options, (error, response, body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Message', msg: 'Message Verified', data: body });
res.status(201).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Message', 'Verify Message Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const listConfigs = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Utility', msg: 'List Configs..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/utility/listConfigs';
request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Utility', msg: 'List Configs Received', data: body });
res.status(200).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Utility', 'List Configs Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};

@ -1,116 +0,0 @@
import * as fs from 'fs';
import { join } from 'path';
import WebSocket from 'ws';
import { Logger } from '../../utils/logger.js';
import { Common } from '../../utils/common.js';
import { WSServer } from '../../utils/webSocketServer.js';
export class CLWebSocketClient {
constructor() {
this.logger = Logger;
this.common = Common;
this.wsServer = WSServer;
this.webSocketClients = [];
this.reconnectTimeOut = null;
this.waitTime = 0.5;
this.reconnet = (clWsClt) => {
if (this.reconnectTimeOut) {
return;
}
this.waitTime = (this.waitTime >= 64) ? 64 : (this.waitTime * 2);
this.reconnectTimeOut = setTimeout(() => {
if (clWsClt.selectedNode) {
this.logger.log({ selectedNode: clWsClt.selectedNode, level: 'INFO', fileName: 'CLWebSocket', msg: 'Reconnecting to the Core Lightning\'s Websocket Server..' });
this.connect(clWsClt.selectedNode);
}
this.reconnectTimeOut = null;
}, this.waitTime * 1000);
};
this.connect = (selectedNode) => {
try {
const clientExists = this.webSocketClients.find((wsc) => wsc.selectedNode.index === selectedNode.index);
if (!clientExists) {
if (selectedNode.ln_server_url) {
const newWebSocketClient = { selectedNode: selectedNode, reConnect: true, webSocketClient: null };
this.connectWithClient(newWebSocketClient);
this.webSocketClients.push(newWebSocketClient);
}
}
else {
if ((!clientExists.webSocketClient || clientExists.webSocketClient.readyState !== WebSocket.OPEN) && selectedNode.ln_server_url) {
clientExists.reConnect = true;
this.connectWithClient(clientExists);
}
}
}
catch (err) {
throw new Error(err);
}
};
this.connectWithClient = (clWsClt) => {
this.logger.log({ selectedNode: clWsClt.selectedNode, level: 'INFO', fileName: 'CLWebSocket', msg: 'Connecting to the Core Lightning\'s Websocket Server..' });
const WS_LINK = (clWsClt.selectedNode.ln_server_url)?.replace(/^http/, 'ws') + '/v1/ws';
const mcrnHexEncoded = Buffer.from(fs.readFileSync(join(clWsClt.selectedNode.macaroon_path, 'access.macaroon'))).toString('hex');
clWsClt.webSocketClient = new WebSocket(WS_LINK, [mcrnHexEncoded, 'hex'], { rejectUnauthorized: false });
clWsClt.webSocketClient.onopen = () => {
this.logger.log({ selectedNode: clWsClt.selectedNode, level: 'INFO', fileName: 'CLWebSocket', msg: 'Connected to the Core Lightning\'s Websocket Server..' });
this.waitTime = 0.5;
};
clWsClt.webSocketClient.onclose = (e) => {
if (clWsClt && clWsClt.selectedNode && clWsClt.selectedNode.ln_implementation === 'CLN') {
this.logger.log({ selectedNode: clWsClt.selectedNode, level: 'INFO', fileName: 'CLWebSocket', msg: 'Web socket disconnected, will reconnect again...' });
clWsClt.webSocketClient.close();
if (clWsClt.reConnect) {
this.reconnet(clWsClt);
}
}
};
clWsClt.webSocketClient.onmessage = (msg) => {
this.logger.log({ selectedNode: clWsClt.selectedNode, level: 'DEBUG', fileName: 'CLWebSocket', msg: 'Received message from the server..', data: msg.data });
msg = (typeof msg.data === 'string') ? JSON.parse(msg.data) : msg.data;
msg['source'] = 'CLN';
const msgStr = JSON.stringify(msg);
this.wsServer.sendEventsToAllLNClients(msgStr, clWsClt.selectedNode);
};
clWsClt.webSocketClient.onerror = (err) => {
if (clWsClt.selectedNode.api_version === '' || !clWsClt.selectedNode.api_version || this.common.isVersionCompatible(clWsClt.selectedNode.api_version, '0.6.0')) {
this.logger.log({ selectedNode: clWsClt.selectedNode, level: 'ERROR', fileName: 'CLWebSocket', msg: 'Web socket error', error: err });
const errStr = ((typeof err === 'object' && err.message) ? JSON.stringify({ error: err.message }) : (typeof err === 'object') ? JSON.stringify({ error: err }) : ('{ "error": ' + err + ' }'));
this.wsServer.sendErrorToAllLNClients(errStr, clWsClt.selectedNode);
clWsClt.webSocketClient.close();
if (clWsClt.reConnect) {
this.reconnet(clWsClt);
}
}
else {
clWsClt.reConnect = false;
}
};
};
this.disconnect = (selectedNode) => {
const clientExists = this.webSocketClients.find((wsc) => wsc.selectedNode.index === selectedNode.index);
if (clientExists && clientExists.webSocketClient && clientExists.webSocketClient.readyState === WebSocket.OPEN) {
this.logger.log({ selectedNode: clientExists.selectedNode, level: 'INFO', fileName: 'CLWebSocket', msg: 'Disconnecting from the Core Lightning\'s Websocket Server..' });
clientExists.reConnect = false;
clientExists.webSocketClient.close();
const clientIdx = this.webSocketClients.findIndex((wsc) => wsc.selectedNode.index === selectedNode.index);
this.webSocketClients.splice(clientIdx, 1);
}
};
this.updateSelectedNode = (newSelectedNode) => {
const clientIdx = this.webSocketClients.findIndex((wsc) => +wsc.selectedNode.index === +newSelectedNode.index);
let newClient = this.webSocketClients[clientIdx];
if (!newClient) {
newClient = { selectedNode: null, reConnect: true, webSocketClient: null };
}
newClient.selectedNode = JSON.parse(JSON.stringify(newSelectedNode));
this.webSocketClients[clientIdx] = newClient;
};
this.wsServer.eventEmitterCLN.on('CONNECT', (nodeIndex) => {
this.connect(this.common.findNode(+nodeIndex));
});
this.wsServer.eventEmitterCLN.on('DISCONNECT', (nodeIndex) => {
this.disconnect(this.common.findNode(+nodeIndex));
});
}
}
export const CLWSClient = new CLWebSocketClient();

@ -1,158 +0,0 @@
import request from 'request-promise';
import { Logger } from '../../utils/logger.js';
import { Common } from '../../utils/common.js';
let options = null;
const logger = Logger;
const common = Common;
export const simplifyAllChannels = (selNode, channels) => {
let channelNodeIds = '';
const simplifiedChannels = [];
channels.forEach((channel) => {
channelNodeIds = channelNodeIds + ',' + channel.nodeId;
simplifiedChannels.push({
nodeId: channel.nodeId ? channel.nodeId : '',
channelId: channel.channelId ? channel.channelId : '',
state: channel.state ? channel.state : '',
announceChannel: channel.data && channel.data.commitments && channel.data.commitments.channelFlags && channel.data.commitments.channelFlags.announceChannel ? channel.data.commitments.channelFlags.announceChannel : false,
toLocal: (channel.data.commitments.localCommit.spec.toLocal) ? Math.round(+channel.data.commitments.localCommit.spec.toLocal / 1000) : 0,
toRemote: (channel.data.commitments.localCommit.spec.toRemote) ? Math.round(+channel.data.commitments.localCommit.spec.toRemote / 1000) : 0,
shortChannelId: channel.data && channel.data.channelUpdate && channel.data.channelUpdate.shortChannelId ? channel.data.channelUpdate.shortChannelId : '',
isFunder: channel.data && channel.data.commitments && channel.data.commitments.localParams && channel.data.commitments.localParams.isFunder ? channel.data.commitments.localParams.isFunder : false,
buried: channel.data && channel.data.buried ? channel.data.buried : false,
feeBaseMsat: channel.data && channel.data.channelUpdate && channel.data.channelUpdate.feeBaseMsat ? channel.data.channelUpdate.feeBaseMsat : 0,
feeRatePerKw: (channel.data.commitments.localCommit.spec.feeratePerKw) ? channel.data.commitments.localCommit.spec.feeratePerKw : 0,
feeProportionalMillionths: channel.data && channel.data.channelUpdate && channel.data.channelUpdate.feeProportionalMillionths ? channel.data.channelUpdate.feeProportionalMillionths : 0,
alias: ''
});
});
channelNodeIds = channelNodeIds.substring(1);
options.url = selNode.ln_server_url + '/nodes';
options.form = { nodeIds: channelNodeIds };
logger.log({ selectedNode: selNode, level: 'DEBUG', fileName: 'Channels', msg: 'Node Ids to find alias', data: channelNodeIds });
return request.post(options).then((nodes) => {
logger.log({ selectedNode: selNode, level: 'DEBUG', fileName: 'Channels', msg: 'Filtered Nodes Received', data: nodes });
let foundPeer = null;
simplifiedChannels?.map((channel) => {
foundPeer = nodes.find((channelWithAlias) => channel.nodeId === channelWithAlias.nodeId);
channel.alias = foundPeer ? foundPeer.alias : channel.nodeId.substring(0, 20);
return channel;
});
return simplifiedChannels;
}).catch((err) => simplifiedChannels);
};
export const getChannels = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'List Channels..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/channels';
options.form = {};
if (req.query && req.query.nodeId) {
options.form = req.query;
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Channels Node Id', data: options.form });
}
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Options', data: options });
if (common.read_dummy_data) {
common.getDummyData('Channels', req.session.selectedNode.ln_implementation).then((data) => { res.status(200).json(data); });
}
else {
request.post(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Channels List Received', data: body });
if (body && body.length) {
return simplifyAllChannels(req.session.selectedNode, body).then((simplifiedChannels) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Simplified Channels with Alias Received', data: simplifiedChannels });
res.status(200).json(simplifiedChannels);
});
}
else {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Empty Channels List Received' });
res.status(200).json([]);
}
}).
catch((errRes) => {
const err = common.handleError(errRes, 'Channels', 'List Channels Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
}
};
export const getChannelStats = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Getting Channel States..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/channelstats';
const today = new Date(Date.now());
const tillToday = (Math.round(today.getTime() / 1000)).toString();
const fromLastMonth = (Math.round(new Date(today.getFullYear(), today.getMonth() - 1, today.getDate() + 1, 0, 0, 0).getTime() / 1000)).toString();
options.form = {
from: fromLastMonth,
to: tillToday
};
request.post(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Channel States Received', data: body });
res.status(201).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Channels', 'Get Channel Stats Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const openChannel = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Opening Channel..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/open';
options.form = req.body;
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Open Channel Params', data: options.form });
request.post(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Channel Opened', data: body });
res.status(201).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Channels', 'Open Channel Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const updateChannelRelayFee = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Updating Channel Relay Fee..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/updaterelayfee';
options.form = req.query;
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Update Relay Fee Params', data: options.form });
request.post(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Channel Relay Fee Updated', data: body });
res.status(201).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Channels', 'Update Relay Fee Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const closeChannel = (req, res, next) => {
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
if (req.query.force !== 'true') {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Closing Channel..' });
options.url = req.session.selectedNode.ln_server_url + '/close';
}
else {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Force Closing Channel..' });
options.url = req.session.selectedNode.ln_server_url + '/forceclose';
}
options.form = { channelId: req.query.channelId };
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Close URL', data: options.url });
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Close Params', data: options.form });
request.post(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Channel Closed', data: body });
res.status(204).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Channels', 'Close Channel Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};

@ -1,144 +0,0 @@
import request from 'request-promise';
import { Logger } from '../../utils/logger.js';
import { Common } from '../../utils/common.js';
let options = null;
const logger = Logger;
const common = Common;
export const arrangeFees = (selNode, body, current_time) => {
const fees = { daily_fee: 0, daily_txs: 0, weekly_fee: 0, weekly_txs: 0, monthly_fee: 0, monthly_txs: 0 };
const week_start_time = current_time - 604800000;
const day_start_time = current_time - 86400000;
let fee = 0;
body.relayed.forEach((relayedEle) => {
fee = Math.round((relayedEle.amountIn - relayedEle.amountOut) / 1000);
if (relayedEle.timestamp) {
if (relayedEle.timestamp.unix) {
if ((relayedEle.timestamp.unix * 1000) >= day_start_time) {
fees.daily_fee = fees.daily_fee + fee;
fees.daily_txs = fees.daily_txs + 1;
}
if ((relayedEle.timestamp.unix * 1000) >= week_start_time) {
fees.weekly_fee = fees.weekly_fee + fee;
fees.weekly_txs = fees.weekly_txs + 1;
}
}
else {
if (relayedEle.timestamp >= day_start_time) {
fees.daily_fee = fees.daily_fee + fee;
fees.daily_txs = fees.daily_txs + 1;
}
if (relayedEle.timestamp >= week_start_time) {
fees.weekly_fee = fees.weekly_fee + fee;
fees.weekly_txs = fees.weekly_txs + 1;
}
}
}
fees.monthly_fee = fees.monthly_fee + fee;
fees.monthly_txs = fees.monthly_txs + 1;
});
logger.log({ selectedNode: selNode, level: 'DEBUG', fileName: 'Fees', msg: 'Arranged Fee Received', data: fees });
return fees;
};
export const arrangePayments = (selNode, body) => {
const payments = {
sent: body && body.sent ? body.sent : [],
received: body && body.received ? body.received : [],
relayed: body && body.relayed ? body.relayed : []
};
payments.sent.forEach((sentEle) => {
if (sentEle.recipientAmount) {
sentEle.recipientAmount = Math.round(sentEle.recipientAmount / 1000);
}
sentEle.parts.forEach((part) => {
if (part.amount) {
part.amount = Math.round(part.amount / 1000);
}
if (part.feesPaid) {
part.feesPaid = Math.round(part.feesPaid / 1000);
}
if (part.timestamp.unix) {
part.timestamp = part.timestamp.unix * 1000;
}
});
if (sentEle.parts && sentEle.parts.length > 0) {
sentEle.firstPartTimestamp = sentEle.parts[0].timestamp;
}
});
payments.received.forEach((receivedEle) => {
receivedEle.parts.forEach((part) => {
if (part.amount) {
part.amount = Math.round(part.amount / 1000);
}
if (part.timestamp.unix) {
part.timestamp = part.timestamp.unix * 1000;
}
});
if (receivedEle.parts && receivedEle.parts.length > 0) {
receivedEle.firstPartTimestamp = receivedEle.parts[0].timestamp;
}
});
payments.relayed.forEach((relayedEle) => {
if (relayedEle.timestamp.unix) {
relayedEle.timestamp = relayedEle.timestamp.unix * 1000;
}
if (relayedEle.amountIn) {
relayedEle.amountIn = Math.round(relayedEle.amountIn / 1000);
}
if (relayedEle.amountOut) {
relayedEle.amountOut = Math.round(relayedEle.amountOut / 1000);
}
});
logger.log({ selectedNode: selNode, level: 'DEBUG', fileName: 'Fees', msg: 'Arranged Payments Received', data: payments });
return payments;
};
export const getFees = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Fees', msg: 'Getting Fees..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/audit';
const today = new Date(Date.now());
const tillToday = (Math.round(today.getTime() / 1000)).toString();
const fromLastMonth = (Math.round(new Date(today.getFullYear(), today.getMonth() - 1, today.getDate() + 1, 0, 0, 0).getTime() / 1000)).toString();
options.form = {
from: fromLastMonth,
to: tillToday
};
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Fees', msg: 'Fee Audit Options', data: options.form });
if (common.read_dummy_data) {
common.getDummyData('Fees', req.session.selectedNode.ln_implementation).then((data) => { res.status(200).json(arrangeFees(req.session.selectedNode, data, Math.round((new Date().getTime())))); });
}
else {
request.post(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Fees', msg: 'Fee Received', data: body });
res.status(200).json(arrangeFees(req.session.selectedNode, body, Math.round((new Date().getTime()))));
}).catch((errRes) => {
const err = common.handleError(errRes, 'Fees', 'Get Fees Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
}
};
export const getPayments = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Fees', msg: 'Getting Payments..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/audit';
const tillToday = (Math.round(new Date(Date.now()).getTime() / 1000)).toString();
options.form = { from: 0, to: tillToday };
if (common.read_dummy_data) {
common.getDummyData('Payments', req.session.selectedNode.ln_implementation).then((data) => { res.status(200).json(arrangePayments(req.session.selectedNode, data)); });
}
else {
request.post(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Fees', msg: 'Payments Received', data: body });
res.status(200).json(arrangePayments(req.session.selectedNode, body));
}).
catch((errRes) => {
const err = common.handleError(errRes, 'Fees', 'Get Payments Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
}
};

@ -1,50 +0,0 @@
import request from 'request-promise';
import { Database } from '../../utils/database.js';
import { Logger } from '../../utils/logger.js';
import { Common } from '../../utils/common.js';
import { ECLWSClient } from './webSocketClient.js';
let options = null;
const logger = Logger;
const common = Common;
const eclWsClient = ECLWSClient;
const databaseService = Database;
export const getInfo = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Getting Eclair Node Information..' });
common.logEnvVariables(req);
common.setOptions(req);
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/getinfo';
options.form = {};
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Selected Node ' + req.session.selectedNode.ln_node });
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Calling Info from Eclair server url ' + options.url });
if (common.read_dummy_data) {
common.getDummyData('GetInfo', req.session.selectedNode.ln_implementation).then((data) => {
data.lnImplementation = 'Eclair';
return res.status(200).json(data);
});
}
else {
if (!options.headers || !options.headers.authorization) {
const errMsg = 'Eclair Get info failed due to missing or wrong password!';
const err = common.handleError({ statusCode: 502, message: 'Missing or Wrong Password', error: errMsg }, 'GetInfo', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
else {
return request.post(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Connecting to the Eclair\'s Websocket Server.' });
body.lnImplementation = 'Eclair';
req.session.selectedNode.ln_version = body.version.split('-')[0] || '';
eclWsClient.updateSelectedNode(req.session.selectedNode);
databaseService.loadDatabase(req.session);
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Node Information Received', data: body });
return res.status(200).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'GetInfo', 'Get Info Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
}
}
};

@ -1,124 +0,0 @@
import request from 'request-promise';
import { Logger } from '../../utils/logger.js';
import { Common } from '../../utils/common.js';
let options = null;
const logger = Logger;
const common = Common;
let pendingInvoices = [];
export const getReceivedPaymentInfo = (lnServerUrl, invoice) => {
let idx = -1;
invoice.expiresAt = (!invoice.expiry) ? null : (+invoice.timestamp + +invoice.expiry);
if (invoice.amount) {
invoice.amount = Math.round(invoice.amount / 1000);
}
idx = pendingInvoices.findIndex((pendingInvoice) => invoice.serialized === pendingInvoice.serialized);
if (idx < 0) {
options.url = lnServerUrl + '/getreceivedinfo';
options.form = { paymentHash: invoice.paymentHash };
return request(options).then((response) => {
invoice.status = response.status.type;
if (response.status && response.status.type === 'received') {
invoice.amountSettled = response.status.amount ? Math.round(response.status.amount / 1000) : 0;
invoice.receivedAt = response.status.receivedAt ? Math.round(response.status.receivedAt / 1000) : 0;
}
return invoice;
}).catch((err) => {
invoice.status = 'unknown';
return invoice;
});
}
else {
pendingInvoices.splice(idx, 1);
invoice.status = 'unpaid';
return invoice;
}
};
export const getInvoice = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Getting Invoice..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/getinvoice';
options.form = { paymentHash: req.params.paymentHash };
request.post(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoice', msg: 'Invoice Found', data: body });
const current_time = (Math.round(new Date(Date.now()).getTime() / 1000));
body.amount = body.amount ? body.amount / 1000 : 0;
body.expiresAt = body.expiresAt ? body.expiresAt : (body.timestamp + body.expiry);
body.status = body.status ? body.status : (+body.expiresAt < current_time ? 'expired' : 'unknown');
res.status(200).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Invoices', 'Get Invoice Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const listInvoices = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoices', msg: 'Getting List Invoices..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
const tillToday = (Math.round(new Date(Date.now()).getTime() / 1000)).toString();
options.form = { from: 0, to: tillToday };
const options1 = JSON.parse(JSON.stringify(options));
options1.url = req.session.selectedNode.ln_server_url + '/listinvoices';
options1.form = { from: 0, to: tillToday };
const options2 = JSON.parse(JSON.stringify(options));
options2.url = req.session.selectedNode.ln_server_url + '/listpendinginvoices';
options2.form = { from: 0, to: tillToday };
if (common.read_dummy_data) {
return common.getDummyData('Invoices', req.session.selectedNode.ln_implementation).then((body) => {
const invoices = (!body[0] || body[0].length <= 0) ? [] : body[0];
pendingInvoices = (!body[1] || body[1].length <= 0) ? [] : body[1];
return Promise.all(invoices?.map((invoice) => getReceivedPaymentInfo(req.session.selectedNode.ln_server_url, invoice))).
then((values) => res.status(200).json(invoices));
});
}
else {
return Promise.all([request(options1), request(options2)]).
then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Invoice', msg: 'Invoices List Received', data: body });
const invoices = (!body[0] || body[0].length <= 0) ? [] : body[0];
pendingInvoices = (!body[1] || body[1].length <= 0) ? [] : body[1];
if (invoices && invoices.length > 0) {
return Promise.all(invoices?.map((invoice) => getReceivedPaymentInfo(req.session.selectedNode.ln_server_url, invoice))).
then((values) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoices', msg: 'Sorted Invoices List Received', data: invoices });
return res.status(200).json(invoices);
}).
catch((errRes) => {
const err = common.handleError(errRes, 'Invoices', 'List Invoices Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
}
else {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoices', msg: 'Empty List Invoice Received' });
return res.status(200).json([]);
}
}).
catch((errRes) => {
const err = common.handleError(errRes, 'Invoices', 'List Invoices Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
}
};
export const createInvoice = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoices', msg: 'Creating Invoice..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/createinvoice';
options.form = req.body;
request.post(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoice', msg: 'Invoice Created', data: body });
if (body.amount) {
body.amount = Math.round(body.amount / 1000);
}
res.status(201).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Invoices', 'Create Invoice Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};

@ -1,22 +0,0 @@
import request from 'request-promise';
import { Logger } from '../../utils/logger.js';
import { Common } from '../../utils/common.js';
let options = null;
const logger = Logger;
const common = Common;
export const getNodes = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Network', msg: 'Node Lookup..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/nodes';
options.form = { nodeIds: req.params.id };
request.post(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Network', msg: 'Node Lookup Finished', data: body });
res.status(200).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Network', 'Node Lookup Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};

@ -1,96 +0,0 @@
import request from 'request-promise';
import { Logger } from '../../utils/logger.js';
import { Common } from '../../utils/common.js';
let options = null;
const logger = Logger;
const common = Common;
export const arrangeBalances = (body) => {
if (!body.confirmed) {
body.confirmed = 0;
}
if (!body.unconfirmed) {
body.unconfirmed = 0;
}
body.total = +body.confirmed + +body.unconfirmed;
body.btc_total = +body.btc_confirmed + +body.btc_unconfirmed;
return body;
};
export const getNewAddress = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'Generating New Address..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/getnewaddress';
options.form = {};
request.post(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'New Address Generated', data: body });
res.status(200).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'OnChain', 'Get New Address Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const getBalance = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'Getting On Chain Balance..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/onchainbalance';
options.form = {};
if (common.read_dummy_data) {
common.getDummyData('OnChainBalance', req.session.selectedNode.ln_implementation).then((data) => { res.status(200).json(arrangeBalances(data)); });
}
else {
request.post(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'On Chain Balance Received', data: body });
res.status(200).json(arrangeBalances(body));
}).
catch((errRes) => {
const err = common.handleError(errRes, 'OnChain', 'Get Balance Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
}
};
export const getTransactions = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'Getting On Chain Transactions..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/onchaintransactions';
options.form = {
count: req.query.count,
skip: req.query.skip
};
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'Getting On Chain Transactions Options', data: options.form });
request.post(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'On Chain Transactions Received', data: body });
res.status(200).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'OnChain', 'Get Transactions Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const sendFunds = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'Sending On Chain Funds..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/sendonchain';
options.form = {
address: req.body.address,
amountSatoshis: req.body.amount,
confirmationTarget: req.body.blocks
};
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Onchain', msg: 'Send Funds Options', data: options.form });
request.post(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Onchain', msg: 'On Chain Funds Sent', data: body });
res.status(201).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'OnChain', 'Send Funds Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};

@ -1,128 +0,0 @@
import request from 'request-promise';
import { Logger } from '../../utils/logger.js';
import { Common } from '../../utils/common.js';
let options = null;
const logger = Logger;
const common = Common;
export const getSentInfoFromPaymentRequest = (selNode, payment) => {
options.url = selNode.ln_server_url + '/getsentinfo';
options.form = { paymentHash: payment };
return request.post(options).then((body) => {
logger.log({ selectedNode: selNode, level: 'DEBUG', fileName: 'Payments', msg: 'Payment Sent Information Received', data: body });
body.forEach((sentPayment) => {
if (sentPayment.amount) {
sentPayment.amount = Math.round(sentPayment.amount / 1000);
}
if (sentPayment.recipientAmount) {
sentPayment.recipientAmount = Math.round(sentPayment.recipientAmount / 1000);
}
});
return body;
}).catch((err) => err);
};
export const getQueryNodes = (selNode, nodeIds) => {
options.url = selNode.ln_server_url + '/nodes';
options.form = { nodeIds: nodeIds?.reduce((acc, curr) => acc + ',' + curr) };
return request.post(options).then((nodes) => {
logger.log({ selectedNode: selNode, level: 'DEBUG', fileName: 'Payments', msg: 'Query Nodes Received', data: nodes });
return nodes;
}).catch((err) => []);
};
export const decodePayment = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Decoding Payment..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/parseinvoice';
options.form = { invoice: req.params.invoice };
request.post(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Payment Decoded', data: body });
if (body.amount) {
body.amount = Math.round(body.amount / 1000);
}
res.status(200).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Payments', 'Decode Payment Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const postPayment = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Paying Invoice..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/payinvoice';
options.form = req.body;
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Payments', msg: 'Send Payment Options', data: options.form });
request.post(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Invoice Paid', data: body });
res.status(201).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Payments', 'Send Payment Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const queryPaymentRoute = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Querying Payment Route..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/findroutetonode';
options.form = {
nodeId: req.query.nodeId,
amountMsat: req.query.amountMsat
};
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Payments', msg: 'Query Payment Route Options', data: options.form });
request.post(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Payments', msg: 'Query Payment Route Received', data: body });
if (body && body.routes && body.routes.length) {
let allRoutesNodeIds = [];
allRoutesNodeIds = body.routes?.reduce((accRoutes, currRoute) => [...new Set([...accRoutes, ...currRoute.nodeIds])], []);
return getQueryNodes(req.session.selectedNode, allRoutesNodeIds).then((nodesWithAlias) => {
let foundPeer = null;
body.routes.forEach((route, i) => {
route.nodeIds?.map((node, j) => {
foundPeer = nodesWithAlias.find((nodeWithAlias) => node === nodeWithAlias.nodeId);
body.routes[i].nodeIds[j] = { nodeId: node, alias: foundPeer ? foundPeer.alias : '' };
return node;
});
});
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Query Routes with Alias Received', data: body });
res.status(200).json(body);
});
}
else {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Empty Payment Route Information Received' });
res.status(200).json({ routes: [] });
}
}).catch((errRes) => {
const err = common.handleError(errRes, 'Payments', 'Query Route Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const getSentPaymentsInformation = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Getting Sent Payment Information..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
if (req.body.payments) {
const paymentsArr = req.body.payments.split(',');
return Promise.all(paymentsArr?.map((payment) => getSentInfoFromPaymentRequest(req.session.selectedNode, payment))).
then((values) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Payment Sent Information Received', data: values });
return res.status(200).json(values);
}).
catch((errRes) => {
const err = common.handleError(errRes, 'Payments', 'Sent Payment Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
}
else {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Empty Sent Payment Information Received' });
return res.status(200).json([]);
}
};

@ -1,128 +0,0 @@
import request from 'request-promise';
import { Logger } from '../../utils/logger.js';
import { Common } from '../../utils/common.js';
let options = null;
const logger = Logger;
const common = Common;
export const getFilteredNodes = (selNode, peersNodeIds) => {
options.url = selNode.ln_server_url + '/nodes';
options.form = { nodeIds: peersNodeIds };
return request.post(options).then((nodes) => {
logger.log({ selectedNode: selNode, level: 'DEBUG', fileName: 'Peers', msg: 'Filtered Nodes Received', data: nodes });
return nodes;
}).catch((err) => []);
};
export const getPeers = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Getting Peers..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/peers';
options.form = {};
if (common.read_dummy_data) {
common.getDummyData('Peers', req.session.selectedNode.ln_implementation).then((data) => { res.status(200).json(data); });
}
else {
request.post(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Peers', msg: 'Peers List Received', data: body });
if (body && body.length) {
let peersNodeIds = '';
body.forEach((peer) => { peersNodeIds = peersNodeIds + ',' + peer.nodeId; });
peersNodeIds = peersNodeIds.substring(1);
return getFilteredNodes(req.session.selectedNode, peersNodeIds).then((peersWithAlias) => {
let foundPeer = null;
body?.map((peer) => {
foundPeer = peersWithAlias.find((peerWithAlias) => peer.nodeId === peerWithAlias.nodeId);
peer.alias = foundPeer ? foundPeer.alias : peer.nodeId.substring(0, 20);
return peer;
});
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Sorted Peers List Received', data: body });
res.status(200).json(body);
});
}
else {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Empty Peers Received' });
res.status(200).json([]);
}
}).
catch((errRes) => {
const err = common.handleError(errRes, 'Peers', 'List Peers Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
}
};
export const connectPeer = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Conneting Peer..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/connect';
options.form = {};
if (req.query) {
options.form = req.query;
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Peers', msg: 'Connect Peer Params', data: options.form });
}
request.post(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Peers', msg: 'Peer Connected', data: body });
if (typeof body === 'string' && body.includes('already connected')) {
const err = common.handleError({ statusCode: 500, message: 'Connect Peer Error', error: body }, 'Peers', body, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
else if (typeof body === 'string' && body.includes('connection failed')) {
const err = common.handleError({ statusCode: 500, message: 'Connect Peer Error', error: body }, 'Peers', body, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
options.url = req.session.selectedNode.ln_server_url + '/peers';
options.form = {};
request.post(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Peers', msg: 'Peers List after Connect', data: body });
if (body && body.length) {
let peersNodeIds = '';
body.forEach((peer) => { peersNodeIds = peersNodeIds + ',' + peer.nodeId; });
peersNodeIds = peersNodeIds.substring(1);
return getFilteredNodes(req.session.selectedNode, peersNodeIds).then((peersWithAlias) => {
let foundPeer = null;
body?.map((peer) => {
foundPeer = peersWithAlias.find((peerWithAlias) => peer.nodeId === peerWithAlias.nodeId);
peer.alias = foundPeer ? foundPeer.alias : peer.nodeId.substring(0, 20);
return peer;
});
const peers = common.newestOnTop(body || [], 'nodeId', req.query.nodeId ? req.query.nodeId : req.query.uri ? req.query.uri.substring(0, req.query.uri.indexOf('@')) : '');
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Peers List after Connect Received', data: peers });
res.status(201).json(peers);
});
}
else {
res.status(201).json([]);
}
}).catch((errRes) => {
const err = common.handleError(errRes, 'Peers', 'Connect Peer Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
}).catch((errRes) => {
const err = common.handleError(errRes, 'Peers', 'Connect Peer Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const deletePeer = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Disconneting Peer..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/disconnect';
options.form = {};
if (req.params.nodeId) {
options.form = { nodeId: req.params.nodeId };
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Peers', msg: 'Disconnect Peer Params', data: options.form });
}
request.post(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Peer Disconnected', data: body });
res.status(204).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Peers', 'Disconnect Peer Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};

@ -1,130 +0,0 @@
import WebSocket from 'ws';
import { Logger } from '../../utils/logger.js';
import { Common } from '../../utils/common.js';
import { WSServer } from '../../utils/webSocketServer.js';
import { ECLWSEventsEnum } from '../../models/ecl.model.js';
export class ECLWebSocketClient {
constructor() {
this.logger = Logger;
this.common = Common;
this.wsServer = WSServer;
this.webSocketClients = [];
this.reconnectTimeOut = null;
this.waitTime = 0.5;
this.reconnet = (eclWsClt) => {
if (this.reconnectTimeOut) {
return;
}
this.waitTime = (this.waitTime >= 64) ? 64 : (this.waitTime * 2);
this.reconnectTimeOut = setTimeout(() => {
if (eclWsClt.selectedNode) {
this.logger.log({ selectedNode: eclWsClt.selectedNode, level: 'INFO', fileName: 'ECLWebSocket', msg: 'Reconnecting to the Eclair\'s Websocket Server..' });
this.connect(eclWsClt.selectedNode);
}
this.reconnectTimeOut = null;
}, this.waitTime * 1000);
};
this.connect = (selectedNode) => {
try {
const clientExists = this.webSocketClients.find((wsc) => wsc.selectedNode.index === selectedNode.index);
if (!clientExists) {
if (selectedNode.ln_server_url) {
const newWebSocketClient = { selectedNode: selectedNode, reConnect: true, webSocketClient: null };
this.connectWithClient(newWebSocketClient);
this.webSocketClients.push(newWebSocketClient);
}
}
else {
if ((!clientExists.webSocketClient || clientExists.webSocketClient.readyState !== WebSocket.OPEN) && selectedNode.ln_server_url) {
clientExists.reConnect = true;
this.connectWithClient(clientExists);
}
}
}
catch (err) {
throw new Error(err);
}
};
this.connectWithClient = (eclWsClt) => {
this.logger.log({ selectedNode: eclWsClt.selectedNode, level: 'INFO', fileName: 'ECLWebSocket', msg: 'Connecting to the Eclair\'s Websocket Server..' });
const UpdatedLNServerURL = (eclWsClt.selectedNode.ln_server_url)?.replace(/^http/, 'ws');
const firstSubStrIndex = (UpdatedLNServerURL.indexOf('//') + 2);
const WS_LINK = UpdatedLNServerURL.slice(0, firstSubStrIndex) + ':' + eclWsClt.selectedNode.ln_api_password + '@' + UpdatedLNServerURL.slice(firstSubStrIndex) + '/ws';
eclWsClt.webSocketClient = new WebSocket(WS_LINK);
eclWsClt.webSocketClient.onopen = () => {
this.logger.log({ selectedNode: eclWsClt.selectedNode, level: 'INFO', fileName: 'ECLWebSocket', msg: 'Connected to the Eclair\'s Websocket Server..' });
this.waitTime = 0.5;
this.heartbeat(eclWsClt);
};
eclWsClt.webSocketClient.onclose = (e) => {
if (eclWsClt && eclWsClt.selectedNode && eclWsClt.selectedNode.ln_implementation === 'ECL') {
this.logger.log({ selectedNode: eclWsClt.selectedNode, level: 'INFO', fileName: 'ECLWebSocket', msg: 'Web socket disconnected, will reconnect again...' });
eclWsClt.webSocketClient.close();
if (eclWsClt.reConnect) {
this.reconnet(eclWsClt);
}
}
};
eclWsClt.webSocketClient.onmessage = (msg) => {
this.logger.log({ selectedNode: eclWsClt.selectedNode, level: 'DEBUG', fileName: 'ECLWebSocket', msg: 'Received message from the server..', data: msg.data });
msg = (typeof msg.data === 'string') ? JSON.parse(msg.data) : msg.data;
if (msg.type && msg.type !== ECLWSEventsEnum.PAY_RELAYED && msg.type !== ECLWSEventsEnum.PAY_SETTLING_ONCHAIN && msg.type !== ECLWSEventsEnum.ONION_MESSAGE_RECEIVED) {
msg['source'] = 'ECL';
const msgStr = JSON.stringify(msg);
this.wsServer.sendEventsToAllLNClients(msgStr, eclWsClt.selectedNode);
}
};
eclWsClt.webSocketClient.onerror = (err) => {
if (eclWsClt.selectedNode.ln_version === '' || !eclWsClt.selectedNode.ln_version || this.common.isVersionCompatible(eclWsClt.selectedNode.ln, '0.5.0')) {
this.logger.log({ selectedNode: eclWsClt.selectedNode, level: 'ERROR', fileName: 'ECLWebSocket', msg: 'Web socket error', error: err });
const errStr = ((typeof err === 'object' && err.message) ? JSON.stringify({ error: err.message }) : (typeof err === 'object') ? JSON.stringify({ error: err }) : ('{ "error": ' + err + ' }'));
this.wsServer.sendErrorToAllLNClients(errStr, eclWsClt.selectedNode);
eclWsClt.webSocketClient.close();
if (eclWsClt.reConnect) {
this.reconnet(eclWsClt);
}
}
else {
eclWsClt.reConnect = false;
}
};
};
this.disconnect = (selectedNode) => {
const clientExists = this.webSocketClients.find((wsc) => wsc.selectedNode.index === selectedNode.index);
if (clientExists && clientExists.webSocketClient && clientExists.webSocketClient.readyState === WebSocket.OPEN) {
this.logger.log({ selectedNode: clientExists.selectedNode, level: 'INFO', fileName: 'ECLWebSocket', msg: 'Disconnecting from the Eclair\'s Websocket Server..' });
clientExists.reConnect = false;
clientExists.webSocketClient.close();
const clientIdx = this.webSocketClients.findIndex((wsc) => wsc.selectedNode.index === selectedNode.index);
this.webSocketClients.splice(clientIdx, 1);
}
};
this.updateSelectedNode = (newSelectedNode) => {
const clientIdx = this.webSocketClients.findIndex((wsc) => +wsc.selectedNode.index === +newSelectedNode.index);
let newClient = this.webSocketClients[clientIdx];
if (!newClient) {
newClient = { selectedNode: null, reConnect: true, webSocketClient: null };
}
newClient.selectedNode = JSON.parse(JSON.stringify(newSelectedNode));
this.webSocketClients[clientIdx] = newClient;
};
this.heartbeat = (eclWsClt) => {
this.logger.log({ selectedNode: eclWsClt.selectedNode, level: 'DEBUG', fileName: 'ECLWebSocket', msg: 'Websocket Server Heartbeat..' });
if (!eclWsClt.webSocketClient)
return;
if (eclWsClt.webSocketClient.readyState !== 1)
return;
eclWsClt.webSocketClient.ping();
setTimeout(() => {
this.heartbeat(eclWsClt);
}, 59 * 1000);
};
this.wsServer.eventEmitterECL.on('CONNECT', (nodeIndex) => {
this.connect(this.common.findNode(+nodeIndex));
});
this.wsServer.eventEmitterECL.on('DISCONNECT', (nodeIndex) => {
this.disconnect(this.common.findNode(+nodeIndex));
});
}
}
export const ECLWSClient = new ECLWebSocketClient();

@ -1,35 +0,0 @@
import request from 'request-promise';
import { Logger } from '../../utils/logger.js';
import { Common } from '../../utils/common.js';
let options = null;
const logger = Logger;
const common = Common;
export const getBlockchainBalance = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Balance', msg: 'Getting Balance..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/balance/blockchain';
options.qs = req.query;
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Balance', msg: 'Request params', data: req.params });
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Balance', msg: 'Request Query', data: req.query });
request(options).then((body) => {
if (body) {
if (!body.total_balance) {
body.total_balance = 0;
}
if (!body.confirmed_balance) {
body.confirmed_balance = 0;
}
if (!body.unconfirmed_balance) {
body.unconfirmed_balance = 0;
}
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Balance', msg: 'Balance Received', data: body });
res.status(200).json(body);
}
}).catch((errRes) => {
const err = common.handleError(errRes, 'Balance', 'Get Balance Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};

@ -1,267 +0,0 @@
import request from 'request-promise';
import { Logger } from '../../utils/logger.js';
import { Common } from '../../utils/common.js';
let options = null;
const logger = Logger;
const common = Common;
export const getAliasForChannel = (selNode, channel) => {
const pubkey = (channel.remote_pubkey) ? channel.remote_pubkey : (channel.remote_node_pub) ? channel.remote_node_pub : '';
options.url = selNode.ln_server_url + '/v1/graph/node/' + pubkey;
return request(options).then((aliasBody) => {
logger.log({ selectedNode: selNode, level: 'DEBUG', fileName: 'Channels', msg: 'Alias Received', data: aliasBody.node.alias });
channel.remote_alias = aliasBody.node.alias && aliasBody.node.alias !== '' ? aliasBody.node.alias : aliasBody.node.pub_key.slice(0, 20);
return channel;
}).catch((err) => {
channel.remote_alias = pubkey.slice(0, 20);
return channel;
});
};
export const getAllChannels = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Getting Channels..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/channels';
options.qs = req.query;
let local = 0;
let remote = 0;
let total = 0;
request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Channels List Received', data: body });
if (body.channels) {
return Promise.all(body.channels?.map((channel) => {
local = (channel.local_balance) ? +channel.local_balance : 0;
remote = (channel.remote_balance) ? +channel.remote_balance : 0;
total = local + remote;
channel.balancedness = (total === 0) ? 1 : (1 - Math.abs((local - remote) / total)).toFixed(3);
return getAliasForChannel(req.session.selectedNode, channel);
})).then((values) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Sorted Channels List Received', data: body });
return res.status(200).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Channels', 'Get All Channel Aliases Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
}
else {
body.channels = [];
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Empty Channels List Received' });
return res.status(200).json(body);
}
}).catch((errRes) => {
const err = common.handleError(errRes, 'Channels', 'List Channels Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const getPendingChannels = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Getting Pending Channels..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/channels/pending';
options.qs = req.query;
request(options).then((body) => {
if (!body.total_limbo_balance) {
body.total_limbo_balance = 0;
}
const promises = [];
if (body.pending_open_channels && body.pending_open_channels.length > 0) {
body.pending_open_channels?.map((channel) => promises.push(getAliasForChannel(req.session.selectedNode, channel.channel)));
}
if (body.pending_force_closing_channels && body.pending_force_closing_channels.length > 0) {
body.pending_force_closing_channels?.map((channel) => promises.push(getAliasForChannel(req.session.selectedNode, channel.channel)));
}
if (body.pending_closing_channels && body.pending_closing_channels.length > 0) {
body.pending_closing_channels?.map((channel) => promises.push(getAliasForChannel(req.session.selectedNode, channel.channel)));
}
if (body.waiting_close_channels && body.waiting_close_channels.length > 0) {
body.waiting_close_channels?.map((channel) => promises.push(getAliasForChannel(req.session.selectedNode, channel.channel)));
}
return Promise.all(promises).then((values) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Pending Channels List Received', data: body });
return res.status(200).json(body);
}).
catch((errRes) => {
const err = common.handleError(errRes, 'Channels', 'Get Pending Channel Aliases Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
}).catch((errRes) => {
const err = common.handleError(errRes, 'Channels', 'List Pending Channels Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const getClosedChannels = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Getting Closed Channels..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/channels/closed';
options.qs = req.query;
request(options).then((body) => {
if (body.channels && body.channels.length > 0) {
return Promise.all(body.channels?.map((channel) => {
channel.close_type = (!channel.close_type) ? 'COOPERATIVE_CLOSE' : channel.close_type;
return getAliasForChannel(req.session.selectedNode, channel);
})).then((values) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Closed Channels List Received', data: body });
return res.status(200).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Channels', 'Get Closed Channel Aliases Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
}
else {
body.channels = [];
return res.status(200).json(body);
}
}).catch((errRes) => {
const err = common.handleError(errRes, 'Channels', 'List Closed Channels Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const postChannel = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Opening Channel..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/channels';
options.form = {
node_pubkey_string: req.body.node_pubkey,
local_funding_amount: req.body.local_funding_amount,
private: req.body.private,
spend_unconfirmed: req.body.spend_unconfirmed
};
if (req.body.trans_type === '1') {
options.form.target_conf = req.body.trans_type_value;
}
else if (req.body.trans_type === '2') {
options.form.sat_per_byte = req.body.trans_type_value;
}
options.form = JSON.stringify(options.form);
request.post(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Channel Opened', data: body });
res.status(201).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Channels', 'Open Channel Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const postTransactions = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Sending Payment..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/channels/transaction-stream';
options.form = { payment_request: req.body.paymentReq };
if (req.body.paymentAmount) {
options.form.amt = req.body.paymentAmount;
}
if (req.body.feeLimit) {
options.form.fee_limit = req.body.feeLimit;
}
if (req.body.outgoingChannel) {
options.form.outgoing_chan_id = req.body.outgoingChannel;
}
if (req.body.allowSelfPayment) {
options.form.allow_self_payment = req.body.allowSelfPayment;
}
if (req.body.lastHopPubkey) {
options.form.last_hop_pubkey = Buffer.from(req.body.lastHopPubkey, 'hex').toString('base64');
}
options.form = JSON.stringify(options.form);
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Send Payment Options', data: options.form });
request.post(options).then((body) => {
if (body.payment_error) {
const err = common.handleError(body.payment_error, 'Channels', 'Send Payment Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
else {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Payment Sent', data: body });
res.status(201).json(body);
}
}).catch((errRes) => {
const err = common.handleError(errRes, 'Channels', 'Send Payment Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const closeChannel = (req, res, next) => {
try {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Closing Channel..' });
if (!req.session.selectedNode) {
const err = common.handleError({ message: 'Session Expired after a day\'s inactivity.', statusCode: 401 }, 'Session Expired', 'Session Expiry Error', null);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
const channelpoint = req.params.channelPoint?.replace(':', '/');
options.url = req.session.selectedNode.ln_server_url + '/v1/channels/' + channelpoint + '?force=' + req.query.force;
if (req.query.target_conf) {
options.url = options.url + '&target_conf=' + req.query.target_conf;
}
if (req.query.sat_per_byte) {
options.url = options.url + '&sat_per_byte=' + req.query.sat_per_byte;
}
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Closing Channel Options URL', data: options.url });
request.delete(options);
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Channel Close Requested' });
res.status(202).json({ message: 'Close channel request has been submitted.' });
}
catch (error) {
logger.log({ selectedNode: req.session.selectedNode, level: 'ERROR', fileName: 'Channels', msg: 'Close Channel Error', error: error.message });
return res.status(500).json({ message: 'Close Channel Error', error: error.message });
}
};
export const postChanPolicy = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Updating Channel Policy..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/chanpolicy';
if (req.body.chanPoint === 'all') {
options.form = JSON.stringify({
global: true,
base_fee_msat: req.body.baseFeeMsat,
fee_rate: parseFloat((req.body.feeRate / 1000000).toString()),
time_lock_delta: parseInt(req.body.timeLockDelta)
});
}
else {
const breakPoint = req.body.chanPoint.indexOf(':');
const txid_str = req.body.chanPoint.substring(0, breakPoint);
const output_idx = req.body.chanPoint.substring(breakPoint + 1, req.body.chanPoint.length);
const optionsBody = {
base_fee_msat: req.body.baseFeeMsat,
fee_rate: parseFloat((req.body.feeRate / 1000000).toString()),
time_lock_delta: parseInt(req.body.timeLockDelta),
chan_point: { funding_txid_str: txid_str, output_index: parseInt(output_idx) }
};
if (req.body.max_htlc_msat) {
optionsBody['max_htlc_msat'] = req.body.max_htlc_msat;
}
if (req.body.min_htlc_msat) {
optionsBody['min_htlc_msat'] = req.body.min_htlc_msat;
optionsBody['min_htlc_msat_specified'] = true;
}
options.form = JSON.stringify(optionsBody);
}
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Update Channel Policy Options', data: options.form });
request.post(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Channel Policy Updated', data: body });
if (body.failed_updates && body.failed_updates.length && body.failed_updates[0].update_error) {
const err = common.handleError({ error: body.failed_updates[0].update_error }, 'Channels', 'Update Channel Policy Error', req.session.selectedNode);
return res.status(500).json({ message: err.message, error: err.error });
}
res.status(201).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Channels', 'Update Channel Policy Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};

@ -1,239 +0,0 @@
import * as fs from 'fs';
import { sep } from 'path';
import request from 'request-promise';
import { Logger } from '../../utils/logger.js';
import { Common } from '../../utils/common.js';
let options = null;
const logger = Logger;
const common = Common;
function getFilesList(channelBackupPath, callback) {
const files_list = [];
let all_restore_exists = false;
let response = { all_restore_exists: false, files: [] } || { message: '', error: {}, statusCode: 500 };
fs.readdir(channelBackupPath + sep + 'restore', (err, files) => {
if (err && err.code !== 'ENOENT' && err.errno !== -4058) {
response = { message: 'Channels Restore List Failed!', error: err, statusCode: 500 };
}
if (files && files.length > 0) {
files.forEach((file) => {
if (!file.includes('.restored')) {
if (file.toLowerCase() === 'channel-all.bak' || file.toLowerCase() === 'backup-channel-all.bak') {
all_restore_exists = true;
}
else {
files_list.push({ channel_point: file.substring(8, file.length - 4)?.replace('-', ':') });
}
}
});
}
response = { all_restore_exists: all_restore_exists, files: files_list };
callback(response);
});
}
export const getBackup = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'ChannelBackup', msg: 'Getting Channel Backup..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
let channel_backup_file = '';
let message = '';
if (req.params.channelPoint === 'ALL') {
channel_backup_file = req.session.selectedNode.channel_backup_path + sep + 'channel-all.bak';
message = 'All Channels Backup Successful.';
options.url = req.session.selectedNode.ln_server_url + '/v1/channels/backup';
}
else {
channel_backup_file = req.session.selectedNode.channel_backup_path + sep + 'channel-' + req.params.channelPoint?.replace(':', '-') + '.bak';
message = 'Channel Backup Successful.';
const channelpoint = req.params.channelPoint?.replace(':', '/');
options.url = req.session.selectedNode.ln_server_url + '/v1/channels/backup/' + channelpoint;
const exists = fs.existsSync(channel_backup_file);
if (exists) {
fs.writeFile(channel_backup_file, '', () => { });
}
else {
try {
const createStream = fs.createWriteStream(channel_backup_file);
createStream.end();
}
catch (errRes) {
const err = common.handleError(errRes, 'ChannelsBackup', 'Backup Channels Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
}
}
request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'ChannelsBackup', msg: 'Channel Backup Received', data: body });
fs.writeFile(channel_backup_file, JSON.stringify(body), (errRes) => {
if (errRes) {
const err = common.handleError(errRes, 'ChannelsBackup', 'Backup Channels Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
else {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'ChannelBackup', msg: 'Channel Backed up and Saved', data: body });
res.status(200).json({ message: message });
}
});
}).catch((errRes) => {
const err = common.handleError(errRes, 'ChannelsBackup', 'Backup Channels Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const postBackupVerify = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'ChannelBackup', msg: 'Verifying Channel Backup..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/channels/backup/verify';
let channel_verify_file = '';
let message = '';
let verify_backup = '';
if (req.params.channelPoint === 'ALL') {
message = 'All Channels Verify Successful.';
channel_verify_file = req.session.selectedNode.channel_backup_path + sep + 'channel-all.bak';
const exists = fs.existsSync(channel_verify_file);
if (exists) {
verify_backup = fs.readFileSync(channel_verify_file, 'utf-8');
if (verify_backup !== '') {
const verify_backup_json = JSON.parse(verify_backup);
delete verify_backup_json.single_chan_backups;
options.form = JSON.stringify(verify_backup_json);
}
else {
const errMsg = 'Channel backup to verify does not Exist.';
const err = common.handleError({ statusCode: 404, message: 'Verify Channel Error', error: errMsg }, 'ChannelBackup', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
}
else {
verify_backup = '';
const errMsg = 'Channel backup to verify does not Exist.';
const err = common.handleError({ statusCode: 404, message: 'Verify Channel Error', error: errMsg }, 'ChannelBackup', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
}
else {
message = 'Channel Verify Successful.';
channel_verify_file = req.session.selectedNode.channel_backup_path + sep + 'channel-' + req.params.channelPoint?.replace(':', '-') + '.bak';
const exists = fs.existsSync(channel_verify_file);
if (exists) {
verify_backup = fs.readFileSync(channel_verify_file, 'utf-8');
options.form = JSON.stringify({ single_chan_backups: { chan_backups: [JSON.parse(verify_backup)] } });
}
else {
verify_backup = '';
const errMsg = 'Channel backup to verify does not Exist.';
const err = common.handleError({ statusCode: 404, message: 'Verify Channel Error', error: errMsg }, 'ChannelBackup', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
}
if (verify_backup !== '') {
request.post(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'ChannelBackup', msg: 'Channel Backup Verified', data: body });
res.status(201).json({ message: message });
}).
catch((errRes) => {
const err = common.handleError(errRes, 'ChannelsBackup', 'Verify Channels Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
}
};
export const postRestore = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'ChannelBackup', msg: 'Restoring Channel Backup..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/channels/backup/restore';
let channel_restore_file = '';
let message = '';
let restore_backup = '';
if (req.params.channelPoint === 'ALL') {
message = 'All Channels Restore Successful.';
channel_restore_file = req.session.selectedNode.channel_backup_path + sep + 'restore' + sep;
const exists = fs.existsSync(channel_restore_file + 'channel-all.bak');
const downloaded_exists = fs.existsSync(channel_restore_file + 'backup-channel-all.bak');
if (exists) {
restore_backup = fs.readFileSync(channel_restore_file + 'channel-all.bak', 'utf-8');
if (restore_backup !== '') {
const restore_backup_json = JSON.parse(restore_backup);
options.form = JSON.stringify({ multi_chan_backup: restore_backup_json.multi_chan_backup.multi_chan_backup });
}
else {
const errMsg = 'Channel backup to restore does not Exist.';
const err = common.handleError({ statusCode: 404, message: 'Restore Channel Error', error: errMsg }, 'ChannelBackup', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
}
else if (downloaded_exists) {
restore_backup = fs.readFileSync(channel_restore_file + 'backup-channel-all.bak', 'utf-8');
if (restore_backup !== '') {
const restore_backup_json = JSON.parse(restore_backup);
options.form = JSON.stringify({ multi_chan_backup: restore_backup_json.multi_chan_backup.multi_chan_backup });
}
else {
const errMsg = 'Channel backup to restore does not Exist.';
const err = common.handleError({ statusCode: 404, message: 'Restore Channel Error', error: errMsg }, 'ChannelBackup', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
}
else {
restore_backup = '';
const errMsg = 'Channel backup to restore does not Exist.';
const err = common.handleError({ statusCode: 404, message: 'Restore Channel Error', error: errMsg }, 'ChannelBackup', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
}
else {
message = 'Channel Restore Successful.';
channel_restore_file = req.session.selectedNode.channel_backup_path + sep + 'restore' + sep + 'channel-' + req.params.channelPoint?.replace(':', '-') + '.bak';
const exists = fs.existsSync(channel_restore_file);
if (exists) {
restore_backup = fs.readFileSync(channel_restore_file, 'utf-8');
options.form = JSON.stringify({ chan_backups: { chan_backups: [JSON.parse(restore_backup)] } });
}
else {
restore_backup = '';
const errMsg = 'Channel backup to restore does not Exist.';
const err = common.handleError({ statusCode: 404, message: 'Restore Channel Error', error: errMsg }, 'ChannelBackup', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
}
if (restore_backup !== '') {
request.post(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'ChannelBackup', msg: 'Channel Restored', data: body });
if (req.params.channelPoint === 'ALL') {
channel_restore_file = channel_restore_file + 'channel-all.bak';
}
fs.rename(channel_restore_file, channel_restore_file + '.restored', () => {
getFilesList(req.session.selectedNode.channel_backup_path, (getFilesListRes) => {
if (getFilesListRes.error) {
const errMsg = getFilesListRes.error;
const err = common.handleError({ statusCode: 500, message: 'Restore Channel Error', error: errMsg }, 'ChannelBackup', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.error, list: getFilesListRes });
}
else {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'ChannelBackup', msg: 'Channel Restored and Saved' });
return res.status(201).json({ message: message, list: getFilesListRes });
}
});
});
}).
catch((errRes) => {
const err = common.handleError(errRes, 'ChannelsBackup', 'Restore Channel Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
}
};
export const getRestoreList = (req, res, next) => {
getFilesList(req.session.selectedNode.channel_backup_path, (getFilesListRes) => {
if (getFilesListRes.error) {
return res.status(getFilesListRes.statusCode).json(getFilesListRes);
}
else {
return res.status(200).json(getFilesListRes);
}
});
};

@ -1,48 +0,0 @@
import request from 'request-promise';
import { Logger } from '../../utils/logger.js';
import { Common } from '../../utils/common.js';
import { getAllForwardingEvents } from './switch.js';
let options = null;
const logger = Logger;
const common = Common;
export const getFees = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Fees', msg: 'Getting Fees..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/fees';
request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Fees', msg: 'Fee Received', data: body });
const today = new Date(Date.now());
const start_date = new Date(today.getFullYear(), today.getMonth(), 1, 0, 0, 0);
const current_time = (Math.round(today.getTime() / 1000));
const month_start_time = (Math.round(start_date.getTime() / 1000));
const week_start_time = current_time - 604800;
const day_start_time = current_time - 86400;
return getAllForwardingEvents(req, month_start_time, current_time, 0, 'fees', (history) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Fees', msg: 'Forwarding History Received', data: history });
const daily_sum = history.forwarding_events?.reduce((acc, curr) => ((curr.timestamp >= day_start_time) ? [(acc[0] + 1), (acc[1] + +curr.fee_msat)] : acc), [0, 0]);
const weekly_sum = history.forwarding_events?.reduce((acc, curr) => ((curr.timestamp >= week_start_time) ? [(acc[0] + 1), (acc[1] + +curr.fee_msat)] : acc), [0, 0]);
const monthly_sum = history.forwarding_events?.reduce((acc, curr) => [(acc[0] + 1), (acc[1] + +curr.fee_msat)], [0, 0]);
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Fees', msg: 'Daily Sum (Transactions, Fee)', data: daily_sum });
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Fees', msg: 'Weekly Sum (Transactions, Fee)', data: weekly_sum });
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Fees', msg: 'Monthly Sum (Transactions, Fee)', data: monthly_sum });
body.daily_tx_count = daily_sum[0];
body.weekly_tx_count = weekly_sum[0];
body.monthly_tx_count = monthly_sum[0];
body.day_fee_sum = (daily_sum[1] / 1000).toFixed(2);
body.week_fee_sum = (weekly_sum[1] / 1000).toFixed(2);
body.month_fee_sum = (monthly_sum[1] / 1000).toFixed(2);
body.forwarding_events_history = history;
if (history.error) {
logger.log({ selectedNode: req.session.selectedNode, level: 'ERROR', fileName: 'Fees', msg: 'Fetch Forwarding Events Error', error: history.error });
}
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Fees', msg: 'Fees Received', data: body });
res.status(200).json(body);
});
}).catch((errRes) => {
const err = common.handleError(errRes, 'Fees', 'Get Forwarding Events Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};

@ -1,56 +0,0 @@
import request from 'request-promise';
import { Database } from '../../utils/database.js';
import { Logger } from '../../utils/logger.js';
import { Common } from '../../utils/common.js';
import { LNDWSClient } from './webSocketClient.js';
let options = null;
const logger = Logger;
const common = Common;
const lndWsClient = LNDWSClient;
const databaseService = Database;
export const getInfo = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Getting LND Node Information..' });
common.logEnvVariables(req);
common.setOptions(req);
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/getinfo';
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Selected Node ' + req.session.selectedNode.ln_node });
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Calling Info from LND server url ' + options.url });
if (!options.headers || !options.headers['Grpc-Metadata-macaroon']) {
const errMsg = 'LND Get info failed due to bad or missing macaroon! Please check RTL-Config.json to verify the setup!';
const err = common.handleError({ statusCode: 502, message: 'Bad or Missing Macaroon', error: errMsg }, 'GetInfo', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
else {
common.nodes?.map((node) => {
if (node.ln_implementation === 'LND') {
common.getAllNodeAllChannelBackup(node);
}
return node;
});
return request(options).then((body) => {
const body_str = (!body) ? '' : JSON.stringify(body);
const search_idx = (!body) ? -1 : body_str.search('Not Found');
if (!body || search_idx > -1 || body.error) {
if (body && !body.error) {
body.error = 'Error From Server!';
}
const err = common.handleError(body, 'GetInfo', 'Get Info Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
else {
req.session.selectedNode.ln_version = body.version.split('-')[0] || '';
lndWsClient.updateSelectedNode(req.session.selectedNode);
databaseService.loadDatabase(req.session);
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Node Information Received', data: body });
return res.status(200).json(body);
}
}).catch((errRes) => {
const err = common.handleError(errRes, 'GetInfo', 'Get Info Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
}
};

@ -1,164 +0,0 @@
import request from 'request-promise';
import { Logger } from '../../utils/logger.js';
import { Common } from '../../utils/common.js';
let options = null;
const logger = Logger;
const common = Common;
export const getAliasFromPubkey = (selNode, pubkey) => {
options.url = selNode.ln_server_url + '/v1/graph/node/' + pubkey;
return request(options).then((res) => {
logger.log({ selectedNode: selNode, level: 'DEBUG', fileName: 'Graph', msg: 'Alias Received', data: res.node.alias });
return res.node.alias;
}).
catch((err) => pubkey.substring(0, 20));
};
export const getDescribeGraph = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Getting Network Graph..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/graph';
request.get(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Network Graph Received', data: body });
res.status(200).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Graph', 'Describe Graph Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const getGraphInfo = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Getting Graph Information..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/graph/info';
request.get(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Graph Information Received', data: body });
res.status(200).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Graph', 'Graph Information Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const getGraphNode = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Getting Graph Node Information..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/graph/node/' + req.params.pubKey;
request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Graph Node Information Received', data: body });
res.status(200).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Graph', 'Get Node Info Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const getGraphEdge = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Getting Graph Edge Information..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/graph/edge/' + req.params.chanid;
request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Graph Edge Information Received', data: body });
res.status(200).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Graph', 'Get Edge Info Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const getQueryRoutes = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Getting Graph Routes..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/graph/routes/' + req.params.destPubkey + '/' + req.params.amount;
if (req.query.outgoing_chan_id) {
options.url = options.url + '?outgoing_chan_id=' + req.query.outgoing_chan_id;
}
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Graph', msg: 'Query Routes URL', data: options.url });
request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Graph', msg: 'Query Routes Received', data: body });
if (body.routes && body.routes.length && body.routes.length > 0 && body.routes[0].hops && body.routes[0].hops.length && body.routes[0].hops.length > 0) {
return Promise.all(body.routes[0].hops?.map((hop) => getAliasFromPubkey(req.session.selectedNode, hop.pub_key))).
then((values) => {
body.routes[0].hops?.map((hop, i) => {
hop.hop_sequence = i + 1;
hop.pubkey_alias = values[i];
return hop;
});
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Graph Routes with Alias Received', data: body });
res.status(200).json(body);
}).
catch((errRes) => {
const err = common.handleError(errRes, 'Graph', 'Get Query Routes Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
}
else {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Graph Routes Received', data: body });
return res.status(200).json(body);
}
}).catch((errRes) => {
const err = common.handleError(errRes, 'Graph', 'Get Query Routes Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const getRemoteFeePolicy = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Getting Remote Fee Policy..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/graph/edge/' + req.params.chanid;
request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Graph', msg: 'Edge Info Received', data: body });
let remoteNodeFee = {};
if (body.node1_pub === req.params.localPubkey) {
remoteNodeFee = {
time_lock_delta: body.node2_policy.time_lock_delta,
fee_base_msat: body.node2_policy.fee_base_msat,
fee_rate_milli_msat: body.node2_policy.fee_rate_milli_msat
};
}
else if (body.node2_pub === req.params.localPubkey) {
remoteNodeFee = {
time_lock_delta: body.node1_policy.time_lock_delta,
fee_base_msat: body.node1_policy.fee_base_msat,
fee_rate_milli_msat: body.node1_policy.fee_rate_milli_msat
};
}
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Remote Fee Policy Received', data: remoteNodeFee });
res.status(200).json(remoteNodeFee);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Graph', 'Remote Fee Policy Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const getAliasesForPubkeys = (req, res, next) => {
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
if (req.query.pubkeys) {
const pubkeyArr = req.query.pubkeys.split(',');
return Promise.all(pubkeyArr?.map((pubkey) => getAliasFromPubkey(req.session.selectedNode, pubkey))).
then((values) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Node Alias', data: values });
res.status(200).json(values);
}).
catch((errRes) => {
const err = common.handleError(errRes, 'Graph', 'Get Aliases for Pubkeys Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
}
else {
return res.status(200).json([]);
}
};

@ -1,84 +0,0 @@
import request from 'request-promise';
import { Logger } from '../../utils/logger.js';
import { Common } from '../../utils/common.js';
import { LNDWSClient } from './webSocketClient.js';
let options = null;
const logger = Logger;
const common = Common;
const lndWsClient = LNDWSClient;
export const invoiceLookup = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoice', msg: 'Getting Invoice Information..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v2/invoices/lookup';
if (req.query.payment_addr) {
options.url = options.url + '?payment_addr=' + req.query.payment_addr;
}
else {
options.url = options.url + '?payment_hash=' + req.query.payment_hash;
}
request(options).then((body) => {
body.r_preimage = body.r_preimage ? Buffer.from(body.r_preimage, 'base64').toString('hex') : '';
body.r_hash = body.r_hash ? Buffer.from(body.r_hash, 'base64').toString('hex') : '';
body.description_hash = body.description_hash ? Buffer.from(body.description_hash, 'base64').toString('hex') : null;
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoice', msg: 'Invoice Information Received', data: body });
res.status(200).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Invoices', 'Invoice Lookup Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const listInvoices = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoice', msg: 'Getting List Invoices..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/invoices?num_max_invoices=' + req.query.num_max_invoices + '&index_offset=' + req.query.index_offset +
'&reversed=' + req.query.reversed;
request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Invoice', msg: 'Invoices List Received', data: body });
if (body.invoices && body.invoices.length > 0) {
body.invoices.forEach((invoice) => {
invoice.r_preimage = invoice.r_preimage ? Buffer.from(invoice.r_preimage, 'base64').toString('hex') : '';
invoice.r_hash = invoice.r_hash ? Buffer.from(invoice.r_hash, 'base64').toString('hex') : '';
invoice.description_hash = invoice.description_hash ? Buffer.from(invoice.description_hash, 'base64').toString('hex') : null;
});
}
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoice', msg: 'Sorted Invoices List Received', data: body });
res.status(200).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Invoices', 'List Invoices Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const addInvoice = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoice', msg: 'Adding Invoice..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/invoices';
options.form = JSON.stringify(req.body);
request.post(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoice', msg: 'Invoice Added', data: body });
try {
if (body.r_hash) {
lndWsClient.subscribeToInvoice(options, req.session.selectedNode, body.r_hash);
}
}
catch (errRes) {
const err = common.handleError(errRes, 'Invoices', 'Subscribe to Newly Added Invoice Error', req.session.selectedNode);
logger.log({ selectedNode: req.session.selectedNode, level: 'ERROR', fileName: 'Invoice', msg: 'Subscribe to Newly Added Invoice Error', error: err });
}
body.r_preimage = body.r_preimage ? Buffer.from(body.r_preimage, 'base64').toString('hex') : '';
body.r_hash = body.r_hash ? Buffer.from(body.r_hash, 'base64').toString('hex') : '';
body.description_hash = body.description_hash ? Buffer.from(body.description_hash, 'base64').toString('hex') : null;
res.status(201).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Invoices', 'Add Invoice Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};

@ -1,43 +0,0 @@
import request from 'request-promise';
import { Logger } from '../../utils/logger.js';
import { Common } from '../../utils/common.js';
let options = null;
const logger = Logger;
const common = Common;
export const signMessage = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Message', msg: 'Signing Message..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/signmessage';
options.form = JSON.stringify({
msg: Buffer.from(req.body.message).toString('base64')
});
request.post(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Message', msg: 'Message Signed', data: body });
res.status(201).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Messages', 'Sign Message Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const verifyMessage = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Message', msg: 'Verifying Message..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/verifymessage';
options.form = JSON.stringify({
msg: Buffer.from(req.body.message).toString('base64'),
signature: req.body.signature
});
request.post(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Message', msg: 'Message Verified', data: body });
res.status(201).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Messages', 'Verify Message Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};

@ -1,21 +0,0 @@
import request from 'request-promise';
import { Logger } from '../../utils/logger.js';
import { Common } from '../../utils/common.js';
let options = null;
const logger = Logger;
const common = Common;
export const getNewAddress = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'NewAddress', msg: 'Getting New Address..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/newaddress?type=' + req.query.type;
request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'NewAddress', msg: 'New Address Generated', data: body });
res.status(200).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'NewAddress', 'New Address Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};

@ -1,98 +0,0 @@
import request from 'request-promise';
import { Logger } from '../../utils/logger.js';
import { Common } from '../../utils/common.js';
let options = null;
const logger = Logger;
const common = Common;
export const decodePaymentFromPaymentRequest = (selNode, payment) => {
options.url = selNode.ln_server_url + '/v1/payreq/' + payment;
return request(options).then((res) => {
logger.log({ selectedNode: selNode, level: 'DEBUG', fileName: 'PayReq', msg: 'Description Received', data: res.description });
return res;
}).catch((err) => { });
};
export const decodePayment = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'PayRequest', msg: 'Decoding Payment..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/payreq/' + req.params.payRequest;
request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'PayRequest', msg: 'Payment Decoded', data: body });
res.status(200).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'PayRequest', 'Decode Payment Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const decodePayments = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'PayRequest', msg: 'Decoding Payments List..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
if (req.body.payments) {
const paymentsArr = req.body.payments.split(',');
return Promise.all(paymentsArr?.map((payment) => decodePaymentFromPaymentRequest(req.session.selectedNode, payment))).
then((values) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'PayRequest', msg: 'Payment List Decoded', data: values });
res.status(200).json(values);
}).
catch((errRes) => {
const err = common.handleError(errRes, 'PayRequest', 'Decode Payments Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
}
else {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'PayRequest', msg: 'Empty Payment List Decoded' });
return res.status(200).json([]);
}
};
export const getPayments = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Getting Payments List..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/payments?max_payments=' + req.query.max_payments + '&index_offset=' + req.query.index_offset + '&reversed=' + req.query.reversed;
request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Payments', msg: 'Payment List Received', data: body });
res.status(200).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Payments', 'List Payments Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const getAllLightningTransactions = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Getting All Lightning Transactions..' });
const options1 = JSON.parse(JSON.stringify(common.getOptions(req)));
const options2 = JSON.parse(JSON.stringify(common.getOptions(req)));
// options1.url = req.session.selectedNode.ln_server_url + '/v1/payments?max_payments=100000&index_offset=0&reversed=true';
options2.url = req.session.selectedNode.ln_server_url + '/v1/invoices?num_max_invoices=100000&index_offset=0&reversed=true';
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Payments', msg: 'All Payments Options', data: options1 });
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Payments', msg: 'All Invoices Options', data: options2 });
// return Promise.all([request(options1), request(options2)]).then((values) => {
return Promise.all([{ payments: [] }, request(options2)]).then((values) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'All Lightning Transactions Received', data: ({ totalPayments: values[0].payments.length || 0, totalInvoices: values[1].invoices.length || 0 }) });
res.status(200).json({ listPaymentsAll: values[0], listInvoicesAll: values[1] });
}).catch((errRes) => {
const err = common.handleError(errRes, 'Payments', 'All Lightning Transactions Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const paymentLookup = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Looking up Payment..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v2/router/track/' + req.params.paymentHash;
request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Payment Information Received for ' + req.params.paymentHash, data: body });
res.status(200).json(body.result || body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Payments', 'Payment Lookup Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};

@ -1,87 +0,0 @@
import request from 'request-promise';
import { Logger } from '../../utils/logger.js';
import { Common } from '../../utils/common.js';
let options = null;
const logger = Logger;
const common = Common;
export const getAliasForPeers = (selNode, peer) => {
options.url = selNode.ln_server_url + '/v1/graph/node/' + peer.pub_key;
return request(options).then((aliasBody) => {
logger.log({ selectedNode: selNode, level: 'DEBUG', fileName: 'Peers', msg: 'Alias Received', data: aliasBody.node.alias });
peer.alias = aliasBody.node.alias;
return aliasBody.node.alias;
}).catch((err) => {
peer.alias = peer.pub_key.slice(0, 20);
return peer.pub_key;
});
};
export const getPeers = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Getting Peers..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/peers';
request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Peers', msg: 'Peers List Received', data: body });
const peers = !body.peers ? [] : body.peers;
return Promise.all(peers?.map((peer) => getAliasForPeers(req.session.selectedNode, peer))).then((values) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Sorted Peers List Received', data: body.peers });
res.status(200).json(body.peers);
});
}).catch((errRes) => {
const err = common.handleError(errRes, 'Peers', 'List Peers Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const postPeer = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Connecting Peer..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/peers';
options.form = JSON.stringify({
addr: { host: req.body.host, pubkey: req.body.pubkey },
perm: req.body.perm
});
request.post(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Peers', msg: 'Peer Connected', data: body });
options.url = req.session.selectedNode.ln_server_url + '/v1/peers';
request(options).then((body) => {
const peers = (!body.peers) ? [] : body.peers;
return Promise.all(peers?.map((peer) => getAliasForPeers(req.session.selectedNode, peer))).then((values) => {
if (body.peers) {
body.peers = common.newestOnTop(body.peers, 'pub_key', req.body.pubkey);
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Peers List after Connect Received', data: body });
}
res.status(201).json(body.peers);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Peers', 'Connect Peer Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
}).catch((errRes) => {
const err = common.handleError(errRes, 'Peers', 'Connect Peer Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
}).catch((errRes) => {
const err = common.handleError(errRes, 'Peers', 'Connect Peer Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const deletePeer = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Disconnecting Peer..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/peers/' + req.params.peerPubKey;
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Peer Disconnect Pubkey', data: req.params.peerPubKey });
request.delete(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Peer Disconneted', data: body });
res.status(204).json({});
}).catch((errRes) => {
const err = common.handleError(errRes, 'Peers', 'Disconnect Peer Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};

@ -1,58 +0,0 @@
import request from 'request-promise';
import { Logger } from '../../utils/logger.js';
import { Common } from '../../utils/common.js';
let options = null;
const logger = Logger;
const common = Common;
const responseData = { switch: { forwarding_events: [], last_offset_index: 0 }, fees: { forwarding_events: [], last_offset_index: 0 } };
const num_max_events = 100;
export const forwardingHistory = (req, res, next) => {
getAllForwardingEvents(req, req.body.start_time, req.body.end_time, 0, 'switch', (eventsResponse) => {
if (eventsResponse.error) {
res.status(eventsResponse.error.statusCode).json(eventsResponse);
}
else {
res.status(201).json(eventsResponse);
}
});
};
export const getAllForwardingEvents = (req, start, end, offset, caller, callback) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Switch', msg: 'Getting Forwarding Events..' });
if (offset === 0) {
responseData[caller] = { forwarding_events: [], last_offset_index: 0 };
}
if (!req.session.selectedNode) {
const err = common.handleError({ message: 'Session Expired after a day\'s inactivity.', statusCode: 401 }, 'Balance', 'Get Balance Error', req.session.selectedNode);
return callback({ message: err.message, error: err.error, statusCode: err.statusCode });
}
options = common.getOptions(req);
options.url = req.session.selectedNode.ln_server_url + '/v1/switch';
options.form = {};
if (start) {
options.form.start_time = start;
}
if (end) {
options.form.end_time = end;
}
options.form.num_max_events = num_max_events;
options.form.index_offset = offset;
options.form = JSON.stringify(options.form);
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Switch', msg: 'Forwarding Events Params', data: options.form });
return request.post(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Switch', msg: 'Forwarding Events Received', data: body });
if (body.forwarding_events) {
responseData[caller].forwarding_events.push(...body.forwarding_events);
responseData[caller].last_offset_index = body.last_offset_index ? body.last_offset_index : 0;
}
if (!body.last_offset_index || body.last_offset_index < offset + num_max_events) {
responseData[caller].last_offset_index = body.last_offset_index ? body.last_offset_index : 0;
return callback(responseData[caller]);
}
else {
return getAllForwardingEvents(req, start, end, offset + num_max_events, caller, callback);
}
}).catch((errRes) => {
const err = common.handleError(errRes, 'Switch', 'Get All Forwarding Events Error', req.session.selectedNode);
return callback({ message: err.message, error: err.error, statusCode: err.statusCode });
});
};

@ -1,46 +0,0 @@
import request from 'request-promise';
import { Logger } from '../../utils/logger.js';
import { Common } from '../../utils/common.js';
let options = null;
const logger = Logger;
const common = Common;
export const getTransactions = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Transactions', msg: 'Getting Transactions..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/transactions';
request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Transactions', msg: 'Transactions List Received', data: body });
res.status(200).json(body.transactions);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Transactions', 'List Transactions Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const postTransactions = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Transactions', msg: 'Sending Transaction..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/transactions';
options.form = {
amount: req.body.amount,
addr: req.body.address,
sat_per_byte: req.body.fees,
target_conf: req.body.blocks
};
if (req.body.sendAll) {
options.form.send_all = req.body.sendAll;
}
options.form = JSON.stringify(options.form);
request.post(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Transactions', msg: 'Transaction Sent', data: body });
res.status(201).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Transactions', 'Send Transaction Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};

@ -1,213 +0,0 @@
import atob from 'atob';
import request from 'request-promise';
import { Logger } from '../../utils/logger.js';
import { Common } from '../../utils/common.js';
let options = null;
const logger = Logger;
const common = Common;
export const genSeed = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Wallet', msg: 'Generating Seed..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
if (req.params.passphrase) {
options.url = req.session.selectedNode.ln_server_url + '/v1/genseed?aezeed_passphrase=' + Buffer.from(atob(req.params.passphrase)).toString('base64');
}
else {
options.url = req.session.selectedNode.ln_server_url + '/v1/genseed';
}
request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Wallet', msg: 'Seed Generated', data: body });
res.status(200).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Wallet', 'Gen Seed Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const operateWallet = (req, res, next) => {
let err_message = '';
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.method = 'POST';
if (!req.params.operation || req.params.operation === 'unlockwallet') {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Wallet', msg: 'Unlocking Wallet..' });
options.url = req.session.selectedNode.ln_server_url + '/v1/unlockwallet';
options.form = JSON.stringify({
wallet_password: Buffer.from(atob(req.body.wallet_password)).toString('base64')
});
err_message = 'Unlocking wallet failed! Verify that lnd is running and the wallet is locked!';
}
else {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Wallet', msg: 'Initializing Wallet..' });
options.url = req.session.selectedNode.ln_server_url + '/v1/initwallet';
if (req.body.aezeed_passphrase && req.body.aezeed_passphrase !== '') {
options.form = JSON.stringify({
wallet_password: Buffer.from(atob(req.body.wallet_password)).toString('base64'),
cipher_seed_mnemonic: req.body.cipher_seed_mnemonic,
aezeed_passphrase: Buffer.from(atob(req.body.aezeed_passphrase)).toString('base64')
});
}
else {
options.form = JSON.stringify({
wallet_password: Buffer.from(atob(req.body.wallet_password)).toString('base64'),
cipher_seed_mnemonic: req.body.cipher_seed_mnemonic
});
}
err_message = 'Initializing wallet failed!';
}
request(options).then((body) => {
const body_str = (!body) ? '' : JSON.stringify(body);
const search_idx = (!body) ? -1 : body_str.search('Not Found');
if (!body) {
const err = common.handleError({ statusCode: 500, message: 'Wallet Error', error: err_message }, 'Wallet', err_message, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.error, error: err.error });
}
else if (search_idx > -1) {
const err = common.handleError({ statusCode: 500, message: 'Wallet Error', error: err_message }, 'Wallet', err_message, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.error, error: err.error });
}
else if (body.error) {
if ((body.code === 1 && body.error === 'context canceled') || (body.code === 14 && body.error === 'transport is closing')) {
res.status(201).json('Successful');
}
else {
const errMsg = (body.error && typeof body.error === 'object') ? JSON.stringify(body.error) : (body.error && typeof body.error === 'string') ? body.error : err_message;
const err = common.handleError({ statusCode: 500, message: 'Wallet Error', error: errMsg }, 'Wallet', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.error, error: err.error });
}
}
else {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Wallet', msg: 'Wallet Unlocked/Initialized', data: body });
res.status(201).json('Successful');
}
}).catch((errRes) => {
if ((errRes.error.code === 1 && errRes.error.error === 'context canceled') || (errRes.error.code === 14 && errRes.error.error === 'transport is closing')) {
res.status(201).json('Successful');
}
else {
const err = common.handleError(errRes, 'Wallet', err_message, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
});
};
export const updateSelNodeOptions = (req, res, next) => {
const response = common.updateSelectedNodeOptions(req);
res.status(response.status).json({ updateMessage: response.message });
};
export const getUTXOs = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Wallet', msg: 'Getting UTXOs..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v2/wallet/utxos';
if (common.isVersionCompatible(req.session.selectedNode.ln_version, '0.14.0')) {
options.form = JSON.stringify({ max_confs: req.query.max_confs });
}
else {
options.url = options.url + '?max_confs=' + req.query.max_confs;
}
request.post(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Wallet', msg: 'UTXOs List Received', data: body });
res.status(200).json(body.utxos ? body.utxos : []);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Wallet', 'List UTXOs Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const bumpFee = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Wallet', msg: 'Bumping Fee..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v2/wallet/bumpfee';
options.form = {};
options.form.outpoint = {
txid_str: req.body.txid,
output_index: req.body.outputIndex
};
if (req.body.targetConf) {
options.form.target_conf = req.body.targetConf;
}
else if (req.body.satPerByte) {
options.form.sat_per_byte = req.body.satPerByte;
}
options.form = JSON.stringify(options.form);
request.post(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Wallet', msg: 'Fee Bumped', data: body });
res.status(200).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Wallet', 'Bump Fee Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const labelTransaction = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Wallet', msg: 'Labelling Transaction..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v2/wallet/tx/label';
options.form = {};
options.form.txid = req.body.txid;
options.form.label = req.body.label;
options.form.overwrite = req.body.overwrite;
options.form = JSON.stringify(options.form);
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Wallet', msg: 'Label Transaction Options', data: options.form });
request.post(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Wallet', msg: 'Transaction Labelled', data: body });
res.status(200).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Wallet', 'Label Transaction Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const leaseUTXO = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Wallet', msg: 'Leasing UTXO..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v2/wallet/utxos/lease';
options.form = {};
options.form.id = req.body.txid;
options.form.outpoint = {
txid_bytes: req.body.txid,
output_index: req.body.outputIndex
};
options.form = JSON.stringify(options.form);
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Wallet', msg: 'UTXO Lease Options', data: options.form });
request.post(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Wallet', msg: 'UTXO Leased', data: body });
res.status(200).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Wallet', 'Lease UTXO Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const releaseUTXO = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Wallet', msg: 'Releasing UTXO..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v2/wallet/utxos/release';
options.form = {};
options.form.id = req.body.txid;
options.form.outpoint = {
txid_bytes: req.body.txid,
output_index: req.body.outputIndex
};
options.form = JSON.stringify(options.form);
request.post(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Wallet', msg: 'UTXO Released', data: body });
res.status(200).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Wallet', 'Release UTXO Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};

@ -1,122 +0,0 @@
import request from 'request-promise';
import * as fs from 'fs';
import { join } from 'path';
import { Logger } from '../../utils/logger.js';
import { Common } from '../../utils/common.js';
import { WSServer } from '../../utils/webSocketServer.js';
export class LNDWebSocketClient {
constructor() {
this.logger = Logger;
this.common = Common;
this.wsServer = WSServer;
this.webSocketClients = [];
this.connect = (selectedNode) => {
try {
const clientExists = this.webSocketClients.find((wsc) => wsc.selectedNode.index === selectedNode.index);
if (!clientExists && selectedNode.ln_server_url) {
const newWebSocketClient = { selectedNode: selectedNode };
this.webSocketClients.push(newWebSocketClient);
}
}
catch (err) {
throw new Error(err);
}
};
this.fetchUnpaidInvoices = (selectedNode) => {
this.logger.log({ selectedNode: selectedNode, level: 'INFO', fileName: 'WebSocketClient', msg: 'Getting Unpaid Invoices..' });
const options = this.setOptionsForSelNode(selectedNode);
options.url = selectedNode.ln_server_url + '/v1/invoices?pending_only=true';
return request(options).then((body) => {
this.logger.log({ selectedNode: selectedNode, level: 'INFO', fileName: 'WebSocketClient', msg: 'Unpaid Invoices Received', data: body });
if (body.invoices && body.invoices.length > 0) {
body.invoices.forEach((invoice) => {
if (invoice.state === 'OPEN') {
this.subscribeToInvoice(options, selectedNode, invoice.r_hash);
}
});
}
return null;
}).catch((errRes) => {
const err = this.common.handleError(errRes, 'WebSocketClient', 'Pending Invoices Error', selectedNode);
return ({ message: err.message, error: err.error });
});
};
this.subscribeToInvoice = (options, selectedNode, rHash) => {
rHash = rHash?.replace(/\+/g, '-')?.replace(/[/]/g, '_');
this.logger.log({ selectedNode: selectedNode, level: 'INFO', fileName: 'WebSocketClient', msg: 'Subscribing to Invoice ' + rHash + ' ..' });
options.url = selectedNode.ln_server_url + '/v2/invoices/subscribe/' + rHash;
request(options).then((msg) => {
this.logger.log({ selectedNode: selectedNode, level: 'INFO', fileName: 'WebSocketClient', msg: 'Invoice Information Received for ' + rHash });
if (typeof msg === 'string') {
const results = msg.split('\n');
msg = (results.length && results.length > 1) ? JSON.parse(results[1]) : JSON.parse(msg);
msg.result.r_preimage = msg.result.r_preimage ? Buffer.from(msg.result.r_preimage, 'base64').toString('hex') : '';
msg.result.r_hash = msg.result.r_hash ? Buffer.from(msg.result.r_hash, 'base64').toString('hex') : '';
msg.result.description_hash = msg.result.description_hash ? Buffer.from(msg.result.description_hash, 'base64').toString('hex') : null;
}
msg['type'] = 'invoice';
msg['source'] = 'LND';
const msgStr = JSON.stringify(msg);
this.logger.log({ selectedNode: selectedNode, level: 'INFO', fileName: 'WebSocketClient', msg: 'Invoice Info Received', data: msgStr });
this.wsServer.sendEventsToAllLNClients(msgStr, selectedNode);
}).catch((errRes) => {
const err = this.common.handleError(errRes, 'Invoices', 'Subscribe to Invoice Error for ' + rHash, selectedNode);
const errStr = ((typeof err === 'object' && err.message) ? JSON.stringify({ error: err.message + ' ' + rHash }) : (typeof err === 'object') ? JSON.stringify({ error: err + ' ' + rHash }) : ('{ "error": ' + err + ' ' + rHash + ' }'));
this.wsServer.sendErrorToAllLNClients(errStr, selectedNode);
});
};
this.subscribeToPayment = (options, selectedNode, paymentHash) => {
this.logger.log({ selectedNode: selectedNode, level: 'INFO', fileName: 'WebSocketClient', msg: 'Subscribing to Payment ' + paymentHash + ' ..' });
options.url = selectedNode.ln_server_url + '/v2/router/track/' + paymentHash;
request(options).then((msg) => {
this.logger.log({ selectedNode: selectedNode, level: 'INFO', fileName: 'WebSocketClient', msg: 'Payment Information Received for ' + paymentHash });
msg['type'] = 'payment';
msg['source'] = 'LND';
const msgStr = JSON.stringify(msg);
this.logger.log({ selectedNode: selectedNode, level: 'INFO', fileName: 'WebSocketClient', msg: 'Payment Info Received', data: msgStr });
this.wsServer.sendEventsToAllLNClients(msgStr, selectedNode);
}).catch((errRes) => {
const err = this.common.handleError(errRes, 'Payment', 'Subscribe to Payment Error for ' + paymentHash, selectedNode);
const errStr = ((typeof err === 'object' && err.message) ? JSON.stringify({ error: err.message + ' ' + paymentHash }) : (typeof err === 'object') ? JSON.stringify({ error: err + ' ' + paymentHash }) : ('{ "error": ' + err + ' ' + paymentHash + ' }'));
this.wsServer.sendErrorToAllLNClients(errStr, selectedNode);
});
};
this.setOptionsForSelNode = (selectedNode) => {
const options = { url: '', rejectUnauthorized: false, json: true, form: null };
try {
options['headers'] = { 'Grpc-Metadata-macaroon': fs.readFileSync(join(selectedNode.macaroon_path, 'admin.macaroon')).toString('hex') };
}
catch (err) {
this.logger.log({ selectedNode: selectedNode, level: 'ERROR', fileName: 'WebSocketClient', msg: 'Set Options Error', error: JSON.stringify(err) });
}
return options;
};
this.disconnect = (selectedNode) => {
const clientExists = this.webSocketClients.find((wsc) => wsc.selectedNode.index === selectedNode.index);
if (clientExists) {
this.logger.log({ selectedNode: clientExists.selectedNode, level: 'INFO', fileName: 'CLWebSocket', msg: 'Disconnecting from the LND\'s Websocket Server..' });
const clientIdx = this.webSocketClients.findIndex((wsc) => wsc.selectedNode.index === selectedNode.index);
this.webSocketClients.splice(clientIdx, 1);
}
};
this.updateSelectedNode = (newSelectedNode) => {
const clientIdx = this.webSocketClients.findIndex((wsc) => +wsc.selectedNode.index === +newSelectedNode.index);
let newClient = this.webSocketClients[clientIdx];
if (!newClient) {
newClient = { selectedNode: null };
}
newClient.selectedNode = JSON.parse(JSON.stringify(newSelectedNode));
this.webSocketClients[clientIdx] = newClient;
if (this.webSocketClients[clientIdx].selectedNode.ln_version === '' || !this.webSocketClients[clientIdx].selectedNode.ln_version || this.common.isVersionCompatible(this.webSocketClients[clientIdx].selectedNode.ln_version, '0.11.0')) {
this.fetchUnpaidInvoices(this.webSocketClients[clientIdx].selectedNode);
}
};
this.wsServer.eventEmitterLND.on('CONNECT', (nodeIndex) => {
this.connect(this.common.findNode(+nodeIndex));
});
this.wsServer.eventEmitterLND.on('DISCONNECT', (nodeIndex) => {
this.disconnect(this.common.findNode(+nodeIndex));
});
}
}
export const LNDWSClient = new LNDWebSocketClient();

@ -1,391 +0,0 @@
import * as fs from 'fs';
import { sep } from 'path';
import ini from 'ini';
import parseHocon from 'hocon-parser';
import request from 'request-promise';
import { Database } from '../../utils/database.js';
import { Logger } from '../../utils/logger.js';
import { Common } from '../../utils/common.js';
import { WSServer } from '../../utils/webSocketServer.js';
const options = { url: '' };
const logger = Logger;
const common = Common;
const wsServer = WSServer;
const databaseService = Database;
export const updateSelectedNode = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'Updating Selected Node..' });
const selNodeIndex = req.params.currNodeIndex ? +req.params.currNodeIndex : common.initSelectedNode ? +common.initSelectedNode.index : 1;
req.session.selectedNode = common.findNode(selNodeIndex);
if (req.headers && req.headers.authorization && req.headers.authorization !== '') {
wsServer.updateLNWSClientDetails(req.session.id, +req.session.selectedNode.index, +req.params.prevNodeIndex);
if (req.params.prevNodeIndex !== -1) {
databaseService.unloadDatabase(req.params.prevNodeIndex, req.session.id);
}
}
const responseVal = !req.session.selectedNode.ln_node ? '' : req.session.selectedNode.ln_node;
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'Selected Node Updated To ' + responseVal });
res.status(200).json({ status: 'Selected Node Updated To: ' + JSON.stringify(responseVal) + '!' });
};
export const getRTLConfigInitial = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'Getting Initial RTL Configuration..' });
const confFile = common.rtl_conf_file_path + sep + 'RTL-Config.json';
fs.readFile(confFile, 'utf8', (errRes, data) => {
if (errRes) {
if (errRes.code === 'ENOENT') {
logger.log({ selectedNode: req.session.selectedNode, level: 'ERROR', fileName: 'RTLConf', msg: 'Node config does not exist!', error: { error: 'Node config does not exist.' } });
res.status(200).json({ defaultNodeIndex: 0, selectedNodeIndex: 0, sso: {}, nodes: [] });
}
else {
const errMsg = 'Get Node Config Error';
const err = common.handleError({ statusCode: 500, message: errMsg, error: errRes }, 'RTLConf', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.error, error: err.error });
}
}
else {
const nodeConfData = JSON.parse(data);
const sso = { rtlSSO: common.rtl_sso, logoutRedirectLink: common.logout_redirect_link };
const enable2FA = !!common.rtl_secret2fa;
const allowPasswordUpdate = common.flg_allow_password_update;
const nodesArr = [];
if (common.nodes && common.nodes.length > 0) {
common.nodes.forEach((node, i) => {
const settings = { unannouncedChannels: false };
settings.userPersona = node.user_persona ? node.user_persona : 'MERCHANT';
settings.themeMode = (node.theme_mode) ? node.theme_mode : 'DAY';
settings.themeColor = (node.theme_color) ? node.theme_color : 'PURPLE';
settings.unannouncedChannels = !!node.unannounced_channels || false;
settings.fiatConversion = (node.fiat_conversion) ? !!node.fiat_conversion : false;
settings.currencyUnit = node.currency_unit;
nodesArr.push({
index: node.index,
lnNode: node.ln_node,
lnImplementation: node.ln_implementation,
settings: settings,
authentication: {}
});
});
}
const body = { defaultNodeIndex: nodeConfData.defaultNodeIndex, selectedNodeIndex: (req.session.selectedNode && req.session.selectedNode.index ? req.session.selectedNode.index : common.initSelectedNode.index), sso: sso, enable2FA: enable2FA, allowPasswordUpdate: allowPasswordUpdate, nodes: nodesArr };
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'Initial RTL Configuration Received', data: body });
res.status(200).json(body);
}
});
};
export const getRTLConfig = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'Getting RTL Configuration..' });
const confFile = common.rtl_conf_file_path + sep + 'RTL-Config.json';
fs.readFile(confFile, 'utf8', (errRes, data) => {
if (errRes) {
if (errRes.code === 'ENOENT') {
logger.log({ selectedNode: req.session.selectedNode, level: 'ERROR', fileName: 'RTLConf', msg: 'Node config does not exist!', error: { error: 'Node config does not exist.' } });
res.status(200).json({ defaultNodeIndex: 0, selectedNodeIndex: 0, sso: {}, nodes: [] });
}
else {
const errMsg = 'Get Node Config Error';
const err = common.handleError({ statusCode: 500, message: errMsg, error: errRes }, 'RTLConf', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.error, error: err.error });
}
}
else {
const nodeConfData = JSON.parse(data);
const sso = { rtlSSO: common.rtl_sso, logoutRedirectLink: common.logout_redirect_link };
const enable2FA = !!common.rtl_secret2fa;
const allowPasswordUpdate = common.flg_allow_password_update;
const nodesArr = [];
if (common.nodes && common.nodes.length > 0) {
common.nodes.forEach((node, i) => {
const authentication = {};
authentication.configPath = (node.config_path) ? node.config_path : '';
authentication.swapMacaroonPath = (node.swap_macaroon_path) ? node.swap_macaroon_path : '';
authentication.boltzMacaroonPath = (node.boltz_macaroon_path) ? node.boltz_macaroon_path : '';
const settings = { unannouncedChannels: false };
settings.userPersona = node.user_persona ? node.user_persona : 'MERCHANT';
settings.themeMode = (node.theme_mode) ? node.theme_mode : 'DAY';
settings.themeColor = (node.theme_color) ? node.theme_color : 'PURPLE';
settings.unannouncedChannels = !!node.unannounced_channels || false;
settings.fiatConversion = (node.fiat_conversion) ? !!node.fiat_conversion : false;
settings.bitcoindConfigPath = node.bitcoind_config_path;
settings.logLevel = node.log_level ? node.log_level : 'ERROR';
settings.lnServerUrl = node.ln_server_url;
settings.swapServerUrl = node.swap_server_url;
settings.boltzServerUrl = node.boltz_server_url;
settings.enableOffers = node.enable_offers;
settings.enablePeerswap = node.enable_peerswap;
settings.channelBackupPath = node.channel_backup_path;
settings.currencyUnit = node.currency_unit;
nodesArr.push({
index: node.index,
lnNode: node.ln_node,
lnImplementation: node.ln_implementation,
settings: settings,
authentication: authentication
});
});
}
const body = { defaultNodeIndex: nodeConfData.defaultNodeIndex, selectedNodeIndex: (req.session.selectedNode && req.session.selectedNode.index ? req.session.selectedNode.index : common.initSelectedNode.index), sso: sso, enable2FA: enable2FA, allowPasswordUpdate: allowPasswordUpdate, nodes: nodesArr };
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'RTL Configuration Received', data: body });
res.status(200).json(body);
}
});
};
export const updateUISettings = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'Updating UI Settings..' });
const RTLConfFile = common.rtl_conf_file_path + sep + 'RTL-Config.json';
const config = JSON.parse(fs.readFileSync(RTLConfFile, 'utf-8'));
const node = config.nodes.find((node) => (node.index === req.session.selectedNode.index));
if (node && node.Settings) {
node.Settings.userPersona = req.body.updatedSettings.userPersona;
node.Settings.themeMode = req.body.updatedSettings.themeMode;
node.Settings.themeColor = req.body.updatedSettings.themeColor;
node.Settings.unannouncedChannels = req.body.updatedSettings.unannouncedChannels;
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;
}
const selectedNode = common.findNode(req.session.selectedNode.index);
selectedNode.user_persona = req.body.updatedSettings.userPersona;
selectedNode.theme_mode = req.body.updatedSettings.themeMode;
selectedNode.theme_color = req.body.updatedSettings.themeColor;
selectedNode.unannounced_channels = req.body.updatedSettings.unannouncedChannels;
selectedNode.fiat_conversion = req.body.updatedSettings.fiatConversion;
if (req.body.updatedSettings.fiatConversion) {
selectedNode.currency_unit = req.body.updatedSettings.currencyUnit ? req.body.updatedSettings.currencyUnit : 'USD';
}
else {
delete selectedNode.currency_unit;
}
common.replaceNode(req, selectedNode);
}
try {
fs.writeFileSync(RTLConfFile, JSON.stringify(config, null, 2), 'utf-8');
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'UI Settings Updated', data: maskPasswords(config) });
res.status(201).json({ message: 'Node Settings Updated Successfully' });
}
catch (errRes) {
const errMsg = 'Update Node Settings Error';
const err = common.handleError({ statusCode: 500, message: errMsg, error: errRes }, 'RTLConf', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.error, error: err.error });
}
};
export const update2FASettings = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'Updating 2FA Settings..' });
const RTLConfFile = common.rtl_conf_file_path + sep + 'RTL-Config.json';
const config = JSON.parse(fs.readFileSync(RTLConfFile, 'utf-8'));
if (req.body.secret2fa && req.body.secret2fa.trim() !== '') {
config.secret2fa = req.body.secret2fa;
}
else {
delete config.secret2fa;
}
const message = req.body.secret2fa.trim() === '' ? 'Two factor authentication disabled successfully.' : 'Two factor authentication enabled successfully.';
try {
fs.writeFileSync(RTLConfFile, JSON.stringify(config, null, 2), 'utf-8');
common.rtl_secret2fa = config.secret2fa;
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: message });
res.status(201).json({ message: message });
}
catch (errRes) {
const errMsg = 'Update 2FA Settings Error';
const err = common.handleError({ statusCode: 500, message: errMsg, error: errRes }, 'RTLConf', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.error, error: err.error });
}
};
export const updateDefaultNode = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'Updating Default Node..' });
const RTLConfFile = common.rtl_conf_file_path + sep + 'RTL-Config.json';
const config = JSON.parse(fs.readFileSync(RTLConfFile, 'utf-8'));
config.defaultNodeIndex = req.body.defaultNodeIndex;
try {
fs.writeFileSync(RTLConfFile, JSON.stringify(config, null, 2), 'utf-8');
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'Default Node Updated', data: maskPasswords(config) });
res.status(201).json({ message: 'Default Node Updated Successfully' });
}
catch (errRes) {
const errMsg = 'Update Default Node Error';
const err = common.handleError({ statusCode: 500, message: errMsg, error: errRes }, 'RTLConf', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.error, error: err.error });
}
};
export const getConfig = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'Reading Configuration File..' });
let confFile = '';
let fileFormat = 'INI';
switch (req.params.nodeType) {
case 'ln':
confFile = req.session.selectedNode.config_path;
break;
case 'bitcoind':
confFile = req.session.selectedNode.bitcoind_config_path;
break;
case 'rtl':
fileFormat = 'JSON';
confFile = common.rtl_conf_file_path + sep + 'RTL-Config.json';
break;
default:
confFile = '';
break;
}
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'RTLConf', msg: 'Node Type', data: req.params.nodeType });
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'RTLConf', msg: 'File Path', data: confFile });
fs.readFile(confFile, 'utf8', (errRes, data) => {
if (errRes) {
const errMsg = 'Reading Config Error';
const err = common.handleError({ statusCode: 500, message: errMsg, error: errRes }, 'RTLConf', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.error, error: err.error });
}
else {
let jsonConfig = {};
if (fileFormat === 'JSON') {
jsonConfig = JSON.parse(data);
}
else {
fileFormat = 'INI';
data = data?.replace('color=#', 'color=');
jsonConfig = ini.parse(data);
if (jsonConfig['Application Options'] && jsonConfig['Application Options'].color) {
jsonConfig['Application Options'].color = '#' + jsonConfig['Application Options'].color;
}
if (req.params.nodeType === 'ln' && req.session.selectedNode.ln_implementation === 'ECL' && !jsonConfig['eclair.api.password']) {
fileFormat = 'HOCON';
jsonConfig = parseHocon(data);
}
}
jsonConfig = maskPasswords(jsonConfig);
const responseJSON = (fileFormat === 'JSON') ? jsonConfig : ini.stringify(jsonConfig)?.replace('color=\\#', 'color=#');
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'Configuration File Data Received', data: responseJSON });
res.status(200).json({ format: fileFormat, data: responseJSON });
}
});
};
export const getFile = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'Getting File..' });
const file = req.query.path ? req.query.path : (req.session.selectedNode.channel_backup_path + sep + 'channel-' + req.query.channel?.replace(':', '-') + '.bak');
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'RTLConf', msg: 'Channel Point', data: req.query.channel });
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'RTLConf', msg: 'File Path', data: file });
fs.readFile(file, 'utf8', (errRes, data) => {
if (errRes) {
if (errRes.code && errRes.code === 'ENOENT') {
errRes.code = 'File Not Found!';
}
const errMsg = 'Reading File Error';
const err = common.handleError({ statusCode: 500, message: errMsg, error: errRes }, 'RTLConf', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.error, error: err.error });
}
else {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'File Data Received', data: data });
res.status(200).json(data);
}
});
};
export const getCurrencyRates = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'Getting Currency Rates..' });
options.url = 'https://blockchain.info/ticker';
request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'Currency Rates Received', data: body });
res.status(200).json(JSON.parse(body));
}).catch((errRes) => {
const errMsg = 'Get Rates Error';
const err = common.handleError({ statusCode: 500, message: errMsg, error: errRes }, 'RTLConf', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.error, error: err.error });
});
};
export const updateSSO = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'Updating SSO Settings..' });
const RTLConfFile = common.rtl_conf_file_path + sep + 'RTL-Config.json';
const config = JSON.parse(fs.readFileSync(RTLConfFile, 'utf-8'));
delete config.SSO;
config.SSO = req.body.SSO;
try {
fs.writeFileSync(RTLConfFile, JSON.stringify(config, null, 2), 'utf-8');
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'SSO Setting Updated', data: maskPasswords(config) });
res.status(201).json({ message: 'SSO Updated Successfully' });
}
catch (errRes) {
const errMsg = 'Update SSO Error';
const err = common.handleError({ statusCode: 500, message: errMsg, error: errRes }, 'RTLConf', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.error, error: err.error });
}
};
export const updateServiceSettings = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'Updating Service Settings..' });
const RTLConfFile = common.rtl_conf_file_path + sep + 'RTL-Config.json';
const config = JSON.parse(fs.readFileSync(RTLConfFile, 'utf-8'));
const selectedNode = common.findNode(req.session.selectedNode.index);
config.nodes.forEach((node) => {
if (node.index === req.session.selectedNode.index) {
switch (req.body.service) {
case 'LOOP':
if (req.body.settings.enable) {
node.Settings.swapServerUrl = req.body.settings.serverUrl;
node.Authentication.swapMacaroonPath = req.body.settings.macaroonPath;
selectedNode.swap_server_url = req.body.settings.serverUrl;
selectedNode.swap_macaroon_path = req.body.settings.macaroonPath;
}
else {
delete node.Settings.swapServerUrl;
delete node.Authentication.swapMacaroonPath;
delete selectedNode.swap_server_url;
delete selectedNode.swap_macaroon_path;
}
break;
case 'BOLTZ':
if (req.body.settings.enable) {
node.Settings.boltzServerUrl = req.body.settings.serverUrl;
node.Authentication.boltzMacaroonPath = req.body.settings.macaroonPath;
selectedNode.boltz_server_url = req.body.settings.serverUrl;
selectedNode.boltz_macaroon_path = req.body.settings.macaroonPath;
}
else {
delete node.Settings.boltzServerUrl;
delete node.Authentication.boltzMacaroonPath;
delete selectedNode.boltz_server_url;
delete selectedNode.boltz_macaroon_path;
}
break;
case 'OFFERS':
node.Settings.enableOffers = req.body.settings.enableOffers;
selectedNode.enable_offers = req.body.settings.enableOffers;
break;
case 'PEERSWAP':
node.Settings.enablePeerswap = req.body.settings.enablePeerswap;
selectedNode.enable_peerswap = req.body.settings.enablePeerswap;
break;
default:
break;
}
common.replaceNode(req, selectedNode);
}
return node;
});
try {
fs.writeFileSync(RTLConfFile, JSON.stringify(config, null, 2), 'utf-8');
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'Service Settings Updated', data: maskPasswords(config) });
res.status(201).json({ message: 'Service Settings Updated Successfully' });
}
catch (errRes) {
const errMsg = 'Update Service Settings Error';
const err = common.handleError({ statusCode: 500, message: errMsg, error: errRes }, 'RTLConf', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.error, error: err.error });
}
};
export const 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]] = maskPasswords(obj[keys[i]]);
}
if (typeof keys[i] === 'string' &&
(keys[i].toLowerCase().includes('password') || keys[i].toLowerCase().includes('multipass') ||
keys[i].toLowerCase().includes('rpcpass') || keys[i].toLowerCase().includes('rpcpassword') ||
keys[i].toLowerCase().includes('rpcuser'))) {
obj[keys[i]] = '********************';
}
}
}
return obj;
};

@ -1,133 +0,0 @@
import jwt from 'jsonwebtoken';
import * as otplib from 'otplib';
import * as crypto from 'crypto';
import { Database } from '../../utils/database.js';
import { Logger } from '../../utils/logger.js';
import { Common } from '../../utils/common.js';
const logger = Logger;
const common = Common;
const ONE_MINUTE = 60000;
const LOCKING_PERIOD = 30 * ONE_MINUTE; // HALF AN HOUR
const ALLOWED_LOGIN_ATTEMPTS = 5;
const failedLoginAttempts = {};
const databaseService = Database;
const loginInterval = setInterval(() => {
for (const ip in failedLoginAttempts) {
if (new Date().getTime() > (failedLoginAttempts[ip].lastTried + LOCKING_PERIOD)) {
delete failedLoginAttempts[ip];
clearInterval(loginInterval);
}
}
}, LOCKING_PERIOD);
export const getFailedInfo = (reqIP, currentTime) => {
let failed = { count: 0, lastTried: currentTime };
if ((!failedLoginAttempts[reqIP]) || (currentTime > (failed.lastTried + LOCKING_PERIOD))) {
failed = { count: 0, lastTried: currentTime };
failedLoginAttempts[reqIP] = failed;
}
else {
failed = failedLoginAttempts[reqIP];
}
return failed;
};
const handleMultipleFailedAttemptsError = (failed, currentTime, errMsg) => {
if (failed.count >= ALLOWED_LOGIN_ATTEMPTS && (currentTime <= (failed.lastTried + LOCKING_PERIOD))) {
return {
message: 'Multiple Failed Login Attempts!',
error: 'Application locked for ' + (LOCKING_PERIOD / ONE_MINUTE) + ' minutes due to multiple failed attempts!\nTry again after ' + common.convertTimestampToTime((failed.lastTried + LOCKING_PERIOD) / 1000) + '!'
};
}
else {
return {
message: 'Authentication Failed!',
error: errMsg + '\nApplication will be locked after ' + (ALLOWED_LOGIN_ATTEMPTS - failed.count) + ' more unsuccessful attempts!'
};
}
};
export const verifyToken = (twoFAToken) => !!(common.rtl_secret2fa && common.rtl_secret2fa !== '' && otplib.authenticator.check(twoFAToken, common.rtl_secret2fa));
export const authenticateUser = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Authenticate', msg: 'Authenticating User..' });
if (+common.rtl_sso) {
if (req.body.authenticateWith === 'JWT' && jwt.verify(req.body.authenticationValue, common.secret_key)) {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Authenticate', msg: 'User Authenticated' });
res.status(406).json({ message: 'SSO Authentication Error', error: 'Login with Password is not allowed with SSO.' });
}
else if (req.body.authenticateWith === 'PASSWORD') {
if (common.cookie_value.trim().length >= 32 && crypto.timingSafeEqual(Buffer.from(crypto.createHash('sha256').update(common.cookie_value).digest('hex'), 'utf-8'), Buffer.from(req.body.authenticationValue, 'utf-8'))) {
common.refreshCookie();
if (!req.session.selectedNode) {
req.session.selectedNode = common.initSelectedNode;
}
const token = jwt.sign({ user: 'SSO_USER' }, common.secret_key);
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Authenticate', msg: 'User Authenticated' });
res.status(200).json({ token: token });
}
else {
const errMsg = 'SSO Authentication Failed! Access key too short or does not match.';
const err = common.handleError({ statusCode: 406, message: 'SSO Authentication Error', error: errMsg }, 'Authenticate', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
}
}
else {
const currentTime = new Date().getTime();
const reqIP = common.getRequestIP(req);
const failed = getFailedInfo(reqIP, currentTime);
const password = req.body.authenticationValue;
if (common.rtl_pass === password && failed.count < ALLOWED_LOGIN_ATTEMPTS) {
if (req.body.twoFAToken && req.body.twoFAToken !== '') {
if (!verifyToken(req.body.twoFAToken)) {
logger.log({ selectedNode: req.session.selectedNode, level: 'ERROR', fileName: 'Authenticate', msg: 'Invalid Token! Failed IP ' + reqIP, error: { error: 'Invalid token.' } });
failed.count = failed.count + 1;
failed.lastTried = currentTime;
return res.status(401).json(handleMultipleFailedAttemptsError(failed, currentTime, 'Invalid 2FA Token!'));
}
}
if (!req.session.selectedNode) {
req.session.selectedNode = common.initSelectedNode;
}
delete failedLoginAttempts[reqIP];
const token = jwt.sign({ user: 'NODE_USER' }, common.secret_key);
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Authenticate', msg: 'User Authenticated' });
res.status(200).json({ token: token });
}
else {
logger.log({ selectedNode: req.session.selectedNode, level: 'ERROR', fileName: 'Authenticate', msg: 'Invalid Password! Failed IP ' + reqIP, error: { error: 'Invalid password.' } });
failed.count = common.rtl_pass !== password ? (failed.count + 1) : failed.count;
failed.lastTried = common.rtl_pass !== password ? currentTime : failed.lastTried;
return res.status(401).json(handleMultipleFailedAttemptsError(failed, currentTime, 'Invalid Password!'));
}
}
};
export const resetPassword = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Authenticate', msg: 'Resetting Password..' });
if (+common.rtl_sso) {
const errMsg = 'Password cannot be reset for SSO authentication';
const err = common.handleError({ statusCode: 401, message: 'Password Reset Error', error: errMsg }, 'Authenticate', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
else {
const currPassword = req.body.currPassword;
if (common.rtl_pass === currPassword) {
common.rtl_pass = common.replacePasswordWithHash(req.body.newPassword);
const token = jwt.sign({ user: 'NODE_USER' }, common.secret_key);
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Authenticate', msg: 'Password Reset Successful' });
res.status(200).json({ token: token });
}
else {
const errMsg = 'Incorrect Old Password';
const err = common.handleError({ statusCode: 401, message: 'Password Reset Error', error: errMsg }, 'Authenticate', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
}
};
export const logoutUser = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Authenticate', msg: 'Logged out' });
if (req.session.selectedNode && req.session.selectedNode.index) {
databaseService.unloadDatabase(+req.session.selectedNode.index, req.session.id);
}
req.session.destroy((err) => {
res.clearCookie('connect.sid');
res.status(200).json({ loggedout: true });
});
};

@ -1,163 +0,0 @@
import request from 'request-promise';
import { Logger } from '../../utils/logger.js';
import { Common } from '../../utils/common.js';
let options = null;
const logger = Logger;
const common = Common;
export const getInfo = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Boltz', msg: 'Getting Boltz Information..' });
options = common.getBoltzServerOptions(req);
if (options.url === '') {
const errMsg = 'Boltz Server URL is missing in the configuration.';
const err = common.handleError({ statusCode: 500, message: 'Get Info Error', error: errMsg }, 'Boltz', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
options.url = options.url + '/v1/info';
request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Boltz', msg: 'Boltz Information Received', data: body });
res.status(200).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Boltz', 'Get Info Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const getServiceInfo = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Boltz', msg: 'Getting Service Information..' });
options = common.getBoltzServerOptions(req);
if (options.url === '') {
const errMsg = 'Boltz Server URL is missing in the configuration.';
const err = common.handleError({ statusCode: 500, message: 'Get Service Information Error', error: errMsg }, 'Boltz', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
options.url = options.url + '/v1/serviceinfo';
request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Boltz', msg: 'Boltz Get Service Info Received', data: body });
res.status(200).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Boltz', 'Get Service Information Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const listSwaps = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Boltz', msg: 'Getting List Swaps..' });
options = common.getBoltzServerOptions(req);
if (options.url === '') {
const errMsg = 'Boltz Server URL is missing in the configuration.';
const err = common.handleError({ statusCode: 500, message: 'List Swaps Error', error: errMsg }, 'Boltz', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
options.url = options.url + '/v1/listswaps';
request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Boltz', msg: 'Boltz List Swaps Received', data: body });
if (body && body.swaps && body.swaps.length && body.swaps.length > 0) {
body.swaps = body.swaps.reverse();
}
if (body && body.reverseSwaps && body.reverseSwaps.length && body.reverseSwaps.length > 0) {
body.reverseSwaps = body.reverseSwaps.reverse();
}
res.status(200).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Boltz', 'List Swaps Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const getSwapInfo = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Boltz', msg: 'Getting Swap Information..' });
options = common.getBoltzServerOptions(req);
if (options.url === '') {
const errMsg = 'Boltz Server URL is missing in the configuration.';
const err = common.handleError({ statusCode: 500, message: 'Get Swap Information Error', error: errMsg }, 'Boltz', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
options.url = options.url + '/v1/swap/' + req.params.swapId;
request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Boltz', msg: 'Swap Information Received', data: body });
res.status(200).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Boltz', 'Swap Info Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const createSwap = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Boltz', msg: 'Creating Swap..' });
options = common.getBoltzServerOptions(req);
if (options.url === '') {
const errMsg = 'Boltz Server URL is missing in the configuration.';
const err = common.handleError({ statusCode: 500, message: 'Create Swap Error', error: errMsg }, 'Boltz', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
options.url = options.url + '/v1/createswap';
options.body = { amount: req.body.amount };
if (req.body.address !== '') {
options.body.address = req.body.address;
}
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Boltz', msg: 'Create Swap Options Body', data: options.body });
request.post(options).then((createSwapRes) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Boltz', msg: 'Swap Created', data: createSwapRes });
res.status(201).json(createSwapRes);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Boltz', 'Create Swap Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const createReverseSwap = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Boltz', msg: 'Creating Reverse Swap..' });
options = common.getBoltzServerOptions(req);
if (options.url === '') {
const errMsg = 'Boltz Server URL is missing in the configuration.';
const err = common.handleError({ statusCode: 500, message: 'Create Reverse Swap Error', error: errMsg }, 'Boltz', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
options.url = options.url + '/v1/createreverseswap';
options.body = { amount: req.body.amount };
if (req.body.address !== '') {
options.body.address = req.body.address;
}
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Boltz', msg: 'Create Reverse Swap Body', data: options.body });
request.post(options).then((createReverseSwapRes) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Boltz', msg: 'Reverse Swap Created', data: createReverseSwapRes });
res.status(201).json(createReverseSwapRes);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Boltz', 'Create Reverse Swap Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const createChannel = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Boltz', msg: 'Creating Boltz Channel..' });
options = common.getBoltzServerOptions(req);
if (options.url === '') {
const errMsg = 'Boltz Server URL is missing in the configuration.';
const err = common.handleError({ statusCode: 500, message: 'Create Channel Error', error: errMsg }, 'Boltz', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
options.url = options.url + '/v1/createchannel';
options.body = { amount: req.body.amount };
if (req.body.address !== '') {
options.body.address = req.body.address;
}
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Boltz', msg: 'Create Channel Options Body', data: options.body });
request.post(options).then((createChannelRes) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Boltz', msg: 'Boltz Channel Created', data: createChannelRes });
res.status(201).json(createChannelRes);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Boltz', 'Create Channel Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const deposit = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Boltz', msg: 'Boltz Deposit Start..' });
options = common.getBoltzServerOptions(req);
if (options.url === '') {
const errMsg = 'Boltz Server URL is missing in the configuration.';
const err = common.handleError({ statusCode: 500, message: 'Deposit Error', error: errMsg }, 'Boltz', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
options.url = options.url + '/v1/deposit';
request.post(options).then((depositRes) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Boltz', msg: 'Boltz Deposit Finished', data: depositRes });
res.status(201).json(depositRes);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Boltz', 'Deposit Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};

@ -1,241 +0,0 @@
import request from 'request-promise';
import { Logger } from '../../utils/logger.js';
import { Common } from '../../utils/common.js';
let options = null;
const logger = Logger;
const common = Common;
export const loopOut = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Looping Out..' });
options = common.getSwapServerOptions(req);
if (options.url === '') {
const errMsg = 'Loop Server URL is missing in the configuration.';
const err = common.handleError({ statusCode: 500, message: 'Loop Out Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
options.url = options.url + '/v1/loop/out';
options.body = {
amt: req.body.amount,
sweep_conf_target: req.body.targetConf,
max_swap_routing_fee: req.body.swapRoutingFee,
max_miner_fee: req.body.minerFee,
max_prepay_routing_fee: req.body.prepayRoutingFee,
max_prepay_amt: req.body.prepayAmt,
max_swap_fee: req.body.swapFee,
swap_publication_deadline: req.body.swapPublicationDeadline,
initiator: 'RTL'
};
if (req.body.chanId !== '') {
options.body['loop_out_channel'] = req.body.chanId;
}
if (req.body.destAddress !== '') {
options.body['dest'] = req.body.destAddress;
}
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop Out Body', data: options.body });
request.post(options).then((loopOutRes) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Looped Out', data: loopOutRes });
res.status(201).json(loopOutRes);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Loop', 'Loop Out Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const loopOutTerms = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Getting Loop Out Terms..' });
options = common.getSwapServerOptions(req);
if (options.url === '') {
const errMsg = 'Loop Server URL is missing in the configuration.';
const err = common.handleError({ statusCode: 500, message: 'Loop Out Terms Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
options.url = options.url + '/v1/loop/out/terms';
request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Loop Out Terms Received', data: body });
res.status(200).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Loop', 'Loop Out Terms Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const loopOutQuote = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Getting Loop Out Quotes..' });
options = common.getSwapServerOptions(req);
if (options.url === '') {
const errMsg = 'Loop Server URL is missing in the configuration.';
const err = common.handleError({ statusCode: 500, message: 'Loop Out Quotes Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
options.url = options.url + '/v1/loop/out/quote/' + req.params.amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline;
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop Out Quote URL', data: options.url });
request(options).then((quoteRes) => {
quoteRes.amount = +req.params.amount;
quoteRes.swap_payment_dest = quoteRes.swap_payment_dest ? Buffer.from(quoteRes.swap_payment_dest, 'base64').toString('hex') : '';
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Loop Out Quote Received', data: quoteRes });
res.status(200).json(quoteRes);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Loop', 'Loop Out Quotes Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const loopOutTermsAndQuotes = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Getting Loop Out Terms & Quotes..' });
options = common.getSwapServerOptions(req);
if (options.url === '') {
const errMsg = 'Loop Server URL is missing in the configuration.';
const err = common.handleError({ statusCode: 500, message: 'Loop Out Terms & Quotes Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
options.url = options.url + '/v1/loop/out/terms';
request(options).then((terms) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop Out Terms Received', data: terms });
const options1 = common.getSwapServerOptions(req);
const options2 = common.getSwapServerOptions(req);
options1.url = options1.url + '/v1/loop/out/quote/' + terms.min_swap_amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline;
options2.url = options2.url + '/v1/loop/out/quote/' + terms.max_swap_amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline;
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop Out Min Quote Options', data: options1 });
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop Out Max Quote Options', data: options2 });
return Promise.all([request(options1), request(options2)]).then((values) => {
values[0].amount = +terms.min_swap_amount;
values[1].amount = +terms.max_swap_amount;
values[0].swap_payment_dest = values[0].swap_payment_dest ? Buffer.from(values[0].swap_payment_dest, 'base64').toString('hex') : '';
values[1].swap_payment_dest = values[1].swap_payment_dest ? Buffer.from(values[1].swap_payment_dest, 'base64').toString('hex') : '';
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Loop Out Quotes 1 Received', data: values[0] });
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Loop Out Quotes 2 Received', data: values[1] });
res.status(200).json(values);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Loop', 'Loop Out Terms & Quotes Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
}).catch((errRes) => {
const err = common.handleError(errRes, 'Loop', 'Loop Out Terms & Quotes Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const loopIn = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Looping In..' });
options = common.getSwapServerOptions(req);
if (options.url === '') {
const errMsg = 'Loop Server URL is missing in the configuration.';
const err = common.handleError({ statusCode: 500, message: 'Loop In Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
options.url = options.url + '/v1/loop/in';
options.body = {
amt: req.body.amount,
max_swap_fee: req.body.swapFee,
max_miner_fee: req.body.minerFee,
initiator: 'RTL'
};
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Loop In Body', data: options.body });
request.post(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Looped In', data: body });
res.status(201).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Loop', 'Loop In Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const loopInTerms = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Getting Loop In Terms..' });
options = common.getSwapServerOptions(req);
if (options.url === '') {
const errMsg = 'Loop Server URL is missing in the configuration.';
const err = common.handleError({ statusCode: 500, message: 'Loop In Terms Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
options.url = options.url + '/v1/loop/in/terms';
request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Loop In Terms Received', data: body });
res.status(200).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Loop', 'Loop In Terms Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const loopInQuote = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Getting Loop In Quotes..' });
options = common.getSwapServerOptions(req);
if (options.url === '') {
const errMsg = 'Loop Server URL is missing in the configuration.';
const err = common.handleError({ statusCode: 500, message: 'Loop In Quotes Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
options.url = options.url + '/v1/loop/in/quote/' + req.params.amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline;
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop In Quote Options', data: options.url });
request(options).then((body) => {
body.amount = +req.params.amount;
body.swap_payment_dest = body.swap_payment_dest ? Buffer.from(body.swap_payment_dest, 'base64').toString('hex') : '';
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Loop In Qoutes Received', data: body });
res.status(200).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Loop', 'Loop In Quote Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const loopInTermsAndQuotes = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Getting Loop In Terms & Quotes..' });
options = common.getSwapServerOptions(req);
if (options.url === '') {
const errMsg = 'Loop Server URL is missing in the configuration.';
const err = common.handleError({ statusCode: 500, message: 'Loop In Terms & Quotes Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
options.url = options.url + '/v1/loop/in/terms';
request(options).then((terms) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop In Terms Received', data: terms });
const options1 = common.getSwapServerOptions(req);
const options2 = common.getSwapServerOptions(req);
options1.url = options1.url + '/v1/loop/in/quote/' + terms.min_swap_amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline;
options2.url = options2.url + '/v1/loop/in/quote/' + terms.max_swap_amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline;
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop In Min Quote Options', data: options1 });
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop In Max Quote Options', data: options2 });
return Promise.all([request(options1), request(options2)]).then((values) => {
values[0].amount = +terms.min_swap_amount;
values[1].amount = +terms.max_swap_amount;
values[0].swap_payment_dest = values[0].swap_payment_dest ? Buffer.from(values[0].swap_payment_dest, 'base64').toString('hex') : '';
values[1].swap_payment_dest = values[1].swap_payment_dest ? Buffer.from(values[1].swap_payment_dest, 'base64').toString('hex') : '';
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Loop In Quotes 1 Received', data: values[0] });
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Loop In Quotes 2 Received', data: values[1] });
res.status(200).json(values);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Loop', 'Loop In Terms & Quotes Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
}).catch((errRes) => {
const err = common.handleError(errRes, 'Loop', 'Loop In Terms & Quotes Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const swaps = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Getting List Swaps..' });
options = common.getSwapServerOptions(req);
if (options.url === '') {
const errMsg = 'Loop Server URL is missing in the configuration.';
const err = common.handleError({ statusCode: 500, message: 'List Swaps Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
options.url = options.url + '/v1/loop/swaps';
request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop Swaps Received', data: body });
res.status(200).json(body.swaps);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Loop', 'List Swaps Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const swap = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Getting Swap Information..' });
options = common.getSwapServerOptions(req);
if (options.url === '') {
const errMsg = 'Loop Server URL is missing in the configuration.';
const err = common.handleError({ statusCode: 500, message: 'Get Swap Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
options.url = options.url + '/v1/loop/swap/' + req.params.id;
request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Loop Swap Information Received', data: body });
res.status(200).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Loop', 'Get Swap Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};

@ -1,33 +0,0 @@
import { Database } from '../../utils/database.js';
import { Logger } from '../../utils/logger.js';
import { Common } from '../../utils/common.js';
import { CollectionsEnum } from '../../models/database.model.js';
const logger = Logger;
const common = Common;
const databaseService = Database;
export const getPageSettings = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Page Settings', msg: 'Getting Page Settings..' });
databaseService.find(req.session.selectedNode, CollectionsEnum.PAGE_SETTINGS).then((settings) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Page Settings', msg: 'Page Settings Received', data: settings });
res.status(200).json(settings);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Page Settings', 'Page Settings Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const savePageSettings = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Page Settings', msg: 'Saving Page Settings..' });
// eslint-disable-next-line arrow-body-style
return Promise.all(req.body.map((page) => databaseService.validateDocument(CollectionsEnum.PAGE_SETTINGS, page))).then((values) => {
return databaseService.insert(req.session.selectedNode, CollectionsEnum.PAGE_SETTINGS, req.body).then((insertRes) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Page Settings', msg: 'Page Settings Updated', data: insertRes });
res.status(201).json(insertRes);
}).catch((insertErrRes) => {
const err = common.handleError(insertErrRes, 'Page Settings', 'Page Settings Update Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
}).catch((errRes) => {
const err = common.handleError(errRes, 'Page Settings', 'Page Settings Validation Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};

@ -1,66 +0,0 @@
export class CommonSelectedNode {
constructor(options, ln_server_url, macaroon_path, ln_api_password, swap_server_url, boltz_server_url, config_path, rtl_conf_file_path, swap_macaroon_path, boltz_macaroon_path, bitcoind_config_path, channel_backup_path, log_level, log_file, index, ln_node, ln_implementation, user_persona, theme_mode, theme_color, unannounced_channels, fiat_conversion, currency_unit, ln_version, api_version, enable_offers, enable_peerswap) {
this.options = options;
this.ln_server_url = ln_server_url;
this.macaroon_path = macaroon_path;
this.ln_api_password = ln_api_password;
this.swap_server_url = swap_server_url;
this.boltz_server_url = boltz_server_url;
this.config_path = config_path;
this.rtl_conf_file_path = rtl_conf_file_path;
this.swap_macaroon_path = swap_macaroon_path;
this.boltz_macaroon_path = boltz_macaroon_path;
this.bitcoind_config_path = bitcoind_config_path;
this.channel_backup_path = channel_backup_path;
this.log_level = log_level;
this.log_file = log_file;
this.index = index;
this.ln_node = ln_node;
this.ln_implementation = ln_implementation;
this.user_persona = user_persona;
this.theme_mode = theme_mode;
this.theme_color = theme_color;
this.unannounced_channels = unannounced_channels;
this.fiat_conversion = fiat_conversion;
this.currency_unit = currency_unit;
this.ln_version = ln_version;
this.api_version = api_version;
this.enable_offers = enable_offers;
this.enable_peerswap = enable_peerswap;
}
}
export class AuthenticationConfiguration {
constructor(configPath, swapMacaroonPath, boltzMacaroonPath) {
this.configPath = configPath;
this.swapMacaroonPath = swapMacaroonPath;
this.boltzMacaroonPath = boltzMacaroonPath;
}
}
export class NodeSettingsConfiguration {
constructor(userPersona, themeMode, themeColor, unannouncedChannels, fiatConversion, currencyUnit, bitcoindConfigPath, logLevel, lnServerUrl, swapServerUrl, boltzServerUrl, channelBackupPath, enableOffers, enablePeerswap) {
this.userPersona = userPersona;
this.themeMode = themeMode;
this.themeColor = themeColor;
this.unannouncedChannels = unannouncedChannels;
this.fiatConversion = fiatConversion;
this.currencyUnit = currencyUnit;
this.bitcoindConfigPath = bitcoindConfigPath;
this.logLevel = logLevel;
this.lnServerUrl = lnServerUrl;
this.swapServerUrl = swapServerUrl;
this.boltzServerUrl = boltzServerUrl;
this.channelBackupPath = channelBackupPath;
this.enableOffers = enableOffers;
this.enablePeerswap = enablePeerswap;
}
}
export class LogJSONObj {
constructor(level, msg, data, error, fileName, selectedNode) {
this.level = level;
this.msg = msg;
this.data = data;
this.error = error;
this.fileName = fileName;
this.selectedNode = selectedNode;
}
}

@ -1,139 +0,0 @@
export var OfferFieldsEnum;
(function (OfferFieldsEnum) {
OfferFieldsEnum["BOLT12"] = "bolt12";
OfferFieldsEnum["AMOUNTMSAT"] = "amountMSat";
OfferFieldsEnum["TITLE"] = "title";
OfferFieldsEnum["VENDOR"] = "vendor";
OfferFieldsEnum["DESCRIPTION"] = "description";
})(OfferFieldsEnum || (OfferFieldsEnum = {}));
export class Offer {
constructor(bolt12, amountMSat, title, vendor, description, lastUpdatedAt) {
this.bolt12 = bolt12;
this.amountMSat = amountMSat;
this.title = title;
this.vendor = vendor;
this.description = description;
this.lastUpdatedAt = lastUpdatedAt;
}
}
export const validateDocument = (collectionName, documentToValidate) => {
switch (collectionName) {
case CollectionsEnum.OFFERS:
return validateOffer(documentToValidate);
case CollectionsEnum.PAGE_SETTINGS:
return validatePageSettings(documentToValidate);
default:
return ({ isValid: false, error: 'Collection does not exist' });
}
};
export const validateOffer = (documentToValidate) => {
if (!documentToValidate.hasOwnProperty(CollectionFieldsEnum.BOLT12)) {
return ({ isValid: false, error: 'Bolt12 is mandatory.' });
}
if (!documentToValidate.hasOwnProperty(CollectionFieldsEnum.AMOUNTMSAT)) {
return ({ isValid: false, error: 'Amount mSat is mandatory.' });
}
if (!documentToValidate.hasOwnProperty(CollectionFieldsEnum.TITLE)) {
return ({ isValid: false, error: 'Title is mandatory.' });
}
if ((typeof documentToValidate[CollectionFieldsEnum.AMOUNTMSAT] !== 'number')) {
return ({ isValid: false, error: 'Amount mSat should be a number.' });
}
return ({ isValid: true });
};
export var SortOrderEnum;
(function (SortOrderEnum) {
SortOrderEnum["ASCENDING"] = "asc";
SortOrderEnum["DESCENDING"] = "desc";
})(SortOrderEnum || (SortOrderEnum = {}));
export var PageSettingsFieldsEnum;
(function (PageSettingsFieldsEnum) {
PageSettingsFieldsEnum["PAGE_ID"] = "pageId";
PageSettingsFieldsEnum["TABLES"] = "tables";
})(PageSettingsFieldsEnum || (PageSettingsFieldsEnum = {}));
export var TableSettingsFieldsEnum;
(function (TableSettingsFieldsEnum) {
TableSettingsFieldsEnum["TABLE_ID"] = "tableId";
TableSettingsFieldsEnum["RECORDS_PER_PAGE"] = "recordsPerPage";
TableSettingsFieldsEnum["SORT_BY"] = "sortBy";
TableSettingsFieldsEnum["SORT_ORDER"] = "sortOrder";
TableSettingsFieldsEnum["COLUMN_SELECTION"] = "columnSelection";
TableSettingsFieldsEnum["COLUMN_SELECTION_SM"] = "columnSelectionSM";
})(TableSettingsFieldsEnum || (TableSettingsFieldsEnum = {}));
export class TableSetting {
constructor(tableId, recordsPerPage, sortBy, sortOrder, columnSelection) {
this.tableId = tableId;
this.recordsPerPage = recordsPerPage;
this.sortBy = sortBy;
this.sortOrder = sortOrder;
this.columnSelection = columnSelection;
}
}
export class PageSettings {
constructor(pageId, tables) {
this.pageId = pageId;
this.tables = tables;
}
}
export const validatePageSettings = (documentToValidate) => {
let errorMessages = '';
if (!documentToValidate.hasOwnProperty(CollectionFieldsEnum.PAGE_ID)) {
errorMessages = errorMessages + 'Page ID is mandatory.';
}
if (!documentToValidate.hasOwnProperty(CollectionFieldsEnum.TABLES)) {
errorMessages = errorMessages + 'Tables is mandatory.';
}
const tablesMessages = documentToValidate.tables.reduce((tableAcc, table, tableIdx) => {
let errMsg = '';
if (!table.hasOwnProperty(CollectionFieldsEnum.TABLE_ID)) {
errMsg = errMsg + 'Table ID is mandatory.';
}
if (!table.hasOwnProperty(CollectionFieldsEnum.SORT_BY)) {
errMsg = errMsg + 'Sort By is mandatory.';
}
if (!table.hasOwnProperty(CollectionFieldsEnum.SORT_ORDER)) {
errMsg = errMsg + 'Sort Order is mandatory.';
}
if (!table.hasOwnProperty(CollectionFieldsEnum.COLUMN_SELECTION_SM)) {
errMsg = errMsg + 'Column Selection (Mobile Resolution) is mandatory.';
}
if (table[CollectionFieldsEnum.COLUMN_SELECTION_SM].length < 1) {
errMsg = errMsg + 'Column Selection (Mobile Resolution) should have at least 1 field.';
}
if (table[CollectionFieldsEnum.COLUMN_SELECTION_SM].length > 3) {
errMsg = errMsg + 'Column Selection (Mobile Resolution) should have maximum 3 fields.';
}
if (!table.hasOwnProperty(CollectionFieldsEnum.COLUMN_SELECTION)) {
errMsg = errMsg + 'Column Selection (Desktop Resolution) is mandatory.';
}
if (table[CollectionFieldsEnum.COLUMN_SELECTION].length < 2) {
errMsg = errMsg + 'Column Selection (Desktop Resolution) should have at least 2 fields.';
}
if (errMsg.trim() !== '') {
tableAcc.push({ table: (table.hasOwnProperty(CollectionFieldsEnum.TABLE_ID) ? table[CollectionFieldsEnum.TABLE_ID] : (tableIdx + 1)), message: errMsg });
}
return tableAcc;
}, []);
if (errorMessages.trim() === '' && tablesMessages.length === 0) {
return ({ isValid: true });
}
else {
const errObj = { page: (documentToValidate.hasOwnProperty(CollectionFieldsEnum.PAGE_ID) ? documentToValidate[CollectionFieldsEnum.PAGE_ID] : 'Unknown') };
if (errorMessages.trim() !== '') {
errObj['message'] = errorMessages;
}
if (tablesMessages.length && tablesMessages.length > 0) {
errObj['tables'] = tablesMessages;
}
return ({ isValid: false, error: JSON.stringify(errObj) });
}
};
export var CollectionsEnum;
(function (CollectionsEnum) {
CollectionsEnum["OFFERS"] = "Offers";
CollectionsEnum["PAGE_SETTINGS"] = "PageSettings";
})(CollectionsEnum || (CollectionsEnum = {}));
export const CollectionFieldsEnum = { ...OfferFieldsEnum, ...PageSettingsFieldsEnum, ...TableSettingsFieldsEnum };
export const LNDCollection = [CollectionsEnum.PAGE_SETTINGS];
export const ECLCollection = [CollectionsEnum.PAGE_SETTINGS];
export const CLNCollection = [CollectionsEnum.PAGE_SETTINGS, CollectionsEnum.OFFERS];

@ -1,12 +0,0 @@
export var ECLWSEventsEnum;
(function (ECLWSEventsEnum) {
ECLWSEventsEnum["PAY_RECEIVED"] = "payment-received";
ECLWSEventsEnum["PAY_RELAYED"] = "payment-relayed";
ECLWSEventsEnum["PAY_SENT"] = "payment-sent";
ECLWSEventsEnum["PAY_SETTLING_ONCHAIN"] = "payment-settling-onchain";
ECLWSEventsEnum["PAY_FAILED"] = "payment-failed";
ECLWSEventsEnum["CHANNEL_OPENED"] = "channel-opened";
ECLWSEventsEnum["CHANNEL_STATE_CHANGED"] = "channel-state-changed";
ECLWSEventsEnum["CHANNEL_CLOSED"] = "channel-closed";
ECLWSEventsEnum["ONION_MESSAGE_RECEIVED"] = "onion-message-received";
})(ECLWSEventsEnum || (ECLWSEventsEnum = {}));

@ -1,7 +0,0 @@
import exprs from 'express';
const { Router } = exprs;
import { isAuthenticated } from '../../utils/authCheck.js';
import { getBalance } from '../../controllers/cln/balance.js';
const router = Router();
router.get('/', isAuthenticated, getBalance);
export default router;

@ -1,14 +0,0 @@
import exprs from 'express';
const { Router } = exprs;
import { isAuthenticated } from '../../utils/authCheck.js';
import { listChannels, openChannel, setChannelFee, closeChannel, getLocalRemoteBalance, listForwards, funderUpdatePolicy, listForwardsPaginated } from '../../controllers/cln/channels.js';
const router = Router();
router.get('/listChannels', isAuthenticated, listChannels);
router.post('/', isAuthenticated, openChannel);
router.post('/setChannelFee', isAuthenticated, setChannelFee);
router.delete('/:channelId', isAuthenticated, closeChannel);
router.get('/localRemoteBalance', isAuthenticated, getLocalRemoteBalance);
router.get('/listForwards', isAuthenticated, listForwards);
router.get('/listForwardsPaginated', isAuthenticated, listForwardsPaginated);
router.post('/funderUpdate', isAuthenticated, funderUpdatePolicy);
export default router;

@ -1,7 +0,0 @@
import exprs from 'express';
const { Router } = exprs;
import { isAuthenticated } from '../../utils/authCheck.js';
import { getFees } from '../../controllers/cln/fees.js';
const router = Router();
router.get('/', isAuthenticated, getFees);
export default router;

@ -1,7 +0,0 @@
import exprs from 'express';
const { Router } = exprs;
import { isAuthenticated } from '../../utils/authCheck.js';
import { getInfo } from '../../controllers/cln/getInfo.js';
const router = Router();
router.get('/', isAuthenticated, getInfo);
export default router;

@ -1,31 +0,0 @@
import exprs from 'express';
const { Router } = exprs;
import infoCLRoutes from './getInfo.js';
import feesCLRoutes from './fees.js';
import balanceCLRoutes from './balance.js';
import channelsCLRoutes from './channels.js';
import invoicesCLRoutes from './invoices.js';
import onChainCLRoutes from './onchain.js';
import paymentsCLRoutes from './payments.js';
import peersCLRoutes from './peers.js';
import networkCLRoutes from './network.js';
import offersCLRoutes from './offers.js';
import utilityCLRoutes from './utility.js';
const router = Router();
const clRoutes = [
{ path: '/getinfo', route: infoCLRoutes },
{ path: '/fees', route: feesCLRoutes },
{ path: '/balance', route: balanceCLRoutes },
{ path: '/channels', route: channelsCLRoutes },
{ path: '/invoices', route: invoicesCLRoutes },
{ path: '/onchain', route: onChainCLRoutes },
{ path: '/payments', route: paymentsCLRoutes },
{ path: '/peers', route: peersCLRoutes },
{ path: '/network', route: networkCLRoutes },
{ path: '/offers', route: offersCLRoutes },
{ path: '/utility', route: utilityCLRoutes }
];
clRoutes.forEach((route) => {
router.use(route.path, route.route);
});
export default router;

@ -1,9 +0,0 @@
import exprs from 'express';
const { Router } = exprs;
import { isAuthenticated } from '../../utils/authCheck.js';
import { listInvoices, addInvoice, deleteExpiredInvoice } from '../../controllers/cln/invoices.js';
const router = Router();
router.get('/', isAuthenticated, listInvoices);
router.post('/', isAuthenticated, addInvoice);
router.delete('/', isAuthenticated, deleteExpiredInvoice);
export default router;

@ -1,11 +0,0 @@
import exprs from 'express';
const { Router } = exprs;
import { isAuthenticated } from '../../utils/authCheck.js';
import { getRoute, listNode, listChannel, feeRates, listNodes } from '../../controllers/cln/network.js';
const router = Router();
router.get('/getRoute/:destPubkey/:amount', isAuthenticated, getRoute);
router.get('/listNode/:id', isAuthenticated, listNode);
router.get('/listChannel/:channelShortId', isAuthenticated, listChannel);
router.get('/feeRates/:feeRateStyle', isAuthenticated, feeRates);
router.get('/listNodes', isAuthenticated, listNodes);
export default router;

@ -1,12 +0,0 @@
import exprs from 'express';
const { Router } = exprs;
import { isAuthenticated } from '../../utils/authCheck.js';
import { listOfferBookmarks, deleteOfferBookmark, listOffers, disableOffer, createOffer, fetchOfferInvoice } from '../../controllers/cln/offers.js';
const router = Router();
router.get('/offerbookmarks', isAuthenticated, listOfferBookmarks);
router.delete('/offerbookmark/:offerStr', isAuthenticated, deleteOfferBookmark);
router.get('/', isAuthenticated, listOffers);
router.post('/', isAuthenticated, createOffer);
router.post('/fetchOfferInvoice', isAuthenticated, fetchOfferInvoice);
router.delete('/:offerID', isAuthenticated, disableOffer);
export default router;

@ -1,9 +0,0 @@
import exprs from 'express';
const { Router } = exprs;
import { isAuthenticated } from '../../utils/authCheck.js';
import { getNewAddress, onChainWithdraw, getUTXOs } from '../../controllers/cln/onchain.js';
const router = Router();
router.get('/', isAuthenticated, getNewAddress);
router.post('/', isAuthenticated, onChainWithdraw);
router.get('/utxos/', isAuthenticated, getUTXOs);
export default router;

@ -1,8 +0,0 @@
import exprs from 'express';
const { Router } = exprs;
import { isAuthenticated } from '../../utils/authCheck.js';
import { listPayments, postPayment } from '../../controllers/cln/payments.js';
const router = Router();
router.get('/', isAuthenticated, listPayments);
router.post('/', isAuthenticated, postPayment);
export default router;

@ -1,9 +0,0 @@
import exprs from 'express';
const { Router } = exprs;
import { isAuthenticated } from '../../utils/authCheck.js';
import { getPeers, postPeer, deletePeer } from '../../controllers/cln/peers.js';
const router = Router();
router.get('/', isAuthenticated, getPeers);
router.post('/', isAuthenticated, postPeer);
router.delete('/:peerId', isAuthenticated, deletePeer);
export default router;

@ -1,11 +0,0 @@
import exprs from 'express';
const { Router } = exprs;
import { isAuthenticated } from '../../utils/authCheck.js';
import { decodePayments, decodePayment, signMessage, verifyMessage, listConfigs } from '../../controllers/cln/utility.js';
const router = Router();
router.get('/', isAuthenticated, decodePayments);
router.get('/decode/:payReq', isAuthenticated, decodePayment);
router.post('/sign', isAuthenticated, signMessage);
router.post('/verify', isAuthenticated, verifyMessage);
router.get('/listConfigs', isAuthenticated, listConfigs);
export default router;

@ -1,11 +0,0 @@
import exprs from 'express';
const { Router } = exprs;
import { isAuthenticated } from '../../utils/authCheck.js';
import { getChannels, getChannelStats, openChannel, updateChannelRelayFee, closeChannel } from '../../controllers/eclair/channels.js';
const router = Router();
router.get('/', isAuthenticated, getChannels);
router.get('/stats', isAuthenticated, getChannelStats);
router.post('/', isAuthenticated, openChannel);
router.post('/updateRelayFee', isAuthenticated, updateChannelRelayFee);
router.delete('/', isAuthenticated, closeChannel);
export default router;

@ -1,8 +0,0 @@
import exprs from 'express';
const { Router } = exprs;
import { isAuthenticated } from '../../utils/authCheck.js';
import { getFees, getPayments } from '../../controllers/eclair/fees.js';
const router = Router();
router.get('/fees', isAuthenticated, getFees);
router.get('/payments', isAuthenticated, getPayments);
export default router;

@ -1,7 +0,0 @@
import exprs from 'express';
const { Router } = exprs;
import { isAuthenticated } from '../../utils/authCheck.js';
import { getInfo } from '../../controllers/eclair/getInfo.js';
const router = Router();
router.get('/', isAuthenticated, getInfo);
export default router;

@ -1,25 +0,0 @@
import exprs from 'express';
const { Router } = exprs;
import infoECLRoutes from './getInfo.js';
import feesECLRoutes from './fees.js';
import channelsECLRoutes from './channels.js';
import onChainECLRoutes from './onchain.js';
import peersECLRoutes from './peers.js';
import invoicesECLRoutes from './invoices.js';
import paymentsECLRoutes from './payments.js';
import networkECLRoutes from './network.js';
const router = Router();
const eclRoutes = [
{ path: '/getinfo', route: infoECLRoutes },
{ path: '/fees', route: feesECLRoutes },
{ path: '/channels', route: channelsECLRoutes },
{ path: '/onchain', route: onChainECLRoutes },
{ path: '/peers', route: peersECLRoutes },
{ path: '/invoices', route: invoicesECLRoutes },
{ path: '/payments', route: paymentsECLRoutes },
{ path: '/network', route: networkECLRoutes }
];
eclRoutes.forEach((route) => {
router.use(route.path, route.route);
});
export default router;

@ -1,9 +0,0 @@
import exprs from 'express';
const { Router } = exprs;
import { isAuthenticated } from '../../utils/authCheck.js';
import { listInvoices, getInvoice, createInvoice } from '../../controllers/eclair/invoices.js';
const router = Router();
router.get('/', isAuthenticated, listInvoices);
router.get('/:paymentHash', isAuthenticated, getInvoice);
router.post('/', isAuthenticated, createInvoice);
export default router;

@ -1,7 +0,0 @@
import exprs from 'express';
const { Router } = exprs;
import { isAuthenticated } from '../../utils/authCheck.js';
import { getNodes } from '../../controllers/eclair/network.js';
const router = Router();
router.get('/nodes/:id', isAuthenticated, getNodes);
export default router;

@ -1,10 +0,0 @@
import exprs from 'express';
const { Router } = exprs;
import { isAuthenticated } from '../../utils/authCheck.js';
import { getNewAddress, getBalance, getTransactions, sendFunds } from '../../controllers/eclair/onchain.js';
const router = Router();
router.get('/', isAuthenticated, getNewAddress);
router.get('/balance/', isAuthenticated, getBalance);
router.get('/transactions/', isAuthenticated, getTransactions);
router.post('/', isAuthenticated, sendFunds);
export default router;

@ -1,10 +0,0 @@
import exprs from 'express';
const { Router } = exprs;
import { isAuthenticated } from '../../utils/authCheck.js';
import { queryPaymentRoute, decodePayment, getSentPaymentsInformation, postPayment } from '../../controllers/eclair/payments.js';
const router = Router();
router.get('/route/', isAuthenticated, queryPaymentRoute);
router.get('/decode/:invoice', isAuthenticated, decodePayment);
router.post('/getsentinfos', isAuthenticated, getSentPaymentsInformation);
router.post('/', isAuthenticated, postPayment);
export default router;

@ -1,9 +0,0 @@
import exprs from 'express';
const { Router } = exprs;
import { isAuthenticated } from '../../utils/authCheck.js';
import { getPeers, connectPeer, deletePeer } from '../../controllers/eclair/peers.js';
const router = Router();
router.get('/', isAuthenticated, getPeers);
router.post('/', isAuthenticated, connectPeer);
router.delete('/:nodeId', isAuthenticated, deletePeer);
export default router;

@ -1,7 +0,0 @@
import exprs from 'express';
const { Router } = exprs;
import { isAuthenticated } from '../../utils/authCheck.js';
import { getBlockchainBalance } from '../../controllers/lnd/balance.js';
const router = Router();
router.get('/', isAuthenticated, getBlockchainBalance);
export default router;

@ -1,13 +0,0 @@
import exprs from 'express';
const { Router } = exprs;
import { isAuthenticated } from '../../utils/authCheck.js';
import { getAllChannels, getPendingChannels, getClosedChannels, postChannel, postTransactions, closeChannel, postChanPolicy } from '../../controllers/lnd/channels.js';
const router = Router();
router.get('/', isAuthenticated, getAllChannels);
router.get('/pending', isAuthenticated, getPendingChannels);
router.get('/closed', isAuthenticated, getClosedChannels);
router.post('/', isAuthenticated, postChannel);
router.post('/transactions', isAuthenticated, postTransactions);
router.delete('/:channelPoint', isAuthenticated, closeChannel);
router.post('/chanPolicy', isAuthenticated, postChanPolicy);
export default router;

@ -1,10 +0,0 @@
import exprs from 'express';
const { Router } = exprs;
import { isAuthenticated } from '../../utils/authCheck.js';
import { getBackup, getRestoreList, postBackupVerify, postRestore } from '../../controllers/lnd/channelsBackup.js';
const router = Router();
router.get('/:channelPoint', isAuthenticated, getBackup);
router.get('/restore/list', isAuthenticated, getRestoreList);
router.post('/verify/:channelPoint', isAuthenticated, postBackupVerify);
router.post('/restore/:channelPoint', isAuthenticated, postRestore);
export default router;

@ -1,7 +0,0 @@
import exprs from 'express';
const { Router } = exprs;
import { isAuthenticated } from '../../utils/authCheck.js';
import { getFees } from '../../controllers/lnd/fees.js';
const router = Router();
router.get('/', isAuthenticated, getFees);
export default router;

@ -1,7 +0,0 @@
import exprs from 'express';
const { Router } = exprs;
import { isAuthenticated } from '../../utils/authCheck.js';
import { getInfo } from '../../controllers/lnd/getInfo.js';
const router = Router();
router.get('/', isAuthenticated, getInfo);
export default router;

@ -1,13 +0,0 @@
import exprs from 'express';
const { Router } = exprs;
import { isAuthenticated } from '../../utils/authCheck.js';
import { getDescribeGraph, getGraphInfo, getAliasesForPubkeys, getGraphNode, getGraphEdge, getRemoteFeePolicy, getQueryRoutes } from '../../controllers/lnd/graph.js';
const router = Router();
router.get('/', isAuthenticated, getDescribeGraph);
router.get('/info', isAuthenticated, getGraphInfo);
router.get('/nodes', isAuthenticated, getAliasesForPubkeys);
router.get('/node/:pubKey', isAuthenticated, getGraphNode);
router.get('/edge/:chanid', isAuthenticated, getGraphEdge);
router.get('/edge/:chanid/:localPubkey', isAuthenticated, getRemoteFeePolicy);
router.get('/routes/:destPubkey/:amount', isAuthenticated, getQueryRoutes);
export default router;

@ -1,37 +0,0 @@
import exprs from 'express';
const { Router } = exprs;
import infoRoutes from './getInfo.js';
import channelsRoutes from './channels.js';
import channelsBackupRoutes from './channelsBackup.js';
import peersRoutes from './peers.js';
import feesRoutes from './fees.js';
import balanceRoutes from './balance.js';
import walletRoutes from './wallet.js';
import graphRoutes from './graph.js';
import newAddressRoutes from './newAddress.js';
import transactionsRoutes from './transactions.js';
import paymentsRoutes from './payments.js';
import invoiceRoutes from './invoices.js';
import switchRoutes from './switch.js';
import messageRoutes from './message.js';
const router = Router();
const lndRoutes = [
{ path: '/getinfo', route: infoRoutes },
{ path: '/channels', route: channelsRoutes },
{ path: '/channels/backup', route: channelsBackupRoutes },
{ path: '/peers', route: peersRoutes },
{ path: '/fees', route: feesRoutes },
{ path: '/balance', route: balanceRoutes },
{ path: '/wallet', route: walletRoutes },
{ path: '/network', route: graphRoutes },
{ path: '/newaddress', route: newAddressRoutes },
{ path: '/transactions', route: transactionsRoutes },
{ path: '/payments', route: paymentsRoutes },
{ path: '/invoices', route: invoiceRoutes },
{ path: '/switch', route: switchRoutes },
{ path: '/message', route: messageRoutes }
];
lndRoutes.forEach((route) => {
router.use(route.path, route.route);
});
export default router;

@ -1,9 +0,0 @@
import exprs from 'express';
const { Router } = exprs;
import { isAuthenticated } from '../../utils/authCheck.js';
import { listInvoices, invoiceLookup, addInvoice } from '../../controllers/lnd/invoices.js';
const router = Router();
router.get('/', isAuthenticated, listInvoices);
router.get('/lookup/', isAuthenticated, invoiceLookup);
router.post('/', isAuthenticated, addInvoice);
export default router;

@ -1,8 +0,0 @@
import exprs from 'express';
const { Router } = exprs;
import { isAuthenticated } from '../../utils/authCheck.js';
import { signMessage, verifyMessage } from '../../controllers/lnd/message.js';
const router = Router();
router.post('/sign', isAuthenticated, signMessage);
router.post('/verify', isAuthenticated, verifyMessage);
export default router;

@ -1,7 +0,0 @@
import exprs from 'express';
const { Router } = exprs;
import { isAuthenticated } from '../../utils/authCheck.js';
import { getNewAddress } from '../../controllers/lnd/newAddress.js';
const router = Router();
router.get('/', isAuthenticated, getNewAddress);
export default router;

@ -1,11 +0,0 @@
import exprs from 'express';
const { Router } = exprs;
import { isAuthenticated } from '../../utils/authCheck.js';
import { decodePayment, decodePayments, getPayments, getAllLightningTransactions, paymentLookup } from '../../controllers/lnd/payments.js';
const router = Router();
router.get('/', isAuthenticated, getPayments);
router.get('/alltransactions', isAuthenticated, getAllLightningTransactions);
router.get('/decode/:payRequest', isAuthenticated, decodePayment);
router.get('/lookup/:paymentHash', isAuthenticated, paymentLookup);
router.post('/', isAuthenticated, decodePayments);
export default router;

@ -1,9 +0,0 @@
import exprs from 'express';
const { Router } = exprs;
import { isAuthenticated } from '../../utils/authCheck.js';
import { getPeers, postPeer, deletePeer } from '../../controllers/lnd/peers.js';
const router = Router();
router.get('/', isAuthenticated, getPeers);
router.post('/', isAuthenticated, postPeer);
router.delete('/:peerPubKey', isAuthenticated, deletePeer);
export default router;

@ -1,7 +0,0 @@
import exprs from 'express';
const { Router } = exprs;
import { isAuthenticated } from '../../utils/authCheck.js';
import { forwardingHistory } from '../../controllers/lnd/switch.js';
const router = Router();
router.post('/', isAuthenticated, forwardingHistory);
export default router;

@ -1,8 +0,0 @@
import exprs from 'express';
const { Router } = exprs;
import { isAuthenticated } from '../../utils/authCheck.js';
import { getTransactions, postTransactions } from '../../controllers/lnd/transactions.js';
const router = Router();
router.get('/', isAuthenticated, getTransactions);
router.post('/', isAuthenticated, postTransactions);
export default router;

@ -1,14 +0,0 @@
import exprs from 'express';
const { Router } = exprs;
import { isAuthenticated } from '../../utils/authCheck.js';
import { genSeed, updateSelNodeOptions, getUTXOs, operateWallet, bumpFee, labelTransaction, leaseUTXO, releaseUTXO } from '../../controllers/lnd/wallet.js';
const router = Router();
router.get('/genseed/:passphrase?', isAuthenticated, genSeed);
router.get('/updateSelNodeOptions', isAuthenticated, updateSelNodeOptions);
router.get('/getUTXOs', isAuthenticated, getUTXOs);
router.post('/wallet/:operation', isAuthenticated, operateWallet);
router.post('/bumpfee', isAuthenticated, bumpFee);
router.post('/label', isAuthenticated, labelTransaction);
router.post('/lease', isAuthenticated, leaseUTXO);
router.post('/release', isAuthenticated, releaseUTXO);
export default router;

@ -1,17 +0,0 @@
import exprs from 'express';
const { Router } = exprs;
import { isAuthenticated } from '../../utils/authCheck.js';
import { getRTLConfigInitial, getRTLConfig, updateUISettings, update2FASettings, getConfig, getFile, updateSelectedNode, updateDefaultNode, updateServiceSettings, updateSSO, getCurrencyRates } from '../../controllers/shared/RTLConf.js';
const router = Router();
router.get('/rtlconfinit', getRTLConfigInitial);
router.get('/rtlconf', isAuthenticated, getRTLConfig);
router.post('/', isAuthenticated, updateUISettings);
router.post('/update2FA', isAuthenticated, update2FASettings);
router.get('/config/:nodeType', isAuthenticated, getConfig);
router.get('/file', isAuthenticated, getFile);
router.get('/updateSelNode/:currNodeIndex/:prevNodeIndex', updateSelectedNode);
router.post('/updateDefaultNode', updateDefaultNode);
router.post('/updateServiceSettings', updateServiceSettings);
router.post('/updateSSO', updateSSO);
router.get('/rates', getCurrencyRates);
export default router;

@ -1,9 +0,0 @@
import exprs from 'express';
const { Router } = exprs;
import { authenticateUser, verifyToken, resetPassword, logoutUser } from '../../controllers/shared/authenticate.js';
const router = Router();
router.post('/', authenticateUser);
router.post('/token', verifyToken);
router.post('/reset', resetPassword);
router.get('/logout', logoutUser);
export default router;

@ -1,14 +0,0 @@
import exprs from 'express';
const { Router } = exprs;
import { isAuthenticated } from '../../utils/authCheck.js';
import { getInfo, getServiceInfo, listSwaps, getSwapInfo, createSwap, createReverseSwap, createChannel, deposit } from '../../controllers/shared/boltz.js';
const router = Router();
router.get('/info', isAuthenticated, getInfo);
router.get('/serviceInfo', isAuthenticated, getServiceInfo);
router.get('/listSwaps', isAuthenticated, listSwaps);
router.get('/swapInfo/:swapId', isAuthenticated, getSwapInfo);
router.post('/createSwap', isAuthenticated, createSwap);
router.post('/createReverseSwap', isAuthenticated, createReverseSwap);
router.post('/createChannel', isAuthenticated, createChannel);
router.post('/deposit', isAuthenticated, deposit);
export default router;

@ -1,19 +0,0 @@
import exprs from 'express';
const { Router } = exprs;
import authenticateRoutes from './authenticate.js';
import boltzRoutes from './boltz.js';
import loopRoutes from './loop.js';
import RTLConfRoutes from './RTLConf.js';
import pageSettingsRoutes from './pageSettings.js';
const router = Router();
const sharedRoutes = [
{ path: '/authenticate', route: authenticateRoutes },
{ path: '/boltz', route: boltzRoutes },
{ path: '/loop', route: loopRoutes },
{ path: '/conf', route: RTLConfRoutes },
{ path: '/pagesettings', route: pageSettingsRoutes }
];
sharedRoutes.forEach((route) => {
router.use(route.path, route.route);
});
export default router;

@ -1,16 +0,0 @@
import exprs from 'express';
const { Router } = exprs;
import { isAuthenticated } from '../../utils/authCheck.js';
import { loopInTerms, loopInQuote, loopInTermsAndQuotes, loopIn, loopOutTerms, loopOutQuote, loopOutTermsAndQuotes, loopOut, swaps, swap } from '../../controllers/shared/loop.js';
const router = Router();
router.get('/in/terms', isAuthenticated, loopInTerms);
router.get('/in/quote/:amount', isAuthenticated, loopInQuote);
router.get('/in/termsAndQuotes', isAuthenticated, loopInTermsAndQuotes);
router.post('/in', isAuthenticated, loopIn);
router.get('/out/terms', isAuthenticated, loopOutTerms);
router.get('/out/quote/:amount', isAuthenticated, loopOutQuote);
router.get('/out/termsAndQuotes', isAuthenticated, loopOutTermsAndQuotes);
router.post('/out', isAuthenticated, loopOut);
router.get('/swaps', isAuthenticated, swaps);
router.get('/swap/:id', isAuthenticated, swap);
export default router;

@ -1,8 +0,0 @@
import exprs from 'express';
const { Router } = exprs;
import { isAuthenticated } from '../../utils/authCheck.js';
import { getPageSettings, savePageSettings } from '../../controllers/shared/pageSettings.js';
const router = Router();
router.get('/', isAuthenticated, getPageSettings);
router.post('/', isAuthenticated, savePageSettings);
export default router;

@ -1,90 +0,0 @@
import express from 'express';
import sessions from 'express-session';
import cookieParser from 'cookie-parser';
import bodyParser from 'body-parser';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';
import CORS from './cors.js';
import CSRF from './csrf.js';
import sharedRoutes from '../routes/shared/index.js';
import lndRoutes from '../routes/lnd/index.js';
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';
const ONE_DAY = 1000 * 60 * 60 * 24;
export class ExpressApplication {
constructor() {
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 = () => {
this.logger.log({ selectedNode: this.common.initSelectedNode, level: 'INFO', fileName: 'App', msg: 'Setting up Application Routes..' });
this.app.use(this.common.baseHref + '/api', sharedRoutes);
this.app.use(this.common.baseHref + '/api/lnd', lndRoutes);
this.app.use(this.common.baseHref + '/api/cln', clnRoutes);
this.app.use(this.common.baseHref + '/api/ecl', eclRoutes);
this.app.use(this.common.baseHref, express.static(join(this.directoryName, '../..', 'frontend')));
this.app.use((req, res, next) => {
res.cookie('XSRF-TOKEN', req.csrfToken ? req.csrfToken() : (req.cookies && req.cookies._csrf) ? req.cookies._csrf : ''); // RTL Angular Frontend
res.setHeader('XSRF-TOKEN', req.csrfToken ? req.csrfToken() : (req.cookies && req.cookies._csrf) ? req.cookies._csrf : ''); // RTL Quickpay JQuery
res.sendFile(join(this.directoryName, '../..', 'frontend', 'index.html'));
});
this.app.use((err, req, res, next) => {
this.handleApplicationErrors(err, res);
next();
});
this.logger.log({ selectedNode: this.common.initSelectedNode, level: 'INFO', fileName: 'App', msg: 'Application Routes Set' });
};
this.handleApplicationErrors = (err, res) => {
switch (err.code) {
case 'EACCES':
this.logger.log({ selectedNode: this.common.initSelectedNode, level: 'ERROR', fileName: 'App', msg: 'Server requires elevated privileges' });
res.status(406).send('Server requires elevated privileges.');
break;
case 'EADDRINUSE':
this.logger.log({ selectedNode: this.common.initSelectedNode, level: 'ERROR', fileName: 'App', msg: 'Server is already in use' });
res.status(409).send('Server is already in use.');
break;
case 'ECONNREFUSED':
this.logger.log({ selectedNode: this.common.initSelectedNode, level: 'ERROR', fileName: 'App', msg: 'Server is down/locked' });
res.status(401).send('Server is down/locked.');
break;
case 'EBADCSRFTOKEN':
this.logger.log({ selectedNode: this.common.initSelectedNode, level: 'ERROR', fileName: 'App', msg: 'Invalid CSRF token. Form tempered.' });
res.status(403).send('Invalid CSRF token, form tempered.');
break;
default:
this.logger.log({ selectedNode: this.common.initSelectedNode, level: 'ERROR', fileName: 'App', msg: 'DEFUALT ERROR', error: err });
res.status(400).send(JSON.stringify(err));
break;
}
};
this.logger.log({ selectedNode: this.common.initSelectedNode, level: 'INFO', fileName: 'App', msg: 'Starting Express Application..' });
this.app.set('trust proxy', true);
this.app.use(sessions({ secret: this.common.secret_key, saveUninitialized: true, cookie: { secure: false, maxAge: ONE_DAY }, resave: false }));
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();
}
}
export default ExpressApplication;

@ -1,67 +0,0 @@
import jwt from 'jsonwebtoken';
import csurf from 'csurf/index.js';
import { Common } from './common.js';
import { Logger } from './logger.js';
const common = Common;
const logger = Logger;
const csurfProtection = csurf({ cookie: true });
export const isAuthenticated = (req, res, next) => {
try {
const token = req.headers.authorization.split(' ')[1];
jwt.verify(token, common.secret_key);
next();
}
catch (error) {
const errMsg = 'Authentication Failed! Please Login First!';
const err = common.handleError({ statusCode: 401, message: 'Authentication Error', error: errMsg }, 'AuthCheck', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
};
export const verifyWSUser = (info, next) => {
const headers = JSON.parse(JSON.stringify(info.req.headers));
const protocols = !info.req.headers['sec-websocket-protocol'] ? [] : info.req.headers['sec-websocket-protocol'].split(',')?.map((s) => s.trim());
const jwToken = (protocols && protocols.length > 0) ? protocols[0] : '';
if (!jwToken || jwToken === '') {
next(false, 401, 'Authentication Failed! Please Login First!');
}
else {
jwt.verify(jwToken, common.secret_key, (verificationErr) => {
if (verificationErr) {
next(false, 401, 'Authentication Failed! Please Login First!');
}
else {
try {
let updatedReq = null;
try {
updatedReq = JSON.parse(JSON.stringify(info.req));
}
catch (err) {
updatedReq = info.req;
}
let cookies = null;
try {
cookies = '{"' + headers.cookie?.replace(/ /g, '')?.replace(/;/g, '","').trim()?.replace(/[=]/g, '":"') + '"}';
updatedReq['cookies'] = JSON.parse(cookies);
}
catch (err) {
cookies = {};
updatedReq['cookies'] = JSON.parse(cookies);
logger.log({ selectedNode: common.initSelectedNode, level: 'WARN', fileName: 'AuthCheck', msg: '403 Unable to read CSRF token cookie', data: err });
}
csurfProtection(updatedReq, null, (err) => {
if (err) {
next(false, 403, 'Invalid CSRF token!');
}
else {
next(true);
}
});
}
catch (err) {
logger.log({ selectedNode: common.initSelectedNode, level: 'WARN', fileName: 'AuthCheck', msg: '403 Unable to verify CSRF token', data: err });
next(true);
}
}
});
}
};

@ -1,522 +0,0 @@
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 = [];
this.initSelectedNode = null;
this.rtl_conf_file_path = '';
this.port = 3000;
this.host = null;
this.rtl_pass = '';
this.flg_allow_password_update = true;
this.rtl_secret2fa = '';
this.rtl_sso = 0;
this.rtl_cookie_path = '';
this.logout_redirect_link = '';
this.cookie_value = '';
this.api_version = '';
this.secret_key = crypto.randomBytes(64).toString('hex');
this.read_dummy_data = false;
this.baseHref = '/rtl';
this.dummy_data_array_from_file = [];
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 }
];
this.getSwapServerOptions = (req) => {
const swapOptions = {
url: req.session.selectedNode.swap_server_url,
rejectUnauthorized: false,
json: true,
headers: { 'Grpc-Metadata-macaroon': '' }
};
if (req.session.selectedNode.swap_macaroon_path) {
try {
swapOptions.headers = { 'Grpc-Metadata-macaroon': fs.readFileSync(join(req.session.selectedNode.swap_macaroon_path, 'loop.macaroon')).toString('hex') };
}
catch (err) {
this.logger.log({ selectedNode: this.initSelectedNode, level: 'ERROR', fileName: 'Common', msg: 'Loop macaroon Error', error: err });
}
}
this.logger.log({ selectedNode: this.initSelectedNode, level: 'INFO', fileName: 'Common', msg: 'Swap Options', data: swapOptions });
return swapOptions;
};
this.getBoltzServerOptions = (req) => {
const boltzOptions = {
url: req.session.selectedNode.boltz_server_url,
rejectUnauthorized: false,
json: true,
headers: { 'Grpc-Metadata-macaroon': '' }
};
if (req.session.selectedNode.boltz_macaroon_path) {
try {
boltzOptions.headers = { 'Grpc-Metadata-macaroon': fs.readFileSync(join(req.session.selectedNode.boltz_macaroon_path, 'admin.macaroon')).toString('hex') };
}
catch (err) {
this.logger.log({ selectedNode: this.initSelectedNode, level: 'ERROR', fileName: 'Common', msg: 'Boltz macaroon Error', error: err });
}
}
this.logger.log({ selectedNode: this.initSelectedNode, level: 'INFO', fileName: 'Common', msg: 'Boltz Options', data: boltzOptions });
return boltzOptions;
};
this.getOptions = (req) => {
if (req.session.selectedNode && req.session.selectedNode.options) {
req.session.selectedNode.options.method = (req.session.selectedNode.ln_implementation && req.session.selectedNode.ln_implementation.toUpperCase() !== 'ECL') ? 'GET' : 'POST';
delete req.session.selectedNode.options.form;
req.session.selectedNode.options.qs = {};
return req.session.selectedNode.options;
}
return this.handleError({ statusCode: 401, message: 'Session expired after a day\'s inactivity' }, 'Session Expired', 'Session Expiry Error', this.initSelectedNode);
};
this.updateSelectedNodeOptions = (req) => {
if (!req.session.selectedNode) {
req.session.selectedNode = {};
}
req.session.selectedNode.options = {
url: '',
rejectUnauthorized: false,
json: true,
form: null
};
try {
if (req.session.selectedNode && req.session.selectedNode.ln_implementation) {
switch (req.session.selectedNode.ln_implementation.toUpperCase()) {
case 'CLN':
req.session.selectedNode.options.headers = { macaroon: Buffer.from(fs.readFileSync(join(req.session.selectedNode.macaroon_path, 'access.macaroon'))).toString('base64') };
break;
case 'ECL':
req.session.selectedNode.options.headers = { authorization: 'Basic ' + Buffer.from(':' + req.session.selectedNode.ln_api_password).toString('base64') };
break;
default:
req.session.selectedNode.options.headers = { 'Grpc-Metadata-macaroon': fs.readFileSync(join(req.session.selectedNode.macaroon_path, 'admin.macaroon')).toString('hex') };
break;
}
}
if (req.session.selectedNode) {
this.logger.log({ selectedNode: this.initSelectedNode, level: 'INFO', fileName: 'Common', msg: 'Updated Node Options for ' + req.session.selectedNode.ln_node, data: req.session.selectedNode.options });
}
return { status: 200, message: 'Updated Successfully' };
}
catch (err) {
req.session.selectedNode.options = {
url: '',
rejectUnauthorized: false,
json: true,
form: null
};
this.logger.log({ selectedNode: this.initSelectedNode, level: 'ERROR', fileName: 'Common', msg: 'Update Selected Node Options Error', error: err });
return { status: 502, message: err };
}
};
this.setOptions = (req) => {
if (this.nodes[0].options && this.nodes[0].options.headers) {
return;
}
if (this.nodes && this.nodes.length > 0) {
this.nodes.forEach((node) => {
node.options = {
url: '',
rejectUnauthorized: false,
json: true,
form: null
};
try {
if (node.ln_implementation) {
switch (node.ln_implementation.toUpperCase()) {
case 'CLN':
node.options.headers = { macaroon: Buffer.from(fs.readFileSync(join(node.macaroon_path, 'access.macaroon'))).toString('base64') };
break;
case 'ECL':
node.options.headers = { authorization: 'Basic ' + Buffer.from(':' + node.ln_api_password).toString('base64') };
break;
default:
node.options.headers = { 'Grpc-Metadata-macaroon': fs.readFileSync(join(node.macaroon_path, 'admin.macaroon')).toString('hex') };
break;
}
}
}
catch (err) {
this.logger.log({ selectedNode: this.initSelectedNode, level: 'ERROR', fileName: 'Common', msg: 'Common Set Options Error', error: err });
node.options = {
url: '',
rejectUnauthorized: false,
json: true,
form: ''
};
}
this.logger.log({ selectedNode: this.initSelectedNode, level: 'INFO', fileName: 'Common', msg: 'Set Node Options for ' + node.ln_node, data: node.options });
});
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);
array?.unshift(newlyAddedRecord[0]);
return array;
};
this.camelCase = (str) => str?.replace(/(?:^\w|[A-Z]|\b\w)/g, (word, index) => (word.toUpperCase()))?.replace(/\s+/g, '')?.replace(/-/g, ' ');
this.titleCase = (str) => {
if (str.indexOf('!\n') > 0 || str.indexOf('.\n') > 0) {
return str.split('\n')?.reduce((accumulator, currentStr) => accumulator + currentStr.charAt(0).toUpperCase() + currentStr.substring(1).toLowerCase() + '\n', '');
}
else {
if (str.indexOf(' ') > 0) {
return str.split(' ')?.reduce((accumulator, currentStr) => accumulator + currentStr.charAt(0).toUpperCase() + currentStr.substring(1).toLowerCase() + ' ', '');
}
else {
return str.charAt(0).toUpperCase() + str.substring(1).toLowerCase();
}
}
};
this.handleError = (errRes, fileName, errMsg, selectedNode) => {
const err = JSON.parse(JSON.stringify(errRes));
if (!selectedNode) {
selectedNode = this.initSelectedNode;
}
switch (selectedNode.ln_implementation) {
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;
case 'CLN':
if (err.options && err.options.headers && err.options.headers.macaroon) {
delete err.options.headers.macaroon;
}
if (err.response && err.response.request && err.response.request.headers && err.response.request.headers.macaroon) {
delete err.response.request.headers.macaroon;
}
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;
}
this.logger.log({ selectedNode: selectedNode, level: 'ERROR', fileName: fileName, msg: errMsg, error: (typeof err === 'object' ? JSON.stringify(err) : (typeof err === 'string') ? err : 'Unknown Error') });
let newErrorObj = { statusCode: 500, message: '', error: '' };
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')
};
}
if (selectedNode.ln_implementation === 'ECL' && err.message && err.message.indexOf('Authentication Error') < 0 && err.name && err.name === 'StatusCodeError') {
newErrorObj.statusCode = 500;
}
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) => {
const dummyDataFile = this.rtl_conf_file_path + sep + 'ECLDummyData.log';
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') {
this.logger.log({ selectedNode: this.initSelectedNode, level: 'ERROR', fileName: 'Common', msg: 'Dummy data file does not exist' });
}
else {
this.logger.log({ selectedNode: this.initSelectedNode, level: 'ERROR', fileName: 'Common', msg: 'Getting dummy data failed' });
}
}
else {
this.dummy_data_array_from_file = data.split('\n');
resolve(this.filterData(dataKey, lnImplementation));
}
});
}
else {
resolve(this.filterData(dataKey, lnImplementation));
}
});
};
this.readCookie = () => {
const exists = fs.existsSync(this.rtl_cookie_path);
if (exists) {
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 });
throw new Error(err);
}
}
else {
try {
const directoryName = dirname(this.rtl_cookie_path);
this.createDirectory(directoryName);
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 });
throw new Error(err);
}
}
};
this.refreshCookie = () => {
try {
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: 'Common', msg: 'Something went wrong while refreshing cookie', error: err });
throw new Error(err);
}
};
this.createDirectory = (directoryName) => {
const initDir = isAbsolute(directoryName) ? sep : '';
directoryName.split(sep)?.reduce((parentDir, childDir) => {
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) => {
this.rtl_conf_file_path = process.env.RTL_CONFIG_PATH ? process.env.RTL_CONFIG_PATH : join(dirname(fileURLToPath(import.meta.url)), '../..');
try {
const RTLConfFile = this.rtl_conf_file_path + sep + 'RTL-Config.json';
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');
this.logger.log({ selectedNode: this.initSelectedNode, level: 'INFO', fileName: 'Common', msg: 'Please note that, RTL has encrypted the plaintext password into its corresponding hash' });
return config.multiPassHashed;
}
catch (err) {
this.logger.log({ selectedNode: this.initSelectedNode, level: 'ERROR', fileName: 'Common', msg: 'Password hashing failed', error: err });
}
};
this.getAllNodeAllChannelBackup = (node) => {
const channel_backup_file = node.channel_backup_path + sep + 'channel-all.bak';
const options = {
url: node.ln_server_url + '/v1/channels/backup',
rejectUnauthorized: false,
json: true,
headers: { 'Grpc-Metadata-macaroon': fs.readFileSync(node.macaroon_path + '/admin.macaroon').toString('hex') }
};
this.logger.log({ selectedNode: this.initSelectedNode, level: 'INFO', fileName: 'Common', msg: 'Getting Channel Backup for Node ' + node.ln_node + '..' });
request(options).then((body) => {
fs.writeFile(channel_backup_file, JSON.stringify(body), (err) => {
if (err) {
if (node.ln_node) {
this.logger.log({ selectedNode: this.initSelectedNode, level: 'ERROR', fileName: 'Common', msg: 'Error in Channel Backup for Node ' + node.ln_node, error: err });
}
else {
this.logger.log({ selectedNode: this.initSelectedNode, level: 'ERROR', fileName: 'Common', msg: 'Error in Channel Backup for File ' + channel_backup_file, error: err });
}
}
else {
if (node.ln_node) {
this.logger.log({ selectedNode: this.initSelectedNode, level: 'INFO', fileName: 'Common', msg: 'Successful in Channel Backup for Node ' + node.ln_node, data: body });
}
else {
this.logger.log({ selectedNode: this.initSelectedNode, level: 'INFO', fileName: 'Common', msg: 'Successful in Channel Backup for File ' + channel_backup_file, data: body });
}
}
});
}, (err) => {
this.logger.log({ selectedNode: this.initSelectedNode, level: 'ERROR', fileName: 'Common', msg: 'Error in Channel Backup for Node ' + node.ln_node, error: err });
fs.writeFile(channel_backup_file, '', () => { });
});
};
this.isVersionCompatible = (currentVersion, checkVersion) => {
if (currentVersion) {
const versionsArr = currentVersion.trim()?.replace('v', '').split('-')[0].split('.') || [];
const checkVersionsArr = checkVersion.split('.');
return (+versionsArr[0] > +checkVersionsArr[0]) ||
(+versionsArr[0] === +checkVersionsArr[0] && +versionsArr[1] > +checkVersionsArr[1]) ||
(+versionsArr[0] === +checkVersionsArr[0] && +versionsArr[1] === +checkVersionsArr[1] && +versionsArr[2] >= +checkVersionsArr[2]);
}
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) {
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: '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 });
this.logger.log({ selectedNode: selNode, level: 'INFO', fileName: 'Config Setup Variable', msg: 'LN NODE: ' + selNode.ln_node });
this.logger.log({ selectedNode: selNode, level: 'INFO', fileName: 'Config Setup Variable', msg: 'LN IMPLEMENTATION: ' + selNode.ln_implementation });
this.logger.log({ selectedNode: selNode, level: 'INFO', fileName: 'Config Setup Variable', msg: 'FIAT CONVERSION: ' + selNode.fiat_conversion });
this.logger.log({ selectedNode: selNode, level: 'INFO', fileName: 'Config Setup Variable', msg: 'CURRENCY UNIT: ' + selNode.currency_unit });
this.logger.log({ selectedNode: selNode, level: 'INFO', fileName: 'Config Setup Variable', msg: 'LN SERVER URL: ' + selNode.ln_server_url });
this.logger.log({ selectedNode: selNode, level: 'INFO', fileName: 'Config Setup Variable', msg: 'LOGOUT REDIRECT LINK: ' + this.logout_redirect_link + '\r\n' });
}
};
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;
}
}
else if (lnImplementation === 'CLN') {
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();

@ -1,352 +0,0 @@
import * as os from 'os';
import * as fs from 'fs';
import { join, dirname, sep } from 'path';
import { fileURLToPath } from 'url';
import * as crypto from 'crypto';
import ini from 'ini';
import parseHocon from 'hocon-parser';
import { Common } from './common.js';
import { Logger } from './logger.js';
export class ConfigService {
constructor() {
this.platform = os.platform();
this.hash = crypto.createHash('sha256');
this.errMsg = '';
this.directoryName = dirname(fileURLToPath(import.meta.url));
this.common = Common;
this.logger = Logger;
this.setDefaultConfig = () => {
const homeDir = os.userInfo().homedir;
let macaroonPath = '';
let configPath = '';
let channelBackupPath = '';
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';
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';
break;
case 'linux':
macaroonPath = homeDir + '/.lnd/data/chain/bitcoin/mainnet';
configPath = homeDir + '/.lnd/lnd.conf';
channelBackupPath = homeDir + '/backup/node-1';
break;
default:
macaroonPath = '';
configPath = '';
channelBackupPath = '';
break;
}
const configData = {
port: '3000',
defaultNodeIndex: 1,
SSO: {
rtlSSO: 0,
rtlCookiePath: '',
logoutRedirectLink: ''
},
nodes: [
{
index: 1,
lnNode: 'Node 1',
lnImplementation: 'LND',
Authentication: {
macaroonPath: macaroonPath,
configPath: configPath
},
Settings: {
userPersona: 'MERCHANT',
themeMode: 'DAY',
themeColor: 'PURPLE',
channelBackupPath: channelBackupPath,
logLevel: 'ERROR',
lnServerUrl: 'https://127.0.0.1:8080',
fiatConversion: false,
unannouncedChannels: false
}
}
]
};
if (+process.env.RTL_SSO === 0 || configData.SSO.rtlSSO === 0) {
configData['multiPass'] = 'password';
}
return configData;
};
this.normalizePort = (val) => {
const port = parseInt(val, 10);
if (isNaN(port)) {
return val;
}
if (port >= 0) {
return port;
}
return false;
};
this.updateLogByLevel = () => {
let updateLogFlag = false;
this.common.rtl_conf_file_path = process.env.RTL_CONFIG_PATH ? process.env.RTL_CONFIG_PATH : join(this.directoryName, '../..');
try {
const RTLConfFile = this.common.rtl_conf_file_path + sep + 'RTL-Config.json';
const config = JSON.parse(fs.readFileSync(RTLConfFile, 'utf-8'));
config.nodes.forEach((node) => {
if (node.Settings.hasOwnProperty('enableLogging')) {
updateLogFlag = true;
node.Settings.logLevel = node.Settings.enableLogging ? 'INFO' : 'ERROR';
delete node.Settings.enableLogging;
}
});
if (updateLogFlag) {
fs.writeFileSync(RTLConfFile, JSON.stringify(config, null, 2), 'utf-8');
}
}
catch (err) {
this.errMsg = this.errMsg + '\nLog level update failed!';
}
};
this.validateNodeConfig = (config) => {
if ((+process.env.RTL_SSO === 0) || (typeof process.env.RTL_SSO === 'undefined' && +config.SSO.rtlSSO === 0)) {
if (process.env.APP_PASSWORD && process.env.APP_PASSWORD.trim() !== '') {
this.common.rtl_pass = this.hash.update(process.env.APP_PASSWORD).digest('hex');
this.common.flg_allow_password_update = false;
}
else if (config.multiPassHashed && config.multiPassHashed !== '') {
this.common.rtl_pass = config.multiPassHashed;
}
else if (config.multiPass && config.multiPass !== '') {
this.common.rtl_pass = this.common.replacePasswordWithHash(this.hash.update(config.multiPass).digest('hex'));
}
else {
this.errMsg = this.errMsg + '\nNode Authentication can be set with multiPass only. Please set multiPass in RTL-Config.json';
}
this.common.rtl_secret2fa = config.secret2fa;
}
else {
if (process.env.APP_PASSWORD && process.env.APP_PASSWORD.trim() !== '') {
this.errMsg = this.errMsg + '\nRTL Password cannot be set with SSO. Please set SSO as 0 or remove password.';
}
}
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;
if (config.nodes && config.nodes.length > 0) {
config.nodes.forEach((node, idx) => {
this.common.nodes[idx] = {};
this.common.nodes[idx].index = node.index;
this.common.nodes[idx].ln_node = node.lnNode;
this.common.nodes[idx].ln_implementation = (process.env.LN_IMPLEMENTATION) ? process.env.LN_IMPLEMENTATION : node.lnImplementation ? node.lnImplementation : 'LND';
if (this.common.nodes[idx].ln_implementation === 'CLT') {
this.common.nodes[idx].ln_implementation = 'CLN';
}
if (this.common.nodes[idx].ln_implementation !== 'ECL' && process.env.MACAROON_PATH && process.env.MACAROON_PATH.trim() !== '') {
this.common.nodes[idx].macaroon_path = process.env.MACAROON_PATH;
}
else if (this.common.nodes[idx].ln_implementation !== 'ECL' && node.Authentication && node.Authentication.macaroonPath && node.Authentication.macaroonPath.trim() !== '') {
this.common.nodes[idx].macaroon_path = node.Authentication.macaroonPath;
}
else if (this.common.nodes[idx].ln_implementation !== 'ECL') {
this.errMsg = 'Please set macaroon path for node index ' + node.index + ' in RTL-Config.json!';
}
if (this.common.nodes[idx].ln_implementation === 'ECL') {
if (process.env.LN_API_PASSWORD) {
this.common.nodes[idx].ln_api_password = process.env.LN_API_PASSWORD;
}
else if (node.Authentication && node.Authentication.lnApiPassword) {
this.common.nodes[idx].ln_api_password = node.Authentication.lnApiPassword;
}
else {
this.common.nodes[idx].ln_api_password = '';
}
}
if (process.env.CONFIG_PATH) {
this.common.nodes[idx].config_path = process.env.CONFIG_PATH;
}
else if (node.Authentication && node.Authentication.configPath) {
this.common.nodes[idx].config_path = node.Authentication.configPath;
}
else {
this.common.nodes[idx].config_path = '';
}
if (this.common.nodes[idx].ln_implementation === 'ECL' && this.common.nodes[idx].ln_api_password === '' && this.common.nodes[idx].config_path !== '') {
try {
const exists = fs.existsSync(this.common.nodes[idx].config_path);
if (exists) {
try {
const configFile = fs.readFileSync(this.common.nodes[idx].config_path, 'utf-8');
const iniParsed = ini.parse(configFile);
this.common.nodes[idx].ln_api_password = iniParsed['eclair.api.password'] ? iniParsed['eclair.api.password'] : parseHocon(configFile).eclair.api.password;
}
catch (err) {
this.errMsg = this.errMsg + '\nSomething went wrong while reading config file: \n' + err;
}
}
else {
this.errMsg = this.errMsg + '\nInvalid config path: ' + this.common.nodes[idx].config_path;
}
}
catch (err) {
this.errMsg = this.errMsg + '\nUnable to read config file: \n' + err;
}
}
if (this.common.nodes[idx].ln_implementation === 'ECL' && this.common.nodes[idx].ln_api_password === '') {
this.errMsg = this.errMsg + '\nPlease set config path Or api password for node index ' + node.index + ' in RTL-Config.json! It is mandatory for Eclair authentication!';
}
if (process.env.LN_SERVER_URL && process.env.LN_SERVER_URL.trim() !== '') {
this.common.nodes[idx].ln_server_url = process.env.LN_SERVER_URL.endsWith('/v1') ? process.env.LN_SERVER_URL.slice(0, -3) : process.env.LN_SERVER_URL;
}
else if (process.env.LND_SERVER_URL && process.env.LND_SERVER_URL.trim() !== '') {
this.common.nodes[idx].ln_server_url = process.env.LND_SERVER_URL.endsWith('/v1') ? process.env.LND_SERVER_URL.slice(0, -3) : process.env.LND_SERVER_URL;
}
else if (node.Settings.lnServerUrl && node.Settings.lnServerUrl.trim() !== '') {
this.common.nodes[idx].ln_server_url = node.Settings.lnServerUrl.endsWith('/v1') ? node.Settings.lnServerUrl.slice(0, -3) : node.Settings.lnServerUrl;
}
else if (node.Settings.lndServerUrl && node.Settings.lndServerUrl.trim() !== '') {
this.common.nodes[idx].ln_server_url = node.Settings.lndServerUrl.endsWith('/v1') ? node.Settings.lndServerUrl.slice(0, -3) : node.Settings.lndServerUrl;
}
else {
this.errMsg = this.errMsg + '\nPlease set LN Server URL for node index ' + node.index + ' in RTL-Config.json!';
}
this.common.nodes[idx].user_persona = node.Settings.userPersona ? node.Settings.userPersona : 'MERCHANT';
this.common.nodes[idx].theme_mode = node.Settings.themeMode ? node.Settings.themeMode : 'DAY';
this.common.nodes[idx].theme_color = node.Settings.themeColor ? node.Settings.themeColor : 'PURPLE';
this.common.nodes[idx].unannounced_channels = node.Settings.unannouncedChannels ? !!node.Settings.unannouncedChannels : false;
this.common.nodes[idx].log_level = node.Settings.logLevel ? node.Settings.logLevel : 'ERROR';
this.common.nodes[idx].fiat_conversion = node.Settings.fiatConversion ? !!node.Settings.fiatConversion : false;
if (this.common.nodes[idx].fiat_conversion) {
this.common.nodes[idx].currency_unit = node.Settings.currencyUnit ? node.Settings.currencyUnit : 'USD';
}
if (process.env.SWAP_SERVER_URL && process.env.SWAP_SERVER_URL.trim() !== '') {
this.common.nodes[idx].swap_server_url = process.env.SWAP_SERVER_URL.endsWith('/v1') ? process.env.SWAP_SERVER_URL.slice(0, -3) : process.env.SWAP_SERVER_URL;
this.common.nodes[idx].swap_macaroon_path = process.env.SWAP_MACAROON_PATH;
}
else if (node.Settings.swapServerUrl && node.Settings.swapServerUrl.trim() !== '') {
this.common.nodes[idx].swap_server_url = node.Settings.swapServerUrl.endsWith('/v1') ? node.Settings.swapServerUrl.slice(0, -3) : node.Settings.swapServerUrl;
this.common.nodes[idx].swap_macaroon_path = node.Authentication.swapMacaroonPath ? node.Authentication.swapMacaroonPath : '';
}
else {
this.common.nodes[idx].swap_server_url = '';
this.common.nodes[idx].swap_macaroon_path = '';
}
if (process.env.BOLTZ_SERVER_URL && process.env.BOLTZ_SERVER_URL.trim() !== '') {
this.common.nodes[idx].boltz_server_url = process.env.BOLTZ_SERVER_URL.endsWith('/v1') ? process.env.BOLTZ_SERVER_URL.slice(0, -3) : process.env.BOLTZ_SERVER_URL;
this.common.nodes[idx].boltz_macaroon_path = process.env.BOLTZ_MACAROON_PATH;
}
else if (node.Settings.boltzServerUrl && node.Settings.boltzServerUrl.trim() !== '') {
this.common.nodes[idx].boltz_server_url = node.Settings.boltzServerUrl.endsWith('/v1') ? node.Settings.boltzServerUrl.slice(0, -3) : node.Settings.boltzServerUrl;
this.common.nodes[idx].boltz_macaroon_path = node.Authentication.boltzMacaroonPath ? node.Authentication.boltzMacaroonPath : '';
}
else {
this.common.nodes[idx].boltz_server_url = '';
this.common.nodes[idx].boltz_macaroon_path = '';
}
this.common.nodes[idx].enable_offers = process.env.ENABLE_OFFERS ? process.env.ENABLE_OFFERS : (node.Settings.enableOffers) ? node.Settings.enableOffers : false;
this.common.nodes[idx].enable_peerswap = process.env.ENABLE_PEERSWAP ? process.env.ENABLE_PEERSWAP : (node.Settings.enablePeerswap) ? node.Settings.enablePeerswap : false;
this.common.nodes[idx].bitcoind_config_path = process.env.BITCOIND_CONFIG_PATH ? process.env.BITCOIND_CONFIG_PATH : (node.Settings.bitcoindConfigPath) ? node.Settings.bitcoindConfigPath : '';
this.common.nodes[idx].channel_backup_path = process.env.CHANNEL_BACKUP_PATH ? process.env.CHANNEL_BACKUP_PATH : (node.Settings.channelBackupPath) ? node.Settings.channelBackupPath : this.common.rtl_conf_file_path + sep + 'channels-backup' + sep + 'node-' + node.index;
try {
this.common.createDirectory(this.common.nodes[idx].channel_backup_path);
const exists = fs.existsSync(this.common.nodes[idx].channel_backup_path + sep + 'channel-all.bak');
if (!exists) {
try {
if (this.common.nodes[idx].ln_implementation === 'LND') {
this.common.getAllNodeAllChannelBackup(this.common.nodes[idx]);
}
else {
const createStream = fs.createWriteStream(this.common.nodes[idx].channel_backup_path + sep + 'channel-all.bak');
createStream.end();
}
}
catch (err) {
this.logger.log({ selectedNode: this.common.initSelectedNode, level: 'ERROR', fileName: 'Config', msg: 'Something went wrong while creating backup file: \n' + err });
}
}
}
catch (err) {
this.logger.log({ selectedNode: this.common.initSelectedNode, level: 'ERROR', fileName: 'Config', msg: 'Something went wrong while creating the backup directory: \n' + err });
}
this.common.nodes[idx].log_file = this.common.rtl_conf_file_path + '/logs/RTL-Node-' + node.index + '.log';
this.logger.log({ selectedNode: this.common.initSelectedNode, level: 'INFO', fileName: 'Config', msg: 'Node Config: ' + JSON.stringify(this.common.nodes[idx]) });
const log_file = this.common.nodes[idx].log_file;
if (fs.existsSync(log_file)) {
fs.writeFile(log_file, '', () => { });
}
else {
try {
const directoryName = dirname(log_file);
this.common.createDirectory(directoryName);
const createStream = fs.createWriteStream(log_file);
createStream.end();
}
catch (err) {
this.logger.log({ selectedNode: this.common.initSelectedNode, level: 'ERROR', fileName: 'Config', msg: 'Something went wrong while creating log file ' + log_file + ': \n' + err });
}
}
});
}
this.setSSOParams(config);
if (this.errMsg && this.errMsg.trim() !== '') {
throw new Error(this.errMsg);
}
};
this.setSSOParams = (config) => {
if (process.env.RTL_SSO) {
this.common.rtl_sso = +process.env.RTL_SSO;
}
else if (config.SSO && config.SSO.rtlSSO) {
this.common.rtl_sso = config.SSO.rtlSSO;
}
if (process.env.RTL_COOKIE_PATH) {
this.common.rtl_cookie_path = process.env.RTL_COOKIE_PATH;
}
else if (config.SSO && config.SSO.rtlCookiePath) {
this.common.rtl_cookie_path = config.SSO.rtlCookiePath;
}
else {
this.common.rtl_cookie_path = '';
}
if (process.env.LOGOUT_REDIRECT_LINK) {
this.common.logout_redirect_link = process.env.LOGOUT_REDIRECT_LINK;
}
else if (config.SSO && config.SSO.logoutRedirectLink) {
this.common.logout_redirect_link = config.SSO.logoutRedirectLink;
}
if (+this.common.rtl_sso) {
if (!this.common.rtl_cookie_path || this.common.rtl_cookie_path.trim() === '') {
this.errMsg = 'Please set rtlCookiePath value for single sign on option!';
}
else {
this.common.readCookie();
}
}
};
this.setSelectedNode = (config) => {
if (config.defaultNodeIndex) {
this.common.initSelectedNode = this.common.findNode(config.defaultNodeIndex);
}
else {
this.common.initSelectedNode = this.common.findNode(this.common.nodes[0].index);
}
};
this.setServerConfiguration = () => {
try {
this.common.rtl_conf_file_path = (process.env.RTL_CONFIG_PATH) ? process.env.RTL_CONFIG_PATH : join(this.directoryName, '../..');
const confFileFullPath = this.common.rtl_conf_file_path + sep + 'RTL-Config.json';
if (!fs.existsSync(confFileFullPath)) {
fs.writeFileSync(confFileFullPath, JSON.stringify(this.setDefaultConfig()));
}
const config = JSON.parse(fs.readFileSync(confFileFullPath, 'utf-8'));
this.updateLogByLevel();
this.validateNodeConfig(config);
this.setSelectedNode(config);
}
catch (err) {
this.logger.log({ selectedNode: this.common.initSelectedNode, level: 'ERROR', fileName: 'Config', msg: 'Something went wrong while configuring the node server: \n' + err });
throw new Error(err);
}
};
}
}
export const Config = new ConfigService();

@ -1,25 +0,0 @@
import { Logger } from './logger.js';
import { Common } from './common.js';
class CORS {
constructor() {
this.logger = Logger;
this.common = Common;
}
mount(app) {
this.logger.log({ selectedNode: this.common.initSelectedNode, level: 'INFO', fileName: 'CORS', msg: 'Setting up CORS..' });
app.use((req, res, next) => {
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Authorization, filePath');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PATCH, PUT, DELETE, OPTIONS');
if (process.env.NODE_ENV === 'development') {
res.setHeader('Access-Control-Allow-Credentials', 'true');
res.setHeader('Access-Control-Allow-Origin', req.headers.origin ? req.headers.origin : req.headers.host ? req.headers.host : '');
}
next();
});
this.logger.log({ selectedNode: this.common.initSelectedNode, level: 'INFO', fileName: 'CORS', msg: 'CORS Set' });
return app;
}
;
}
export default new CORS;

@ -1,20 +0,0 @@
import csurf from 'csurf/index.js';
import { Logger } from './logger.js';
import { Common } from './common.js';
class CSRF {
constructor() {
this.csrfProtection = csurf({ cookie: true });
this.logger = Logger;
this.common = Common;
}
mount(app) {
this.logger.log({ selectedNode: this.common.initSelectedNode, level: 'INFO', fileName: 'CSRF', msg: 'Setting up CSRF..' });
if (process.env.NODE_ENV !== 'development') {
app.use((req, res, next) => this.csrfProtection(req, res, next));
}
this.logger.log({ selectedNode: this.common.initSelectedNode, level: 'INFO', fileName: 'CSRF', msg: 'CSRF Set' });
return app;
}
;
}
export default new CSRF;

@ -1,296 +0,0 @@
import * as fs from 'fs';
import { join, dirname, sep } from 'path';
import { fileURLToPath } from 'url';
import { Common } from '../utils/common.js';
import { Logger } from '../utils/logger.js';
import { validateDocument, LNDCollection, ECLCollection, CLNCollection } from '../models/database.model.js';
export class DatabaseService {
constructor() {
this.common = Common;
this.logger = Logger;
this.dbDirectory = join(dirname(fileURLToPath(import.meta.url)), '..', '..', 'database');
this.nodeDatabase = {};
}
loadDatabase(session) {
const { id, selectedNode } = session;
try {
if (!this.nodeDatabase[selectedNode.index]) {
this.nodeDatabase[selectedNode.index] = { adapter: null, data: {} };
this.nodeDatabase[selectedNode.index].adapter = new DatabaseAdapter(this.dbDirectory, selectedNode, id);
this.fetchNodeData(selectedNode);
this.logger.log({ selectedNode: selectedNode, level: 'DEBUG', fileName: 'Database', msg: 'Database Loaded', data: this.nodeDatabase[selectedNode.index].data });
}
else {
this.nodeDatabase[selectedNode.index].adapter.insertSession(id);
}
}
catch (err) {
this.logger.log({ selectedNode: selectedNode, level: 'ERROR', fileName: 'Database', msg: 'Database Load Error', error: err });
}
}
fetchNodeData(selectedNode) {
switch (selectedNode.ln_implementation) {
case 'CLN':
for (const collectionName in CLNCollection) {
if (CLNCollection.hasOwnProperty(collectionName)) {
this.nodeDatabase[selectedNode.index].data[CLNCollection[collectionName]] = this.nodeDatabase[selectedNode.index].adapter.fetchData(CLNCollection[collectionName]);
}
}
break;
case 'ECL':
for (const collectionName in ECLCollection) {
if (ECLCollection.hasOwnProperty(collectionName)) {
this.nodeDatabase[selectedNode.index].data[ECLCollection[collectionName]] = this.nodeDatabase[selectedNode.index].adapter.fetchData(ECLCollection[collectionName]);
}
}
break;
default:
for (const collectionName in LNDCollection) {
if (LNDCollection.hasOwnProperty(collectionName)) {
this.nodeDatabase[selectedNode.index].data[LNDCollection[collectionName]] = this.nodeDatabase[selectedNode.index].adapter.fetchData(LNDCollection[collectionName]);
}
}
break;
}
}
validateDocument(collectionName, newDocument) {
return new Promise((resolve, reject) => {
const validationRes = validateDocument(collectionName, newDocument);
if (!validationRes.isValid) {
reject(validationRes.error);
}
else {
resolve(true);
}
});
}
insert(selectedNode, collectionName, newCollection) {
return new Promise((resolve, reject) => {
try {
if (!selectedNode || !selectedNode.index) {
reject(new Error('Selected Node Config Not Found.'));
}
this.nodeDatabase[selectedNode.index].data[collectionName] = newCollection;
this.saveDatabase(selectedNode, collectionName);
resolve(this.nodeDatabase[selectedNode.index].data[collectionName]);
}
catch (errRes) {
reject(errRes);
}
});
}
update(selectedNode, collectionName, updatedDocument, documentFieldName, documentFieldValue) {
return new Promise((resolve, reject) => {
try {
if (!selectedNode || !selectedNode.index) {
reject(new Error('Selected Node Config Not Found.'));
}
let foundDocIdx = -1;
let foundDoc = null;
if (this.nodeDatabase[selectedNode.index].data[collectionName]) {
foundDocIdx = this.nodeDatabase[selectedNode.index].data[collectionName].findIndex((document) => document[documentFieldName] === documentFieldValue);
foundDoc = foundDocIdx > -1 ? JSON.parse(JSON.stringify(this.nodeDatabase[selectedNode.index].data[collectionName][foundDocIdx])) : null;
}
if (foundDocIdx > -1 && foundDoc) {
for (const docKey in updatedDocument) {
if (Object.prototype.hasOwnProperty.call(updatedDocument, docKey)) {
foundDoc[docKey] = updatedDocument[docKey];
}
}
updatedDocument = foundDoc;
}
if (foundDocIdx > -1) {
this.nodeDatabase[selectedNode.index].data[collectionName].splice(foundDocIdx, 1, updatedDocument);
}
else {
if (!this.nodeDatabase[selectedNode.index].data[collectionName]) {
this.nodeDatabase[selectedNode.index].data[collectionName] = [];
}
this.nodeDatabase[selectedNode.index].data[collectionName].push(updatedDocument);
}
this.saveDatabase(selectedNode, collectionName);
resolve(updatedDocument);
}
catch (errRes) {
reject(errRes);
}
});
}
find(selectedNode, collectionName, documentFieldName, documentFieldValue) {
return new Promise((resolve, reject) => {
try {
if (!selectedNode || !selectedNode.index) {
reject(new Error('Selected Node Config Not Found.'));
}
if (documentFieldName && documentFieldValue) {
resolve(this.nodeDatabase[selectedNode.index].data[collectionName].find((document) => document[documentFieldName] === documentFieldValue));
}
else {
resolve(this.nodeDatabase[selectedNode.index].data[collectionName]);
}
}
catch (errRes) {
reject(errRes);
}
});
}
remove(selectedNode, collectionName, documentFieldName, documentFieldValue) {
return new Promise((resolve, reject) => {
try {
if (!selectedNode || !selectedNode.index) {
reject(new Error('Selected Node Config Not Found.'));
}
const removeDocIdx = this.nodeDatabase[selectedNode.index].data[collectionName].findIndex((document) => document[documentFieldName] === documentFieldValue);
if (removeDocIdx > -1) {
this.nodeDatabase[selectedNode.index].data[collectionName].splice(removeDocIdx, 1);
}
else {
reject(new Error('Unable to delete, document not found.'));
}
this.saveDatabase(selectedNode, collectionName);
resolve(documentFieldValue);
}
catch (errRes) {
reject(errRes);
}
});
}
saveDatabase(selectedNode, collectionName) {
const nodeIndex = +selectedNode.index;
try {
if (nodeIndex < 1) {
return true;
}
const selNode = this.nodeDatabase[nodeIndex] && this.nodeDatabase[nodeIndex].adapter && this.nodeDatabase[nodeIndex].adapter.selNode ? this.nodeDatabase[nodeIndex].adapter.selNode : null;
if (!this.nodeDatabase[nodeIndex]) {
this.logger.log({ selectedNode: selNode, level: 'ERROR', fileName: 'Database', msg: 'Database Save Error: Selected Node Setup Not Found.' });
throw new Error('Database Save Error: Selected Node Setup Not Found.');
}
this.nodeDatabase[nodeIndex].adapter.saveData(collectionName, this.nodeDatabase[selectedNode.index].data[collectionName]);
this.logger.log({ selectedNode: this.nodeDatabase[nodeIndex].adapter.selNode, level: 'INFO', fileName: 'Database', msg: 'Database Collection ' + collectionName + ' Saved' });
return true;
}
catch (err) {
const selNode = this.nodeDatabase[nodeIndex] && this.nodeDatabase[nodeIndex].adapter && this.nodeDatabase[nodeIndex].adapter.selNode ? this.nodeDatabase[nodeIndex].adapter.selNode : null;
this.logger.log({ selectedNode: selNode, level: 'ERROR', fileName: 'Database', msg: 'Database Save Error', error: err });
throw err;
}
}
unloadDatabase(nodeIndex, sessionID) {
if (nodeIndex > 0) {
if (this.nodeDatabase[nodeIndex] && this.nodeDatabase[nodeIndex].adapter) {
this.nodeDatabase[nodeIndex].adapter.removeSession(sessionID);
if (this.nodeDatabase[nodeIndex].adapter.userSessions && this.nodeDatabase[nodeIndex].adapter.userSessions.length <= 0) {
delete this.nodeDatabase[nodeIndex];
}
}
}
}
}
export class DatabaseAdapter {
constructor(dbDirectoryPath, selNode = null, id = '') {
this.dbDirectoryPath = dbDirectoryPath;
this.selNode = selNode;
this.id = id;
this.logger = Logger;
this.common = Common;
this.dbFilePath = '';
this.userSessions = [];
this.dbFilePath = dbDirectoryPath + sep + 'node-' + selNode.index;
// For backward compatibility Start
const oldFilePath = dbDirectoryPath + sep + 'rtldb-node-' + selNode.index + '.json';
if (selNode.ln_implementation === 'CLN' && fs.existsSync(oldFilePath)) {
this.renameOldDB(oldFilePath, selNode);
}
// For backward compatibility End
this.insertSession(id);
}
renameOldDB(oldFilePath, selNode = null) {
const newFilePath = this.dbFilePath + sep + 'rtldb-' + selNode.ln_implementation + '-Offers.json';
try {
this.common.createDirectory(this.dbFilePath);
const oldOffers = JSON.parse(fs.readFileSync(oldFilePath, 'utf-8'));
fs.writeFileSync(oldFilePath, JSON.stringify(oldOffers.Offers ? oldOffers.Offers : [], null, 2));
fs.renameSync(oldFilePath, newFilePath);
}
catch (err) {
this.logger.log({ selectedNode: selNode, level: 'ERROR', fileName: 'Database', msg: 'Rename Old Database Error', error: err });
}
}
fetchData(collectionName) {
try {
if (!fs.existsSync(this.dbFilePath)) {
this.common.createDirectory(this.dbFilePath);
}
}
catch (err) {
throw new Error(JSON.stringify(err));
}
const collectionFilePath = this.dbFilePath + sep + 'rtldb-' + this.selNode.ln_implementation + '-' + collectionName + '.json';
try {
if (!fs.existsSync(collectionFilePath)) {
fs.writeFileSync(collectionFilePath, '[]');
}
}
catch (err) {
throw new Error(JSON.stringify(err));
}
try {
const otherFiles = fs.readdirSync(this.dbFilePath);
otherFiles.forEach((oFileName) => {
let collectionValid = false;
switch (this.selNode.ln_implementation) {
case 'CLN':
collectionValid = CLNCollection.reduce((acc, collection) => acc || oFileName === ('rtldb-' + this.selNode.ln_implementation + '-' + collection + '.json'), false);
break;
case 'ECL':
collectionValid = ECLCollection.reduce((acc, collection) => acc || oFileName === ('rtldb-' + this.selNode.ln_implementation + '-' + collection + '.json'), false);
break;
default:
collectionValid = LNDCollection.reduce((acc, collection) => acc || oFileName === ('rtldb-' + this.selNode.ln_implementation + '-' + collection + '.json'), false);
break;
}
if (oFileName.endsWith('.json') && !collectionValid) {
fs.renameSync(this.dbFilePath + sep + oFileName, this.dbFilePath + sep + oFileName + '.tmp');
}
});
}
catch (err) {
this.logger.log({ selectedNode: this.selNode, level: 'ERROR', fileName: 'Database', msg: 'Rename Other Implementation DB Error', error: err });
}
try {
const dataFromFile = fs.readFileSync(collectionFilePath, 'utf-8');
const dataObj = !dataFromFile ? null : JSON.parse(dataFromFile);
return dataObj;
}
catch (err) {
throw new Error(JSON.stringify(err));
}
}
getSelNode() {
return this.selNode;
}
saveData(collectionName, collectionData) {
try {
if (collectionData) {
const collectionFilePath = this.dbFilePath + sep + 'rtldb-' + this.selNode.ln_implementation + '-' + collectionName + '.json';
const tempFile = collectionFilePath + '.tmp';
fs.writeFileSync(tempFile, JSON.stringify(collectionData, null, 2));
fs.renameSync(tempFile, collectionFilePath);
}
return true;
}
catch (err) {
throw err;
}
}
insertSession(id = '') {
if (!this.userSessions.includes(id)) {
this.userSessions.push(id);
}
}
removeSession(sessionID = '') {
this.userSessions.splice(this.userSessions.findIndex((sId) => sId === sessionID), 1);
}
}
export const Database = new DatabaseService();

@ -1,81 +0,0 @@
/* eslint-disable no-console */
import * as fs from 'fs';
export class LoggerService {
constructor() {
this.log = (msgJSON) => {
let msgStr = '[' + new Date().toLocaleString() + '] ' + msgJSON.level + ': ' + msgJSON.fileName + ' => ' + msgJSON.msg;
switch (msgJSON.level) {
case 'ERROR':
if (msgJSON.error) {
msgStr = msgStr + ': ' + ((msgJSON.error.error && msgJSON.error.error.message && typeof msgJSON.error.error.message === 'string') ?
msgJSON.error.error.message : (typeof msgJSON.error === 'object' && msgJSON.error.message && typeof msgJSON.error.message === 'string') ? msgJSON.error.message : (typeof msgJSON.error === 'object' && msgJSON.error.stack && typeof msgJSON.error.stack === 'string') ?
msgJSON.error.stack : (typeof msgJSON.error === 'object') ? JSON.stringify(msgJSON.error) : (typeof msgJSON.error === 'string') ?
msgJSON.error : '') + '\r\n';
}
else {
msgStr = msgStr + '.\r\n';
}
console.error(msgStr);
if (msgJSON.selectedNode && msgJSON.selectedNode.log_file) {
fs.appendFile(msgJSON.selectedNode.log_file, msgStr, () => { });
}
break;
case 'WARN':
msgStr = prepMsgData(msgJSON, msgStr);
if (!msgJSON.selectedNode || msgJSON.selectedNode.log_level === 'WARN' || msgJSON.selectedNode.log_level === 'INFO' || msgJSON.selectedNode.log_level === 'DEBUG') {
if (msgJSON.selectedNode && msgJSON.selectedNode.log_file) {
fs.appendFile(msgJSON.selectedNode.log_file, msgStr, () => { });
}
}
break;
case 'INFO':
if (!msgJSON.selectedNode && msgJSON.fileName === 'RTL') {
console.log(msgStr + '.\r\n');
}
else if (msgJSON.selectedNode && msgJSON.selectedNode.log_level === 'INFO') {
msgStr = msgStr + '.\r\n';
console.log(msgStr);
if (msgJSON.selectedNode.log_file) {
fs.appendFile(msgJSON.selectedNode.log_file, msgStr, () => { });
}
}
else if (msgJSON.selectedNode && msgJSON.selectedNode.log_level === 'DEBUG') {
msgStr = prepMsgData(msgJSON, msgStr);
console.log(msgStr);
if (msgJSON.selectedNode.log_file) {
fs.appendFile(msgJSON.selectedNode.log_file, msgStr, () => { });
}
}
break;
case 'DEBUG':
if (!msgJSON.selectedNode) {
console.log(msgStr + '.\r\n');
}
else if (msgJSON.selectedNode && msgJSON.selectedNode.log_level === 'DEBUG') {
msgStr = prepMsgData(msgJSON, msgStr);
console.log(msgStr);
if (msgJSON.selectedNode.log_file) {
fs.appendFile(msgJSON.selectedNode.log_file, msgStr, () => { });
}
}
break;
default:
console.log(msgStr);
break;
}
};
}
}
;
const prepMsgData = (msgJSON, msgStr) => {
if (msgJSON.data) {
msgStr = msgStr + ': ' + (typeof msgJSON.data === 'object' ? (msgJSON.data.message && typeof msgJSON.data.message === 'string') ?
msgJSON.data.message : (msgJSON.data.stack && typeof msgJSON.data.stack === 'string') ?
msgJSON.data.stack : JSON.stringify(msgJSON.data) : (typeof msgJSON.data === 'string') ? msgJSON.data : '') + '\r\n';
}
else {
msgStr = msgStr + '.\r\n';
}
return msgStr;
};
export const Logger = new LoggerService();

@ -1,179 +0,0 @@
import { parse } from 'cookie';
import * as cookieParser from 'cookie-parser';
import * as crypto from 'crypto';
import { WebSocketServer } from 'ws';
import { Logger } from './logger.js';
import { Common } from './common.js';
import { verifyWSUser } from './authCheck.js';
import { EventEmitter } from 'events';
export class RTLWebSocketServer {
constructor() {
this.logger = Logger;
this.common = Common;
this.clientDetails = [];
this.eventEmitterCLN = new EventEmitter();
this.eventEmitterECL = new EventEmitter();
this.eventEmitterLND = new EventEmitter();
this.webSocketServer = null;
this.pingInterval = setInterval(() => {
if (this.webSocketServer.clients.size && this.webSocketServer.clients.size > 0) {
this.webSocketServer.clients.forEach((client) => {
if (client.isAlive === false) {
this.updateLNWSClientDetails(client.sessionId, -1, client.clientNodeIndex);
return client.terminate();
}
client.isAlive = false;
client.ping();
});
}
}, 1000 * 60 * 60); // Terminate broken connections every hour
this.mount = (httpServer) => {
this.logger.log({ selectedNode: this.common.initSelectedNode, level: 'INFO', fileName: 'WebSocketServer', msg: 'Connecting Websocket Server..' });
this.webSocketServer = new WebSocketServer({ noServer: true, path: this.common.baseHref + '/api/ws', verifyClient: (process.env.NODE_ENV === 'development') ? null : verifyWSUser });
httpServer.on('upgrade', (request, socket, head) => {
if (request.headers['upgrade'] !== 'websocket') {
socket.end('HTTP/1.1 400 Bad Request');
return;
}
const acceptKey = request.headers['sec-websocket-key'];
const hash = this.generateAcceptValue(acceptKey);
const responseHeaders = ['HTTP/1.1 101 Web Socket Protocol Handshake', 'Upgrade: WebSocket', 'Connection: Upgrade', 'Sec-WebSocket-Accept: ' + hash];
const protocols = !request.headers['sec-websocket-protocol'] ? [] : request.headers['sec-websocket-protocol'].split(',')?.map((s) => s.trim());
if (protocols.includes('json')) {
responseHeaders.push('Sec-WebSocket-Protocol: json');
}
this.webSocketServer.handleUpgrade(request, socket, head, this.upgradeCallback);
});
this.webSocketServer.on('connection', this.mountEventsOnConnection);
this.webSocketServer.on('close', () => clearInterval(this.pingInterval));
this.logger.log({ selectedNode: this.common.initSelectedNode, level: 'INFO', fileName: 'WebSocketServer', msg: 'Websocket Server Connected' });
};
this.upgradeCallback = (websocket, request) => {
this.webSocketServer.emit('connection', websocket, request);
};
this.mountEventsOnConnection = (websocket, request) => {
const protocols = !request.headers['sec-websocket-protocol'] ? [] : request.headers['sec-websocket-protocol'].split(',')?.map((s) => s.trim());
const cookies = request.headers.cookie ? parse(request.headers.cookie) : null;
websocket.clientId = Date.now();
websocket.isAlive = true;
websocket.sessionId = cookies && cookies['connect.sid'] ? cookieParser.signedCookie(cookies['connect.sid'], this.common.secret_key) : null;
websocket.clientNodeIndex = +protocols[1];
this.logger.log({ selectedNode: this.common.initSelectedNode, level: 'INFO', fileName: 'WebSocketServer', msg: 'Connected: ' + websocket.clientId + ', Total WS clients: ' + this.webSocketServer.clients.size });
websocket.on('error', this.sendErrorToAllLNClients);
websocket.on('message', this.sendEventsToAllLNClients);
websocket.on('pong', () => { websocket.isAlive = true; });
websocket.on('close', (code, reason) => {
this.updateLNWSClientDetails(websocket.sessionId, -1, websocket.clientNodeIndex);
this.logger.log({ selectedNode: this.common.initSelectedNode, level: 'INFO', fileName: 'WebSocketServer', msg: 'Disconnected due to ' + code + ' : ' + websocket.clientId + ', Total WS clients: ' + this.webSocketServer.clients.size });
});
};
this.updateLNWSClientDetails = (sessionId, currNodeIndex, prevNodeIndex) => {
if (prevNodeIndex >= 0 && currNodeIndex >= 0) {
this.webSocketServer.clients.forEach((client) => {
if (client.sessionId === sessionId) {
client.clientNodeIndex = currNodeIndex;
}
});
this.disconnectFromNodeClient(sessionId, prevNodeIndex);
this.connectToNodeClient(sessionId, currNodeIndex);
}
else if (prevNodeIndex >= 0 && currNodeIndex < 0) {
this.disconnectFromNodeClient(sessionId, prevNodeIndex);
}
else if (prevNodeIndex < 0 && currNodeIndex >= 0) {
this.connectToNodeClient(sessionId, currNodeIndex);
}
else {
const selectedNode = this.common.findNode(currNodeIndex);
this.logger.log({ selectedNode: !selectedNode ? this.common.initSelectedNode : selectedNode, level: 'ERROR', fileName: 'WebSocketServer', msg: 'Invalid Node Selection. Previous and current node indices can not be less than zero.' });
}
};
this.disconnectFromNodeClient = (sessionId, prevNodeIndex) => {
const foundClient = this.clientDetails.find((clientDetail) => clientDetail.index === +prevNodeIndex);
if (foundClient) {
const foundSessionIdx = foundClient.sessionIds.findIndex((sid) => sid === sessionId);
if (foundSessionIdx > -1) {
foundClient.sessionIds.splice(foundSessionIdx, 1);
}
if (foundClient.sessionIds.length === 0) {
const foundClientIdx = this.clientDetails.findIndex((clientDetail) => clientDetail.index === +prevNodeIndex);
this.clientDetails.splice(foundClientIdx, 1);
const prevSelectedNode = this.common.findNode(prevNodeIndex);
if (prevSelectedNode && prevSelectedNode.ln_implementation) {
switch (prevSelectedNode.ln_implementation) {
case 'LND':
this.eventEmitterLND.emit('DISCONNECT', prevNodeIndex);
break;
case 'CLN':
this.eventEmitterCLN.emit('DISCONNECT', prevNodeIndex);
break;
case 'ECL':
this.eventEmitterECL.emit('DISCONNECT', prevNodeIndex);
break;
default:
break;
}
}
}
}
};
this.connectToNodeClient = (sessionId, currNodeIndex) => {
let foundClient = this.clientDetails.find((clientDetail) => clientDetail.index === +currNodeIndex);
if (foundClient) {
const foundSessionIdx = foundClient.sessionIds.findIndex((sid) => sid === sessionId);
if (foundSessionIdx < 0) {
foundClient.sessionIds.push(sessionId);
}
}
else {
const currSelectedNode = this.common.findNode(currNodeIndex);
foundClient = { index: currNodeIndex, sessionIds: [sessionId] };
this.clientDetails.push(foundClient);
if (currSelectedNode && currSelectedNode.ln_implementation) {
switch (currSelectedNode.ln_implementation) {
case 'LND':
this.eventEmitterLND.emit('CONNECT', currNodeIndex);
break;
case 'CLN':
this.eventEmitterCLN.emit('CONNECT', currNodeIndex);
break;
case 'ECL':
this.eventEmitterECL.emit('CONNECT', currNodeIndex);
break;
default:
break;
}
}
}
};
this.sendErrorToAllLNClients = (serverError, selectedNode) => {
try {
this.webSocketServer.clients.forEach((client) => {
this.logger.log({ selectedNode: !selectedNode ? this.common.initSelectedNode : selectedNode, level: 'ERROR', fileName: 'WebSocketServer', msg: 'Broadcasting error to clients...: ' + serverError });
if (+client.clientNodeIndex === +selectedNode.index) {
client.send(serverError);
}
});
}
catch (err) {
this.logger.log({ selectedNode: !selectedNode ? this.common.initSelectedNode : selectedNode, level: 'ERROR', fileName: 'WebSocketServer', msg: 'Error while broadcasting message: ' + JSON.stringify(err) });
}
};
this.sendEventsToAllLNClients = (newMessage, selectedNode) => {
try {
this.webSocketServer.clients.forEach((client) => {
if (+client.clientNodeIndex === +selectedNode.index) {
this.logger.log({ selectedNode: !selectedNode ? this.common.initSelectedNode : selectedNode, level: 'DEBUG', fileName: 'WebSocketServer', msg: 'Broadcasting message to client...: ' + client.clientId });
client.send(newMessage);
}
});
}
catch (err) {
this.logger.log({ selectedNode: !selectedNode ? this.common.initSelectedNode : selectedNode, level: 'ERROR', fileName: 'WebSocketServer', msg: 'Error while broadcasting message: ' + JSON.stringify(err) });
}
};
this.generateAcceptValue = (acceptKey) => crypto.createHash('sha1').update(acceptKey + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', 'binary').digest('base64');
this.getClients = () => this.webSocketServer.clients;
}
}
export const WSServer = new RTLWebSocketServer();

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save