diff --git a/backend/.gitignore b/backend/.gitignore index 5476d633c..67fb162c6 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -1,9 +1,9 @@ # See http://help.github.com/ignore-files/ for more about ignoring files. # production config and external assets -*.json -!mempool-config.sample.json +mempool-config.json +pools.json icons.json # compiled output diff --git a/backend/src/api/blocks.ts b/backend/src/api/blocks.ts index 45ffd6079..d702b4927 100644 --- a/backend/src/api/blocks.ts +++ b/backend/src/api/blocks.ts @@ -593,7 +593,7 @@ class Blocks { // Index the response if needed if (Common.blocksSummariesIndexingEnabled() === true) { - await BlocksSummariesRepository.$saveSummary(block.height, summary, null); + await BlocksSummariesRepository.$saveSummary({height: block.height, mined: summary}); } return summary.transactions; diff --git a/backend/src/api/explorer/channels.api.ts b/backend/src/api/explorer/channels.api.ts index a1cd6a41e..f0d7dc56b 100644 --- a/backend/src/api/explorer/channels.api.ts +++ b/backend/src/api/explorer/channels.api.ts @@ -13,6 +13,30 @@ class ChannelsApi { } } + public async $getAllChannelsGeo(): Promise { + try { + const query = `SELECT nodes_1.public_key as node1_public_key, nodes_1.alias AS node1_alias, + nodes_1.latitude AS node1_latitude, nodes_1.longitude AS node1_longitude, + nodes_2.public_key as node2_public_key, nodes_2.alias AS node2_alias, + nodes_2.latitude AS node2_latitude, nodes_2.longitude AS node2_longitude, + channels.capacity + FROM channels + JOIN nodes AS nodes_1 on nodes_1.public_key = channels.node1_public_key + JOIN nodes AS nodes_2 on nodes_2.public_key = channels.node2_public_key + WHERE nodes_1.latitude IS NOT NULL AND nodes_1.longitude IS NOT NULL + AND nodes_2.latitude IS NOT NULL AND nodes_2.longitude IS NOT NULL + `; + const [rows]: any = await DB.query(query); + return rows.map((row) => [ + row.node1_public_key, row.node1_alias, row.node1_longitude, row.node1_latitude, + row.node2_public_key, row.node2_alias, row.node2_longitude, row.node2_latitude, + row.capacity]); + } catch (e) { + logger.err('$getAllChannelsGeo error: ' + (e instanceof Error ? e.message : e)); + throw e; + } + } + public async $searchChannelsById(search: string): Promise { try { const searchStripped = search.replace('%', '') + '%'; diff --git a/backend/src/api/explorer/channels.routes.ts b/backend/src/api/explorer/channels.routes.ts index 5ad1d8743..c6df30802 100644 --- a/backend/src/api/explorer/channels.routes.ts +++ b/backend/src/api/explorer/channels.routes.ts @@ -11,6 +11,7 @@ class ChannelsRoutes { .get(config.MEMPOOL.API_URL_PREFIX + 'lightning/channels/search/:search', this.$searchChannelsById) .get(config.MEMPOOL.API_URL_PREFIX + 'lightning/channels/:short_id', this.$getChannel) .get(config.MEMPOOL.API_URL_PREFIX + 'lightning/channels', this.$getChannelsForNode) + .get(config.MEMPOOL.API_URL_PREFIX + 'lightning/channels-geo', this.$getChannelsGeo) ; } @@ -93,6 +94,15 @@ class ChannelsRoutes { } } + private async $getChannelsGeo(req: Request, res: Response) { + try { + const channels = await channelsApi.$getAllChannelsGeo(); + res.json(channels); + } catch (e) { + res.status(500).send(e instanceof Error ? e.message : e); + } + } + } export default new ChannelsRoutes(); diff --git a/backend/src/api/websocket-handler.ts b/backend/src/api/websocket-handler.ts index dee44ba63..4896ee058 100644 --- a/backend/src/api/websocket-handler.ts +++ b/backend/src/api/websocket-handler.ts @@ -451,9 +451,12 @@ class WebsocketHandler { value: tx.value, }; }); - BlocksSummariesRepository.$saveSummary(block.height, null, { - id: block.id, - transactions: stripped + BlocksSummariesRepository.$saveSummary({ + height: block.height, + template: { + id: block.id, + transactions: stripped + } }); BlocksAuditsRepository.$saveAudit({ diff --git a/backend/src/repositories/BlocksSummariesRepository.ts b/backend/src/repositories/BlocksSummariesRepository.ts index 2f47f7a35..28b3cc7eb 100644 --- a/backend/src/repositories/BlocksSummariesRepository.ts +++ b/backend/src/repositories/BlocksSummariesRepository.ts @@ -17,18 +17,18 @@ class BlocksSummariesRepository { return undefined; } - public async $saveSummary(height: number, mined: BlockSummary | null = null, template: BlockSummary | null = null) { - const blockId = mined?.id ?? template?.id; + public async $saveSummary(params: { height: number, mined?: BlockSummary, template?: BlockSummary}) { + const blockId = params.mined?.id ?? params.template?.id; try { const [dbSummary]: any[] = await DB.query(`SELECT * FROM blocks_summaries WHERE id = "${blockId}"`); if (dbSummary.length === 0) { // First insertion await DB.query(`INSERT INTO blocks_summaries VALUE (?, ?, ?, ?)`, [ - height, blockId, JSON.stringify(mined?.transactions ?? []), JSON.stringify(template?.transactions ?? []) + params.height, blockId, JSON.stringify(params.mined?.transactions ?? []), JSON.stringify(params.template?.transactions ?? []) ]); - } else if (mined !== null) { // Update mined block summary - await DB.query(`UPDATE blocks_summaries SET transactions = ? WHERE id = "${mined.id}"`, [JSON.stringify(mined?.transactions)]); - } else if (template !== null) { // Update template block summary - await DB.query(`UPDATE blocks_summaries SET template = ? WHERE id = "${template.id}"`, [JSON.stringify(template?.transactions)]); + } else if (params.mined !== undefined) { // Update mined block summary + await DB.query(`UPDATE blocks_summaries SET transactions = ? WHERE id = "${params.mined.id}"`, [JSON.stringify(params.mined.transactions)]); + } else if (params.template !== undefined) { // Update template block summary + await DB.query(`UPDATE blocks_summaries SET template = ? WHERE id = "${params.template.id}"`, [JSON.stringify(params.template?.transactions)]); } } catch (e: any) { if (e.errno === 1062) { // ER_DUP_ENTRY - This scenario is possible upon node backend restart diff --git a/frontend/package-lock.json b/frontend/package-lock.json index c72038f38..04682aac5 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -34,6 +34,7 @@ "clipboard": "^2.0.10", "domino": "^2.1.6", "echarts": "~5.3.2", + "echarts-gl": "^2.0.9", "express": "^4.17.1", "lightweight-charts": "~3.8.0", "ngx-echarts": "8.0.1", @@ -6396,6 +6397,11 @@ "webpack": ">=4.0.1" } }, + "node_modules/claygl": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/claygl/-/claygl-1.3.0.tgz", + "integrity": "sha512-+gGtJjT6SSHD2l2yC3MCubW/sCV40tZuSs5opdtn79vFSGUgp/lH139RNEQ6Jy078/L0aV8odCw8RSrUcMfLaQ==" + }, "node_modules/clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", @@ -8107,6 +8113,18 @@ "zrender": "5.3.1" } }, + "node_modules/echarts-gl": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/echarts-gl/-/echarts-gl-2.0.9.tgz", + "integrity": "sha512-oKeMdkkkpJGWOzjgZUsF41DOh6cMsyrGGXimbjK2l6Xeq/dBQu4ShG2w2Dzrs/1bD27b2pLTGSaUzouY191gzA==", + "dependencies": { + "claygl": "^1.2.1", + "zrender": "^5.1.1" + }, + "peerDependencies": { + "echarts": "^5.1.2" + } + }, "node_modules/echarts/node_modules/tslib": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", @@ -22520,6 +22538,11 @@ "integrity": "sha512-g38K9Cm5WRwlaH6g03B9OEz/0qRizI+2I7n+Gz+L5DxXJAPAiWQvwlYNm1V1jkdpUv95bOe/ASm2vfi/G560jQ==", "requires": {} }, + "claygl": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/claygl/-/claygl-1.3.0.tgz", + "integrity": "sha512-+gGtJjT6SSHD2l2yC3MCubW/sCV40tZuSs5opdtn79vFSGUgp/lH139RNEQ6Jy078/L0aV8odCw8RSrUcMfLaQ==" + }, "clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", @@ -23866,6 +23889,15 @@ } } }, + "echarts-gl": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/echarts-gl/-/echarts-gl-2.0.9.tgz", + "integrity": "sha512-oKeMdkkkpJGWOzjgZUsF41DOh6cMsyrGGXimbjK2l6Xeq/dBQu4ShG2w2Dzrs/1bD27b2pLTGSaUzouY191gzA==", + "requires": { + "claygl": "^1.2.1", + "zrender": "^5.1.1" + } + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index f2d54135e..d2f7f2f6c 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -88,6 +88,7 @@ "clipboard": "^2.0.10", "domino": "^2.1.6", "echarts": "~5.3.2", + "echarts-gl": "^2.0.9", "express": "^4.17.1", "lightweight-charts": "~3.8.0", "ngx-echarts": "8.0.1", diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts index 8f95920f3..97c8f9957 100644 --- a/frontend/src/app/app.module.ts +++ b/frontend/src/app/app.module.ts @@ -6,6 +6,7 @@ import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './components/app/app.component'; import { ElectrsApiService } from './services/electrs-api.service'; import { StateService } from './services/state.service'; +import { EnterpriseService } from './services/enterprise.service'; import { WebsocketService } from './services/websocket.service'; import { AudioService } from './services/audio.service'; import { SeoService } from './services/seo.service'; @@ -36,6 +37,7 @@ import { CapAddressPipe } from './shared/pipes/cap-address-pipe/cap-address-pipe AudioService, SeoService, StorageService, + EnterpriseService, LanguageService, ShortenStringPipe, FiatShortenerPipe, diff --git a/frontend/src/app/components/block-overview-graph/tx-view.ts b/frontend/src/app/components/block-overview-graph/tx-view.ts index edcbf2f58..c0b980d5c 100644 --- a/frontend/src/app/components/block-overview-graph/tx-view.ts +++ b/frontend/src/app/components/block-overview-graph/tx-view.ts @@ -143,9 +143,7 @@ export default class TxView implements TransactionStripped { getColor(): Color { // Block audit - if (this.status === 'found') { - // return hexToColor('1a4987'); - } else if (this.status === 'missing') { + if (this.status === 'missing') { return hexToColor('039BE5'); } else if (this.status === 'added') { return hexToColor('D81B60'); diff --git a/frontend/src/app/components/block-prediction-graph/block-prediction-graph.component.ts b/frontend/src/app/components/block-prediction-graph/block-prediction-graph.component.ts index c708c7574..e88dc07be 100644 --- a/frontend/src/app/components/block-prediction-graph/block-prediction-graph.component.ts +++ b/frontend/src/app/components/block-prediction-graph/block-prediction-graph.component.ts @@ -156,7 +156,7 @@ export class BlockPredictionGraphComponent implements OnInit { type: 'category', axisLine: { onZero: true }, axisLabel: { - formatter: val => formatterXAxisTimeCategory(this.locale, this.timespan, parseInt(val, 10)), + formatter: val => formatterXAxisTimeCategory(this.locale, this.timespan, parseInt(val, 10) * 1000), align: 'center', fontSize: 11, lineHeight: 12, diff --git a/frontend/src/app/components/graphs/graphs.component.html b/frontend/src/app/components/graphs/graphs.component.html index 905b2d296..938d3e817 100644 --- a/frontend/src/app/components/graphs/graphs.component.html +++ b/frontend/src/app/components/graphs/graphs.component.html @@ -39,7 +39,9 @@ Lightning nodes per country Lightning nodes world map + i18n="lightning.lightning.nodes-heatmap">Lightning nodes world map + Lightning nodes channels world map diff --git a/frontend/src/app/components/master-page/master-page.component.html b/frontend/src/app/components/master-page/master-page.component.html index a4979e00d..7b742a5ad 100644 --- a/frontend/src/app/components/master-page/master-page.component.html +++ b/frontend/src/app/components/master-page/master-page.component.html @@ -1,6 +1,11 @@