Backend controllers

clnrest-migration
ShahanaFarooqui 7 months ago
parent 2ea645152c
commit d361df0cff

@ -10,19 +10,24 @@ export const getBalance = (req, res, next) => {
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/getBalance';
options.url = req.session.selectedNode.ln_server_url + '/v1/listfunds';
request.post(options).then((body) => {
if (!body.totalBalance) {
body.totalBalance = 0;
}
if (!body.confBalance) {
body.confBalance = 0;
}
if (!body.unconfBalance) {
body.unconfBalance = 0;
}
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Balance', msg: 'Balance Received', data: body });
res.status(200).json(body);
let confBalance = 0;
let unconfBalance = 0;
let totalBalance = 0;
const versionCompatible = common.isVersionCompatible('23.02');
body.outputs.forEach((output) => {
if (output.status === 'confirmed') {
confBalance = confBalance + (versionCompatible ? (output.amount_msat / 1000) : output.value);
}
else if (output.status === 'unconfirmed') {
unconfBalance = unconfBalance + (versionCompatible ? (output.amount_msat / 1000) : output.value);
}
});
totalBalance = confBalance + unconfBalance;
const walBalance = { totalBalance: totalBalance || 0, confBalance: confBalance || 0, unconfBalance: unconfBalance || 0 };
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Balance', msg: 'Balance Received', data: walBalance });
res.status(200).json(walBalance);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Balance', 'Get Balance Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });

@ -10,59 +10,55 @@ export const listPeerChannels = (req, res, next) => {
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/channel/listPeerChannels';
options.url = req.session.selectedNode.ln_server_url + '/v1/listpeerchannels';
request.post(options).then((body) => {
body?.map((channel) => {
if (!channel.alias || channel.alias === '') {
channel.alias = channel.peer_id.substring(0, 20);
}
body.channels.map((channel) => {
const local = channel.to_us_msat || 0;
const remote = (channel.total_msat - local) || 0;
const total = channel.total_msat || 0;
channel.to_them_msat = remote;
channel.balancedness = (total === 0) ? 1 : (1 - Math.abs((local - remote) / total)).toFixed(3);
return channel;
// return getAliasForChannel(channel).then(channelAlias => {
return {
peer_id: channel.peer_id,
peer_connected: channel.peer_connected,
opener: channel.opener,
owner: channel.owner,
short_channel_id: channel.short_channel_id,
channel_id: channel.channel_id,
funding_txid: channel.funding_txid,
private: channel.private,
to_us_msat: channel.to_us_msat,
total_msat: channel.total_msat,
their_reserve_msat: channel.their_reserve_msat,
our_reserve_msat: channel.our_reserve_msat,
spendable_msat: channel.spendable_msat,
receivable_msat: channel.receivable_msat,
funding: channel.funding,
state: channel.state,
fee_base_msat: channel.fee_base_msat,
fee_proportional_millionths: channel.fee_proportional_millionths,
dust_limit_msat: channel.dust_limit_msat,
htlcs: channel.htlcs,
features: channel.features,
alias: channel.peer_id.substring(0, 20),
to_them_msat: remote,
balancedness: (total === 0) ? 1 : (1 - Math.abs((local - remote) / total)).toFixed(3)
};
});
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Peer Channels List Received', data: body });
res.status(200).json(body);
}).then((listPeerChannels) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Peer Channels List Received', data: listPeerChannels });
res.status(200).json(listPeerChannels);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Channels', 'List Peer Channels Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const listChannels = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Getting Channels..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/channel/listPeerChannels';
request.post(options).then((body) => {
body?.map((channel) => {
if (!channel.alias || channel.alias === '') {
channel.alias = channel.channel_id.substring(0, 20);
}
const local = channel.to_us_msat || 0;
const remote = (channel.total_msat - local) || 0;
const total = channel.total_msat || 0;
channel.to_them_msat = remote;
channel.balancedness = (total === 0) ? 1 : (1 - Math.abs((local - remote) / total)).toFixed(3);
return channel;
});
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Channels List Received', data: body });
res.status(200).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Channels', 'List Channels Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const openChannel = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Opening Channel..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/channel/openChannel';
options.url = req.session.selectedNode.ln_server_url + '/v1/fundchannel';
options.body = req.body;
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Open Channel Options', data: options.body });
request.post(options).then((body) => {
@ -79,7 +75,7 @@ export const setChannelFee = (req, res, next) => {
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/channel/setChannelFee';
options.url = req.session.selectedNode.ln_server_url + '/v1/setchannel';
options.body = req.body;
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Update Channel Policy Options', data: options.body });
request.post(options).then((body) => {
@ -97,10 +93,10 @@ export const closeChannel = (req, res, next) => {
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
const unilateralTimeoutQuery = req.query.force ? '?unilateralTimeout=1' : '';
options.url = req.session.selectedNode.ln_server_url + '/v1/channel/closeChannel/' + req.params.channelId + unilateralTimeoutQuery;
options.url = req.session.selectedNode.ln_server_url + '/v1/close';
options.body = { channelId: req.params.channelId, unilaterlaltimeout: req.query.force ? 1 : null };
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Closing Channel', data: options.url });
request.delete(options).then((body) => {
request.post(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Channel Closed', data: body });
res.status(204).json(body);
}).catch((errRes) => {
@ -114,16 +110,33 @@ export const getLocalRemoteBalance = (req, res, next) => {
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/channel/localremotebal';
options.url = req.session.selectedNode.ln_server_url + '/v1/listfunds';
request.post(options).then((body) => {
if (!body.localBalance) {
body.localBalance = 0;
}
if (!body.remoteBalance) {
body.remoteBalance = 0;
const versionCompatible = common.isVersionCompatible('23.02');
let localBalance = 0;
let remoteBalance = 0;
let pendingBalance = 0;
let inactiveBalance = 0;
body.channels.forEach((channel) => {
if ((channel.state === 'CHANNELD_NORMAL') && channel.connected === true) {
localBalance = localBalance + (versionCompatible ? (channel.our_amount_msat) : channel.channel_sat);
remoteBalance = remoteBalance + (versionCompatible ? (channel.amount_msat - channel.our_amount_msat) : (channel.channel_total_sat - channel.channel_sat));
}
else if ((channel.state === 'CHANNELD_NORMAL') && channel.connected === false) {
inactiveBalance = inactiveBalance + (versionCompatible ? (channel.our_amount_msat) : channel.channel_sat);
}
else if (channel.state === 'CHANNELD_AWAITING_LOCKIN' || channel.state === 'DUALOPEND_AWAITING_LOCKIN') {
pendingBalance = pendingBalance + (versionCompatible ? (channel.our_amount_msat) : channel.channel_sat);
}
});
if (versionCompatible) {
localBalance = localBalance / 1000;
remoteBalance = remoteBalance / 1000;
inactiveBalance = inactiveBalance / 1000;
pendingBalance = pendingBalance / 1000;
}
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Local Remote Balance Received', data: body });
res.status(200).json(body);
res.status(200).json({ localBalance: localBalance || 0, remoteBalance: remoteBalance || 0, inactiveBalance: inactiveBalance || 0, pendingBalance: pendingBalance || 0 });
}).catch((errRes) => {
const err = common.handleError(errRes, 'Channels', 'Local Remote Balance Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
@ -135,10 +148,11 @@ export const listForwards = (req, res, next) => {
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/channel/listForwards?status=' + (req.query.status ? req.query.status : 'settled');
options.url = req.session.selectedNode.ln_server_url + '/v1/listforwards';
options.body = { status: req.query.status || 'settled' };
request.get(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Forwarding History Received For Status ' + req.query.status, data: body });
res.status(200).json(body);
res.status(200).json(!body.forwards ? [] : (req.query.status === 'failed' || req.query.status === 'local_failed') ? body.forwards.slice(Math.max(0, body.forwards.length - 1000), Math.max(1000, body.forwards.length)).reverse() : body.forwards.reverse());
}).catch((errRes) => {
const err = common.handleError(errRes, 'Channels', 'Forwarding History Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
@ -150,10 +164,10 @@ export const funderUpdatePolicy = (req, res, next) => {
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/channel/funderUpdate';
if (req.body && req.body.policy) {
options.body = req.body;
}
options.url = req.session.selectedNode.ln_server_url + '/v1/funderupdate';
// if (req.body && req.body.policy) {
// options.body = req.body;
// }
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Funder Update Body', data: options.body });
request.post(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Funder Policy Received', data: body });
@ -165,23 +179,3 @@ export const funderUpdatePolicy = (req, res, next) => {
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const listForwardsPaginated = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Getting Paginated List Forwards..' });
options = common.getOptions(req);
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
const { status, maxLen, offset } = req.query;
let queryStr = '?status=' + (status ? status : 'settled');
queryStr = queryStr + '&maxLen=' + (maxLen ? maxLen : '10');
queryStr = queryStr + '&offset=' + (offset ? offset : '0');
options.url = req.session.selectedNode.ln_server_url + '/v1/channel/listForwardsPaginated' + queryStr;
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Paginated Forwarding History url' + options.url });
request.get(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Paginated Forwarding History Received For Status ' + req.query.status, data: body });
res.status(200).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Channels', 'Paginated Forwarding History Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};

@ -10,13 +10,12 @@ export const getFees = (req, res, next) => {
if (options.error) {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/getFees';
options.url = req.session.selectedNode.ln_server_url + '/v1/getinfo';
request.post(options).then((body) => {
if (!body.feeCollected) {
body.feeCollected = 0;
}
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Fees', msg: 'Fee Received', data: body });
res.status(200).json(body);
const versionCompatible = common.isVersionCompatible('23.02');
const feeData = { feeCollected: ((versionCompatible ? body.fees_collected_msat : body.msatoshi_fees_collected) || 0) };
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Fees', msg: 'Fee Received', data: feeData });
res.status(200).json(feeData);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Fees', 'Get Fees Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });

@ -48,8 +48,9 @@ export const getInfo = (req, res, next) => {
body.uris.push(body.id + '@' + addr.address + ':' + addr.port);
});
}
req.session.selectedNode.api_version = body.api_version || '';
common.setVersion(body.version || '');
req.session.selectedNode.ln_version = body.version || '';
req.session.selectedNode.api_version = body.api_version || '';
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Connecting to the Core Lightning\'s Websocket Server.' });
clWsClient.updateSelectedNode(req.session.selectedNode);
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Node Information Received', data: body });

@ -12,7 +12,7 @@ export const deleteExpiredInvoice = (req, res, next) => {
}
const queryStr = req.query.maxExpiry ? '?maxexpiry=' + req.query.maxExpiry : '';
options.url = req.session.selectedNode.ln_server_url + '/v1/invoice/delExpiredInvoice' + queryStr;
request.delete(options).then((body) => {
request.post(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoice', msg: 'Invoices Deleted', data: body });
res.status(204).json({ status: 'Invoice Deleted Successfully' });
}).catch((errRes) => {

@ -89,7 +89,7 @@ export const disableOffer = (req, res, next) => {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/offers/disableOffer/' + req.params.offerID;
request.delete(options).then((body) => {
request.post(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Offer Disabled', data: body });
res.status(202).json(body);
}).catch((errRes) => {

@ -55,7 +55,7 @@ export const deletePeer = (req, res, next) => {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/peer/disconnect/' + req.params.peerId + '?force=' + req.query.force;
request.delete(options).then((body) => {
request.post(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Peer Disconnected', data: body });
res.status(204).json({});
}).catch((errRes) => {

@ -72,7 +72,7 @@ export const verifyMessage = (req, res, next) => {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/utility/checkMessage/' + req.body.message + '/' + req.body.signature;
request.get(options, (error, response, body) => {
request.post(options, (error, response, body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Message', msg: 'Message Verified', data: body });
res.status(201).json(body);
}).catch((errRes) => {

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

@ -20,6 +20,7 @@ export class CommonService {
this.rtl_cookie_path = '';
this.logout_redirect_link = '';
this.cookie_value = '';
this.ln_version = '';
this.api_version = '';
this.secret_key = crypto.randomBytes(64).toString('hex');
this.read_dummy_data = false;
@ -423,13 +424,29 @@ export class CommonService {
fs.writeFile(channel_backup_file, '', () => { });
});
};
this.isVersionCompatible = (currentVersion, checkVersion) => {
if (currentVersion) {
const versionsArr = currentVersion.trim()?.replace('v', '').split('-')[0].split('.') || [];
const checkVersionsArr = checkVersion.split('.');
return (+versionsArr[0] > +checkVersionsArr[0]) ||
(+versionsArr[0] === +checkVersionsArr[0] && +versionsArr[1] > +checkVersionsArr[1]) ||
(+versionsArr[0] === +checkVersionsArr[0] && +versionsArr[1] === +checkVersionsArr[1] && +versionsArr[2] >= +checkVersionsArr[2]);
this.setVersion = (version) => {
this.ln_version = version;
};
this.isVersionCompatible = (checkVersion) => {
if (this.ln_version && this.ln_version !== '') {
// eslint-disable-next-line prefer-named-capture-group
const pattern = /v?(\d+(\.\d+)*)/;
const match = this.ln_version.match(pattern);
if (match && match.length && match.length > 1) {
this.logger.log({ selectedNode: this.initSelectedNode, level: 'INFO', fileName: 'Common', msg: 'Global Version ' + match[1] });
this.logger.log({ selectedNode: this.initSelectedNode, level: 'INFO', fileName: 'Common', msg: 'Checking Compatiblility with Version ' + checkVersion });
const currentVersionArr = match[1].split('.') || [];
currentVersionArr[1] = currentVersionArr[1].substring(0, 2);
const checkVersionsArr = checkVersion.split('.');
checkVersionsArr[1] = checkVersionsArr[1].substring(0, 2);
return (+currentVersionArr[0] > +checkVersionsArr[0]) ||
(+currentVersionArr[0] === +checkVersionsArr[0] && +currentVersionArr[1] > +checkVersionsArr[1]) ||
(+currentVersionArr[0] === +checkVersionsArr[0] && +currentVersionArr[1] === +checkVersionsArr[1] && +currentVersionArr[2] >= +checkVersionsArr[2]);
}
else {
this.logger.log({ selectedNode: this.initSelectedNode, level: 'ERROR', fileName: 'Common', msg: 'Invalid Version String ' + this.ln_version });
return false;
}
}
return false;
};

@ -9,13 +9,23 @@ export const getBalance = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Balance', msg: 'Getting Balance..' });
options = common.getOptions(req);
if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); }
options.url = req.session.selectedNode.ln_server_url + '/v1/getBalance';
options.url = req.session.selectedNode.ln_server_url + '/v1/listfunds';
request.post(options).then((body) => {
if (!body.totalBalance) { body.totalBalance = 0; }
if (!body.confBalance) { body.confBalance = 0; }
if (!body.unconfBalance) { body.unconfBalance = 0; }
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Balance', msg: 'Balance Received', data: body });
res.status(200).json(body);
let confBalance = 0;
let unconfBalance = 0;
let totalBalance = 0;
const versionCompatible = common.isVersionCompatible('23.02');
body.outputs.forEach((output) => {
if (output.status === 'confirmed') {
confBalance = confBalance + (versionCompatible ? (output.amount_msat / 1000) : output.value);
} else if (output.status === 'unconfirmed') {
unconfBalance = unconfBalance + (versionCompatible ? (output.amount_msat / 1000) : output.value);
}
});
totalBalance = confBalance + unconfBalance;
const walBalance = { totalBalance: totalBalance || 0, confBalance: confBalance || 0, unconfBalance: unconfBalance || 0 };
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Balance', msg: 'Balance Received', data: walBalance });
res.status(200).json(walBalance);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Balance', 'Get Balance Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });

@ -9,53 +9,54 @@ export const listPeerChannels = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Getting Peer Channels..' });
options = common.getOptions(req);
if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); }
options.url = req.session.selectedNode.ln_server_url + '/v1/channel/listPeerChannels';
options.url = req.session.selectedNode.ln_server_url + '/v1/listpeerchannels';
request.post(options).then((body) => {
body?.map((channel) => {
if (!channel.alias || channel.alias === '') { channel.alias = channel.peer_id.substring(0, 20); }
body.channels.map((channel) => {
const local = channel.to_us_msat || 0;
const remote = (channel.total_msat - local) || 0;
const total = channel.total_msat || 0;
channel.to_them_msat = remote;
channel.balancedness = (total === 0) ? 1 : (1 - Math.abs((local - remote) / total)).toFixed(3);
return channel;
// return getAliasForChannel(channel).then(channelAlias => {
return {
peer_id: channel.peer_id,
peer_connected: channel.peer_connected,
opener: channel.opener,
owner: channel.owner,
short_channel_id: channel.short_channel_id,
channel_id: channel.channel_id,
funding_txid: channel.funding_txid,
private: channel.private,
to_us_msat: channel.to_us_msat,
total_msat: channel.total_msat,
their_reserve_msat: channel.their_reserve_msat,
our_reserve_msat: channel.our_reserve_msat,
spendable_msat: channel.spendable_msat,
receivable_msat: channel.receivable_msat,
funding: channel.funding,
state: channel.state,
fee_base_msat: channel.fee_base_msat,
fee_proportional_millionths: channel.fee_proportional_millionths,
dust_limit_msat: channel.dust_limit_msat,
htlcs: channel.htlcs,
features: channel.features,
alias: channel.peer_id.substring(0, 20),
to_them_msat: remote,
balancedness: (total === 0) ? 1 : (1 - Math.abs((local - remote) / total)).toFixed(3)
};
});
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Peer Channels List Received', data: body });
res.status(200).json(body);
}).then((listPeerChannels) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Peer Channels List Received', data: listPeerChannels });
res.status(200).json(listPeerChannels);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Channels', 'List Peer Channels Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const listChannels = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Getting Channels..' });
options = common.getOptions(req);
if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); }
options.url = req.session.selectedNode.ln_server_url + '/v1/channel/listPeerChannels';
request.post(options).then((body) => {
body?.map((channel) => {
if (!channel.alias || channel.alias === '') { channel.alias = channel.channel_id.substring(0, 20); }
const local = channel.to_us_msat || 0;
const remote = (channel.total_msat - local) || 0;
const total = channel.total_msat || 0;
channel.to_them_msat = remote;
channel.balancedness = (total === 0) ? 1 : (1 - Math.abs((local - remote) / total)).toFixed(3);
return channel;
});
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Channels List Received', data: body });
res.status(200).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Channels', 'List Channels Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const openChannel = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Opening Channel..' });
options = common.getOptions(req);
if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); }
options.url = req.session.selectedNode.ln_server_url + '/v1/channel/openChannel';
options.url = req.session.selectedNode.ln_server_url + '/v1/fundchannel';
options.body = req.body;
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Open Channel Options', data: options.body });
request.post(options).then((body) => {
@ -71,7 +72,7 @@ export const setChannelFee = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Setting Channel Fee..' });
options = common.getOptions(req);
if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); }
options.url = req.session.selectedNode.ln_server_url + '/v1/channel/setChannelFee';
options.url = req.session.selectedNode.ln_server_url + '/v1/setchannel';
options.body = req.body;
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Update Channel Policy Options', data: options.body });
request.post(options).then((body) => {
@ -88,10 +89,10 @@ export const closeChannel = (req, res, next) => {
req.setTimeout(60000 * 10); // timeout 10 mins
options = common.getOptions(req);
if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); }
const unilateralTimeoutQuery = req.query.force ? '?unilateralTimeout=1' : '';
options.url = req.session.selectedNode.ln_server_url + '/v1/channel/closeChannel/' + req.params.channelId + unilateralTimeoutQuery;
options.url = req.session.selectedNode.ln_server_url + '/v1/close';
options.body = { channelId: req.params.channelId, unilaterlaltimeout: req.query.force ? 1 : null };
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Closing Channel', data: options.url });
request.delete(options).then((body) => {
request.post(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Channel Closed', data: body });
res.status(204).json(body);
}).catch((errRes) => {
@ -104,12 +105,31 @@ export const getLocalRemoteBalance = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Getting Local & Remote Balances..' });
options = common.getOptions(req);
if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); }
options.url = req.session.selectedNode.ln_server_url + '/v1/channel/localremotebal';
options.url = req.session.selectedNode.ln_server_url + '/v1/listfunds';
request.post(options).then((body) => {
if (!body.localBalance) { body.localBalance = 0; }
if (!body.remoteBalance) { body.remoteBalance = 0; }
const versionCompatible = common.isVersionCompatible('23.02');
let localBalance = 0;
let remoteBalance = 0;
let pendingBalance = 0;
let inactiveBalance = 0;
body.channels.forEach((channel) => {
if ((channel.state === 'CHANNELD_NORMAL') && channel.connected === true) {
localBalance = localBalance + (versionCompatible ? (channel.our_amount_msat) : channel.channel_sat);
remoteBalance = remoteBalance + (versionCompatible ? (channel.amount_msat - channel.our_amount_msat) : (channel.channel_total_sat - channel.channel_sat));
} else if ((channel.state === 'CHANNELD_NORMAL') && channel.connected === false) {
inactiveBalance = inactiveBalance + (versionCompatible ? (channel.our_amount_msat) : channel.channel_sat);
} else if (channel.state === 'CHANNELD_AWAITING_LOCKIN' || channel.state === 'DUALOPEND_AWAITING_LOCKIN') {
pendingBalance = pendingBalance + (versionCompatible ? (channel.our_amount_msat) : channel.channel_sat);
}
});
if (versionCompatible) {
localBalance = localBalance / 1000;
remoteBalance = remoteBalance / 1000;
inactiveBalance = inactiveBalance / 1000;
pendingBalance = pendingBalance / 1000;
}
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Local Remote Balance Received', data: body });
res.status(200).json(body);
res.status(200).json({ localBalance: localBalance || 0, remoteBalance: remoteBalance || 0, inactiveBalance: inactiveBalance || 0, pendingBalance: pendingBalance || 0 });
}).catch((errRes) => {
const err = common.handleError(errRes, 'Channels', 'Local Remote Balance Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
@ -120,10 +140,11 @@ export const listForwards = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Getting Channel List Forwards..' });
options = common.getOptions(req);
if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); }
options.url = req.session.selectedNode.ln_server_url + '/v1/channel/listForwards?status=' + (req.query.status ? req.query.status : 'settled');
options.url = req.session.selectedNode.ln_server_url + '/v1/listforwards';
options.body = { status: req.query.status || 'settled' };
request.get(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Forwarding History Received For Status ' + req.query.status, data: body });
res.status(200).json(body);
res.status(200).json(!body.forwards ? [] : (req.query.status === 'failed' || req.query.status === 'local_failed') ? body.forwards.slice(Math.max(0, body.forwards.length - 1000), Math.max(1000, body.forwards.length)).reverse() : body.forwards.reverse());
}).catch((errRes) => {
const err = common.handleError(errRes, 'Channels', 'Forwarding History Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
@ -134,10 +155,10 @@ export const funderUpdatePolicy = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Getting or Updating Funder Policy..' });
options = common.getOptions(req);
if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); }
options.url = req.session.selectedNode.ln_server_url + '/v1/channel/funderUpdate';
if (req.body && req.body.policy) {
options.body = req.body;
}
options.url = req.session.selectedNode.ln_server_url + '/v1/funderupdate';
// if (req.body && req.body.policy) {
// options.body = req.body;
// }
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Funder Update Body', data: options.body });
request.post(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Funder Policy Received', data: body });
@ -149,22 +170,3 @@ export const funderUpdatePolicy = (req, res, next) => {
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const listForwardsPaginated = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Getting Paginated List Forwards..' });
options = common.getOptions(req);
if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); }
const { status, maxLen, offset } = req.query;
let queryStr = '?status=' + (status ? status : 'settled');
queryStr = queryStr + '&maxLen=' + (maxLen ? maxLen : '10');
queryStr = queryStr + '&offset=' + (offset ? offset : '0');
options.url = req.session.selectedNode.ln_server_url + '/v1/channel/listForwardsPaginated' + queryStr;
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Paginated Forwarding History url' + options.url });
request.get(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Paginated Forwarding History Received For Status ' + req.query.status, data: body });
res.status(200).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Channels', 'Paginated Forwarding History Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};

@ -9,11 +9,12 @@ export const getFees = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Fees', msg: 'Getting Fees..' });
options = common.getOptions(req);
if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); }
options.url = req.session.selectedNode.ln_server_url + '/v1/getFees';
options.url = req.session.selectedNode.ln_server_url + '/v1/getinfo';
request.post(options).then((body) => {
if (!body.feeCollected) { body.feeCollected = 0; }
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Fees', msg: 'Fee Received', data: body });
res.status(200).json(body);
const versionCompatible = common.isVersionCompatible('23.02');
const feeData = { feeCollected: ((versionCompatible ? body.fees_collected_msat : body.msatoshi_fees_collected) || 0) };
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Fees', msg: 'Fee Received', data: feeData });
res.status(200).json(feeData);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Fees', 'Get Fees Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });

@ -45,8 +45,9 @@ export const getInfo = (req, res, next) => {
body.uris.push(body.id + '@' + addr.address + ':' + addr.port);
});
}
req.session.selectedNode.api_version = body.api_version || '';
common.setVersion(body.version || '');
req.session.selectedNode.ln_version = body.version || '';
req.session.selectedNode.api_version = body.api_version || '';
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Connecting to the Core Lightning\'s Websocket Server.' });
clWsClient.updateSelectedNode(req.session.selectedNode);
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Node Information Received', data: body });

@ -11,7 +11,7 @@ export const deleteExpiredInvoice = (req, res, next) => {
if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); }
const queryStr = req.query.maxExpiry ? '?maxexpiry=' + req.query.maxExpiry : '';
options.url = req.session.selectedNode.ln_server_url + '/v1/invoice/delExpiredInvoice' + queryStr;
request.delete(options).then((body) => {
request.post(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoice', msg: 'Invoices Deleted', data: body });
res.status(204).json({ status: 'Invoice Deleted Successfully' });
}).catch((errRes) => {

@ -88,7 +88,7 @@ export const disableOffer = (req, res, next) => {
options = common.getOptions(req);
if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); }
options.url = req.session.selectedNode.ln_server_url + '/v1/offers/disableOffer/' + req.params.offerID;
request.delete(options).then((body) => {
request.post(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Offer Disabled', data: body });
res.status(202).json(body);
}).catch((errRes) => {

@ -52,7 +52,7 @@ export const deletePeer = (req, res, next) => {
options = common.getOptions(req);
if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); }
options.url = req.session.selectedNode.ln_server_url + '/v1/peer/disconnect/' + req.params.peerId + '?force=' + req.query.force;
request.delete(options).then((body) => {
request.post(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Peer Disconnected', data: body });
res.status(204).json({});
}).catch((errRes) => {

@ -70,7 +70,7 @@ export const verifyMessage = (req, res, next) => {
options = common.getOptions(req);
if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); }
options.url = req.session.selectedNode.ln_server_url + '/v1/utility/checkMessage/' + req.body.message + '/' + req.body.signature;
request.get(options, (error, response, body) => {
request.post(options, (error, response, body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Message', msg: 'Message Verified', data: body });
res.status(201).json(body);
}).catch((errRes) => {

@ -1,11 +1,10 @@
import exprs from 'express';
const { Router } = exprs;
import { isAuthenticated } from '../../utils/authCheck.js';
import { listChannels, listPeerChannels, openChannel, setChannelFee, closeChannel, getLocalRemoteBalance, listForwards, funderUpdatePolicy, listForwardsPaginated } from '../../controllers/cln/channels.js';
import { listPeerChannels, openChannel, setChannelFee, closeChannel, getLocalRemoteBalance, listForwards, funderUpdatePolicy } from '../../controllers/cln/channels.js';
const router = Router();
router.get('/listChannels', isAuthenticated, listChannels);
router.get('/listPeerChannels', isAuthenticated, listPeerChannels);
router.post('/', isAuthenticated, openChannel);
router.post('/setChannelFee', isAuthenticated, setChannelFee);
@ -13,7 +12,6 @@ router.delete('/:channelId', isAuthenticated, closeChannel);
router.get('/localRemoteBalance', isAuthenticated, getLocalRemoteBalance);
router.get('/listForwards', isAuthenticated, listForwards);
router.get('/listForwardsPaginated', isAuthenticated, listForwardsPaginated);
router.post('/funderUpdate', isAuthenticated, funderUpdatePolicy);

@ -22,6 +22,7 @@ export class CommonService {
public rtl_cookie_path = '';
public logout_redirect_link = '';
public cookie_value = '';
public ln_version = '';
public api_version = '';
public secret_key = crypto.randomBytes(64).toString('hex');
public read_dummy_data = false;
@ -432,13 +433,29 @@ export class CommonService {
});
};
public isVersionCompatible = (currentVersion, checkVersion) => {
if (currentVersion) {
const versionsArr = currentVersion.trim()?.replace('v', '').split('-')[0].split('.') || [];
const checkVersionsArr = checkVersion.split('.');
return (+versionsArr[0] > +checkVersionsArr[0]) ||
(+versionsArr[0] === +checkVersionsArr[0] && +versionsArr[1] > +checkVersionsArr[1]) ||
(+versionsArr[0] === +checkVersionsArr[0] && +versionsArr[1] === +checkVersionsArr[1] && +versionsArr[2] >= +checkVersionsArr[2]);
public setVersion = (version) => {
this.ln_version = version;
};
public isVersionCompatible = (checkVersion) => {
if (this.ln_version && this.ln_version !== '') {
// eslint-disable-next-line prefer-named-capture-group
const pattern = /v?(\d+(\.\d+)*)/;
const match = this.ln_version.match(pattern);
if (match && match.length && match.length > 1) {
this.logger.log({ selectedNode: this.initSelectedNode, level: 'INFO', fileName: 'Common', msg: 'Global Version ' + match[1] });
this.logger.log({ selectedNode: this.initSelectedNode, level: 'INFO', fileName: 'Common', msg: 'Checking Compatiblility with Version ' + checkVersion });
const currentVersionArr = match[1].split('.') || [];
currentVersionArr[1] = currentVersionArr[1].substring(0, 2);
const checkVersionsArr = checkVersion.split('.');
checkVersionsArr[1] = checkVersionsArr[1].substring(0, 2);
return (+currentVersionArr[0] > +checkVersionsArr[0]) ||
(+currentVersionArr[0] === +checkVersionsArr[0] && +currentVersionArr[1] > +checkVersionsArr[1]) ||
(+currentVersionArr[0] === +checkVersionsArr[0] && +currentVersionArr[1] === +checkVersionsArr[1] && +currentVersionArr[2] >= +checkVersionsArr[2]);
} else {
this.logger.log({ selectedNode: this.initSelectedNode, level: 'ERROR', fileName: 'Common', msg: 'Invalid Version String ' + this.ln_version });
return false;
}
}
return false;
};

Loading…
Cancel
Save