Merge pull request #1137 from Ride-The-Lightning/Release-0.13.2

Release 0.13.2
pull/1140/head v0.13.2
ShahanaFarooqui 1 year ago committed by GitHub
commit 04f909329e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -34,8 +34,9 @@
"@angular-eslint/arrow-body-style": "off", "@angular-eslint/arrow-body-style": "off",
"@angular-eslint/component-selector": ["error", { "prefix": "rtl", "style": "kebab-case", "type": "element" }], "@angular-eslint/component-selector": ["error", { "prefix": "rtl", "style": "kebab-case", "type": "element" }],
"@angular-eslint/directive-selector": ["error", { "style": "camelCase", "type": "attribute" }], "@angular-eslint/directive-selector": ["error", { "style": "camelCase", "type": "attribute" }],
"@typescript-eslint/type-annotation-spacing": "error",
"@typescript-eslint/member-delimiter-style": ["error", { "multiline": { "delimiter": "semi", "requireLast": true}, "singleline": { "delimiter": "comma", "requireLast": false }}], "@typescript-eslint/member-delimiter-style": ["error", { "multiline": { "delimiter": "semi", "requireLast": true}, "singleline": { "delimiter": "comma", "requireLast": false }}],
"@typescript-eslint/no-non-null-assertion": "off",
"@typescript-eslint/type-annotation-spacing": "error",
"quotes": ["error", "single"], "quotes": ["error", "single"],
"comma-dangle": ["error", "never"], "comma-dangle": ["error", "never"],
"comma-spacing": ["error", { "before": false, "after": true }], "comma-spacing": ["error", { "before": false, "after": true }],
@ -47,7 +48,7 @@
"curly": "error", "curly": "error",
"no-unused-expressions": "error", "no-unused-expressions": "error",
"strict": "error", "strict": "error",
"max-len": ["error", { "code": 450 }], "max-len": ["error", { "code": 320 }],
"no-multiple-empty-lines": "error", "no-multiple-empty-lines": "error",
"no-trailing-spaces": "error", "no-trailing-spaces": "error",
"quote-props": ["error", "as-needed"], "quote-props": ["error", "as-needed"],

1
.github/README.md vendored

@ -104,6 +104,7 @@ Example RTL-Config.json:
"bitcoindConfigPath": "<Optional: path of bitcoind.conf path if available locally>", "bitcoindConfigPath": "<Optional: path of bitcoind.conf path if available locally>",
"logLevel": "INFO", "logLevel": "INFO",
"fiatConversion": false, "fiatConversion": false,
"unannouncedChannels": false,
"lnServerUrl": "<url for LND REST APIs for node #1 e.g. https://192.168.0.1:8080>", "lnServerUrl": "<url for LND REST APIs for node #1 e.g. https://192.168.0.1:8080>",
"swapServerUrl": "<url for swap server REST APIs for the node. e.g. https://localhost:8081>", "swapServerUrl": "<url for swap server REST APIs for the node. e.g. https://localhost:8081>",
"boltzServerUrl": "<url for boltz server REST APIs for the node. e.g. https://localhost:9003>" "boltzServerUrl": "<url for boltz server REST APIs for the node. e.g. https://localhost:9003>"

@ -35,6 +35,7 @@ parameters have `default` values for initial setup and can be updated after RTL
"logLevel": <logging levels, will log in accordance with the logLevel value provided, Allowed values ERROR, WARN, INFO, DEBUG>, "logLevel": <logging levels, will log in accordance with the logLevel value provided, Allowed values ERROR, WARN, INFO, DEBUG>,
"fiatConversion": <parameter to turn fiat conversion off/on. Allowed values - true, false, default false, Required>, "fiatConversion": <parameter to turn fiat conversion off/on. Allowed values - true, false, default false, Required>,
"currencyUnit": "<Optional: Fiat current Unit for currency conversion, default 'USD' If fiatConversion is true, Required if fiatConversion is true>", "currencyUnit": "<Optional: Fiat current Unit for currency conversion, default 'USD' If fiatConversion is true, Required if fiatConversion is true>",
"unannouncedChannels": <parameter to turn off/on setting for opening announced Channels, default false, Optional>
"lnServerUrl": "<Service url for LND/Core Lightning REST APIs for the node, e.g. https://192.168.0.1:8080 OR https://192.168.0.1:3001 OR http://192.168.0.1:8080. Default 'https://localhost:8080', Required", "lnServerUrl": "<Service url for LND/Core Lightning REST APIs for the node, e.g. https://192.168.0.1:8080 OR https://192.168.0.1:3001 OR http://192.168.0.1:8080. Default 'https://localhost:8080', Required",
"swapServerUrl": "<Service url for swap server REST APIs for the node, e.g. https://localhost:8081, Optional>", "swapServerUrl": "<Service url for swap server REST APIs for the node, e.g. https://localhost:8081, Optional>",
"boltzServerUrl": "<Service url for boltz server REST APIs for the node, e.g. https://localhost:9003, Optional>" "boltzServerUrl": "<Service url for boltz server REST APIs for the node, e.g. https://localhost:9003, Optional>"
@ -66,4 +67,5 @@ RTL_CONFIG_PATH (Path for the folder containing 'RTL-Config.json' file, Required
BITCOIND_CONFIG_PATH (Full path of the bitcoind.conf file including the file name, Optional)<br /> BITCOIND_CONFIG_PATH (Full path of the bitcoind.conf file including the file name, Optional)<br />
CHANNEL_BACKUP_PATH (Folder location for saving the channel backup files, valid for LND implementation only, Required if ln implementation=LND else Optional)<br /> CHANNEL_BACKUP_PATH (Folder location for saving the channel backup files, valid for LND implementation only, Required if ln implementation=LND else Optional)<br />
ENABLE_OFFERS (Boolean flag to enable the offers feature on core lighning, default false, optional)<br /> ENABLE_OFFERS (Boolean flag to enable the offers feature on core lighning, default false, optional)<br />
ENABLE_PEERSWAP (Boolean flag to enable the peerswap feature on core lighning, default false, optional)<br />
LN_API_PASSWORD (Password for Eclair implementation if the eclair.conf path is not available, Required if ln implementation=ECL && config path is undefined)<br /> LN_API_PASSWORD (Password for Eclair implementation if the eclair.conf path is not available, Required if ln implementation=ECL && config path is undefined)<br />

@ -82,6 +82,7 @@ Ensure that the follow values are correct per your config:
"bitcoindConfigPath": "", "bitcoindConfigPath": "",
"logLevel": "INFO", "logLevel": "INFO",
"fiatConversion": false, "fiatConversion": false,
"unannouncedChannels": false,
"lnServerUrl": "https://<cl-rest api server ip address>:3001" "lnServerUrl": "https://<cl-rest api server ip address>:3001"
} }
} }

@ -77,6 +77,7 @@ Ensure that the follow values are correct per your config:
"bitcoindConfigPath": "", "bitcoindConfigPath": "",
"logLevel": "INFO", "logLevel": "INFO",
"fiatConversion": false, "fiatConversion": false,
"unannouncedChannels": false,
"lnServerUrl": "http://<eclair api server ip address>:port" "lnServerUrl": "http://<eclair api server ip address>:port"
} }
} }

@ -39,6 +39,7 @@ If your running RTL and LND on different devices on your local LAN, certain conf
"bitcoindConfigPath": "<Optional: path of bitcoind.conf path if available locally>", "bitcoindConfigPath": "<Optional: path of bitcoind.conf path if available locally>",
"logLevel": "INFO", "logLevel": "INFO",
"fiatConversion": false, "fiatConversion": false,
"unannouncedChannels": false,
"lnServerUrl": "<https://<ip-address-of-device-running-lnd>:8080; e.g. https://192.168.0.1:8080>", "lnServerUrl": "<https://<ip-address-of-device-running-lnd>:8080; e.g. https://192.168.0.1:8080>",
"swapServerUrl": "<https://<localhost>:8081>", "swapServerUrl": "<https://<localhost>:8081>",
"boltzServerUrl": "<https://<localhost>:9003>" "boltzServerUrl": "<https://<localhost>:9003>"

@ -0,0 +1,23 @@
name: Pull Request Stats
on:
push:
branches: [ master, 'Release-*' ]
tags: [ 'v*' ]
release:
types: [released]
# Triggers the workflow only when merging pull request to the branches.
pull_request:
types: [opened, closed]
branches: [ master, 'Release-*', '*' ]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
jobs:
stats:
runs-on: ubuntu-latest
steps:
- name: Run pull request stats
uses: flowwer-dev/pull-request-stats@master
with:
period: 365

@ -27,7 +27,8 @@
"lnServerUrl": "https://localhost:8080", "lnServerUrl": "https://localhost:8080",
"swapServerUrl": "https://localhost:8081", "swapServerUrl": "https://localhost:8081",
"boltzServerUrl": "https://localhost:9003", "boltzServerUrl": "https://localhost:9003",
"fiatConversion": false "fiatConversion": false,
"unannouncedChannels": false
} }
} }
] ]

@ -62,7 +62,7 @@ export const getInfo = (req, res, next) => {
req.session.selectedNode.ln_version = body.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.' }); logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Connecting to the Core Lightning\'s Websocket Server.' });
clWsClient.updateSelectedNode(req.session.selectedNode); clWsClient.updateSelectedNode(req.session.selectedNode);
databaseService.loadDatabase(req.session.selectedNode); databaseService.loadDatabase(req.session);
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Node Information Received', data: body }); logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Node Information Received', data: body });
return res.status(200).json(body); return res.status(200).json(body);
} }

@ -31,10 +31,6 @@ export const listInvoices = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Invoice', msg: 'Invoices List URL', data: options.url }); logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Invoice', msg: 'Invoices List URL', data: options.url });
request(options).then((body) => { request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Invoice', msg: 'Invoices List Received', data: 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 = common.sortDescByKey(body.invoices, 'expires_at');
}
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoice', msg: 'Sorted Invoices List Received', data: body });
res.status(200).json(body); res.status(200).json(body);
}).catch((errRes) => { }).catch((errRes) => {
const err = common.handleError(errRes, 'Invoice', 'List Invoices Error', req.session.selectedNode); const err = common.handleError(errRes, 'Invoice', 'List Invoices Error', req.session.selectedNode);

@ -78,8 +78,10 @@ export const listNodes = (req, res, next) => {
body.forEach((node) => { body.forEach((node) => {
var _a, _b; var _a, _b;
if (node.option_will_fund) { 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')) ? (_a = node.option_will_fund.lease_fee_base_msat) === null || _a === void 0 ? void 0 : _a.replace('msat', '') : node.option_will_fund.lease_fee_base_msat; 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.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')) ? (_b = node.option_will_fund.channel_fee_max_base_msat) === null || _b === void 0 ? void 0 : _b.replace('msat', '') : node.option_will_fund.channel_fee_max_base_msat; node.option_will_fund.lease_fee_base_msat.includes('msat')) ? (_a = node.option_will_fund.lease_fee_base_msat) === null || _a === void 0 ? void 0 : _a.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')) ? (_b = node.option_will_fund.channel_fee_max_base_msat) === null || _b === void 0 ? void 0 : _b.replace('msat', '') : node.option_will_fund.channel_fee_max_base_msat;
} }
return node; return node;
}); });

@ -11,9 +11,6 @@ export const listOfferBookmarks = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Getting Offer Bookmarks..' }); logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Getting Offer Bookmarks..' });
databaseService.find(req.session.selectedNode, CollectionsEnum.OFFERS).then((offers) => { 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 }); logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Offer Bookmarks Received', data: offers });
if (offers && offers.length > 0) {
offers = common.sortDescByKey(offers, 'lastUpdatedAt');
}
res.status(200).json(offers); res.status(200).json(offers);
}).catch((errRes) => { }).catch((errRes) => {
const err = common.handleError(errRes, 'Offers', 'Offer Bookmarks Error', req.session.selectedNode); const err = common.handleError(errRes, 'Offers', 'Offer Bookmarks Error', req.session.selectedNode);
@ -22,7 +19,7 @@ export const listOfferBookmarks = (req, res, next) => {
}; };
export const deleteOfferBookmark = (req, res, next) => { export const deleteOfferBookmark = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Deleting Offer Bookmark..' }); logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Deleting Offer Bookmark..' });
databaseService.destroy(req.session.selectedNode, CollectionsEnum.OFFERS, CollectionFieldsEnum.BOLT12, req.params.offerStr).then((deleteRes) => { 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 }); logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Offer Bookmark Deleted', data: deleteRes });
res.status(204).json(req.params.offerStr); res.status(204).json(req.params.offerStr);
}).catch((errRes) => { }).catch((errRes) => {

@ -44,9 +44,6 @@ export const getUTXOs = (req, res, next) => {
} }
options.url = req.session.selectedNode.ln_server_url + '/v1/listFunds'; options.url = req.session.selectedNode.ln_server_url + '/v1/listFunds';
request(options).then((body) => { request(options).then((body) => {
if (body.outputs) {
body.outputs = common.sortDescByStrKey(body.outputs, 'status');
}
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'Funds List Received', data: body }); logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'Funds List Received', data: body });
res.status(200).json(body); res.status(200).json(body);
}).catch((errRes) => { }).catch((errRes) => {

@ -73,10 +73,6 @@ export const listPayments = (req, res, next) => {
options.url = req.session.selectedNode.ln_server_url + '/v1/pay/listPayments'; options.url = req.session.selectedNode.ln_server_url + '/v1/pay/listPayments';
request(options).then((body) => { request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Payments', msg: 'Payment List Received', data: body.payments }); logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Payments', msg: 'Payment List Received', data: body.payments });
if (body && body.payments && body.payments.length > 0) {
body.payments = common.sortDescByKey(body.payments, 'created_at');
}
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Sorted Payments List Received', data: body.payments });
res.status(200).json(groupBy(body.payments)); res.status(200).json(groupBy(body.payments));
}).catch((errRes) => { }).catch((errRes) => {
const err = common.handleError(errRes, 'Payments', 'List Payments Error', req.session.selectedNode); const err = common.handleError(errRes, 'Payments', 'List Payments Error', req.session.selectedNode);
@ -108,19 +104,25 @@ export const postPayment = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Payment Sent', data: body }); logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Payment Sent', data: body });
if (req.body.paymentType === 'OFFER') { if (req.body.paymentType === 'OFFER') {
if (req.body.saveToDB && req.body.bolt12) { 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() }; 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) { if (req.body.vendor) {
offerToUpdate['vendor'] = req.body.vendor; offerToUpdate['vendor'] = req.body.vendor;
} }
if (req.body.description) { if (req.body.description) {
offerToUpdate['description'] = req.body.description; offerToUpdate['description'] = req.body.description;
} }
return databaseService.update(req.session.selectedNode, CollectionsEnum.OFFERS, offerToUpdate, CollectionFieldsEnum.BOLT12, req.body.bolt12).then((updatedOffer) => { // eslint-disable-next-line arrow-body-style
logger.log({ level: 'DEBUG', fileName: 'Offer', msg: 'Offer Updated', data: updatedOffer }); return databaseService.validateDocument(CollectionsEnum.OFFERS, offerToUpdate).then((validated) => {
return res.status(201).json({ paymentResponse: body, saveToDBResponse: updatedOffer }); return databaseService.update(req.session.selectedNode, CollectionsEnum.OFFERS, offerToUpdate, CollectionFieldsEnum.BOLT12, req.body.bolt12).then((updatedOffer) => {
}).catch((errDB) => { logger.log({ level: 'DEBUG', fileName: 'Payments', msg: 'Offer Updated', data: updatedOffer });
logger.log({ selectedNode: req.session.selectedNode, level: 'ERROR', fileName: 'Payments', msg: 'Offer DB update error', error: errDB }); return res.status(201).json({ paymentResponse: body, saveToDBResponse: updatedOffer });
return res.status(201).json({ paymentResponse: body, saveToDBError: errDB }); }).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 { else {

@ -17,9 +17,8 @@ export const getPeers = (req, res, next) => {
peer.alias = peer.id.substring(0, 20); peer.alias = peer.id.substring(0, 20);
} }
}); });
const peers = (body) ? common.sortDescByStrKey(body, 'alias') : []; logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Peers with Alias Received', data: body });
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Peers with Alias Received', data: peers }); res.status(200).json(body || []);
res.status(200).json(peers);
}).catch((errRes) => { }).catch((errRes) => {
const err = common.handleError(errRes, 'Peers', 'List Peers Error', req.session.selectedNode); const err = common.handleError(errRes, 'Peers', 'List Peers Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error }); return res.status(err.statusCode).json({ message: err.message, error: err.error });
@ -33,12 +32,11 @@ export const postPeer = (req, res, next) => {
} }
options.url = req.session.selectedNode.ln_server_url + '/v1/peer/connect'; options.url = req.session.selectedNode.ln_server_url + '/v1/peer/connect';
options.body = req.body; options.body = req.body;
request.post(options).then((body) => { request.post(options).then((connectRes) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Peers', msg: 'Peer Connected', data: body }); 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'; options.url = req.session.selectedNode.ln_server_url + '/v1/peer/listPeers';
request(options).then((body) => { request(options).then((listPeersRes) => {
let peers = (body) ? common.sortDescByStrKey(body, 'alias') : []; const peers = listPeersRes ? common.newestOnTop(listPeersRes, 'id', req.body.id) : [];
peers = common.newestOnTop(peers, 'id', req.body.id);
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Peers List after Connect Received', data: peers }); logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Peers List after Connect Received', data: peers });
res.status(201).json(peers); res.status(201).json(peers);
}).catch((errRes) => { }).catch((errRes) => {

@ -13,10 +13,10 @@ export const simplifyAllChannels = (selNode, channels) => {
nodeId: channel.nodeId ? channel.nodeId : '', nodeId: channel.nodeId ? channel.nodeId : '',
channelId: channel.channelId ? channel.channelId : '', channelId: channel.channelId ? channel.channelId : '',
state: channel.state ? channel.state : '', state: channel.state ? channel.state : '',
channelFlags: channel.data && channel.data.commitments && channel.data.commitments.channelFlags ? channel.data.commitments.channelFlags : 0, 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, 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, toRemote: (channel.data.commitments.localCommit.spec.toRemote) ? Math.round(+channel.data.commitments.localCommit.spec.toRemote / 1000) : 0,
shortChannelId: channel.data && channel.data.shortChannelId ? channel.data.shortChannelId : '', 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, 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, 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, feeBaseMsat: channel.data && channel.data.channelUpdate && channel.data.channelUpdate.feeBaseMsat ? channel.data.channelUpdate.feeBaseMsat : 0,

@ -88,9 +88,6 @@ export const arrangePayments = (selNode, body) => {
relayedEle.amountOut = Math.round(relayedEle.amountOut / 1000); relayedEle.amountOut = Math.round(relayedEle.amountOut / 1000);
} }
}); });
payments.sent = common.sortDescByKey(payments.sent, 'firstPartTimestamp');
payments.received = common.sortDescByKey(payments.received, 'firstPartTimestamp');
payments.relayed = common.sortDescByKey(payments.relayed, 'timestamp');
logger.log({ selectedNode: selNode, level: 'DEBUG', fileName: 'Fees', msg: 'Arranged Payments Received', data: payments }); logger.log({ selectedNode: selNode, level: 'DEBUG', fileName: 'Fees', msg: 'Arranged Payments Received', data: payments });
return payments; return payments;
}; };

@ -38,11 +38,11 @@ export const getInfo = (req, res, next) => {
body.lnImplementation = 'Eclair'; body.lnImplementation = 'Eclair';
req.session.selectedNode.ln_version = body.version.split('-')[0] || ''; req.session.selectedNode.ln_version = body.version.split('-')[0] || '';
eclWsClient.updateSelectedNode(req.session.selectedNode); eclWsClient.updateSelectedNode(req.session.selectedNode);
databaseService.loadDatabase(req.session.selectedNode); databaseService.loadDatabase(req.session);
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Node Information Received', data: body }); logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Node Information Received', data: body });
return res.status(200).json(body); return res.status(200).json(body);
}).catch((errRes) => { }).catch((errRes) => {
const err = common.handleError(errRes, 'GetInfo', 'Get Info Error', req); const err = common.handleError(errRes, 'GetInfo', 'Get Info Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error }); return res.status(err.statusCode).json({ message: err.message, error: err.error });
}); });
} }

