diff --git a/backend/src/api/explorer/nodes.api.ts b/backend/src/api/explorer/nodes.api.ts index 590ed1f20..f76975d0a 100644 --- a/backend/src/api/explorer/nodes.api.ts +++ b/backend/src/api/explorer/nodes.api.ts @@ -93,6 +93,35 @@ class NodesApi { throw e; } } + + public async $getNodesAsShare() { + try { + let query = `SELECT names, COUNT(*) as nodesCount from nodes + JOIN geo_names ON geo_names.id = nodes.as_number + GROUP BY as_number + ORDER BY COUNT(*) DESC + LIMIT 20 + `; + const [nodesCountPerAS]: any = await DB.query(query); + + query = `SELECT COUNT(*) as total FROM nodes WHERE as_number IS NOT NULL`; + const [nodesWithAS]: any = await DB.query(query); + + const nodesPerAs: any[] = []; + for (const as of nodesCountPerAS) { + nodesPerAs.push({ + name: JSON.parse(as.names), + count: as.nodesCount, + share: Math.floor(as.nodesCount / nodesWithAS[0].total * 10000) / 100, + }) + } + + return nodesPerAs; + } catch (e) { + logger.err(`Cannot get nodes grouped by AS. Reason: ${e instanceof Error ? e.message : e}`); + throw e; + } + } } export default new NodesApi(); diff --git a/backend/src/api/explorer/nodes.routes.ts b/backend/src/api/explorer/nodes.routes.ts index 6c79c8201..d2960155b 100644 --- a/backend/src/api/explorer/nodes.routes.ts +++ b/backend/src/api/explorer/nodes.routes.ts @@ -8,6 +8,7 @@ class NodesRoutes { app .get(config.MEMPOOL.API_URL_PREFIX + 'lightning/nodes/search/:search', this.$searchNode) .get(config.MEMPOOL.API_URL_PREFIX + 'lightning/nodes/top', this.$getTopNodes) + .get(config.MEMPOOL.API_URL_PREFIX + 'lightning/nodes/asShare', this.$getNodesAsShare) .get(config.MEMPOOL.API_URL_PREFIX + 'lightning/nodes/:public_key/statistics', this.$getHistoricalNodeStats) .get(config.MEMPOOL.API_URL_PREFIX + 'lightning/nodes/:public_key', this.$getNode) ; @@ -56,6 +57,18 @@ class NodesRoutes { res.status(500).send(e instanceof Error ? e.message : e); } } + + private async $getNodesAsShare(req: Request, res: Response) { + try { + const nodesPerAs = await nodesApi.$getNodesAsShare(); + res.header('Pragma', 'public'); + res.header('Cache-control', 'public'); + res.setHeader('Expires', new Date(Date.now() + 1000 * 600).toUTCString()); + res.json(nodesPerAs); + } catch (e) { + res.status(500).send(e instanceof Error ? e.message : e); + } + } } export default new NodesRoutes();