From a04df3f4ee078cfbfbfb5333f19bbb4d612f9f03 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Fri, 1 Aug 2025 13:14:16 +0000 Subject: [PATCH] add hybrid build hash to monitoring --- backend/src/api/bitcoin/esplora-api.ts | 46 ++++++++++++++++++- .../server-health.component.html | 5 +- .../server-health/server-health.component.ts | 7 +++ 3 files changed, 55 insertions(+), 3 deletions(-) diff --git a/backend/src/api/bitcoin/esplora-api.ts b/backend/src/api/bitcoin/esplora-api.ts index 6ee51fda4..950bb18b4 100644 --- a/backend/src/api/bitcoin/esplora-api.ts +++ b/backend/src/api/bitcoin/esplora-api.ts @@ -1,6 +1,7 @@ import config from '../../config'; import axios, { isAxiosError } from 'axios'; import http from 'http'; +import https from 'https'; import { AbstractBitcoinApi, HealthCheckHost } from './bitcoin-api-abstract-factory'; import { IEsploraApi } from './esplora-api.interface'; import logger from '../../logger'; @@ -23,6 +24,7 @@ interface FailoverHost { publicDomain: string, hashes: { frontend?: string, + hybrid?: string, backend?: string, electrs?: string, lastUpdated: number, @@ -142,7 +144,8 @@ class FailoverRouter { if (Date.now() - host.hashes.lastUpdated > this.gitHashInterval) { await Promise.all([ this.$updateFrontendGitHash(host), - this.$updateBackendGitHash(host) + this.$updateBackendGitHash(host), + config.MEMPOOL.OFFICIAL ? this.$updateHybridGitHash(host) : Promise.resolve(), ]); host.hashes.lastUpdated = Date.now(); } @@ -251,6 +254,47 @@ class FailoverRouter { if (match && match[1]?.length) { host.hashes.frontend = match[1]; } + const hybridMatch = response.data.match(/GIT_COMMIT_HASH_MEMPOOL_SPACE\s*=\s*['"](.*?)['"]/); + if (hybridMatch && hybridMatch[1]?.length) { + host.hashes.hybrid = hybridMatch[1]; + } + } catch (e) { + // failed to get frontend build hash - do nothing + } + } + + private async $updateHybridGitHash(host: FailoverHost): Promise { + try { + const response: string = await new Promise((resolve, reject) => { + const req = https.request({ + hostname: host.publicDomain.replace('https://', '').replace('http://', ''), + port: 443, + path: '/en-US/resources/config.js', + method: 'GET', + headers: { + 'Host': 'mempool.space' + }, + timeout: config.ESPLORA.FALLBACK_TIMEOUT, + }, (res) => { + let data = ''; + res.on('data', chunk => data += chunk); + res.on('end', () => { + if (res.statusCode === 200) { + resolve(data); + } else { + reject(new Error(`Failed to get hybrid git hash: ${res.statusCode}`)); + } + }); + }); + req.on('error', (e) => { + reject(e); + }); + req.end(); + }); + const match = response.match(/GIT_COMMIT_HASH_MEMPOOL_SPACE\s*=\s*['"](.*?)['"]/); + if (match && match[1]?.length) { + host.hashes.hybrid = match[1]; + } } catch (e) { // failed to get frontend build hash - do nothing } diff --git a/frontend/src/app/components/server-health/server-health.component.html b/frontend/src/app/components/server-health/server-health.component.html index a3a4a31e5..e5dfd5038 100644 --- a/frontend/src/app/components/server-health/server-health.component.html +++ b/frontend/src/app/components/server-health/server-health.component.html @@ -19,6 +19,7 @@ RTT RTT Height + Hybrid Front Back Electrs @@ -31,10 +32,10 @@ {{ (host.rtt / 1000) | number : '1.1-1' }} {{ host.rtt == null ? '' : 's'}} {{ !host.checked ? '⏳' : (host.unreachable ? '🔥' : '✅') }} {{ host.rtt | number : '1.0-0' }} {{ host.rtt == null ? '' : 'ms'}} {{ !host.checked ? '⏳' : (host.unreachable ? '🔥' : '✅') }} {{ host.latestHeight }} {{ !host.checked ? '⏳' : (host.outOfSync ? '🚫' : (host.latestHeight && host.latestHeight < maxHeight ? '🟧' : '✅')) }} - + @if (host.hashes?.[type]) { - {{ host.hashes[type].slice(0, 8) || '?' }} + {{ host.hashes[type].slice(0, 8) || '?' }} } @else { ? } diff --git a/frontend/src/app/components/server-health/server-health.component.ts b/frontend/src/app/components/server-health/server-health.component.ts index c1b6fe443..63f9f952b 100644 --- a/frontend/src/app/components/server-health/server-health.component.ts +++ b/frontend/src/app/components/server-health/server-health.component.ts @@ -17,6 +17,13 @@ export class ServerHealthComponent implements OnInit { interval: number; now: number = Date.now(); + repoMap = { + frontend: 'mempool', + hybrid: 'mempool.space', + backend: 'mempool', + electrs: 'electrs', + }; + constructor( private websocketService: WebsocketService, private stateService: StateService,