@ -71,10 +71,7 @@ export const listInvoices = (req, res, next) => {
const invoices = (!body[0] || body[0].length <= 0) ? [] : body[0]; const invoices = (!body[0] || body[0].length <= 0) ? [] : body[0];
pendingInvoices = (!body[1] || body[1].length <= 0) ? [] : body[1]; pendingInvoices = (!body[1] || body[1].length <= 0) ? [] : body[1];
return Promise.all(invoices === null || invoices === void 0 ? void 0 : invoices.map((invoice) => getReceivedPaymentInfo(req.session.selectedNode.ln_server_url, invoice))). return Promise.all(invoices === null || invoices === void 0 ? void 0 : invoices.map((invoice) => getReceivedPaymentInfo(req.session.selectedNode.ln_server_url, invoice))).
then((values) => { then((values) => res.status(200).json(invoices));
body = common.sortDescByKey(invoices, 'expiresAt');
return res.status(200).json(invoices);
});
}); });
} }
else { else {
@ -86,7 +83,6 @@ export const listInvoices = (req, res, next) => {
if (invoices && invoices.length > 0) { if (invoices && invoices.length > 0) {
return Promise.all(invoices === null || invoices === void 0 ? void 0 : invoices.map((invoice) => getReceivedPaymentInfo(req.session.selectedNode.ln_server_url, invoice))). return Promise.all(invoices === null || invoices === void 0 ? void 0 : invoices.map((invoice) => getReceivedPaymentInfo(req.session.selectedNode.ln_server_url, invoice))).
then((values) => { then((values) => {
body = common.sortDescByKey(invoices, 'expiresAt');
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoices', msg: 'Sorted Invoices List Received', data: invoices }); logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoices', msg: 'Sorted Invoices List Received', data: invoices });
return res.status(200).json(invoices); return res.status(200).json(invoices);
}). }).

@ -66,9 +66,6 @@ export const getTransactions = (req, res, next) => {
}; };
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'Getting On Chain Transactions Options', data: options.form }); logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'Getting On Chain Transactions Options', data: options.form });
request.post(options).then((body) => { request.post(options).then((body) => {
if (body && body.length > 0) {
body = common.sortDescByKey(body, 'timestamp');
}
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'On Chain Transactions Received', data: body }); logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'On Chain Transactions Received', data: body });
res.status(200).json(body); res.status(200).json(body);
}).catch((errRes) => { }).catch((errRes) => {

@ -37,7 +37,6 @@ export const getPeers = (req, res, next) => {
peer.alias = foundPeer ? foundPeer.alias : peer.nodeId.substring(0, 20); peer.alias = foundPeer ? foundPeer.alias : peer.nodeId.substring(0, 20);
return peer; return peer;
}); });
body = common.sortDescByStrKey(body, 'alias');
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Sorted Peers List Received', data: body }); logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Sorted Peers List Received', data: body });
res.status(200).json(body); res.status(200).json(body);
}); });
@ -90,8 +89,7 @@ export const connectPeer = (req, res, next) => {
peer.alias = foundPeer ? foundPeer.alias : peer.nodeId.substring(0, 20); peer.alias = foundPeer ? foundPeer.alias : peer.nodeId.substring(0, 20);
return peer; return peer;
}); });
let peers = (body) ? common.sortDescByStrKey(body, 'alias') : []; const peers = common.newestOnTop(body || [], 'nodeId', req.query.nodeId ? req.query.nodeId : req.query.uri ? req.query.uri.substring(0, req.query.uri.indexOf('@')) : '');
peers = common.newestOnTop(peers, '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 }); logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Peers List after Connect Received', data: peers });
res.status(201).json(peers); res.status(201).json(peers);
}); });

@ -9,11 +9,11 @@ export const getAliasForChannel = (selNode, channel) => {
options.url = selNode.ln_server_url + '/v1/graph/node/' + pubkey; options.url = selNode.ln_server_url + '/v1/graph/node/' + pubkey;
return request(options).then((aliasBody) => { return request(options).then((aliasBody) => {
logger.log({ selectedNode: selNode, level: 'DEBUG', fileName: 'Channels', msg: 'Alias Received', data: aliasBody.node.alias }); logger.log({ selectedNode: selNode, level: 'DEBUG', fileName: 'Channels', msg: 'Alias Received', data: aliasBody.node.alias });
channel.remote_alias = aliasBody.node.alias; channel.remote_alias = aliasBody.node.alias && aliasBody.node.alias !== '' ? aliasBody.node.alias : aliasBody.node.pub_key.slice(0, 20);
return aliasBody.node.alias; return channel;
}).catch((err) => { }).catch((err) => {
channel.remote_alias = pubkey.slice(0, 10) + '...' + pubkey.slice(-10); channel.remote_alias = pubkey.slice(0, 20);
return pubkey; return channel;
}); });
}; };
export const getAllChannels = (req, res, next) => { export const getAllChannels = (req, res, next) => {
@ -38,7 +38,6 @@ export const getAllChannels = (req, res, next) => {
channel.balancedness = (total === 0) ? 1 : (1 - Math.abs((local - remote) / total)).toFixed(3); channel.balancedness = (total === 0) ? 1 : (1 - Math.abs((local - remote) / total)).toFixed(3);
return getAliasForChannel(req.session.selectedNode, channel); return getAliasForChannel(req.session.selectedNode, channel);
})).then((values) => { })).then((values) => {
body.channels = common.sortDescByKey(body.channels, 'balancedness');
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Sorted Channels List Received', data: body }); logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Sorted Channels List Received', data: body });
return res.status(200).json(body); return res.status(200).json(body);
}).catch((errRes) => { }).catch((errRes) => {
@ -73,11 +72,11 @@ export const getPendingChannels = (req, res, next) => {
if (body.pending_open_channels && body.pending_open_channels.length > 0) { if (body.pending_open_channels && body.pending_open_channels.length > 0) {
(_a = body.pending_open_channels) === null || _a === void 0 ? void 0 : _a.map((channel) => promises.push(getAliasForChannel(req.session.selectedNode, channel.channel))); (_a = body.pending_open_channels) === null || _a === void 0 ? void 0 : _a.map((channel) => promises.push(getAliasForChannel(req.session.selectedNode, channel.channel)));
} }
if (body.pending_closing_channels && body.pending_closing_channels.length > 0) {
(_b = body.pending_closing_channels) === null || _b === void 0 ? void 0 : _b.map((channel) => promises.push(getAliasForChannel(req.session.selectedNode, channel.channel)));
}
if (body.pending_force_closing_channels && body.pending_force_closing_channels.length > 0) { if (body.pending_force_closing_channels && body.pending_force_closing_channels.length > 0) {
(_c = body.pending_force_closing_channels) === null || _c === void 0 ? void 0 : _c.map((channel) => promises.push(getAliasForChannel(req.session.selectedNode, channel.channel))); (_b = body.pending_force_closing_channels) === null || _b === void 0 ? void 0 : _b.map((channel) => promises.push(getAliasForChannel(req.session.selectedNode, channel.channel)));
}
if (body.pending_closing_channels && body.pending_closing_channels.length > 0) {
(_c = body.pending_closing_channels) === null || _c === void 0 ? void 0 : _c.map((channel) => promises.push(getAliasForChannel(req.session.selectedNode, channel.channel)));
} }
if (body.waiting_close_channels && body.waiting_close_channels.length > 0) { if (body.waiting_close_channels && body.waiting_close_channels.length > 0) {
(_d = body.waiting_close_channels) === null || _d === void 0 ? void 0 : _d.map((channel) => promises.push(getAliasForChannel(req.session.selectedNode, channel.channel))); (_d = body.waiting_close_channels) === null || _d === void 0 ? void 0 : _d.map((channel) => promises.push(getAliasForChannel(req.session.selectedNode, channel.channel)));
@ -110,7 +109,6 @@ export const getClosedChannels = (req, res, next) => {
channel.close_type = (!channel.close_type) ? 'COOPERATIVE_CLOSE' : channel.close_type; channel.close_type = (!channel.close_type) ? 'COOPERATIVE_CLOSE' : channel.close_type;
return getAliasForChannel(req.session.selectedNode, channel); return getAliasForChannel(req.session.selectedNode, channel);
})).then((values) => { })).then((values) => {
body.channels = common.sortDescByKey(body.channels, 'close_height');
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Closed Channels List Received', data: body }); logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Closed Channels List Received', data: body });
return res.status(200).json(body); return res.status(200).json(body);
}).catch((errRes) => { }).catch((errRes) => {
@ -161,7 +159,7 @@ export const postTransactions = (req, res, next) => {
if (options.error) { if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error }); return res.status(options.statusCode).json({ message: options.message, error: options.error });
} }
options.url = req.session.selectedNode.ln_server_url + '/v1/channels/transactions'; options.url = req.session.selectedNode.ln_server_url + '/v1/channels/transaction-stream';
options.form = { payment_request: req.body.paymentReq }; options.form = { payment_request: req.body.paymentReq };
if (req.body.paymentAmount) { if (req.body.paymentAmount) {
options.form.amt = req.body.paymentAmount; options.form.amt = req.body.paymentAmount;

@ -45,7 +45,7 @@ export const getInfo = (req, res, next) => {
else { else {
req.session.selectedNode.ln_version = body.version.split('-')[0] || ''; req.session.selectedNode.ln_version = body.version.split('-')[0] || '';
lndWsClient.updateSelectedNode(req.session.selectedNode); lndWsClient.updateSelectedNode(req.session.selectedNode);
databaseService.loadDatabase(req.session.selectedNode); databaseService.loadDatabase(req.session);
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Node Information Received', data: body }); logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Node Information Received', data: body });
return res.status(200).json(body); return res.status(200).json(body);
} }

@ -10,7 +10,7 @@ export const getAliasFromPubkey = (selNode, pubkey) => {
logger.log({ selectedNode: selNode, level: 'DEBUG', fileName: 'Graph', msg: 'Alias Received', data: res.node.alias }); logger.log({ selectedNode: selNode, level: 'DEBUG', fileName: 'Graph', msg: 'Alias Received', data: res.node.alias });
return res.node.alias; return res.node.alias;
}). }).
catch((err) => pubkey.substring(0, 17) + '...'); catch((err) => pubkey.substring(0, 20));
}; };
export const getDescribeGraph = (req, res, next) => { export const getDescribeGraph = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Getting Network Graph..' }); logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Getting Network Graph..' });

