diff --git a/backend/src/api/explorer/nodes.api.ts b/backend/src/api/explorer/nodes.api.ts index e6ed68115..a14f7336f 100644 --- a/backend/src/api/explorer/nodes.api.ts +++ b/backend/src/api/explorer/nodes.api.ts @@ -129,7 +129,7 @@ class NodesApi { public async $getNodesPerCountry(countryId: string) { try { const query = ` - SELECT DISTINCT node_stats.public_key, node_stats.capacity, node_stats.channels, nodes.alias, + SELECT node_stats.public_key, node_stats.capacity, node_stats.channels, nodes.alias, UNIX_TIMESTAMP(nodes.first_seen) as first_seen, UNIX_TIMESTAMP(nodes.updated_at) as updated_at, geo_names_city.names as city FROM node_stats @@ -139,8 +139,8 @@ class NodesApi { GROUP BY public_key ) as b ON b.public_key = node_stats.public_key AND b.last_added = node_stats.added JOIN nodes ON nodes.public_key = node_stats.public_key - JOIN geo_names geo_names_country ON geo_names_country.id = nodes.country_id - LEFT JOIN geo_names geo_names_city ON geo_names_city.id = nodes.city_id + JOIN geo_names geo_names_country ON geo_names_country.id = nodes.country_id AND geo_names_country.type = 'country' + LEFT JOIN geo_names geo_names_city ON geo_names_city.id = nodes.city_id AND geo_names_city.type = 'city' WHERE geo_names_country.id = ? ORDER BY capacity DESC `; @@ -186,6 +186,39 @@ class NodesApi { throw e; } } + + public async $getNodesCountries() { + try { + let query = `SELECT geo_names.names as names, geo_names_iso.names as iso_code, COUNT(DISTINCT nodes.public_key) as nodesCount, SUM(capacity) as capacity + FROM nodes + JOIN geo_names ON geo_names.id = nodes.country_id AND geo_names.type = 'country' + JOIN geo_names geo_names_iso ON geo_names_iso.id = nodes.country_id AND geo_names_iso.type = 'country_iso_code' + JOIN channels ON channels.node1_public_key = nodes.public_key OR channels.node2_public_key = nodes.public_key + GROUP BY country_id + ORDER BY COUNT(DISTINCT nodes.public_key) DESC + `; + const [nodesCountPerCountry]: any = await DB.query(query); + + query = `SELECT COUNT(*) as total FROM nodes WHERE country_id IS NOT NULL`; + const [nodesWithAS]: any = await DB.query(query); + + const nodesPerCountry: any[] = []; + for (const country of nodesCountPerCountry) { + nodesPerCountry.push({ + name: JSON.parse(country.names), + iso: country.iso_code, + count: country.nodesCount, + share: Math.floor(country.nodesCount / nodesWithAS[0].total * 10000) / 100, + capacity: country.capacity, + }) + } + + return nodesPerCountry; + } 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 9840ddbeb..bbc8efb5a 100644 --- a/backend/src/api/explorer/nodes.routes.ts +++ b/backend/src/api/explorer/nodes.routes.ts @@ -13,6 +13,7 @@ class NodesRoutes { .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/isp', this.$getNodesISP) + .get(config.MEMPOOL.API_URL_PREFIX + 'lightning/nodes/countries', this.$getNodesCountries) .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) ; @@ -128,6 +129,18 @@ class NodesRoutes { res.status(500).send(e instanceof Error ? e.message : e); } } + + private async $getNodesCountries(req: Request, res: Response) { + try { + const nodesPerAs = await nodesApi.$getNodesCountries(); + 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(); diff --git a/backend/src/tasks/price-feeds/kraken-api.ts b/backend/src/tasks/price-feeds/kraken-api.ts index ce76d62c2..ddb3c4f65 100644 --- a/backend/src/tasks/price-feeds/kraken-api.ts +++ b/backend/src/tasks/price-feeds/kraken-api.ts @@ -62,7 +62,7 @@ class KrakenApi implements PriceFeed { // CHF weekly price history goes back to timestamp 1575504000 (December 5, 2019) // AUD weekly price history goes back to timestamp 1591833600 (June 11, 2020) - const priceHistory: any = {}; // map: timestamp -> Prices + let priceHistory: any = {}; // map: timestamp -> Prices for (const currency of this.currencies) { const response = await query(this.urlHist.replace('{GRANULARITY}', '10080') + currency); @@ -83,6 +83,10 @@ class KrakenApi implements PriceFeed { } for (const time in priceHistory) { + if (priceHistory[time].USD === -1) { + delete priceHistory[time]; + continue; + } await PricesRepository.$savePrices(parseInt(time, 10), priceHistory[time]); } diff --git a/frontend/src/app/components/graphs/graphs.component.html b/frontend/src/app/components/graphs/graphs.component.html index ab3671459..0849c8acd 100644 --- a/frontend/src/app/components/graphs/graphs.component.html +++ b/frontend/src/app/components/graphs/graphs.component.html @@ -31,11 +31,13 @@