add hybrid build hash to monitoring

This commit is contained in:
Mononaut 2025-08-01 13:14:16 +00:00
parent 5fc8a9f594
commit a04df3f4ee
No known key found for this signature in database
GPG key ID: A3F058E41374C04E
3 changed files with 55 additions and 3 deletions

View file

@ -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<void> {
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
}

View file

@ -19,6 +19,7 @@
<th class="rtt only-small">RTT</th>
<th class="rtt only-large">RTT</th>
<th class="height">Height</th>
<th class="hybrid only-large">Hybrid</th>
<th class="frontend only-large">Front</th>
<th class="backend only-large">Back</th>
<th class="electrs only-large">Electrs</th>
@ -31,10 +32,10 @@
<td class="rtt only-small">{{ (host.rtt / 1000) | number : '1.1-1' }} {{ host.rtt == null ? '' : 's'}} {{ !host.checked ? '⏳' : (host.unreachable ? '🔥' : '✅') }}</td>
<td class="rtt only-large">{{ host.rtt | number : '1.0-0' }} {{ host.rtt == null ? '' : 'ms'}} {{ !host.checked ? '⏳' : (host.unreachable ? '🔥' : '✅') }}</td>
<td class="height">{{ host.latestHeight }} {{ !host.checked ? '⏳' : (host.outOfSync ? '🚫' : (host.latestHeight && host.latestHeight < maxHeight ? '🟧' : '')) }}</td>
<ng-container *ngFor="let type of ['frontend', 'backend', 'electrs']">
<ng-container *ngFor="let type of ['hybrid', 'frontend', 'backend', 'electrs']">
<td class="{{type}} only-large" [style.background-color]="host.hashes?.[type] ? '#' + host.hashes[type].slice(0, 6) : ''">
@if (host.hashes?.[type]) {
<a [style.color]="'white'" href="https://github.com/mempool/{{type === 'electrs' ? 'electrs' : 'mempool'}}/commit/{{ host.hashes[type] }}" target="_blank">{{ host.hashes[type].slice(0, 8) || '?' }}</a>
<a [style.color]="'white'" href="https://github.com/mempool/{{repoMap[type]}}/commit/{{ host.hashes[type] }}" target="_blank">{{ host.hashes[type].slice(0, 8) || '?' }}</a>
} @else {
<span>?</span>
}

View file

@ -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,