@ -46,7 +46,6 @@ export const listInvoices = (req, res, next) => {
invoice.r_hash = invoice.r_hash ? Buffer.from(invoice.r_hash, '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; invoice.description_hash = invoice.description_hash ? Buffer.from(invoice.description_hash, 'base64').toString('hex') : null;
}); });
body.invoices = common.sortDescByKey(body.invoices, 'creation_date');
} }
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoice', msg: 'Sorted Invoices List Received', data: body }); logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoice', msg: 'Sorted Invoices List Received', data: body });
res.status(200).json(body); res.status(200).json(body);
@ -62,18 +61,7 @@ export const addInvoice = (req, res, next) => {
return res.status(options.statusCode).json({ message: options.message, error: 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.url = req.session.selectedNode.ln_server_url + '/v1/invoices';
options.form = { options.form = JSON.stringify(req.body);
memo: req.body.memo,
private: req.body.private,
expiry: req.body.expiry
};
if (req.body.amount > 0 && req.body.amount < 1) {
options.form.value_msat = req.body.amount * 1000;
}
else {
options.form.value = req.body.amount;
}
options.form = JSON.stringify(options.form);
request.post(options).then((body) => { request.post(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoice', msg: 'Invoice Added', data: body }); logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoice', msg: 'Invoice Added', data: body });
try { try {

@ -58,10 +58,6 @@ export const getPayments = (req, res, next) => {
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; 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) => { request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Payments', msg: 'Payment List Received', data: body }); logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Payments', msg: 'Payment List Received', data: body });
if (body.payments && body.payments.length > 0) {
body.payments = common.sortDescByKey(body.payments, 'creation_date');
}
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Sorted Payments List Received', data: body });
res.status(200).json(body); res.status(200).json(body);
}).catch((errRes) => { }).catch((errRes) => {
const err = common.handleError(errRes, 'Payments', 'List Payments Error', req.session.selectedNode); const err = common.handleError(errRes, 'Payments', 'List Payments Error', req.session.selectedNode);

@ -11,7 +11,7 @@ export const getAliasForPeers = (selNode, peer) => {
peer.alias = aliasBody.node.alias; peer.alias = aliasBody.node.alias;
return aliasBody.node.alias; return aliasBody.node.alias;
}).catch((err) => { }).catch((err) => {
peer.alias = peer.pub_key.slice(0, 10) + '...' + peer.pub_key.slice(-10); peer.alias = peer.pub_key.slice(0, 20);
return peer.pub_key; return peer.pub_key;
}); });
}; };
@ -26,10 +26,6 @@ export const getPeers = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Peers', msg: 'Peers List Received', data: body }); logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Peers', msg: 'Peers List Received', data: body });
const peers = !body.peers ? [] : body.peers; const peers = !body.peers ? [] : body.peers;
return Promise.all(peers === null || peers === void 0 ? void 0 : peers.map((peer) => getAliasForPeers(req.session.selectedNode, peer))).then((values) => { return Promise.all(peers === null || peers === void 0 ? void 0 : peers.map((peer) => getAliasForPeers(req.session.selectedNode, peer))).then((values) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Peers', msg: 'Peers with Alias before Sort', data: body });
if (body.peers) {
body.peers = common.sortDescByStrKey(body.peers, 'alias');
}
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Sorted Peers List Received', data: body.peers }); logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Sorted Peers List Received', data: body.peers });
res.status(200).json(body.peers); res.status(200).json(body.peers);
}); });
@ -56,7 +52,6 @@ export const postPeer = (req, res, next) => {
const peers = (!body.peers) ? [] : body.peers; const peers = (!body.peers) ? [] : body.peers;
return Promise.all(peers === null || peers === void 0 ? void 0 : peers.map((peer) => getAliasForPeers(req.session.selectedNode, peer))).then((values) => { return Promise.all(peers === null || peers === void 0 ? void 0 : peers.map((peer) => getAliasForPeers(req.session.selectedNode, peer))).then((values) => {
if (body.peers) { if (body.peers) {
body.peers = common.sortDescByStrKey(body.peers, 'alias');
body.peers = common.newestOnTop(body.peers, 'pub_key', req.body.pubkey); 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 }); logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Peers List after Connect Received', data: body });
} }

@ -46,9 +46,6 @@ export const getAllForwardingEvents = (req, start, end, offset, caller, callback
} }
if (!body.last_offset_index || body.last_offset_index < offset + num_max_events) { 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; responseData[caller].last_offset_index = body.last_offset_index ? body.last_offset_index : 0;
if (responseData[caller].forwarding_events) {
responseData[caller].forwarding_events = common.sortDescByKey(responseData[caller].forwarding_events, 'timestamp');
}
return callback(responseData[caller]); return callback(responseData[caller]);
} }
else { else {

@ -13,10 +13,6 @@ export const getTransactions = (req, res, next) => {
options.url = req.session.selectedNode.ln_server_url + '/v1/transactions'; options.url = req.session.selectedNode.ln_server_url + '/v1/transactions';
request(options).then((body) => { request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Transactions', msg: 'Transactions List Received', data: body }); logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Transactions', msg: 'Transactions List Received', data: body });
if (body.transactions && body.transactions.length > 0) {
body.transactions = common.sortDescByKey(body.transactions, 'time_stamp');
}
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Transactions', msg: 'Sorted Transactions List Received', data: body.transactions });
res.status(200).json(body.transactions); res.status(200).json(body.transactions);
}).catch((errRes) => { }).catch((errRes) => {
const err = common.handleError(errRes, 'Transactions', 'List Transactions Error', req.session.selectedNode); const err = common.handleError(errRes, 'Transactions', 'List Transactions Error', req.session.selectedNode);

@ -19,7 +19,7 @@ export const updateSelectedNode = (req, res, next) => {
if (req.headers && req.headers.authorization && req.headers.authorization !== '') { if (req.headers && req.headers.authorization && req.headers.authorization !== '') {
wsServer.updateLNWSClientDetails(req.session.id, +req.session.selectedNode.index, +req.params.prevNodeIndex); wsServer.updateLNWSClientDetails(req.session.id, +req.session.selectedNode.index, +req.params.prevNodeIndex);
if (req.params.prevNodeIndex !== -1) { if (req.params.prevNodeIndex !== -1) {
databaseService.unloadDatabase(req.params.prevNodeIndex); databaseService.unloadDatabase(req.params.prevNodeIndex, req.session.id);
} }
} }
const responseVal = !req.session.selectedNode.ln_node ? '' : req.session.selectedNode.ln_node; const responseVal = !req.session.selectedNode.ln_node ? '' : req.session.selectedNode.ln_node;
@ -49,10 +49,11 @@ export const getRTLConfigInitial = (req, res, next) => {
const nodesArr = []; const nodesArr = [];
if (common.nodes && common.nodes.length > 0) { if (common.nodes && common.nodes.length > 0) {
common.nodes.forEach((node, i) => { common.nodes.forEach((node, i) => {
const settings = {}; const settings = { unannouncedChannels: false };
settings.userPersona = node.user_persona ? node.user_persona : 'MERCHANT'; settings.userPersona = node.user_persona ? node.user_persona : 'MERCHANT';
settings.themeMode = (node.theme_mode) ? node.theme_mode : 'DAY'; settings.themeMode = (node.theme_mode) ? node.theme_mode : 'DAY';
settings.themeColor = (node.theme_color) ? node.theme_color : 'PURPLE'; 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.fiatConversion = (node.fiat_conversion) ? !!node.fiat_conversion : false;
settings.currencyUnit = node.currency_unit; settings.currencyUnit = node.currency_unit;
nodesArr.push({ nodesArr.push({
@ -97,10 +98,11 @@ export const getRTLConfig = (req, res, next) => {
authentication.configPath = (node.config_path) ? node.config_path : ''; authentication.configPath = (node.config_path) ? node.config_path : '';
authentication.swapMacaroonPath = (node.swap_macaroon_path) ? node.swap_macaroon_path : ''; authentication.swapMacaroonPath = (node.swap_macaroon_path) ? node.swap_macaroon_path : '';
authentication.boltzMacaroonPath = (node.boltz_macaroon_path) ? node.boltz_macaroon_path : ''; authentication.boltzMacaroonPath = (node.boltz_macaroon_path) ? node.boltz_macaroon_path : '';
const settings = {}; const settings = { unannouncedChannels: false };
settings.userPersona = node.user_persona ? node.user_persona : 'MERCHANT'; settings.userPersona = node.user_persona ? node.user_persona : 'MERCHANT';
settings.themeMode = (node.theme_mode) ? node.theme_mode : 'DAY'; settings.themeMode = (node.theme_mode) ? node.theme_mode : 'DAY';
settings.themeColor = (node.theme_color) ? node.theme_color : 'PURPLE'; 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.fiatConversion = (node.fiat_conversion) ? !!node.fiat_conversion : false;
settings.bitcoindConfigPath = node.bitcoind_config_path; settings.bitcoindConfigPath = node.bitcoind_config_path;
settings.logLevel = node.log_level ? node.log_level : 'ERROR'; settings.logLevel = node.log_level ? node.log_level : 'ERROR';
@ -108,6 +110,7 @@ export const getRTLConfig = (req, res, next) => {
settings.swapServerUrl = node.swap_server_url; settings.swapServerUrl = node.swap_server_url;
settings.boltzServerUrl = node.boltz_server_url; settings.boltzServerUrl = node.boltz_server_url;
settings.enableOffers = node.enable_offers; settings.enableOffers = node.enable_offers;
settings.enablePeerswap = node.enable_peerswap;
settings.channelBackupPath = node.channel_backup_path; settings.channelBackupPath = node.channel_backup_path;
settings.currencyUnit = node.currency_unit; settings.currencyUnit = node.currency_unit;
nodesArr.push({ nodesArr.push({
@ -134,6 +137,7 @@ export const updateUISettings = (req, res, next) => {
node.Settings.userPersona = req.body.updatedSettings.userPersona; node.Settings.userPersona = req.body.updatedSettings.userPersona;
node.Settings.themeMode = req.body.updatedSettings.themeMode; node.Settings.themeMode = req.body.updatedSettings.themeMode;
node.Settings.themeColor = req.body.updatedSettings.themeColor; node.Settings.themeColor = req.body.updatedSettings.themeColor;
node.Settings.unannouncedChannels = req.body.updatedSettings.unannouncedChannels;
node.Settings.fiatConversion = req.body.updatedSettings.fiatConversion; node.Settings.fiatConversion = req.body.updatedSettings.fiatConversion;
if (req.body.updatedSettings.fiatConversion) { if (req.body.updatedSettings.fiatConversion) {
node.Settings.currencyUnit = req.body.updatedSettings.currencyUnit ? req.body.updatedSettings.currencyUnit : 'USD'; node.Settings.currencyUnit = req.body.updatedSettings.currencyUnit ? req.body.updatedSettings.currencyUnit : 'USD';
@ -145,6 +149,7 @@ export const updateUISettings = (req, res, next) => {
selectedNode.user_persona = req.body.updatedSettings.userPersona; selectedNode.user_persona = req.body.updatedSettings.userPersona;
selectedNode.theme_mode = req.body.updatedSettings.themeMode; selectedNode.theme_mode = req.body.updatedSettings.themeMode;
selectedNode.theme_color = req.body.updatedSettings.themeColor; selectedNode.theme_color = req.body.updatedSettings.themeColor;
selectedNode.unannounced_channels = req.body.updatedSettings.unannouncedChannels;
selectedNode.fiat_conversion = req.body.updatedSettings.fiatConversion; selectedNode.fiat_conversion = req.body.updatedSettings.fiatConversion;
if (req.body.updatedSettings.fiatConversion) { if (req.body.updatedSettings.fiatConversion) {
selectedNode.currency_unit = req.body.updatedSettings.currencyUnit ? req.body.updatedSettings.currencyUnit : 'USD'; selectedNode.currency_unit = req.body.updatedSettings.currencyUnit ? req.body.updatedSettings.currencyUnit : 'USD';
@ -244,7 +249,7 @@ export const getConfig = (req, res, next) => {
if (jsonConfig['Application Options'] && jsonConfig['Application Options'].color) { if (jsonConfig['Application Options'] && jsonConfig['Application Options'].color) {
jsonConfig['Application Options'].color = '#' + jsonConfig['Application Options'].color; jsonConfig['Application Options'].color = '#' + jsonConfig['Application Options'].color;
} }
if (req.session.selectedNode.ln_implementation === 'ECL' && !jsonConfig['eclair.api.password']) { if (req.params.nodeType === 'ln' && req.session.selectedNode.ln_implementation === 'ECL' && !jsonConfig['eclair.api.password']) {
fileFormat = 'HOCON'; fileFormat = 'HOCON';
jsonConfig = parseHocon(data); jsonConfig = parseHocon(data);
} }
@ -311,7 +316,7 @@ export const updateServiceSettings = (req, res, next) => {
const RTLConfFile = common.rtl_conf_file_path + sep + 'RTL-Config.json'; const RTLConfFile = common.rtl_conf_file_path + sep + 'RTL-Config.json';
const config = JSON.parse(fs.readFileSync(RTLConfFile, 'utf-8')); const config = JSON.parse(fs.readFileSync(RTLConfFile, 'utf-8'));
const selectedNode = common.findNode(req.session.selectedNode.index); const selectedNode = common.findNode(req.session.selectedNode.index);
config.nodes.find((node) => { config.nodes.forEach((node) => {
if (node.index === req.session.selectedNode.index) { if (node.index === req.session.selectedNode.index) {
switch (req.body.service) { switch (req.body.service) {
case 'LOOP': case 'LOOP':
@ -346,6 +351,10 @@ export const updateServiceSettings = (req, res, next) => {
node.Settings.enableOffers = req.body.settings.enableOffers; node.Settings.enableOffers = req.body.settings.enableOffers;
selectedNode.enable_offers = req.body.settings.enableOffers; selectedNode.enable_offers = req.body.settings.enableOffers;
break; break;
case 'PEERSWAP':
node.Settings.enablePeerswap = req.body.settings.enablePeerswap;
selectedNode.enable_peerswap = req.body.settings.enablePeerswap;
break;
default: default:
break; break;
} }
@ -374,7 +383,8 @@ export const maskPasswords = (obj) => {
} }
if (typeof keys[i] === 'string' && if (typeof keys[i] === 'string' &&
(keys[i].toLowerCase().includes('password') || keys[i].toLowerCase().includes('multipass') || (keys[i].toLowerCase().includes('password') || keys[i].toLowerCase().includes('multipass') ||
keys[i].toLowerCase().includes('rpcpass') || keys[i].toLowerCase().includes('rpcpassword'))) { keys[i].toLowerCase().includes('rpcpass') || keys[i].toLowerCase().includes('rpcpassword') ||
keys[i].toLowerCase().includes('rpcuser'))) {
obj[keys[i]] = '********************'; obj[keys[i]] = '********************';
} }
} }

@ -124,7 +124,7 @@ export const resetPassword = (req, res, next) => {
export const logoutUser = (req, res, next) => { export const logoutUser = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Authenticate', msg: 'Logged out' }); logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Authenticate', msg: 'Logged out' });
if (req.session.selectedNode && req.session.selectedNode.index) { if (req.session.selectedNode && req.session.selectedNode.index) {
databaseService.unloadDatabase(+req.session.selectedNode.index); databaseService.unloadDatabase(+req.session.selectedNode.index, req.session.id);
} }
req.session.destroy((err) => { req.session.destroy((err) => {
res.clearCookie('connect.sid'); res.clearCookie('connect.sid');

@ -216,10 +216,6 @@ export const swaps = (req, res, next) => {
options.url = options.url + '/v1/loop/swaps'; options.url = options.url + '/v1/loop/swaps';
request(options).then((body) => { request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop Swaps Received', data: body }); logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop Swaps Received', data: body });
if (body.swaps && body.swaps.length > 0) {
body.swaps = common.sortDescByKey(body.swaps, 'initiation_time');
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Sorted Loop Swaps List Received', data: body });
}
res.status(200).json(body.swaps); res.status(200).json(body.swaps);
}).catch((errRes) => { }).catch((errRes) => {
const err = common.handleError(errRes, 'Loop', 'List Swaps Error', req.session.selectedNode); const err = common.handleError(errRes, 'Loop', 'List Swaps Error', req.session.selectedNode);

@ -0,0 +1,33 @@
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,5 +1,5 @@
export class CommonSelectedNode { 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, fiat_conversion, currency_unit, ln_version, api_version, enable_offers) { 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.options = options;
this.ln_server_url = ln_server_url; this.ln_server_url = ln_server_url;
this.macaroon_path = macaroon_path; this.macaroon_path = macaroon_path;
@ -20,11 +20,13 @@ export class CommonSelectedNode {
this.user_persona = user_persona; this.user_persona = user_persona;
this.theme_mode = theme_mode; this.theme_mode = theme_mode;
this.theme_color = theme_color; this.theme_color = theme_color;
this.unannounced_channels = unannounced_channels;
this.fiat_conversion = fiat_conversion; this.fiat_conversion = fiat_conversion;
this.currency_unit = currency_unit; this.currency_unit = currency_unit;
this.ln_version = ln_version; this.ln_version = ln_version;
this.api_version = api_version; this.api_version = api_version;
this.enable_offers = enable_offers; this.enable_offers = enable_offers;
this.enable_peerswap = enable_peerswap;
} }
} }
export class AuthenticationConfiguration { export class AuthenticationConfiguration {
@ -35,10 +37,11 @@ export class AuthenticationConfiguration {
} }
} }
export class NodeSettingsConfiguration { export class NodeSettingsConfiguration {
constructor(userPersona, themeMode, themeColor, fiatConversion, currencyUnit, bitcoindConfigPath, logLevel, lnServerUrl, swapServerUrl, boltzServerUrl, channelBackupPath, enableOffers) { constructor(userPersona, themeMode, themeColor, unannouncedChannels, fiatConversion, currencyUnit, bitcoindConfigPath, logLevel, lnServerUrl, swapServerUrl, boltzServerUrl, channelBackupPath, enableOffers, enablePeerswap) {
this.userPersona = userPersona; this.userPersona = userPersona;
this.themeMode = themeMode; this.themeMode = themeMode;
this.themeColor = themeColor; this.themeColor = themeColor;
this.unannouncedChannels = unannouncedChannels;
this.fiatConversion = fiatConversion; this.fiatConversion = fiatConversion;
this.currencyUnit = currencyUnit; this.currencyUnit = currencyUnit;
this.bitcoindConfigPath = bitcoindConfigPath; this.bitcoindConfigPath = bitcoindConfigPath;
@ -48,6 +51,7 @@ export class NodeSettingsConfiguration {
this.boltzServerUrl = boltzServerUrl; this.boltzServerUrl = boltzServerUrl;
this.channelBackupPath = channelBackupPath; this.channelBackupPath = channelBackupPath;
this.enableOffers = enableOffers; this.enableOffers = enableOffers;
this.enablePeerswap = enablePeerswap;
} }
} }
export class LogJSONObj { export class LogJSONObj {

@ -1,38 +1,139 @@
export var CollectionsEnum;
(function (CollectionsEnum) {
CollectionsEnum["OFFERS"] = "Offers";
})(CollectionsEnum || (CollectionsEnum = {}));
export var OfferFieldsEnum; export var OfferFieldsEnum;
(function (OfferFieldsEnum) { (function (OfferFieldsEnum) {
OfferFieldsEnum["BOLT12"] = "bolt12"; OfferFieldsEnum["BOLT12"] = "bolt12";
OfferFieldsEnum["AMOUNTMSAT"] = "amountmSat"; OfferFieldsEnum["AMOUNTMSAT"] = "amountMSat";
OfferFieldsEnum["TITLE"] = "title"; OfferFieldsEnum["TITLE"] = "title";
OfferFieldsEnum["VENDOR"] = "vendor"; OfferFieldsEnum["VENDOR"] = "vendor";
OfferFieldsEnum["DESCRIPTION"] = "description"; OfferFieldsEnum["DESCRIPTION"] = "description";
})(OfferFieldsEnum || (OfferFieldsEnum = {})); })(OfferFieldsEnum || (OfferFieldsEnum = {}));
export const CollectionFieldsEnum = Object.assign({}, OfferFieldsEnum);
export class Offer { export class Offer {
constructor(bolt12, amountmSat, title, vendor, description, lastUpdatedAt) { constructor(bolt12, amountMSat, title, vendor, description, lastUpdatedAt) {
this.bolt12 = bolt12; this.bolt12 = bolt12;
this.amountmSat = amountmSat; this.amountMSat = amountMSat;
this.title = title; this.title = title;
this.vendor = vendor; this.vendor = vendor;
this.description = description; this.description = description;
this.lastUpdatedAt = lastUpdatedAt; 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) => { export const validateOffer = (documentToValidate) => {
if (!documentToValidate.hasOwnProperty(CollectionFieldsEnum.BOLT12)) { if (!documentToValidate.hasOwnProperty(CollectionFieldsEnum.BOLT12)) {
return ({ isValid: false, error: CollectionFieldsEnum.BOLT12 + 'is mandatory.' }); return ({ isValid: false, error: 'Bolt12 is mandatory.' });
} }
if (!documentToValidate.hasOwnProperty(CollectionFieldsEnum.AMOUNTMSAT)) { if (!documentToValidate.hasOwnProperty(CollectionFieldsEnum.AMOUNTMSAT)) {
return ({ isValid: false, error: CollectionFieldsEnum.AMOUNTMSAT + 'is mandatory.' }); return ({ isValid: false, error: 'Amount mSat is mandatory.' });
} }
if (!documentToValidate.hasOwnProperty(CollectionFieldsEnum.TITLE)) { if (!documentToValidate.hasOwnProperty(CollectionFieldsEnum.TITLE)) {
return ({ isValid: false, error: CollectionFieldsEnum.TITLE + 'is mandatory.' }); return ({ isValid: false, error: 'Title is mandatory.' });
} }
if ((typeof documentToValidate[CollectionFieldsEnum.AMOUNTMSAT] !== 'number')) { if ((typeof documentToValidate[CollectionFieldsEnum.AMOUNTMSAT] !== 'number')) {
return ({ isValid: false, error: CollectionFieldsEnum.AMOUNTMSAT + 'should be a number.' }); return ({ isValid: false, error: 'Amount mSat should be a number.' });
} }
return ({ isValid: true }); 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 = Object.assign(Object.assign(Object.assign({}, OfferFieldsEnum), PageSettingsFieldsEnum), TableSettingsFieldsEnum);
export const LNDCollection = [CollectionsEnum.PAGE_SETTINGS];
export const ECLCollection = [CollectionsEnum.PAGE_SETTINGS];
export const CLNCollection = [CollectionsEnum.PAGE_SETTINGS, CollectionsEnum.OFFERS];

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

@ -0,0 +1,8 @@
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;

@ -41,13 +41,14 @@ export class ExpressApplication {
this.app.use(this.common.baseHref + '/api/ecl', eclRoutes); this.app.use(this.common.baseHref + '/api/ecl', eclRoutes);
this.app.use(this.common.baseHref, express.static(join(this.directoryName, '../..', 'frontend'))); this.app.use(this.common.baseHref, express.static(join(this.directoryName, '../..', 'frontend')));
this.app.use((req, res, next) => { this.app.use((req, res, next) => {
// For Angular App res.cookie('XSRF-TOKEN', req.csrfToken ? req.csrfToken() : ''); // RTL Angular Frontend
res.cookie('XSRF-TOKEN', req.csrfToken ? req.csrfToken() : ''); res.setHeader('XSRF-TOKEN', req.csrfToken ? req.csrfToken() : ''); // RTL Quickpay JQuery
// For JQuery Browser Plugin
res.setHeader('XSRF-TOKEN', req.csrfToken ? req.csrfToken() : '');
res.sendFile(join(this.directoryName, '../..', 'frontend', 'index.html')); res.sendFile(join(this.directoryName, '../..', 'frontend', 'index.html'));
}); });
this.app.use((err, req, res, next) => this.handleApplicationErrors(err, res)); 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.logger.log({ selectedNode: this.common.initSelectedNode, level: 'INFO', fileName: 'App', msg: 'Application Routes Set' });
}; };
this.handleApplicationErrors = (err, res) => { this.handleApplicationErrors = (err, res) => {

@ -24,7 +24,10 @@ export class CommonService {
this.read_dummy_data = false; this.read_dummy_data = false;
this.baseHref = '/rtl'; this.baseHref = '/rtl';
this.dummy_data_array_from_file = []; 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.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) => { this.getSwapServerOptions = (req) => {
const swapOptions = { const swapOptions = {
url: req.session.selectedNode.swap_server_url, url: req.session.selectedNode.swap_server_url,
@ -253,16 +256,29 @@ export class CommonService {
break; 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') }); this.logger.log({ selectedNode: selectedNode, level: 'ERROR', fileName: fileName, msg: errMsg, error: (typeof err === 'object' ? JSON.stringify(err) : (typeof err === 'string') ? err : 'Unknown Error') });
const newErrorObj = { let newErrorObj = { statusCode: 500, message: '', error: '' };
statusCode: err.statusCode ? err.statusCode : err.status ? err.status : (err.error && err.error.code && err.error.code === 'ECONNREFUSED') ? 503 : 500, if (err.code && err.code === 'ENOENT') {
message: (err.error && err.error.message) ? err.error.message : err.message ? err.message : errMsg, newErrorObj = {
error: ((err.error && err.error.error && err.error.error.error && typeof err.error.error.error === 'string') ? err.error.error.error : statusCode: 500,
(err.error && err.error.error && typeof err.error.error === 'string') ? err.error.error : message: 'No such file or directory ' + (err.path ? err.path : ''),
(err.error && err.error.error && err.error.error.message && typeof err.error.error.message === 'string') ? err.error.error.message : error: 'No such file or directory ' + (err.path ? err.path : '')
(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') 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.indexOf('Authentication Error') < 0 && err.name === 'StatusCodeError') {
newErrorObj.statusCode = 500;
}
return newErrorObj; return newErrorObj;
}; };
this.getRequestIP = (req) => ((typeof req.headers['x-forwarded-for'] === 'string' && req.headers['x-forwarded-for'].split(',').shift()) || this.getRequestIP = (req) => ((typeof req.headers['x-forwarded-for'] === 'string' && req.headers['x-forwarded-for'].split(',').shift()) ||

@ -66,12 +66,13 @@ export class ConfigService {
channelBackupPath: channelBackupPath, channelBackupPath: channelBackupPath,
logLevel: 'ERROR', logLevel: 'ERROR',
lnServerUrl: 'https://localhost:8080', lnServerUrl: 'https://localhost:8080',
fiatConversion: false fiatConversion: false,
unannouncedChannels: false
} }
} }
] ]
}; };
if (+process.env.RTL_SSO === 0) { if (+process.env.RTL_SSO === 0 || configData.SSO.rtlSSO === 0) {
configData['multiPass'] = 'password'; configData['multiPass'] = 'password';
} }
return configData; return configData;
@ -211,6 +212,7 @@ export class ConfigService {
this.common.nodes[idx].user_persona = node.Settings.userPersona ? node.Settings.userPersona : 'MERCHANT'; 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_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].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].log_level = node.Settings.logLevel ? node.Settings.logLevel : 'ERROR';
this.common.nodes[idx].fiat_conversion = node.Settings.fiatConversion ? !!node.Settings.fiatConversion : false; this.common.nodes[idx].fiat_conversion = node.Settings.fiatConversion ? !!node.Settings.fiatConversion : false;
if (this.common.nodes[idx].fiat_conversion) { if (this.common.nodes[idx].fiat_conversion) {
@ -241,6 +243,7 @@ export class ConfigService {
this.common.nodes[idx].boltz_macaroon_path = ''; 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_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].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; 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 { try {

@ -3,7 +3,7 @@ import { join, dirname, sep } from 'path';
import { fileURLToPath } from 'url'; import { fileURLToPath } from 'url';
import { Common } from '../utils/common.js'; import { Common } from '../utils/common.js';
import { Logger } from '../utils/logger.js'; import { Logger } from '../utils/logger.js';
import { CollectionsEnum, validateOffer } from '../models/database.model.js'; import { validateDocument, LNDCollection, ECLCollection, CLNCollection } from '../models/database.model.js';
export class DatabaseService { export class DatabaseService {
constructor() { constructor() {
this.common = Common; this.common = Common;
@ -11,33 +11,68 @@ export class DatabaseService {
this.dbDirectory = join(dirname(fileURLToPath(import.meta.url)), '..', '..', 'database'); this.dbDirectory = join(dirname(fileURLToPath(import.meta.url)), '..', '..', 'database');
this.nodeDatabase = {}; this.nodeDatabase = {};
} }
loadDatabase(selectedNode) { loadDatabase(session) {
const { id, selectedNode } = session;
try { try {
if (!this.nodeDatabase[selectedNode.index]) { if (!this.nodeDatabase[selectedNode.index]) {
this.nodeDatabase[selectedNode.index] = { adapter: null, data: null }; 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);
} }
this.nodeDatabase[selectedNode.index].adapter = new DatabaseAdapter(this.dbDirectory, 'rtldb', selectedNode);
this.nodeDatabase[selectedNode.index].data = this.nodeDatabase[selectedNode.index].adapter.fetchData();
} }
catch (err) { catch (err) {
this.logger.log({ selectedNode: selectedNode, level: 'ERROR', fileName: 'Database', msg: 'Database Load Error', error: err }); this.logger.log({ selectedNode: selectedNode, level: 'ERROR', fileName: 'Database', msg: 'Database Load Error', error: err });
} }
} }
create(selectedNode, collectionName, newDocument) { 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) => { return new Promise((resolve, reject) => {
try { try {
if (!selectedNode || !selectedNode.index) { if (!selectedNode || !selectedNode.index) {
reject(new Error('Selected Node Config Not Found.')); reject(new Error('Selected Node Config Not Found.'));
} }
const validationRes = this.validateDocument(CollectionsEnum.OFFERS, newDocument); this.nodeDatabase[selectedNode.index].data[collectionName] = newCollection;
if (!validationRes.isValid) { this.saveDatabase(selectedNode, collectionName);
reject(validationRes.error); resolve(this.nodeDatabase[selectedNode.index].data[collectionName]);
}
else {
this.nodeDatabase[selectedNode.index].data[collectionName].push(newDocument);
this.saveDatabase(+selectedNode.index);
resolve(newDocument);
}
} }
catch (errRes) { catch (errRes) {
reject(errRes); reject(errRes);
@ -64,23 +99,17 @@ export class DatabaseService {
} }
updatedDocument = foundDoc; updatedDocument = foundDoc;
} }
const validationRes = this.validateDocument(CollectionsEnum.OFFERS, updatedDocument); if (foundDocIdx > -1) {
if (!validationRes.isValid) { this.nodeDatabase[selectedNode.index].data[collectionName].splice(foundDocIdx, 1, updatedDocument);
reject(validationRes.error);
} }
else { else {
if (foundDocIdx > -1) { if (!this.nodeDatabase[selectedNode.index].data[collectionName]) {
this.nodeDatabase[selectedNode.index].data[collectionName].splice(foundDocIdx, 1, updatedDocument); this.nodeDatabase[selectedNode.index].data[collectionName] = [];
}
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.index); this.nodeDatabase[selectedNode.index].data[collectionName].push(updatedDocument);
resolve(updatedDocument);
} }
this.saveDatabase(selectedNode, collectionName);
resolve(updatedDocument);
} }
catch (errRes) { catch (errRes) {
reject(errRes); reject(errRes);
@ -105,7 +134,7 @@ export class DatabaseService {
} }
}); });
} }
destroy(selectedNode, collectionName, documentFieldName, documentFieldValue) { remove(selectedNode, collectionName, documentFieldName, documentFieldValue) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
try { try {
if (!selectedNode || !selectedNode.index) { if (!selectedNode || !selectedNode.index) {
@ -118,7 +147,7 @@ export class DatabaseService {
else { else {
reject(new Error('Unable to delete, document not found.')); reject(new Error('Unable to delete, document not found.'));
} }
this.saveDatabase(+selectedNode.index); this.saveDatabase(selectedNode, collectionName);
resolve(documentFieldValue); resolve(documentFieldValue);
} }
catch (errRes) { catch (errRes) {
@ -126,17 +155,10 @@ export class DatabaseService {
} }
}); });
} }
validateDocument(collectionName, documentToValidate) { saveDatabase(selectedNode, collectionName) {
switch (collectionName) { const nodeIndex = +selectedNode.index;
case CollectionsEnum.OFFERS:
return validateOffer(documentToValidate);
default:
return ({ isValid: false, error: 'Collection does not exist' });
}
}
saveDatabase(nodeIndex) {
try { try {
if (+nodeIndex < 1) { if (nodeIndex < 1) {
return true; return true;
} }
const selNode = this.nodeDatabase[nodeIndex] && this.nodeDatabase[nodeIndex].adapter && this.nodeDatabase[nodeIndex].adapter.selNode ? this.nodeDatabase[nodeIndex].adapter.selNode : null; const selNode = this.nodeDatabase[nodeIndex] && this.nodeDatabase[nodeIndex].adapter && this.nodeDatabase[nodeIndex].adapter.selNode ? this.nodeDatabase[nodeIndex].adapter.selNode : null;
@ -144,69 +166,131 @@ export class DatabaseService {
this.logger.log({ selectedNode: selNode, level: 'ERROR', fileName: 'Database', msg: 'Database Save Error: Selected Node Setup Not Found.' }); 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.'); throw new Error('Database Save Error: Selected Node Setup Not Found.');
} }
this.nodeDatabase[nodeIndex].adapter.saveData(this.nodeDatabase[nodeIndex].data); 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 Saved' }); this.logger.log({ selectedNode: this.nodeDatabase[nodeIndex].adapter.selNode, level: 'INFO', fileName: 'Database', msg: 'Database Collection ' + collectionName + ' Saved' });
return true; return true;
} }
catch (err) { catch (err) {
const selNode = this.nodeDatabase[nodeIndex] && this.nodeDatabase[nodeIndex].adapter && this.nodeDatabase[nodeIndex].adapter.selNode ? this.nodeDatabase[nodeIndex].adapter.selNode : null; 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 }); this.logger.log({ selectedNode: selNode, level: 'ERROR', fileName: 'Database', msg: 'Database Save Error', error: err });
return new Error(err); throw err;
} }
} }
unloadDatabase(nodeIndex) { unloadDatabase(nodeIndex, sessionID) {
this.saveDatabase(nodeIndex); if (nodeIndex > 0) {
this.nodeDatabase[nodeIndex] = null; 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 { export class DatabaseAdapter {
constructor(dbDirectoryPath, fileName, selNode = null) { constructor(dbDirectoryPath, selNode = null, id = '') {
this.dbDirectoryPath = dbDirectoryPath; this.dbDirectoryPath = dbDirectoryPath;
this.fileName = fileName;
this.selNode = selNode; this.selNode = selNode;
this.dbFile = ''; this.id = id;
this.dbFile = dbDirectoryPath + sep + fileName + '-node-' + selNode.index + '.json'; 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);
} }
fetchData() { renameOldDB(oldFilePath, selNode = null) {
const newFilePath = this.dbFilePath + sep + 'rtldb-' + selNode.ln_implementation + '-Offers.json';
try { try {
if (!fs.existsSync(this.dbDirectoryPath)) { this.common.createDirectory(this.dbFilePath);
fs.mkdirSync(this.dbDirectoryPath); const oldOffers = JSON.parse(fs.readFileSync(oldFilePath, 'utf-8'));
fs.writeFileSync(oldFilePath, JSON.stringify(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) { catch (err) {
return new Error('Unable to Create Directory Error ' + JSON.stringify(err)); throw new Error(JSON.stringify(err));
} }
const collectionFilePath = this.dbFilePath + sep + 'rtldb-' + this.selNode.ln_implementation + '-' + collectionName + '.json';
try { try {
if (!fs.existsSync(this.dbFile)) { if (!fs.existsSync(collectionFilePath)) {
fs.writeFileSync(this.dbFile, '{}'); fs.writeFileSync(collectionFilePath, '[]');
} }
} }
catch (err) { catch (err) {
return new Error('Unable to Create Database File Error ' + JSON.stringify(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 { try {
const dataFromFile = fs.readFileSync(this.dbFile, 'utf-8'); const dataFromFile = fs.readFileSync(collectionFilePath, 'utf-8');
return !dataFromFile ? null : JSON.parse(dataFromFile); const dataObj = !dataFromFile ? null : JSON.parse(dataFromFile);
return dataObj;
} }
catch (err) { catch (err) {
return new Error('Database Read Error ' + JSON.stringify(err)); throw new Error(JSON.stringify(err));
} }
} }
getSelNode() { getSelNode() {
return this.selNode; return this.selNode;
} }
saveData(data) { saveData(collectionName, collectionData) {
try { try {
if (data) { if (collectionData) {
const tempFile = this.dbFile + '.tmp'; const collectionFilePath = this.dbFilePath + sep + 'rtldb-' + this.selNode.ln_implementation + '-' + collectionName + '.json';
fs.writeFileSync(tempFile, JSON.stringify(data, null, 2)); const tempFile = collectionFilePath + '.tmp';
fs.renameSync(tempFile, this.dbFile); fs.writeFileSync(tempFile, JSON.stringify(collectionData, null, 2));
fs.renameSync(tempFile, collectionFilePath);
} }
return true; return true;
} }
catch (err) { catch (err) {
return new Error('Database Write Error ' + JSON.stringify(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(); export const Database = new DatabaseService();

@ -7,8 +7,10 @@ export class LoggerService {
switch (msgJSON.level) { switch (msgJSON.level) {
case 'ERROR': case 'ERROR':
if (msgJSON.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') ? msgStr = msgStr + ': ' + ((msgJSON.error.error && msgJSON.error.error.message && typeof msgJSON.error.error.message === 'string') ?
msgJSON.error.stack : (typeof msgJSON.error === 'object') ? JSON.stringify(msgJSON.error) : (typeof msgJSON.error === 'string') ? msgJSON.error : '') + '\r\n'; 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 { else {
msgStr = msgStr + '.\r\n'; msgStr = msgStr + '.\r\n';
@ -67,7 +69,9 @@ export class LoggerService {
; ;
const prepMsgData = (msgJSON, msgStr) => { const prepMsgData = (msgJSON, msgStr) => {
if (msgJSON.data) { 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'; 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 { else {
msgStr = msgStr + '.\r\n'; msgStr = msgStr + '.\r\n';

@ -55,10 +55,10 @@ export class RTLWebSocketServer {
this.mountEventsOnConnection = (websocket, request) => { this.mountEventsOnConnection = (websocket, request) => {
var _a; var _a;
const protocols = !request.headers['sec-websocket-protocol'] ? [] : (_a = request.headers['sec-websocket-protocol'].split(',')) === null || _a === void 0 ? void 0 : _a.map((s) => s.trim()); const protocols = !request.headers['sec-websocket-protocol'] ? [] : (_a = request.headers['sec-websocket-protocol'].split(',')) === null || _a === void 0 ? void 0 : _a.map((s) => s.trim());
const cookies = parse(request.headers.cookie); const cookies = request.headers.cookie ? parse(request.headers.cookie) : null;
websocket.clientId = Date.now(); websocket.clientId = Date.now();
websocket.isAlive = true; websocket.isAlive = true;
websocket.sessionId = cookieParser.signedCookie(cookies['connect.sid'], this.common.secret_key); websocket.sessionId = cookies && cookies['connect.sid'] ? cookieParser.signedCookie(cookies['connect.sid'], this.common.secret_key) : null;
websocket.clientNodeIndex = +protocols[1]; 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 }); 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('error', this.sendErrorToAllLNClients);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -69,32 +69,6 @@ MIT
@angular/router @angular/router
MIT MIT
@babel/runtime
MIT
MIT License
Copyright (c) 2014-present Sebastian McKenzie and other contributors
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@fortawesome/angular-fontawesome @fortawesome/angular-fontawesome
MIT MIT
MIT License MIT License
@ -2021,7 +1995,7 @@ MIT
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2014-2015 bpampuch Copyright (c) 2014-2015 bpampuch
2016-2021 liborm85 2016-2022 liborm85
Permission is hereby granted, free of charge, to any person obtaining a copy of Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in this software and associated documentation files (the "Software"), to deal in
@ -2204,31 +2178,6 @@ IN THE SOFTWARE.
""" """
regenerator-runtime
MIT
MIT License
Copyright (c) 2014-present, Facebook, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
resize-observer-polyfill resize-observer-polyfill
MIT MIT
The MIT License (MIT) The MIT License (MIT)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -3,12 +3,12 @@
"short_name": "", "short_name": "",
"icons": [ "icons": [
{ {
"src": "/android-chrome-192x192.png", "src": "/rtl/assets/images/favicon-dark/android-chrome-192x192.png",
"sizes": "192x192", "sizes": "192x192",
"type": "image/png" "type": "image/png"
}, },
{ {
"src": "/android-chrome-512x512.png", "src": "/rtl/assets/images/favicon-dark/android-chrome-512x512.png",
"sizes": "512x512", "sizes": "512x512",
"type": "image/png" "type": "image/png"
} }

@ -3,12 +3,12 @@
"short_name": "", "short_name": "",
"icons": [ "icons": [
{ {
"src": "/android-chrome-192x192.png", "src": "/rtl/assets/images/favicon-light/android-chrome-192x192.png",
"sizes": "192x192", "sizes": "192x192",
"type": "image/png" "type": "image/png"
}, },
{ {
"src": "/android-chrome-512x512.png", "src": "/rtl/assets/images/favicon-light/android-chrome-512x512.png",
"sizes": "512x512", "sizes": "512x512",
"type": "image/png" "type": "image/png"
} }

@ -10,9 +10,9 @@
<link i18n-rel="" rel="mask-icon" href="assets/images/favicon-light/safari-pinned-tab.svg" color="#5bbad5"> <link i18n-rel="" rel="mask-icon" href="assets/images/favicon-light/safari-pinned-tab.svg" color="#5bbad5">
<meta i18n-content="" name="msapplication-TileColor" content="#da532c"> <meta i18n-content="" name="msapplication-TileColor" content="#da532c">
<meta i18n-content="" name="theme-color" content="#ffffff"> <meta i18n-content="" name="theme-color" content="#ffffff">
<style>@font-face{font-family:Roboto;src:url(Roboto-Thin.f7a95c9c5999532c.woff2) format("woff2"),url(Roboto-Thin.c13c157cb81e8ebb.woff) format("woff");font-weight:100;font-style:normal}@font-face{font-family:Roboto;src:url(Roboto-ThinItalic.b0e084abf689f393.woff2) format("woff2"),url(Roboto-ThinItalic.1111028df6cea564.woff) format("woff");font-weight:100;font-style:italic}@font-face{font-family:Roboto;src:url(Roboto-Light.0e01b6cd13b3857f.woff2) format("woff2"),url(Roboto-Light.603ca9a537b88428.woff) format("woff");font-weight:300;font-style:normal}@font-face{font-family:Roboto;src:url(Roboto-LightItalic.232ef4b20215f720.woff2) format("woff2"),url(Roboto-LightItalic.1b5e142f787151c8.woff) format("woff");font-weight:300;font-style:italic}@font-face{font-family:Roboto;src:url(Roboto-Regular.475ba9e4e2d63456.woff2) format("woff2"),url(Roboto-Regular.bcefbfee882bc1cb.woff) format("woff");font-weight:400;font-style:normal}@font-face{font-family:Roboto;src:url(Roboto-RegularItalic.e3a9ebdaac06bbc4.woff2) format("woff2"),url(Roboto-RegularItalic.0668fae6af0cf8c2.woff) format("woff");font-weight:400;font-style:italic}@font-face{font-family:Roboto;src:url(Roboto-Medium.457532032ceb0168.woff2) format("woff2"),url(Roboto-Medium.6e1ae5f0b324a0aa.woff) format("woff");font-weight:500;font-style:normal}@font-face{font-family:Roboto;src:url(Roboto-MediumItalic.872f7060602d55d2.woff2) format("woff2"),url(Roboto-MediumItalic.e06fb533801cbb08.woff) format("woff");font-weight:500;font-style:italic}@font-face{font-family:Roboto;src:url(Roboto-Bold.447291a88c067396.woff2) format("woff2"),url(Roboto-Bold.fc482e6133cf5e26.woff) format("woff");font-weight:700;font-style:normal}@font-face{font-family:Roboto;src:url(Roboto-BoldItalic.1b15168ef6fa4e16.woff2) format("woff2"),url(Roboto-BoldItalic.e26ba339b06f09f7.woff) format("woff");font-weight:700;font-style:italic}@font-face{font-family:Roboto;src:url(Roboto-Black.2eaa390d458c877d.woff2) format("woff2"),url(Roboto-Black.b25f67ad8583da68.woff) format("woff");font-weight:900;font-style:normal}@font-face{font-family:Roboto;src:url(Roboto-BlackItalic.7dc03ee444552bc5.woff2) format("woff2"),url(Roboto-BlackItalic.c8dc642467cb3099.woff) format("woff");font-weight:900;font-style:italic}html{width:100%;height:99%;line-height:1.5;overflow-x:hidden;font-family:Roboto,sans-serif!important;font-size:62.5%}body{box-sizing:border-box;height:100%;margin:0;overflow:hidden}*{margin:0;padding:0}</style><link rel="stylesheet" href="styles.43515fc39338348b.css" media="print" onload="this.media='all'"><noscript><link rel="stylesheet" href="styles.43515fc39338348b.css"></noscript></head> <style>@font-face{font-family:Roboto;src:url(Roboto-Thin.f7a95c9c5999532c.woff2) format("woff2"),url(Roboto-Thin.c13c157cb81e8ebb.woff) format("woff");font-weight:100;font-style:normal}@font-face{font-family:Roboto;src:url(Roboto-ThinItalic.b0e084abf689f393.woff2) format("woff2"),url(Roboto-ThinItalic.1111028df6cea564.woff) format("woff");font-weight:100;font-style:italic}@font-face{font-family:Roboto;src:url(Roboto-Light.0e01b6cd13b3857f.woff2) format("woff2"),url(Roboto-Light.603ca9a537b88428.woff) format("woff");font-weight:300;font-style:normal}@font-face{font-family:Roboto;src:url(Roboto-LightItalic.232ef4b20215f720.woff2) format("woff2"),url(Roboto-LightItalic.1b5e142f787151c8.woff) format("woff");font-weight:300;font-style:italic}@font-face{font-family:Roboto;src:url(Roboto-Regular.475ba9e4e2d63456.woff2) format("woff2"),url(Roboto-Regular.bcefbfee882bc1cb.woff) format("woff");font-weight:400;font-style:normal}@font-face{font-family:Roboto;src:url(Roboto-RegularItalic.e3a9ebdaac06bbc4.woff2) format("woff2"),url(Roboto-RegularItalic.0668fae6af0cf8c2.woff) format("woff");font-weight:400;font-style:italic}@font-face{font-family:Roboto;src:url(Roboto-Medium.457532032ceb0168.woff2) format("woff2"),url(Roboto-Medium.6e1ae5f0b324a0aa.woff) format("woff");font-weight:500;font-style:normal}@font-face{font-family:Roboto;src:url(Roboto-MediumItalic.872f7060602d55d2.woff2) format("woff2"),url(Roboto-MediumItalic.e06fb533801cbb08.woff) format("woff");font-weight:500;font-style:italic}@font-face{font-family:Roboto;src:url(Roboto-Bold.447291a88c067396.woff2) format("woff2"),url(Roboto-Bold.fc482e6133cf5e26.woff) format("woff");font-weight:700;font-style:normal}@font-face{font-family:Roboto;src:url(Roboto-BoldItalic.1b15168ef6fa4e16.woff2) format("woff2"),url(Roboto-BoldItalic.e26ba339b06f09f7.woff) format("woff");font-weight:700;font-style:italic}@font-face{font-family:Roboto;src:url(Roboto-Black.2eaa390d458c877d.woff2) format("woff2"),url(Roboto-Black.b25f67ad8583da68.woff) format("woff");font-weight:900;font-style:normal}@font-face{font-family:Roboto;src:url(Roboto-BlackItalic.7dc03ee444552bc5.woff2) format("woff2"),url(Roboto-BlackItalic.c8dc642467cb3099.woff) format("woff");font-weight:900;font-style:italic}html{width:100%;height:99%;line-height:1.5;overflow-x:hidden;font-family:Roboto,sans-serif!important;font-size:62.5%}body{box-sizing:border-box;height:100%;margin:0;overflow:hidden}*{margin:0;padding:0}</style><link rel="stylesheet" href="styles.74a7770ce3bccfdd.css" media="print" onload="this.media='all'"><noscript><link rel="stylesheet" href="styles.74a7770ce3bccfdd.css"></noscript></head>
<body> <body>
<rtl-app></rtl-app> <rtl-app></rtl-app>
<script src="runtime.1ca59cd92764ed45.js" type="module"></script><script src="polyfills.eddc63f1737a019a.js" type="module"></script><script src="main.9b30da1402d5f9f2.js" type="module"></script> <script src="runtime.4c81b553a9df7303.js" type="module"></script><script src="polyfills.eddc63f1737a019a.js" type="module"></script><script src="main.41600bb9f8f7e3c5.js" type="module"></script>
</body></html> </body></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -1 +1 @@
(()=>{"use strict";var e,v={},g={};function r(e){var n=g[e];if(void 0!==n)return n.exports;var t=g[e]={id:e,loaded:!1,exports:{}};return v[e].call(t.exports,t,t.exports,r),t.loaded=!0,t.exports}r.m=v,e=[],r.O=(n,t,f,o)=>{if(!t){var a=1/0;for(i=0;i<e.length;i++){for(var[t,f,o]=e[i],c=!0,u=0;u<t.length;u++)(!1&o||a>=o)&&Object.keys(r.O).every(b=>r.O[b](t[u]))?t.splice(u--,1):(c=!1,o<a&&(a=o));if(c){e.splice(i--,1);var d=f();void 0!==d&&(n=d)}}return n}o=o||0;for(var i=e.length;i>0&&e[i-1][2]>o;i--)e[i]=e[i-1];e[i]=[t,f,o]},r.n=e=>{var n=e&&e.__esModule?()=>e.default:()=>e;return r.d(n,{a:n}),n},r.d=(e,n)=>{for(var t in n)r.o(n,t)&&!r.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:n[t]})},r.f={},r.e=e=>Promise.all(Object.keys(r.f).reduce((n,t)=>(r.f[t](e,n),n),[])),r.u=e=>e+"."+{564:"3d38ee9330b2ba94",636:"95c8ae357b1ed820",893:"9a615c46b89a5a79",924:"1c1eb885f1f101d2"}[e]+".js",r.miniCssF=e=>{},r.o=(e,n)=>Object.prototype.hasOwnProperty.call(e,n),(()=>{var e={},n="RTLApp:";r.l=(t,f,o,i)=>{if(e[t])e[t].push(f);else{var a,c;if(void 0!==o)for(var u=document.getElementsByTagName("script"),d=0;d<u.length;d++){var l=u[d];if(l.getAttribute("src")==t||l.getAttribute("data-webpack")==n+o){a=l;break}}a||(c=!0,(a=document.createElement("script")).type="module",a.charset="utf-8",a.timeout=120,r.nc&&a.setAttribute("nonce",r.nc),a.setAttribute("data-webpack",n+o),a.src=r.tu(t)),e[t]=[f];var s=(m,b)=>{a.onerror=a.onload=null,clearTimeout(p);var h=e[t];if(delete e[t],a.parentNode&&a.parentNode.removeChild(a),h&&h.forEach(_=>_(b)),m)return m(b)},p=setTimeout(s.bind(null,void 0,{type:"timeout",target:a}),12e4);a.onerror=s.bind(null,a.onerror),a.onload=s.bind(null,a.onload),c&&document.head.appendChild(a)}}})(),r.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.nmd=e=>(e.paths=[],e.children||(e.children=[]),e),(()=>{var e;r.tt=()=>(void 0===e&&(e={createScriptURL:n=>n},"undefined"!=typeof trustedTypes&&trustedTypes.createPolicy&&(e=trustedTypes.createPolicy("angular#bundler",e))),e)})(),r.tu=e=>r.tt().createScriptURL(e),r.p="",(()=>{var e={666:0};r.f.j=(f,o)=>{var i=r.o(e,f)?e[f]:void 0;if(0!==i)if(i)o.push(i[2]);else if(666!=f){var a=new Promise((l,s)=>i=e[f]=[l,s]);o.push(i[2]=a);var c=r.p+r.u(f),u=new Error;r.l(c,l=>{if(r.o(e,f)&&(0!==(i=e[f])&&(e[f]=void 0),i)){var s=l&&("load"===l.type?"missing":l.type),p=l&&l.target&&l.target.src;u.message="Loading chunk "+f+" failed.\n("+s+": "+p+")",u.name="ChunkLoadError",u.type=s,u.request=p,i[1](u)}},"chunk-"+f,f)}else e[f]=0},r.O.j=f=>0===e[f];var n=(f,o)=>{var u,d,[i,a,c]=o,l=0;if(i.some(p=>0!==e[p])){for(u in a)r.o(a,u)&&(r.m[u]=a[u]);if(c)var s=c(r)}for(f&&f(o);l<i.length;l++)r.o(e,d=i[l])&&e[d]&&e[d][0](),e[d]=0;return r.O(s)},t=self.webpackChunkRTLApp=self.webpackChunkRTLApp||[];t.forEach(n.bind(null,0)),t.push=n.bind(null,t.push.bind(t))})()})(); (()=>{"use strict";var e,v={},g={};function r(e){var n=g[e];if(void 0!==n)return n.exports;var t=g[e]={id:e,loaded:!1,exports:{}};return v[e].call(t.exports,t,t.exports,r),t.loaded=!0,t.exports}r.m=v,e=[],r.O=(n,t,f,o)=>{if(!t){var a=1/0;for(i=0;i<e.length;i++){for(var[t,f,o]=e[i],c=!0,u=0;u<t.length;u++)(!1&o||a>=o)&&Object.keys(r.O).every(b=>r.O[b](t[u]))?t.splice(u--,1):(c=!1,o<a&&(a=o));if(c){e.splice(i--,1);var l=f();void 0!==l&&(n=l)}}return n}o=o||0;for(var i=e.length;i>0&&e[i-1][2]>o;i--)e[i]=e[i-1];e[i]=[t,f,o]},r.n=e=>{var n=e&&e.__esModule?()=>e.default:()=>e;return r.d(n,{a:n}),n},r.d=(e,n)=>{for(var t in n)r.o(n,t)&&!r.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:n[t]})},r.f={},r.e=e=>Promise.all(Object.keys(r.f).reduce((n,t)=>(r.f[t](e,n),n),[])),r.u=e=>e+"."+{258:"fb8729850462aa0e",267:"4a643eeda98f6031",564:"c60bd98c3a9d472b",636:"2c7ab7c33992b609"}[e]+".js",r.miniCssF=e=>{},r.o=(e,n)=>Object.prototype.hasOwnProperty.call(e,n),(()=>{var e={},n="RTLApp:";r.l=(t,f,o,i)=>{if(e[t])e[t].push(f);else{var a,c;if(void 0!==o)for(var u=document.getElementsByTagName("script"),l=0;l<u.length;l++){var d=u[l];if(d.getAttribute("src")==t||d.getAttribute("data-webpack")==n+o){a=d;break}}a||(c=!0,(a=document.createElement("script")).type="module",a.charset="utf-8",a.timeout=120,r.nc&&a.setAttribute("nonce",r.nc),a.setAttribute("data-webpack",n+o),a.src=r.tu(t)),e[t]=[f];var s=(m,b)=>{a.onerror=a.onload=null,clearTimeout(p);var h=e[t];if(delete e[t],a.parentNode&&a.parentNode.removeChild(a),h&&h.forEach(_=>_(b)),m)return m(b)},p=setTimeout(s.bind(null,void 0,{type:"timeout",target:a}),12e4);a.onerror=s.bind(null,a.onerror),a.onload=s.bind(null,a.onload),c&&document.head.appendChild(a)}}})(),r.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.nmd=e=>(e.paths=[],e.children||(e.children=[]),e),(()=>{var e;r.tt=()=>(void 0===e&&(e={createScriptURL:n=>n},"undefined"!=typeof trustedTypes&&trustedTypes.createPolicy&&(e=trustedTypes.createPolicy("angular#bundler",e))),e)})(),r.tu=e=>r.tt().createScriptURL(e),r.p="",(()=>{var e={666:0};r.f.j=(f,o)=>{var i=r.o(e,f)?e[f]:void 0;if(0!==i)if(i)o.push(i[2]);else if(666!=f){var a=new Promise((d,s)=>i=e[f]=[d,s]);o.push(i[2]=a);var c=r.p+r.u(f),u=new Error;r.l(c,d=>{if(r.o(e,f)&&(0!==(i=e[f])&&(e[f]=void 0),i)){var s=d&&("load"===d.type?"missing":d.type),p=d&&d.target&&d.target.src;u.message="Loading chunk "+f+" failed.\n("+s+": "+p+")",u.name="ChunkLoadError",u.type=s,u.request=p,i[1](u)}},"chunk-"+f,f)}else e[f]=0},r.O.j=f=>0===e[f];var n=(f,o)=>{var u,l,[i,a,c]=o,d=0;if(i.some(p=>0!==e[p])){for(u in a)r.o(a,u)&&(r.m[u]=a[u]);if(c)var s=c(r)}for(f&&f(o);d<i.length;d++)r.o(e,l=i[d])&&e[l]&&e[l][0](),e[l]=0;return r.O(s)},t=self.webpackChunkRTLApp=self.webpackChunkRTLApp||[];t.forEach(n.bind(null,0)),t.push=n.bind(null,t.push.bind(t))})()})();

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

3269
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -1,6 +1,6 @@
{ {
"name": "rtl", "name": "rtl",
"version": "0.13.1-beta", "version": "0.13.2-beta",
"license": "MIT", "license": "MIT",
"type": "module", "type": "module",
"scripts": { "scripts": {
@ -12,8 +12,8 @@
"buildfrontend": "ng build --configuration production", "buildfrontend": "ng build --configuration production",
"buildbackend": "tsc --project tsconfig.json", "buildbackend": "tsc --project tsconfig.json",
"watchbackend": "tsc --project tsconfig.json --watch", "watchbackend": "tsc --project tsconfig.json --watch",
"server": "set NODE_ENV=development&&nodemon ./rtl.js", "server": "set NODE_ENV=development&&nodemon --watch backend --watch server ./rtl.js",
"serverUbuntu": "NODE_ENV=development nodemon ./rtl.js", "serverUbuntu": "NODE_ENV=development nodemon --watch backend --watch server ./rtl.js",
"testdev": "ng test --watch=true --code-coverage", "testdev": "ng test --watch=true --code-coverage",
"test": "ng test --watch=false", "test": "ng test --watch=false",
"lint": "ng lint", "lint": "ng lint",
@ -21,21 +21,6 @@
}, },
"private": true, "private": true,
"dependencies": { "dependencies": {
"@angular/animations": "~13.3.0",
"@angular/cdk": "^13.3.9",
"@angular/common": "~13.3.0",
"@angular/compiler": "~13.3.0",
"@angular/core": "~13.3.0",
"@angular/flex-layout": "^13.0.0-beta.38",
"@angular/forms": "~13.3.0",
"@angular/material": "^13.3.9",
"@angular/platform-browser": "~13.3.0",
"@angular/platform-browser-dynamic": "~13.3.0",
"@angular/router": "~13.3.0",
"@fortawesome/angular-fontawesome": "^0.10.2",
"@fortawesome/fontawesome-svg-core": "^6.1.2",
"@fortawesome/free-regular-svg-icons": "^6.1.2",
"@fortawesome/free-solid-svg-icons": "^6.1.2",
"@ngrx/effects": "^13.2.0", "@ngrx/effects": "^13.2.0",
"@ngrx/store": "^13.2.0", "@ngrx/store": "^13.2.0",
"@swimlane/ngx-charts": "^20.1.0", "@swimlane/ngx-charts": "^20.1.0",
@ -48,17 +33,14 @@
"hocon-parser": "^1.0.1", "hocon-parser": "^1.0.1",
"ini": "^3.0.0", "ini": "^3.0.0",
"jsonwebtoken": "^8.5.1", "jsonwebtoken": "^8.5.1",
"material-design-icons": "^3.0.1",
"ng-qrcode": "^6.0.0", "ng-qrcode": "^6.0.0",
"ngx-perfect-scrollbar-next": "^10.1.1", "ngx-perfect-scrollbar-next": "^10.1.1",
"otplib": "^12.0.1", "otplib": "^12.0.1",
"pdfmake": "^0.2.5", "pdfmake": "^0.2.5",
"request-promise": "^4.2.6", "request-promise": "^4.2.6",
"roboto-fontface": "^0.10.0",
"rxjs": "^7.4.0", "rxjs": "^7.4.0",
"sha256": "^0.2.0", "sha256": "^0.2.0",
"tslib": "^2.3.0", "tslib": "^2.3.0",
"typescript": "~4.6.2",
"ws": "^8.8.1", "ws": "^8.8.1",
"zone.js": "~0.11.4" "zone.js": "~0.11.4"
}, },
@ -69,8 +51,23 @@
"@angular-eslint/eslint-plugin-template": "13.0.1", "@angular-eslint/eslint-plugin-template": "13.0.1",
"@angular-eslint/schematics": "13.0.1", "@angular-eslint/schematics": "13.0.1",
"@angular-eslint/template-parser": "13.0.1", "@angular-eslint/template-parser": "13.0.1",
"@angular/animations": "~13.3.0",
"@angular/cdk": "^13.3.9",
"@angular/cli": "~13.3.5", "@angular/cli": "~13.3.5",
"@angular/common": "~13.3.0",
"@angular/compiler": "~13.3.0",
"@angular/compiler-cli": "~13.3.0", "@angular/compiler-cli": "~13.3.0",
"@angular/core": "~13.3.0",
"@angular/flex-layout": "^13.0.0-beta.38",
"@angular/forms": "~13.3.0",
"@angular/material": "^13.3.9",
"@angular/platform-browser": "~13.3.0",
"@angular/platform-browser-dynamic": "~13.3.0",
"@angular/router": "~13.3.0",
"@fortawesome/angular-fontawesome": "^0.10.2",
"@fortawesome/fontawesome-svg-core": "^6.1.2",
"@fortawesome/free-regular-svg-icons": "^6.1.2",
"@fortawesome/free-solid-svg-icons": "^6.1.2",
"@ngrx/store-devtools": "^13.0.2", "@ngrx/store-devtools": "^13.0.2",
"@types/jasmine": "~3.10.0", "@types/jasmine": "~3.10.0",
"@types/node": "^12.11.1", "@types/node": "^12.11.1",
@ -87,9 +84,12 @@
"karma-coverage": "~2.1.0", "karma-coverage": "~2.1.0",
"karma-jasmine": "~4.0.0", "karma-jasmine": "~4.0.0",
"karma-jasmine-html-reporter": "~1.7.0", "karma-jasmine-html-reporter": "~1.7.0",
"material-design-icons": "^3.0.1",
"nodemon": "~2.0.19", "nodemon": "~2.0.19",
"protractor": "~7.0.0", "protractor": "~7.0.0",
"roboto-fontface": "^0.10.0",
"stream-browserify": "^3.0.0", "stream-browserify": "^3.0.0",
"ts-node": "~10.9.1" "ts-node": "~10.9.1",
"typescript": "~4.6.2"
} }
} }

@ -56,7 +56,7 @@ export const getInfo = (req, res, next) => {
req.session.selectedNode.ln_version = body.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.' }); logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Connecting to the Core Lightning\'s Websocket Server.' });
clWsClient.updateSelectedNode(req.session.selectedNode); clWsClient.updateSelectedNode(req.session.selectedNode);
databaseService.loadDatabase(req.session.selectedNode); databaseService.loadDatabase(req.session);
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Node Information Received', data: body }); logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Node Information Received', data: body });
return res.status(200).json(body); return res.status(200).json(body);
} }

@ -29,10 +29,6 @@ export const listInvoices = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Invoice', msg: 'Invoices List URL', data: options.url }); logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Invoice', msg: 'Invoices List URL', data: options.url });
request(options).then((body) => { request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Invoice', msg: 'Invoices List Received', data: 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 = common.sortDescByKey(body.invoices, 'expires_at');
}
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoice', msg: 'Sorted Invoices List Received', data: body });
res.status(200).json(body); res.status(200).json(body);
}).catch((errRes) => { }).catch((errRes) => {
const err = common.handleError(errRes, 'Invoice', 'List Invoices Error', req.session.selectedNode); const err = common.handleError(errRes, 'Invoice', 'List Invoices Error', req.session.selectedNode);

@ -72,8 +72,10 @@ export const listNodes = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Network', msg: 'List Nodes Finished', data: body }); logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Network', msg: 'List Nodes Finished', data: body });
body.forEach((node) => { body.forEach((node) => {
if (node.option_will_fund) { 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.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.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; 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; return node;
}); });

@ -11,11 +11,8 @@ const databaseService: DatabaseService = Database;
export const listOfferBookmarks = (req, res, next) => { export const listOfferBookmarks = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Getting Offer Bookmarks..' }); logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Getting Offer Bookmarks..' });
databaseService.find(req.session.selectedNode, CollectionsEnum.OFFERS).then((offers: Offer[]) => { databaseService.find(req.session.selectedNode, CollectionsEnum.OFFERS).then((offers: any) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Offer Bookmarks Received', data: offers }); logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Offer Bookmarks Received', data: offers });
if (offers && offers.length > 0) {
offers = common.sortDescByKey(offers, 'lastUpdatedAt');
}
res.status(200).json(offers); res.status(200).json(offers);
}).catch((errRes) => { }).catch((errRes) => {
const err = common.handleError(errRes, 'Offers', 'Offer Bookmarks Error', req.session.selectedNode); const err = common.handleError(errRes, 'Offers', 'Offer Bookmarks Error', req.session.selectedNode);
@ -25,7 +22,7 @@ export const listOfferBookmarks = (req, res, next) => {
export const deleteOfferBookmark = (req, res, next) => { export const deleteOfferBookmark = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Deleting Offer Bookmark..' }); logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Deleting Offer Bookmark..' });
databaseService.destroy(req.session.selectedNode, CollectionsEnum.OFFERS, CollectionFieldsEnum.BOLT12, req.params.offerStr).then((deleteRes) => { 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 }); logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Offer Bookmark Deleted', data: deleteRes });
res.status(204).json(req.params.offerStr); res.status(204).json(req.params.offerStr);
}).catch((errRes) => { }).catch((errRes) => {

@ -41,7 +41,6 @@ export const getUTXOs = (req, res, next) => {
if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } 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'; options.url = req.session.selectedNode.ln_server_url + '/v1/listFunds';
request(options).then((body) => { request(options).then((body) => {
if (body.outputs) { body.outputs = common.sortDescByStrKey(body.outputs, 'status'); }
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'Funds List Received', data: body }); logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'Funds List Received', data: body });
res.status(200).json(body); res.status(200).json(body);
}).catch((errRes) => { }).catch((errRes) => {

@ -63,10 +63,6 @@ export const listPayments = (req, res, next) => {
options.url = req.session.selectedNode.ln_server_url + '/v1/pay/listPayments'; options.url = req.session.selectedNode.ln_server_url + '/v1/pay/listPayments';
request(options).then((body) => { request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Payments', msg: 'Payment List Received', data: body.payments }); logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Payments', msg: 'Payment List Received', data: body.payments });
if (body && body.payments && body.payments.length > 0) {
body.payments = common.sortDescByKey(body.payments, 'created_at');
}
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Sorted Payments List Received', data: body.payments });
res.status(200).json(groupBy(body.payments)); res.status(200).json(groupBy(body.payments));
}).catch((errRes) => { }).catch((errRes) => {
const err = common.handleError(errRes, 'Payments', 'List Payments Error', req.session.selectedNode); const err = common.handleError(errRes, 'Payments', 'List Payments Error', req.session.selectedNode);
@ -95,15 +91,21 @@ export const postPayment = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Payment Sent', data: body }); logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Payment Sent', data: body });
if (req.body.paymentType === 'OFFER') { if (req.body.paymentType === 'OFFER') {
if (req.body.saveToDB && req.body.bolt12) { if (req.body.saveToDB && req.body.bolt12) {
const offerToUpdate: Offer = { bolt12: req.body.bolt12, amountmSat: (req.body.zeroAmtOffer ? 0 : req.body.amount), title: req.body.title, lastUpdatedAt: new Date(Date.now()).getTime() }; const offerToUpdate: Offer = { 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.vendor) { offerToUpdate['vendor'] = req.body.vendor; }
if (req.body.description) { offerToUpdate['description'] = req.body.description; } if (req.body.description) { offerToUpdate['description'] = req.body.description; }
return databaseService.update(req.session.selectedNode, CollectionsEnum.OFFERS, offerToUpdate, CollectionFieldsEnum.BOLT12, req.body.bolt12).then((updatedOffer) => { // eslint-disable-next-line arrow-body-style
logger.log({ level: 'DEBUG', fileName: 'Offer', msg: 'Offer Updated', data: updatedOffer }); return databaseService.validateDocument(CollectionsEnum.OFFERS, offerToUpdate).then((validated) => {
return res.status(201).json({ paymentResponse: body, saveToDBResponse: updatedOffer }); return databaseService.update(req.session.selectedNode, CollectionsEnum.OFFERS, offerToUpdate, CollectionFieldsEnum.BOLT12, req.body.bolt12).then((updatedOffer) => {
}).catch((errDB) => { logger.log({ level: 'DEBUG', fileName: 'Payments', msg: 'Offer Updated', data: updatedOffer });
logger.log({ selectedNode: req.session.selectedNode, level: 'ERROR', fileName: 'Payments', msg: 'Offer DB update error', error: errDB }); return res.status(201).json({ paymentResponse: body, saveToDBResponse: updatedOffer });
return res.status(201).json({ paymentResponse: body, saveToDBError: errDB }); }).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 { } else {
return res.status(201).json({ paymentResponse: body, saveToDBResponse: 'NA' }); return res.status(201).json({ paymentResponse: body, saveToDBResponse: 'NA' });

@ -16,9 +16,8 @@ export const getPeers = (req, res, next) => {
peer.alias = peer.id.substring(0, 20); peer.alias = peer.id.substring(0, 20);
} }
}); });
const peers = (body) ? common.sortDescByStrKey(body, 'alias') : []; logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Peers with Alias Received', data: body });
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Peers with Alias Received', data: peers }); res.status(200).json(body || []);
res.status(200).json(peers);
}).catch((errRes) => { }).catch((errRes) => {
const err = common.handleError(errRes, 'Peers', 'List Peers Error', req.session.selectedNode); const err = common.handleError(errRes, 'Peers', 'List Peers Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error }); return res.status(err.statusCode).json({ message: err.message, error: err.error });
@ -31,12 +30,11 @@ export const postPeer = (req, res, next) => {
if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } 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.url = req.session.selectedNode.ln_server_url + '/v1/peer/connect';
options.body = req.body; options.body = req.body;
request.post(options).then((body) => { request.post(options).then((connectRes) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Peers', msg: 'Peer Connected', data: body }); 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'; options.url = req.session.selectedNode.ln_server_url + '/v1/peer/listPeers';
request(options).then((body) => { request(options).then((listPeersRes) => {
let peers = (body) ? common.sortDescByStrKey(body, 'alias') : []; const peers = listPeersRes ? common.newestOnTop(listPeersRes, 'id', req.body.id) : [];
peers = common.newestOnTop(peers, 'id', req.body.id);
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Peers List after Connect Received', data: peers }); logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Peers List after Connect Received', data: peers });
res.status(201).json(peers); res.status(201).json(peers);
}).catch((errRes) => { }).catch((errRes) => {

@ -15,10 +15,10 @@ export const simplifyAllChannels = (selNode: CommonSelectedNode, channels) => {
nodeId: channel.nodeId ? channel.nodeId : '', nodeId: channel.nodeId ? channel.nodeId : '',
channelId: channel.channelId ? channel.channelId : '', channelId: channel.channelId ? channel.channelId : '',
state: channel.state ? channel.state : '', state: channel.state ? channel.state : '',
channelFlags: channel.data && channel.data.commitments && channel.data.commitments.channelFlags ? channel.data.commitments.channelFlags : 0, 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, 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, toRemote: (channel.data.commitments.localCommit.spec.toRemote) ? Math.round(+channel.data.commitments.localCommit.spec.toRemote / 1000) : 0,
shortChannelId: channel.data && channel.data.shortChannelId ? channel.data.shortChannelId : '', 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, 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, 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, feeBaseMsat: channel.data && channel.data.channelUpdate && channel.data.channelUpdate.feeBaseMsat ? channel.data.channelUpdate.feeBaseMsat : 0,

@ -72,9 +72,6 @@ export const arrangePayments = (selNode: CommonSelectedNode, body) => {
if (relayedEle.amountIn) { relayedEle.amountIn = Math.round(relayedEle.amountIn / 1000); } if (relayedEle.amountIn) { relayedEle.amountIn = Math.round(relayedEle.amountIn / 1000); }
if (relayedEle.amountOut) { relayedEle.amountOut = Math.round(relayedEle.amountOut / 1000); } if (relayedEle.amountOut) { relayedEle.amountOut = Math.round(relayedEle.amountOut / 1000); }
}); });
payments.sent = common.sortDescByKey(payments.sent, 'firstPartTimestamp');
payments.received = common.sortDescByKey(payments.received, 'firstPartTimestamp');
payments.relayed = common.sortDescByKey(payments.relayed, 'timestamp');
logger.log({ selectedNode: selNode, level: 'DEBUG', fileName: 'Fees', msg: 'Arranged Payments Received', data: payments }); logger.log({ selectedNode: selNode, level: 'DEBUG', fileName: 'Fees', msg: 'Arranged Payments Received', data: payments });
return payments; return payments;
}; };

@ -36,11 +36,11 @@ export const getInfo = (req, res, next) => {
body.lnImplementation = 'Eclair'; body.lnImplementation = 'Eclair';
req.session.selectedNode.ln_version = body.version.split('-')[0] || ''; req.session.selectedNode.ln_version = body.version.split('-')[0] || '';
eclWsClient.updateSelectedNode(req.session.selectedNode); eclWsClient.updateSelectedNode(req.session.selectedNode);
databaseService.loadDatabase(req.session.selectedNode); databaseService.loadDatabase(req.session);
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Node Information Received', data: body }); logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Node Information Received', data: body });
return res.status(200).json(body); return res.status(200).json(body);
}).catch((errRes) => { }).catch((errRes) => {
const err = common.handleError(errRes, 'GetInfo', 'Get Info Error', req); const err = common.handleError(errRes, 'GetInfo', 'Get Info Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error }); return res.status(err.statusCode).json({ message: err.message, error: err.error });
}); });
} }

@ -67,10 +67,7 @@ export const listInvoices = (req, res, next) => {
const invoices = (!body[0] || body[0].length <= 0) ? [] : body[0]; const invoices = (!body[0] || body[0].length <= 0) ? [] : body[0];
pendingInvoices = (!body[1] || body[1].length <= 0) ? [] : body[1]; pendingInvoices = (!body[1] || body[1].length <= 0) ? [] : body[1];
return Promise.all(invoices?.map((invoice) => getReceivedPaymentInfo(req.session.selectedNode.ln_server_url, invoice))). return Promise.all(invoices?.map((invoice) => getReceivedPaymentInfo(req.session.selectedNode.ln_server_url, invoice))).
then((values) => { then((values) => res.status(200).json(invoices));
body = common.sortDescByKey(invoices, 'expiresAt');
return res.status(200).json(invoices);
});
}); });
} else { } else {
return Promise.all([request(options1), request(options2)]). return Promise.all([request(options1), request(options2)]).
@ -81,7 +78,6 @@ export const listInvoices = (req, res, next) => {
if (invoices && invoices.length > 0) { if (invoices && invoices.length > 0) {
return Promise.all(invoices?.map((invoice) => getReceivedPaymentInfo(req.session.selectedNode.ln_server_url, invoice))). return Promise.all(invoices?.map((invoice) => getReceivedPaymentInfo(req.session.selectedNode.ln_server_url, invoice))).
then((values) => { then((values) => {
body = common.sortDescByKey(invoices, 'expiresAt');
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoices', msg: 'Sorted Invoices List Received', data: invoices }); logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoices', msg: 'Sorted Invoices List Received', data: invoices });
return res.status(200).json(invoices); return res.status(200).json(invoices);
}). }).

@ -59,7 +59,6 @@ export const getTransactions = (req, res, next) => {
}; };
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'Getting On Chain Transactions Options', data: options.form }); logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'Getting On Chain Transactions Options', data: options.form });
request.post(options).then((body) => { request.post(options).then((body) => {
if (body && body.length > 0) { body = common.sortDescByKey(body, 'timestamp'); }
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'On Chain Transactions Received', data: body }); logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'On Chain Transactions Received', data: body });
res.status(200).json(body); res.status(200).json(body);
}).catch((errRes) => { }).catch((errRes) => {

@ -37,7 +37,6 @@ export const getPeers = (req, res, next) => {
peer.alias = foundPeer ? foundPeer.alias : peer.nodeId.substring(0, 20); peer.alias = foundPeer ? foundPeer.alias : peer.nodeId.substring(0, 20);
return peer; return peer;
}); });
body = common.sortDescByStrKey(body, 'alias');
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Sorted Peers List Received', data: body }); logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Sorted Peers List Received', data: body });
res.status(200).json(body); res.status(200).json(body);
}); });
@ -87,8 +86,7 @@ export const connectPeer = (req, res, next) => {
peer.alias = foundPeer ? foundPeer.alias : peer.nodeId.substring(0, 20); peer.alias = foundPeer ? foundPeer.alias : peer.nodeId.substring(0, 20);
return peer; return peer;
}); });
let peers = (body) ? common.sortDescByStrKey(body, 'alias') : []; const peers = common.newestOnTop(body || [], 'nodeId', req.query.nodeId ? req.query.nodeId : req.query.uri ? req.query.uri.substring(0, req.query.uri.indexOf('@')) : '');
peers = common.newestOnTop(peers, '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 }); logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Peers List after Connect Received', data: peers });
res.status(201).json(peers); res.status(201).json(peers);
}); });

@ -11,11 +11,11 @@ export const getAliasForChannel = (selNode: CommonSelectedNode, channel) => {
options.url = selNode.ln_server_url + '/v1/graph/node/' + pubkey; options.url = selNode.ln_server_url + '/v1/graph/node/' + pubkey;
return request(options).then((aliasBody) => { return request(options).then((aliasBody) => {
logger.log({ selectedNode: selNode, level: 'DEBUG', fileName: 'Channels', msg: 'Alias Received', data: aliasBody.node.alias }); logger.log({ selectedNode: selNode, level: 'DEBUG', fileName: 'Channels', msg: 'Alias Received', data: aliasBody.node.alias });
channel.remote_alias = aliasBody.node.alias; channel.remote_alias = aliasBody.node.alias && aliasBody.node.alias !== '' ? aliasBody.node.alias : aliasBody.node.pub_key.slice(0, 20);
return aliasBody.node.alias; return channel;
}).catch((err) => { }).catch((err) => {
channel.remote_alias = pubkey.slice(0, 10) + '...' + pubkey.slice(-10); channel.remote_alias = pubkey.slice(0, 20);
return pubkey; return channel;
}); });
}; };
@ -40,7 +40,6 @@ export const getAllChannels = (req, res, next) => {
return getAliasForChannel(req.session.selectedNode, channel); return getAliasForChannel(req.session.selectedNode, channel);
}) })
).then((values) => { ).then((values) => {
body.channels = common.sortDescByKey(body.channels, 'balancedness');
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Sorted Channels List Received', data: body }); logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Sorted Channels List Received', data: body });
return res.status(200).json(body); return res.status(200).json(body);
}).catch((errRes) => { }).catch((errRes) => {
@ -72,12 +71,12 @@ export const getPendingChannels = (req, res, next) => {
if (body.pending_open_channels && body.pending_open_channels.length > 0) { 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))); body.pending_open_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.pending_force_closing_channels && body.pending_force_closing_channels.length > 0) { 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))); 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) { 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))); body.waiting_close_channels?.map((channel) => promises.push(getAliasForChannel(req.session.selectedNode, channel.channel)));
} }
@ -109,7 +108,6 @@ export const getClosedChannels = (req, res, next) => {
return getAliasForChannel(req.session.selectedNode, channel); return getAliasForChannel(req.session.selectedNode, channel);
}) })
).then((values) => { ).then((values) => {
body.channels = common.sortDescByKey(body.channels, 'close_height');
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Closed Channels List Received', data: body }); logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Closed Channels List Received', data: body });
return res.status(200).json(body); return res.status(200).json(body);
}).catch((errRes) => { }).catch((errRes) => {
@ -156,7 +154,7 @@ export const postTransactions = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Sending Payment..' }); logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Sending Payment..' });
options = common.getOptions(req); options = common.getOptions(req);
if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } 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/transactions'; options.url = req.session.selectedNode.ln_server_url + '/v1/channels/transaction-stream';
options.form = { payment_request: req.body.paymentReq }; options.form = { payment_request: req.body.paymentReq };
if (req.body.paymentAmount) { if (req.body.paymentAmount) {
options.form.amt = req.body.paymentAmount; options.form.amt = req.body.paymentAmount;

@ -40,7 +40,7 @@ export const getInfo = (req, res, next) => {
} else { } else {
req.session.selectedNode.ln_version = body.version.split('-')[0] || ''; req.session.selectedNode.ln_version = body.version.split('-')[0] || '';
lndWsClient.updateSelectedNode(req.session.selectedNode); lndWsClient.updateSelectedNode(req.session.selectedNode);
databaseService.loadDatabase(req.session.selectedNode); databaseService.loadDatabase(req.session);
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Node Information Received', data: body }); logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Node Information Received', data: body });
return res.status(200).json(body); return res.status(200).json(body);
} }

@ -12,7 +12,7 @@ export const getAliasFromPubkey = (selNode: CommonSelectedNode, pubkey) => {
logger.log({ selectedNode: selNode, level: 'DEBUG', fileName: 'Graph', msg: 'Alias Received', data: res.node.alias }); logger.log({ selectedNode: selNode, level: 'DEBUG', fileName: 'Graph', msg: 'Alias Received', data: res.node.alias });
return res.node.alias; return res.node.alias;
}). }).
catch((err) => pubkey.substring(0, 17) + '...'); catch((err) => pubkey.substring(0, 20));
}; };
export const getDescribeGraph = (req, res, next) => { export const getDescribeGraph = (req, res, next) => {

@ -44,7 +44,6 @@ export const listInvoices = (req, res, next) => {
invoice.r_hash = invoice.r_hash ? Buffer.from(invoice.r_hash, '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; invoice.description_hash = invoice.description_hash ? Buffer.from(invoice.description_hash, 'base64').toString('hex') : null;
}); });
body.invoices = common.sortDescByKey(body.invoices, 'creation_date');
} }
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoice', msg: 'Sorted Invoices List Received', data: body }); logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoice', msg: 'Sorted Invoices List Received', data: body });
res.status(200).json(body); res.status(200).json(body);
@ -59,17 +58,7 @@ export const addInvoice = (req, res, next) => {
options = common.getOptions(req); options = common.getOptions(req);
if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } 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.url = req.session.selectedNode.ln_server_url + '/v1/invoices';
options.form = { options.form = JSON.stringify(req.body);
memo: req.body.memo,
private: req.body.private,
expiry: req.body.expiry
};
if (req.body.amount > 0 && req.body.amount < 1) {
options.form.value_msat = req.body.amount * 1000;
} else {
options.form.value = req.body.amount;
}
options.form = JSON.stringify(options.form);
request.post(options).then((body) => { request.post(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoice', msg: 'Invoice Added', data: body }); logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoice', msg: 'Invoice Added', data: body });
try { try {

@ -57,10 +57,6 @@ export const getPayments = (req, res, next) => {
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; 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) => { request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Payments', msg: 'Payment List Received', data: body }); logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Payments', msg: 'Payment List Received', data: body });
if (body.payments && body.payments.length > 0) {
body.payments = common.sortDescByKey(body.payments, 'creation_date');
}
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Sorted Payments List Received', data: body });
res.status(200).json(body); res.status(200).json(body);
}).catch((errRes) => { }).catch((errRes) => {
const err = common.handleError(errRes, 'Payments', 'List Payments Error', req.session.selectedNode); const err = common.handleError(errRes, 'Payments', 'List Payments Error', req.session.selectedNode);

@ -13,7 +13,7 @@ export const getAliasForPeers = (selNode: CommonSelectedNode, peer) => {
peer.alias = aliasBody.node.alias; peer.alias = aliasBody.node.alias;
return aliasBody.node.alias; return aliasBody.node.alias;
}).catch((err) => { }).catch((err) => {
peer.alias = peer.pub_key.slice(0, 10) + '...' + peer.pub_key.slice(-10); peer.alias = peer.pub_key.slice(0, 20);
return peer.pub_key; return peer.pub_key;
}); });
}; };
@ -27,10 +27,6 @@ export const getPeers = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Peers', msg: 'Peers List Received', data: body }); logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Peers', msg: 'Peers List Received', data: body });
const peers = !body.peers ? [] : body.peers; const peers = !body.peers ? [] : body.peers;
return Promise.all(peers?.map((peer) => getAliasForPeers(req.session.selectedNode, peer))).then((values) => { return Promise.all(peers?.map((peer) => getAliasForPeers(req.session.selectedNode, peer))).then((values) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Peers', msg: 'Peers with Alias before Sort', data: body });
if (body.peers) {
body.peers = common.sortDescByStrKey(body.peers, 'alias');
}
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Sorted Peers List Received', data: body.peers }); logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Sorted Peers List Received', data: body.peers });
res.status(200).json(body.peers); res.status(200).json(body.peers);
}); });
@ -56,7 +52,6 @@ export const postPeer = (req, res, next) => {
const peers = (!body.peers) ? [] : body.peers; const peers = (!body.peers) ? [] : body.peers;
return Promise.all(peers?.map((peer) => getAliasForPeers(req.session.selectedNode, peer))).then((values) => { return Promise.all(peers?.map((peer) => getAliasForPeers(req.session.selectedNode, peer))).then((values) => {
if (body.peers) { if (body.peers) {
body.peers = common.sortDescByStrKey(body.peers, 'alias');
body.peers = common.newestOnTop(body.peers, 'pub_key', req.body.pubkey); 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 }); logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Peers List after Connect Received', data: body });
} }

@ -40,9 +40,6 @@ export const getAllForwardingEvents = (req, start, end, offset, caller, callback
} }
if (!body.last_offset_index || body.last_offset_index < offset + num_max_events) { 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; responseData[caller].last_offset_index = body.last_offset_index ? body.last_offset_index : 0;
if (responseData[caller].forwarding_events) {
responseData[caller].forwarding_events = common.sortDescByKey(responseData[caller].forwarding_events, 'timestamp');
}
return callback(responseData[caller]); return callback(responseData[caller]);
} else { } else {
return getAllForwardingEvents(req, start, end, offset + num_max_events, caller, callback); return getAllForwardingEvents(req, start, end, offset + num_max_events, caller, callback);

@ -12,10 +12,6 @@ export const getTransactions = (req, res, next) => {
options.url = req.session.selectedNode.ln_server_url + '/v1/transactions'; options.url = req.session.selectedNode.ln_server_url + '/v1/transactions';
request(options).then((body) => { request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Transactions', msg: 'Transactions List Received', data: body }); logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Transactions', msg: 'Transactions List Received', data: body });
if (body.transactions && body.transactions.length > 0) {
body.transactions = common.sortDescByKey(body.transactions, 'time_stamp');
}
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Transactions', msg: 'Sorted Transactions List Received', data: body.transactions });
res.status(200).json(body.transactions); res.status(200).json(body.transactions);
}).catch((errRes) => { }).catch((errRes) => {
const err = common.handleError(errRes, 'Transactions', 'List Transactions Error', req.session.selectedNode); const err = common.handleError(errRes, 'Transactions', 'List Transactions Error', req.session.selectedNode);

@ -22,7 +22,7 @@ export const updateSelectedNode = (req, res, next) => {
if (req.headers && req.headers.authorization && req.headers.authorization !== '') { if (req.headers && req.headers.authorization && req.headers.authorization !== '') {
wsServer.updateLNWSClientDetails(req.session.id, +req.session.selectedNode.index, +req.params.prevNodeIndex); wsServer.updateLNWSClientDetails(req.session.id, +req.session.selectedNode.index, +req.params.prevNodeIndex);
if (req.params.prevNodeIndex !== -1) { if (req.params.prevNodeIndex !== -1) {
databaseService.unloadDatabase(req.params.prevNodeIndex); databaseService.unloadDatabase(req.params.prevNodeIndex, req.session.id);
} }
} }
const responseVal = !req.session.selectedNode.ln_node ? '' : req.session.selectedNode.ln_node; const responseVal = !req.session.selectedNode.ln_node ? '' : req.session.selectedNode.ln_node;
@ -51,10 +51,11 @@ export const getRTLConfigInitial = (req, res, next) => {
const nodesArr = []; const nodesArr = [];
if (common.nodes && common.nodes.length > 0) { if (common.nodes && common.nodes.length > 0) {
common.nodes.forEach((node, i) => { common.nodes.forEach((node, i) => {
const settings: NodeSettingsConfiguration = {}; const settings: NodeSettingsConfiguration = { unannouncedChannels: false };
settings.userPersona = node.user_persona ? node.user_persona : 'MERCHANT'; settings.userPersona = node.user_persona ? node.user_persona : 'MERCHANT';
settings.themeMode = (node.theme_mode) ? node.theme_mode : 'DAY'; settings.themeMode = (node.theme_mode) ? node.theme_mode : 'DAY';
settings.themeColor = (node.theme_color) ? node.theme_color : 'PURPLE'; 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.fiatConversion = (node.fiat_conversion) ? !!node.fiat_conversion : false;
settings.currencyUnit = node.currency_unit; settings.currencyUnit = node.currency_unit;
nodesArr.push({ nodesArr.push({
@ -98,10 +99,11 @@ export const getRTLConfig = (req, res, next) => {
authentication.configPath = (node.config_path) ? node.config_path : ''; authentication.configPath = (node.config_path) ? node.config_path : '';
authentication.swapMacaroonPath = (node.swap_macaroon_path) ? node.swap_macaroon_path : ''; authentication.swapMacaroonPath = (node.swap_macaroon_path) ? node.swap_macaroon_path : '';
authentication.boltzMacaroonPath = (node.boltz_macaroon_path) ? node.boltz_macaroon_path : ''; authentication.boltzMacaroonPath = (node.boltz_macaroon_path) ? node.boltz_macaroon_path : '';
const settings: NodeSettingsConfiguration = {}; const settings: NodeSettingsConfiguration = { unannouncedChannels: false };
settings.userPersona = node.user_persona ? node.user_persona : 'MERCHANT'; settings.userPersona = node.user_persona ? node.user_persona : 'MERCHANT';
settings.themeMode = (node.theme_mode) ? node.theme_mode : 'DAY'; settings.themeMode = (node.theme_mode) ? node.theme_mode : 'DAY';
settings.themeColor = (node.theme_color) ? node.theme_color : 'PURPLE'; 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.fiatConversion = (node.fiat_conversion) ? !!node.fiat_conversion : false;
settings.bitcoindConfigPath = node.bitcoind_config_path; settings.bitcoindConfigPath = node.bitcoind_config_path;
settings.logLevel = node.log_level ? node.log_level : 'ERROR'; settings.logLevel = node.log_level ? node.log_level : 'ERROR';
@ -109,6 +111,7 @@ export const getRTLConfig = (req, res, next) => {
settings.swapServerUrl = node.swap_server_url; settings.swapServerUrl = node.swap_server_url;
settings.boltzServerUrl = node.boltz_server_url; settings.boltzServerUrl = node.boltz_server_url;
settings.enableOffers = node.enable_offers; settings.enableOffers = node.enable_offers;
settings.enablePeerswap = node.enable_peerswap;
settings.channelBackupPath = node.channel_backup_path; settings.channelBackupPath = node.channel_backup_path;
settings.currencyUnit = node.currency_unit; settings.currencyUnit = node.currency_unit;
nodesArr.push({ nodesArr.push({
@ -136,6 +139,7 @@ export const updateUISettings = (req, res, next) => {
node.Settings.userPersona = req.body.updatedSettings.userPersona; node.Settings.userPersona = req.body.updatedSettings.userPersona;
node.Settings.themeMode = req.body.updatedSettings.themeMode; node.Settings.themeMode = req.body.updatedSettings.themeMode;
node.Settings.themeColor = req.body.updatedSettings.themeColor; node.Settings.themeColor = req.body.updatedSettings.themeColor;
node.Settings.unannouncedChannels = req.body.updatedSettings.unannouncedChannels;
node.Settings.fiatConversion = req.body.updatedSettings.fiatConversion; node.Settings.fiatConversion = req.body.updatedSettings.fiatConversion;
if (req.body.updatedSettings.fiatConversion) { if (req.body.updatedSettings.fiatConversion) {
node.Settings.currencyUnit = req.body.updatedSettings.currencyUnit ? req.body.updatedSettings.currencyUnit : 'USD'; node.Settings.currencyUnit = req.body.updatedSettings.currencyUnit ? req.body.updatedSettings.currencyUnit : 'USD';
@ -146,6 +150,7 @@ export const updateUISettings = (req, res, next) => {
selectedNode.user_persona = req.body.updatedSettings.userPersona; selectedNode.user_persona = req.body.updatedSettings.userPersona;
selectedNode.theme_mode = req.body.updatedSettings.themeMode; selectedNode.theme_mode = req.body.updatedSettings.themeMode;
selectedNode.theme_color = req.body.updatedSettings.themeColor; selectedNode.theme_color = req.body.updatedSettings.themeColor;
selectedNode.unannounced_channels = req.body.updatedSettings.unannouncedChannels;
selectedNode.fiat_conversion = req.body.updatedSettings.fiatConversion; selectedNode.fiat_conversion = req.body.updatedSettings.fiatConversion;
if (req.body.updatedSettings.fiatConversion) { if (req.body.updatedSettings.fiatConversion) {
selectedNode.currency_unit = req.body.updatedSettings.currencyUnit ? req.body.updatedSettings.currencyUnit : 'USD'; selectedNode.currency_unit = req.body.updatedSettings.currencyUnit ? req.body.updatedSettings.currencyUnit : 'USD';
@ -240,7 +245,7 @@ export const getConfig = (req, res, next) => {
if (jsonConfig['Application Options'] && jsonConfig['Application Options'].color) { if (jsonConfig['Application Options'] && jsonConfig['Application Options'].color) {
jsonConfig['Application Options'].color = '#' + jsonConfig['Application Options'].color; jsonConfig['Application Options'].color = '#' + jsonConfig['Application Options'].color;
} }
if (req.session.selectedNode.ln_implementation === 'ECL' && !jsonConfig['eclair.api.password']) { if (req.params.nodeType === 'ln' && req.session.selectedNode.ln_implementation === 'ECL' && !jsonConfig['eclair.api.password']) {
fileFormat = 'HOCON'; fileFormat = 'HOCON';
jsonConfig = parseHocon(data); jsonConfig = parseHocon(data);
} }
@ -306,7 +311,7 @@ export const updateServiceSettings = (req, res, next) => {
const RTLConfFile = common.rtl_conf_file_path + sep + 'RTL-Config.json'; const RTLConfFile = common.rtl_conf_file_path + sep + 'RTL-Config.json';
const config = JSON.parse(fs.readFileSync(RTLConfFile, 'utf-8')); const config = JSON.parse(fs.readFileSync(RTLConfFile, 'utf-8'));
const selectedNode = common.findNode(req.session.selectedNode.index); const selectedNode = common.findNode(req.session.selectedNode.index);
config.nodes.find((node) => { config.nodes.forEach((node) => {
if (node.index === req.session.selectedNode.index) { if (node.index === req.session.selectedNode.index) {
switch (req.body.service) { switch (req.body.service) {
case 'LOOP': case 'LOOP':
@ -342,6 +347,11 @@ export const updateServiceSettings = (req, res, next) => {
selectedNode.enable_offers = req.body.settings.enableOffers; selectedNode.enable_offers = req.body.settings.enableOffers;
break; break;
case 'PEERSWAP':
node.Settings.enablePeerswap = req.body.settings.enablePeerswap;
selectedNode.enable_peerswap = req.body.settings.enablePeerswap;
break;
default: default:
break; break;
} }
@ -370,7 +380,8 @@ export const maskPasswords = (obj) => {
} }
if (typeof keys[i] === 'string' && if (typeof keys[i] === 'string' &&
(keys[i].toLowerCase().includes('password') || keys[i].toLowerCase().includes('multipass') || (keys[i].toLowerCase().includes('password') || keys[i].toLowerCase().includes('multipass') ||
keys[i].toLowerCase().includes('rpcpass') || keys[i].toLowerCase().includes('rpcpassword')) keys[i].toLowerCase().includes('rpcpass') || keys[i].toLowerCase().includes('rpcpassword') ||
keys[i].toLowerCase().includes('rpcuser'))
) { ) {
obj[keys[i]] = '********************'; obj[keys[i]] = '********************';
} }

@ -120,7 +120,7 @@ export const resetPassword = (req, res, next) => {
export const logoutUser = (req, res, next) => { export const logoutUser = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Authenticate', msg: 'Logged out' }); logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Authenticate', msg: 'Logged out' });
if (req.session.selectedNode && req.session.selectedNode.index) { if (req.session.selectedNode && req.session.selectedNode.index) {
databaseService.unloadDatabase(+req.session.selectedNode.index); databaseService.unloadDatabase(+req.session.selectedNode.index, req.session.id);
} }
req.session.destroy((err) => { req.session.destroy((err) => {
res.clearCookie('connect.sid'); res.clearCookie('connect.sid');

@ -219,10 +219,6 @@ export const swaps = (req, res, next) => {
options.url = options.url + '/v1/loop/swaps'; options.url = options.url + '/v1/loop/swaps';
request(options).then((body) => { request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop Swaps Received', data: body }); logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop Swaps Received', data: body });
if (body.swaps && body.swaps.length > 0) {
body.swaps = common.sortDescByKey(body.swaps, 'initiation_time');
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Sorted Loop Swaps List Received', data: body });
}
res.status(200).json(body.swaps); res.status(200).json(body.swaps);
}).catch((errRes) => { }).catch((errRes) => {
const err = common.handleError(errRes, 'Loop', 'List Swaps Error', req.session.selectedNode); const err = common.handleError(errRes, 'Loop', 'List Swaps Error', req.session.selectedNode);

@ -0,0 +1,36 @@
import { Database, DatabaseService } from '../../utils/database.js';
import { Logger, LoggerService } from '../../utils/logger.js';
import { Common, CommonService } from '../../utils/common.js';
import { CollectionsEnum, PageSettings } from '../../models/database.model.js';
const logger: LoggerService = Logger;
const common: CommonService = Common;
const databaseService: 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: PageSettings) => {
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 });
});
};

@ -21,11 +21,13 @@ export class CommonSelectedNode {
public user_persona?: string, public user_persona?: string,
public theme_mode?: string, public theme_mode?: string,
public theme_color?: string, public theme_color?: string,
public unannounced_channels?: boolean,
public fiat_conversion?: boolean, public fiat_conversion?: boolean,
public currency_unit?: string, public currency_unit?: string,
public ln_version?: string, public ln_version?: string,
public api_version?: string, public api_version?: string,
public enable_offers?: boolean public enable_offers?: boolean,
public enable_peerswap?: boolean
) { } ) { }
} }
@ -46,6 +48,7 @@ export class NodeSettingsConfiguration {
public userPersona?: string, public userPersona?: string,
public themeMode?: string, public themeMode?: string,
public themeColor?: string, public themeColor?: string,
public unannouncedChannels?: boolean,
public fiatConversion?: boolean, public fiatConversion?: boolean,
public currencyUnit?: string, public currencyUnit?: string,
public bitcoindConfigPath?: string, public bitcoindConfigPath?: string,
@ -54,7 +57,8 @@ export class NodeSettingsConfiguration {
public swapServerUrl?: string, public swapServerUrl?: string,
public boltzServerUrl?: string, public boltzServerUrl?: string,
public channelBackupPath?: string, public channelBackupPath?: string,
public enableOffers?: boolean public enableOffers?: boolean,
public enablePeerswap?: boolean
) { } ) { }
} }

@ -1,26 +1,16 @@
export enum CollectionsEnum {
OFFERS = 'Offers'
}
export type Collections = {
Offers: Offer[];
}
export enum OfferFieldsEnum { export enum OfferFieldsEnum {
BOLT12 = 'bolt12', BOLT12 = 'bolt12',
AMOUNTMSAT = 'amountmSat', AMOUNTMSAT = 'amountMSat',
TITLE = 'title', TITLE = 'title',
VENDOR = 'vendor', VENDOR = 'vendor',
DESCRIPTION = 'description' DESCRIPTION = 'description'
} }
export const CollectionFieldsEnum = { ...OfferFieldsEnum };
export class Offer { export class Offer {
constructor( constructor(
public bolt12: string, public bolt12: string,
public amountmSat: number, public amountMSat: number,
public title: string, public title: string,
public vendor?: string, public vendor?: string,
public description?: string, public description?: string,
@ -29,18 +19,138 @@ export class Offer {
} }
export const validateDocument = (collectionName: CollectionsEnum, documentToValidate: any): any => {
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): any => { export const validateOffer = (documentToValidate): any => {
if (!documentToValidate.hasOwnProperty(CollectionFieldsEnum.BOLT12)) { if (!documentToValidate.hasOwnProperty(CollectionFieldsEnum.BOLT12)) {
return ({ isValid: false, error: CollectionFieldsEnum.BOLT12 + 'is mandatory.' }); return ({ isValid: false, error: 'Bolt12 is mandatory.' });
} }
if (!documentToValidate.hasOwnProperty(CollectionFieldsEnum.AMOUNTMSAT)) { if (!documentToValidate.hasOwnProperty(CollectionFieldsEnum.AMOUNTMSAT)) {
return ({ isValid: false, error: CollectionFieldsEnum.AMOUNTMSAT + 'is mandatory.' }); return ({ isValid: false, error: 'Amount mSat is mandatory.' });
} }
if (!documentToValidate.hasOwnProperty(CollectionFieldsEnum.TITLE)) { if (!documentToValidate.hasOwnProperty(CollectionFieldsEnum.TITLE)) {
return ({ isValid: false, error: CollectionFieldsEnum.TITLE + 'is mandatory.' }); return ({ isValid: false, error: 'Title is mandatory.' });
} }
if ((typeof documentToValidate[CollectionFieldsEnum.AMOUNTMSAT] !== 'number')) { if ((typeof documentToValidate[CollectionFieldsEnum.AMOUNTMSAT] !== 'number')) {
return ({ isValid: false, error: CollectionFieldsEnum.AMOUNTMSAT + 'should be a number.' }); return ({ isValid: false, error: 'Amount mSat should be a number.' });
} }
return ({ isValid: true }); return ({ isValid: true });
}; };
export enum SortOrderEnum {
ASCENDING = 'asc',
DESCENDING = 'desc'
}
export enum PageSettingsFieldsEnum {
PAGE_ID = 'pageId',
TABLES = 'tables'
}
export enum TableSettingsFieldsEnum {
TABLE_ID = 'tableId',
RECORDS_PER_PAGE = 'recordsPerPage',
SORT_BY = 'sortBy',
SORT_ORDER = 'sortOrder',
COLUMN_SELECTION = 'columnSelection',
COLUMN_SELECTION_SM = 'columnSelectionSM'
}
export class TableSetting {
constructor(
public tableId: string,
public recordsPerPage?: number,
public sortBy?: string,
public sortOrder?: SortOrderEnum,
public columnSelection?: any[]
) { }
}
export class PageSettings {
constructor(
public pageId: string,
public tables: TableSetting[]
) { }
}
export const validatePageSettings = (documentToValidate): any => {
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: TableSetting, 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 enum CollectionsEnum {
OFFERS = 'Offers',
PAGE_SETTINGS = 'PageSettings'
}
export type Collections = {
Offers: Offer[];
PageSettings: PageSettings[];
}
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];

@ -4,6 +4,7 @@ import authenticateRoutes from './authenticate.js';
import boltzRoutes from './boltz.js'; import boltzRoutes from './boltz.js';
import loopRoutes from './loop.js'; import loopRoutes from './loop.js';
import RTLConfRoutes from './RTLConf.js'; import RTLConfRoutes from './RTLConf.js';
import pageSettingsRoutes from './pageSettings.js';
const router = Router(); const router = Router();
@ -11,7 +12,8 @@ const sharedRoutes = [
{ path: '/authenticate', route: authenticateRoutes }, { path: '/authenticate', route: authenticateRoutes },
{ path: '/boltz', route: boltzRoutes }, { path: '/boltz', route: boltzRoutes },
{ path: '/loop', route: loopRoutes }, { path: '/loop', route: loopRoutes },
{ path: '/conf', route: RTLConfRoutes } { path: '/conf', route: RTLConfRoutes },
{ path: '/pagesettings', route: pageSettingsRoutes }
]; ];
sharedRoutes.forEach((route) => { sharedRoutes.forEach((route) => {

@ -0,0 +1,11 @@
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;

@ -63,13 +63,14 @@ export class ExpressApplication {
this.app.use(this.common.baseHref + '/api/ecl', eclRoutes); this.app.use(this.common.baseHref + '/api/ecl', eclRoutes);
this.app.use(this.common.baseHref, express.static(join(this.directoryName, '../..', 'frontend'))); this.app.use(this.common.baseHref, express.static(join(this.directoryName, '../..', 'frontend')));
this.app.use((req: any, res, next) => { this.app.use((req: any, res, next) => {
// For Angular App res.cookie('XSRF-TOKEN', req.csrfToken ? req.csrfToken() : ''); // RTL Angular Frontend
res.cookie('XSRF-TOKEN', req.csrfToken ? req.csrfToken() : ''); res.setHeader('XSRF-TOKEN', req.csrfToken ? req.csrfToken() : ''); // RTL Quickpay JQuery
// For JQuery Browser Plugin
res.setHeader('XSRF-TOKEN', req.csrfToken ? req.csrfToken() : '');
res.sendFile(join(this.directoryName, '../..', 'frontend', 'index.html')); res.sendFile(join(this.directoryName, '../..', 'frontend', 'index.html'));
}); });
this.app.use((err, req, res, next) => this.handleApplicationErrors(err, res)); 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.logger.log({ selectedNode: this.common.initSelectedNode, level: 'INFO', fileName: 'App', msg: 'Application Routes Set' });
}; };

@ -26,7 +26,10 @@ export class CommonService {
public read_dummy_data = false; public read_dummy_data = false;
public baseHref = '/rtl'; public baseHref = '/rtl';
private dummy_data_array_from_file = []; private dummy_data_array_from_file = [];
private MONTHS = [{ name: 'JAN', days: 31 }, { name: 'FEB', days: 28 }, { name: 'MAR', days: 31 }, { name: 'APR', days: 30 }, { name: 'MAY', days: 31 }, { name: 'JUN', days: 30 }, { name: 'JUL', days: 31 }, { name: 'AUG', days: 31 }, { name: 'SEP', days: 30 }, { name: 'OCT', days: 31 }, { name: 'NOV', days: 30 }, { name: 'DEC', days: 31 }]; private MONTHS = [
{ name: 'JAN', days: 31 }, { name: 'FEB', days: 28 }, { name: 'MAR', days: 31 }, { name: 'APR', days: 30 }, { name: 'MAY', days: 31 }, { name: 'JUN', days: 30 },
{ name: 'JUL', days: 31 }, { name: 'AUG', days: 31 }, { name: 'SEP', days: 30 }, { name: 'OCT', days: 31 }, { name: 'NOV', days: 30 }, { name: 'DEC', days: 31 }
];
constructor() { } constructor() { }
@ -268,18 +271,28 @@ export class CommonService {
break; 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') }); this.logger.log({ selectedNode: selectedNode, level: 'ERROR', fileName: fileName, msg: errMsg, error: (typeof err === 'object' ? JSON.stringify(err) : (typeof err === 'string') ? err : 'Unknown Error') });
const newErrorObj = { let newErrorObj = { statusCode: 500, message: '', error: '' };
statusCode: err.statusCode ? err.statusCode : err.status ? err.status : (err.error && err.error.code && err.error.code === 'ECONNREFUSED') ? 503 : 500, if (err.code && err.code === 'ENOENT') {
message: (err.error && err.error.message) ? err.error.message : err.message ? err.message : errMsg, newErrorObj = {
error: ( statusCode: 500,
(err.error && err.error.error && err.error.error.error && typeof err.error.error.error === 'string') ? err.error.error.error : message: 'No such file or directory ' + (err.path ? err.path : ''),
(err.error && err.error.error && typeof err.error.error === 'string') ? err.error.error : error: 'No such file or directory ' + (err.path ? err.path : '')
(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 : } else {
(err.error && typeof err.error === 'string') ? err.error : newErrorObj = {
(err.message && typeof err.message === 'string') ? err.message : (typeof err === 'string') ? err : 'Unknown Error' 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.indexOf('Authentication Error') < 0 && err.name === 'StatusCodeError') { newErrorObj.statusCode = 500; }
return newErrorObj; return newErrorObj;
}; };

@ -70,12 +70,13 @@ export class ConfigService {
channelBackupPath: channelBackupPath, channelBackupPath: channelBackupPath,
logLevel: 'ERROR', logLevel: 'ERROR',
lnServerUrl: 'https://localhost:8080', lnServerUrl: 'https://localhost:8080',
fiatConversion: false fiatConversion: false,
unannouncedChannels: false
} }
} }
] ]
}; };
if (+process.env.RTL_SSO === 0) { if (+process.env.RTL_SSO === 0 || configData.SSO.rtlSSO === 0) {
configData['multiPass'] = 'password'; configData['multiPass'] = 'password';
} }
return configData; return configData;
@ -200,6 +201,7 @@ export class ConfigService {
this.common.nodes[idx].user_persona = node.Settings.userPersona ? node.Settings.userPersona : 'MERCHANT'; 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_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].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].log_level = node.Settings.logLevel ? node.Settings.logLevel : 'ERROR';
this.common.nodes[idx].fiat_conversion = node.Settings.fiatConversion ? !!node.Settings.fiatConversion : false; this.common.nodes[idx].fiat_conversion = node.Settings.fiatConversion ? !!node.Settings.fiatConversion : false;
if (this.common.nodes[idx].fiat_conversion) { if (this.common.nodes[idx].fiat_conversion) {
@ -226,6 +228,7 @@ export class ConfigService {
this.common.nodes[idx].boltz_macaroon_path = ''; 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_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].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; 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 { try {

@ -3,7 +3,7 @@ import { join, dirname, sep } from 'path';
import { fileURLToPath } from 'url'; import { fileURLToPath } from 'url';
import { Common, CommonService } from '../utils/common.js'; import { Common, CommonService } from '../utils/common.js';
import { Logger, LoggerService } from '../utils/logger.js'; import { Logger, LoggerService } from '../utils/logger.js';
import { Collections, CollectionsEnum, validateOffer } from '../models/database.model.js'; import { Collections, CollectionsEnum, validateDocument, LNDCollection, ECLCollection, CLNCollection } from '../models/database.model.js';
import { CommonSelectedNode } from '../models/config.model.js'; import { CommonSelectedNode } from '../models/config.model.js';
export class DatabaseService { export class DatabaseService {
@ -15,32 +15,70 @@ export class DatabaseService {
constructor() { } constructor() { }
loadDatabase(selectedNode: CommonSelectedNode) { loadDatabase(session: any) {
const { id, selectedNode } = session;
try { try {
if (!this.nodeDatabase[selectedNode.index]) { if (!this.nodeDatabase[selectedNode.index]) {
this.nodeDatabase[selectedNode.index] = { adapter: null, data: null }; 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);
} }
this.nodeDatabase[selectedNode.index].adapter = new DatabaseAdapter(this.dbDirectory, 'rtldb', selectedNode);
this.nodeDatabase[selectedNode.index].data = this.nodeDatabase[selectedNode.index].adapter.fetchData();
} catch (err) { } catch (err) {
this.logger.log({ selectedNode: selectedNode, level: 'ERROR', fileName: 'Database', msg: 'Database Load Error', error: err }); this.logger.log({ selectedNode: selectedNode, level: 'ERROR', fileName: 'Database', msg: 'Database Load Error', error: err });
} }
} }
create(selectedNode: CommonSelectedNode, collectionName: CollectionsEnum, newDocument: any) { fetchNodeData(selectedNode: CommonSelectedNode) {
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: CommonSelectedNode, collectionName: CollectionsEnum, newCollection: any) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
try { try {
if (!selectedNode || !selectedNode.index) { if (!selectedNode || !selectedNode.index) {
reject(new Error('Selected Node Config Not Found.')); reject(new Error('Selected Node Config Not Found.'));
} }
const validationRes = this.validateDocument(CollectionsEnum.OFFERS, newDocument); this.nodeDatabase[selectedNode.index].data[collectionName] = newCollection;
if (!validationRes.isValid) { this.saveDatabase(selectedNode, collectionName);
reject(validationRes.error); resolve(this.nodeDatabase[selectedNode.index].data[collectionName]);
} else {
this.nodeDatabase[selectedNode.index].data[collectionName].push(newDocument);
this.saveDatabase(+selectedNode.index);
resolve(newDocument);
}
} catch (errRes) { } catch (errRes) {
reject(errRes); reject(errRes);
} }
@ -67,21 +105,16 @@ export class DatabaseService {
} }
updatedDocument = foundDoc; updatedDocument = foundDoc;
} }
const validationRes = this.validateDocument(CollectionsEnum.OFFERS, updatedDocument); if (foundDocIdx > -1) {
if (!validationRes.isValid) { this.nodeDatabase[selectedNode.index].data[collectionName].splice(foundDocIdx, 1, updatedDocument);
reject(validationRes.error);
} else { } else {
if (foundDocIdx > -1) { if (!this.nodeDatabase[selectedNode.index].data[collectionName]) {
this.nodeDatabase[selectedNode.index].data[collectionName].splice(foundDocIdx, 1, updatedDocument); this.nodeDatabase[selectedNode.index].data[collectionName] = [];
} 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.index); this.nodeDatabase[selectedNode.index].data[collectionName].push(updatedDocument);
resolve(updatedDocument);
} }
this.saveDatabase(selectedNode, collectionName);
resolve(updatedDocument);
} catch (errRes) { } catch (errRes) {
reject(errRes); reject(errRes);
} }
@ -105,7 +138,7 @@ export class DatabaseService {
}); });
} }
destroy(selectedNode: CommonSelectedNode, collectionName: CollectionsEnum, documentFieldName: string, documentFieldValue: string) { remove(selectedNode: CommonSelectedNode, collectionName: CollectionsEnum, documentFieldName: string, documentFieldValue: string) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
try { try {
if (!selectedNode || !selectedNode.index) { if (!selectedNode || !selectedNode.index) {
@ -117,7 +150,7 @@ export class DatabaseService {
} else { } else {
reject(new Error('Unable to delete, document not found.')); reject(new Error('Unable to delete, document not found.'));
} }
this.saveDatabase(+selectedNode.index); this.saveDatabase(selectedNode, collectionName);
resolve(documentFieldValue); resolve(documentFieldValue);
} catch (errRes) { } catch (errRes) {
reject(errRes); reject(errRes);
@ -125,19 +158,10 @@ export class DatabaseService {
}); });
} }
validateDocument(collectionName: CollectionsEnum, documentToValidate: any) { saveDatabase(selectedNode: CommonSelectedNode, collectionName: CollectionsEnum) {
switch (collectionName) { const nodeIndex = +selectedNode.index;
case CollectionsEnum.OFFERS:
return validateOffer(documentToValidate);
default:
return ({ isValid: false, error: 'Collection does not exist' });
}
}
saveDatabase(nodeIndex: number) {
try { try {
if (+nodeIndex < 1) { if (nodeIndex < 1) {
return true; return true;
} }
const selNode = this.nodeDatabase[nodeIndex] && this.nodeDatabase[nodeIndex].adapter && this.nodeDatabase[nodeIndex].adapter.selNode ? this.nodeDatabase[nodeIndex].adapter.selNode : null; const selNode = this.nodeDatabase[nodeIndex] && this.nodeDatabase[nodeIndex].adapter && this.nodeDatabase[nodeIndex].adapter.selNode ? this.nodeDatabase[nodeIndex].adapter.selNode : null;
@ -145,51 +169,103 @@ export class DatabaseService {
this.logger.log({ selectedNode: selNode, level: 'ERROR', fileName: 'Database', msg: 'Database Save Error: Selected Node Setup Not Found.' }); 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.'); throw new Error('Database Save Error: Selected Node Setup Not Found.');
} }
this.nodeDatabase[nodeIndex].adapter.saveData(this.nodeDatabase[nodeIndex].data); 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 Saved' }); this.logger.log({ selectedNode: this.nodeDatabase[nodeIndex].adapter.selNode, level: 'INFO', fileName: 'Database', msg: 'Database Collection ' + collectionName + ' Saved' });
return true; return true;
} catch (err) { } catch (err) {
const selNode = this.nodeDatabase[nodeIndex] && this.nodeDatabase[nodeIndex].adapter && this.nodeDatabase[nodeIndex].adapter.selNode ? this.nodeDatabase[nodeIndex].adapter.selNode : null; 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 }); this.logger.log({ selectedNode: selNode, level: 'ERROR', fileName: 'Database', msg: 'Database Save Error', error: err });
return new Error(err); throw err;
} }
} }
unloadDatabase(nodeIndex: number) { unloadDatabase(nodeIndex: number, sessionID: string) {
this.saveDatabase(nodeIndex); if (nodeIndex > 0) {
this.nodeDatabase[nodeIndex] = null; 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 { export class DatabaseAdapter {
private dbFile = ''; private logger: LoggerService = Logger;
private common: CommonService = Common;
private dbFilePath = '';
private userSessions = [];
constructor(public dbDirectoryPath: string, public fileName: string, private selNode: CommonSelectedNode = null) { constructor(public dbDirectoryPath: string, private selNode: CommonSelectedNode = null, private id: string = '') {
this.dbFile = dbDirectoryPath + sep + fileName + '-node-' + selNode.index + '.json'; 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);
} }
fetchData() { renameOldDB(oldFilePath: string, selNode: CommonSelectedNode = null) {
const newFilePath = this.dbFilePath + sep + 'rtldb-' + selNode.ln_implementation + '-Offers.json';
try { try {
if (!fs.existsSync(this.dbDirectoryPath)) { this.common.createDirectory(this.dbFilePath);
fs.mkdirSync(this.dbDirectoryPath); const oldOffers: any = JSON.parse(fs.readFileSync(oldFilePath, 'utf-8'));
fs.writeFileSync(oldFilePath, JSON.stringify(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: string) {
try {
if (!fs.existsSync(this.dbFilePath)) {
this.common.createDirectory(this.dbFilePath);
} }
} catch (err) { } catch (err) {
return new Error('Unable to Create Directory Error ' + JSON.stringify(err)); throw new Error(JSON.stringify(err));
} }
const collectionFilePath = this.dbFilePath + sep + 'rtldb-' + this.selNode.ln_implementation + '-' + collectionName + '.json';
try { try {
if (!fs.existsSync(this.dbFile)) { if (!fs.existsSync(collectionFilePath)) {
fs.writeFileSync(this.dbFile, '{}'); fs.writeFileSync(collectionFilePath, '[]');
} }
} catch (err) { } catch (err) {
return new Error('Unable to Create Database File Error ' + JSON.stringify(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 { try {
const dataFromFile = fs.readFileSync(this.dbFile, 'utf-8'); const dataFromFile = fs.readFileSync(collectionFilePath, 'utf-8');
return !dataFromFile ? null : (<Collections>JSON.parse(dataFromFile)); const dataObj = !dataFromFile ? null : (<Collections>JSON.parse(dataFromFile));
return dataObj;
} catch (err) { } catch (err) {
return new Error('Database Read Error ' + JSON.stringify(err)); throw new Error(JSON.stringify(err));
} }
} }
@ -197,19 +273,30 @@ export class DatabaseAdapter {
return this.selNode; return this.selNode;
} }
saveData(data: any) { saveData(collectionName: string, collectionData: any) {
try { try {
if (data) { if (collectionData) {
const tempFile = this.dbFile + '.tmp'; const collectionFilePath = this.dbFilePath + sep + 'rtldb-' + this.selNode.ln_implementation + '-' + collectionName + '.json';
fs.writeFileSync(tempFile, JSON.stringify(data, null, 2)); const tempFile = collectionFilePath + '.tmp';
fs.renameSync(tempFile, this.dbFile); fs.writeFileSync(tempFile, JSON.stringify(collectionData, null, 2));
fs.renameSync(tempFile, collectionFilePath);
} }
return true; return true;
} catch (err) { } catch (err) {
return new Error('Database Write Error ' + JSON.stringify(err)); throw err;
} }
} }
insertSession(id: string = '') {
if (!this.userSessions.includes(id)) {
this.userSessions.push(id);
}
}
removeSession(sessionID: string = '') {
this.userSessions.splice(this.userSessions.findIndex((sId) => sId === sessionID), 1);
}
} }
export const Database = new DatabaseService(); export const Database = new DatabaseService();

@ -9,8 +9,10 @@ export class LoggerService {
switch (msgJSON.level) { switch (msgJSON.level) {
case 'ERROR': case 'ERROR':
if (msgJSON.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') ? msgStr = msgStr + ': ' + ((msgJSON.error.error && msgJSON.error.error.message && typeof msgJSON.error.error.message === 'string') ?
msgJSON.error.stack : (typeof msgJSON.error === 'object') ? JSON.stringify(msgJSON.error) : (typeof msgJSON.error === 'string') ? msgJSON.error : '') + '\r\n'; 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 { } else {
msgStr = msgStr + '.\r\n'; msgStr = msgStr + '.\r\n';
} }
@ -69,7 +71,9 @@ export class LoggerService {
const prepMsgData = (msgJSON, msgStr) => { const prepMsgData = (msgJSON, msgStr) => {
if (msgJSON.data) { 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'; 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 { } else {
msgStr = msgStr + '.\r\n'; msgStr = msgStr + '.\r\n';
} }

@ -57,10 +57,10 @@ export class RTLWebSocketServer {
public mountEventsOnConnection = (websocket, request) => { public mountEventsOnConnection = (websocket, request) => {
const protocols = !request.headers['sec-websocket-protocol'] ? [] : request.headers['sec-websocket-protocol'].split(',')?.map((s) => s.trim()); const protocols = !request.headers['sec-websocket-protocol'] ? [] : request.headers['sec-websocket-protocol'].split(',')?.map((s) => s.trim());
const cookies = parse(request.headers.cookie); const cookies = request.headers.cookie ? parse(request.headers.cookie) : null;
websocket.clientId = Date.now(); websocket.clientId = Date.now();
websocket.isAlive = true; websocket.isAlive = true;
websocket.sessionId = cookieParser.signedCookie(cookies['connect.sid'], this.common.secret_key); websocket.sessionId = cookies && cookies['connect.sid'] ? cookieParser.signedCookie(cookies['connect.sid'], this.common.secret_key) : null;
websocket.clientNodeIndex = +protocols[1]; 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 }); 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('error', this.sendErrorToAllLNClients);

@ -39,7 +39,7 @@ import { ECLReducer } from './eclair/store/ecl.reducers';
routing, routing,
LayoutModule, LayoutModule,
HammerModule, HammerModule,
UserIdleModule.forRoot({ idle: 3590, timeout: 10, ping: 12000 }), // One hour UserIdleModule.forRoot({ idle: 3590, timeout: 10, ping: 12000 }), // One hour => 3590 + 10 = 3600
StoreModule.forRoot( StoreModule.forRoot(
{ root: RootReducer, lnd: LNDReducer, cln: CLNReducer, ecl: ECLReducer }, { root: RootReducer, lnd: LNDReducer, cln: CLNReducer, ecl: ECLReducer },
{ {

@ -8,6 +8,7 @@ import { BitcoinConfigComponent } from './shared/components/settings/bitcoin-con
import { NodeConfigComponent } from './shared/components/node-config/node-config.component'; import { NodeConfigComponent } from './shared/components/node-config/node-config.component';
import { LNPConfigComponent } from './shared/components/node-config/lnp-config/lnp-config.component'; import { LNPConfigComponent } from './shared/components/node-config/lnp-config/lnp-config.component';
import { NodeSettingsComponent } from './shared/components/node-config/node-settings/node-settings.component'; import { NodeSettingsComponent } from './shared/components/node-config/node-settings/node-settings.component';
import { PageSettingsComponent } from './shared/components/node-config/page-settings/page-settings.component';
import { ServicesSettingsComponent } from './shared/components/node-config/services-settings/services-settings.component'; import { ServicesSettingsComponent } from './shared/components/node-config/services-settings/services-settings.component';
import { LoopServiceSettingsComponent } from './shared/components/node-config/services-settings/loop-service-settings/loop-service-settings.component'; import { LoopServiceSettingsComponent } from './shared/components/node-config/services-settings/loop-service-settings/loop-service-settings.component';
import { BoltzServiceSettingsComponent } from './shared/components/node-config/services-settings/boltz-service-settings/boltz-service-settings.component'; import { BoltzServiceSettingsComponent } from './shared/components/node-config/services-settings/boltz-service-settings/boltz-service-settings.component';
@ -36,8 +37,9 @@ export const routes: Routes = [
}, },
{ {
path: 'config', component: NodeConfigComponent, canActivate: [AuthGuard], children: [ path: 'config', component: NodeConfigComponent, canActivate: [AuthGuard], children: [
{ path: '', pathMatch: 'full', redirectTo: 'layout' }, { path: '', pathMatch: 'full', redirectTo: 'nodesettings' },
{ path: 'layout', component: NodeSettingsComponent, canActivate: [AuthGuard] }, { path: 'nodesettings', component: NodeSettingsComponent, canActivate: [AuthGuard] },
{ path: 'pglayout', component: PageSettingsComponent, canActivate: [AuthGuard] },
{ {
path: 'services', component: ServicesSettingsComponent, canActivate: [AuthGuard], children: [ path: 'services', component: ServicesSettingsComponent, canActivate: [AuthGuard], children: [
{ path: '', pathMatch: 'full', redirectTo: 'loop' }, { path: '', pathMatch: 'full', redirectTo: 'loop' },
@ -65,4 +67,4 @@ export const routes: Routes = [
]; ];
// Export const routing: ModuleWithProviders<RouterModule> = RouterModule.forRoot(routes, { enableTracing: true }); // Export const routing: ModuleWithProviders<RouterModule> = RouterModule.forRoot(routes, { enableTracing: true });
export const routing: ModuleWithProviders<RouterModule> = RouterModule.forRoot(routes); export const routing: ModuleWithProviders<RouterModule> = RouterModule.forRoot(routes, { scrollPositionRestoration: 'enabled' });

@ -8,7 +8,7 @@
</div> </div>
<mat-divider [inset]="true"></mat-divider> <mat-divider [inset]="true"></mat-divider>
<div fxLayout="column" fxFlex="20" class="my-1"> <div fxLayout="column" fxFlex="20" class="my-1">
<h4 class="font-bold-500">Short Channel Id</h4> <h4 class="font-bold-500">Short Channel ID</h4>
<span class="foreground-secondary-text">{{lookupResult[0]?.short_channel_id}}</span> <span class="foreground-secondary-text">{{lookupResult[0]?.short_channel_id}}</span>
</div> </div>
<mat-divider [inset]="true"></mat-divider> <mat-divider [inset]="true"></mat-divider>
@ -89,7 +89,7 @@
</div> </div>
<mat-divider [inset]="true"></mat-divider> <mat-divider [inset]="true"></mat-divider>
<div fxLayout="column" fxFlex="20" class="my-1"> <div fxLayout="column" fxFlex="20" class="my-1">
<h4 class="font-bold-500">Short Channel Id</h4> <h4 class="font-bold-500">Short Channel ID</h4>
<span class="foreground-secondary-text">{{lookupResult[1]?.short_channel_id}}</span> <span class="foreground-secondary-text">{{lookupResult[1]?.short_channel_id}}</span>
</div> </div>
<mat-divider [inset]="true"></mat-divider> <mat-divider [inset]="true"></mat-divider>

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

Loading…
Cancel
Save