mirror of
https://github.com/mempool/mempool.git
synced 2025-03-13 03:24:28 +01:00
Merge pull request #4744 from mempool/mononaut/send-nodes
More status page polish
This commit is contained in:
commit
1d877a746f
13 changed files with 65 additions and 33 deletions
|
@ -49,4 +49,5 @@ export interface HealthCheckHost {
|
|||
outOfSync: boolean;
|
||||
unreachable: boolean;
|
||||
checked: boolean;
|
||||
lastChecked: number;
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ interface FailoverHost {
|
|||
unreachable?: boolean,
|
||||
preferred?: boolean,
|
||||
checked: boolean,
|
||||
lastChecked?: number,
|
||||
}
|
||||
|
||||
class FailoverRouter {
|
||||
|
@ -122,7 +123,7 @@ class FailoverRouter {
|
|||
}
|
||||
}
|
||||
host.checked = true;
|
||||
|
||||
host.lastChecked = Date.now();
|
||||
|
||||
// switch if the current host is out of sync or significantly slower than the next best alternative
|
||||
const rankOrder = this.sortHosts();
|
||||
|
@ -361,6 +362,7 @@ class ElectrsApi implements AbstractBitcoinApi {
|
|||
outOfSync: !!host.outOfSync,
|
||||
unreachable: !!host.unreachable,
|
||||
checked: !!host.checked,
|
||||
lastChecked: host.lastChecked || 0,
|
||||
}));
|
||||
} else {
|
||||
return [];
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<footer class="footer">
|
||||
<footer class="footer" [class.inline-footer]="inline">
|
||||
<div class="container-xl">
|
||||
<div class="row text-center" *ngIf="mempoolInfoData$ | async as mempoolInfoData">
|
||||
<div class="col d-none d-sm-block">
|
||||
|
|
|
@ -6,6 +6,12 @@
|
|||
background-color: #1d1f31;
|
||||
box-shadow: 15px 15px 15px 15px #000;
|
||||
z-index: 10;
|
||||
|
||||
&.inline-footer {
|
||||
position: relative;
|
||||
bottom: unset;
|
||||
top: -44px;
|
||||
}
|
||||
}
|
||||
|
||||
.sub-text {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Component, OnInit, ChangeDetectionStrategy } from '@angular/core';
|
||||
import { Component, OnInit, ChangeDetectionStrategy, Input } from '@angular/core';
|
||||
import { StateService } from '../../services/state.service';
|
||||
import { Observable, combineLatest } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
@ -23,6 +23,8 @@ interface MempoolInfoData {
|
|||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class FooterComponent implements OnInit {
|
||||
@Input() inline = false;
|
||||
|
||||
mempoolBlocksData$: Observable<MempoolBlocksData>;
|
||||
mempoolInfoData$: Observable<MempoolInfoData>;
|
||||
vBytesPerSecondLimit = 1667;
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
<div class="tomahawk container-xl dashboard-container">
|
||||
<div class="tomahawk">
|
||||
<div class="links">
|
||||
<span>Status</span>
|
||||
<a [routerLink]='"/network"'>Live</a>
|
||||
<span>Monitoring</span>
|
||||
<a [routerLink]='"/nodes"'>Nodes</a>
|
||||
</div>
|
||||
<h1 class="dashboard-title">Node Status</h1>
|
||||
|
||||
<app-start [showLoadingIndicator]="true"></app-start>
|
||||
<app-footer [inline]="true"></app-footer>
|
||||
|
||||
<ng-container *ngIf="(hosts$ | async) as hosts">
|
||||
<div class="status-panel">
|
||||
|
@ -13,6 +15,7 @@
|
|||
<th class="rank"></th>
|
||||
<th class="flag"></th>
|
||||
<th class="host">Host</th>
|
||||
<th class="updated">Last checked</th>
|
||||
<th class="rtt only-small">RTT</th>
|
||||
<th class="rtt only-large">RTT</th>
|
||||
<th class="height">Height</th>
|
||||
|
@ -21,6 +24,7 @@
|
|||
<td class="rank">{{ i + 1 }}</td>
|
||||
<td class="flag">{{ host.active ? '⭐️' : host.flag }}</td>
|
||||
<td class="host">{{ host.link }}</td>
|
||||
<td class="updated">{{ getLastUpdateSeconds(host) }}</td>
|
||||
<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 < tip ? '🟧' : '✅')) }}</td>
|
||||
|
|
|
@ -1,22 +1,16 @@
|
|||
.tomahawk {
|
||||
.links {
|
||||
float: right;
|
||||
text-align: right;
|
||||
margin-top: 1em;
|
||||
margin-inline-end: 1em;
|
||||
|
||||
a, span {
|
||||
margin-left: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
.dashboard-title {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.status-panel {
|
||||
max-width: 720px;
|
||||
margin: auto;
|
||||
margin-top: 2em;
|
||||
padding: 1em;
|
||||
background: #24273e;
|
||||
}
|
||||
|
@ -31,6 +25,12 @@
|
|||
width: 28px;
|
||||
text-align: right;
|
||||
}
|
||||
&.updated {
|
||||
display: none;
|
||||
width: 130px;
|
||||
text-align: right;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
&.rtt, &.height {
|
||||
width: 92px;
|
||||
text-align: right;
|
||||
|
@ -57,6 +57,9 @@
|
|||
&.rank, &.flag {
|
||||
width: 32px;
|
||||
}
|
||||
&.updated {
|
||||
display: table-cell;
|
||||
}
|
||||
&.rtt, &.height {
|
||||
width: 96px;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Component, OnInit, ChangeDetectionStrategy, SecurityContext } from '@angular/core';
|
||||
import { Component, OnInit, ChangeDetectionStrategy, SecurityContext, ChangeDetectorRef } from '@angular/core';
|
||||
import { WebsocketService } from '../../services/websocket.service';
|
||||
import { Observable, Subject, map } from 'rxjs';
|
||||
import { StateService } from '../../services/state.service';
|
||||
|
@ -14,17 +14,20 @@ import { DomSanitizer } from '@angular/platform-browser';
|
|||
export class ServerHealthComponent implements OnInit {
|
||||
hosts$: Observable<HealthCheckHost[]>;
|
||||
tip$: Subject<number>;
|
||||
interval: number;
|
||||
now: number = Date.now();
|
||||
|
||||
constructor(
|
||||
private websocketService: WebsocketService,
|
||||
private stateService: StateService,
|
||||
private cd: ChangeDetectorRef,
|
||||
public sanitizer: DomSanitizer,
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.hosts$ = this.stateService.serverHealth$.pipe(
|
||||
map((hosts) => {
|
||||
const subpath = window.location.pathname.slice(0, -6);
|
||||
const subpath = window.location.pathname.slice(0, -11);
|
||||
for (const host of hosts) {
|
||||
let statusUrl = '';
|
||||
let linkHost = '';
|
||||
|
@ -44,13 +47,27 @@ export class ServerHealthComponent implements OnInit {
|
|||
})
|
||||
);
|
||||
this.tip$ = this.stateService.chainTip$;
|
||||
this.websocketService.want(['blocks', 'tomahawk']);
|
||||
this.websocketService.want(['mempool-blocks', 'stats', 'blocks', 'tomahawk']);
|
||||
|
||||
this.interval = window.setInterval(() => {
|
||||
this.now = Date.now();
|
||||
this.cd.markForCheck();
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
trackByFn(index: number, host: HealthCheckHost): string {
|
||||
return host.host;
|
||||
}
|
||||
|
||||
getLastUpdateSeconds(host: HealthCheckHost): string {
|
||||
if (host.lastChecked) {
|
||||
const seconds = Math.ceil((this.now - host.lastChecked) / 1000);
|
||||
return `${seconds} second${seconds > 1 ? 's' : ' '} ago`;
|
||||
} else {
|
||||
return '~';
|
||||
}
|
||||
}
|
||||
|
||||
private parseFlag(host: string): string {
|
||||
if (host.includes('.fra.')) {
|
||||
return '🇩🇪';
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
<div class="tomahawk">
|
||||
<div class="container-xl dashboard-container">
|
||||
<div class="links">
|
||||
<a [routerLink]='"/nodes"'>Status</a>
|
||||
<span>Live</span>
|
||||
</div>
|
||||
<h1 class="dashboard-title">Live Network</h1>
|
||||
<div class="links">
|
||||
<a [routerLink]='"/monitoring"'>Monitoring</a>
|
||||
<span>Nodes</span>
|
||||
</div>
|
||||
|
||||
<app-start [showLoadingIndicator]="true"></app-start>
|
||||
<app-footer [inline]="true"></app-footer>
|
||||
|
||||
<ng-container *ngFor="let host of hosts; trackBy: trackByFn">
|
||||
<h5 [id]="host.host" class="hostLink">
|
||||
<a [href]="'https://' + host.link">{{ host.link }}</a>
|
||||
|
|
|
@ -1,21 +1,17 @@
|
|||
.tomahawk {
|
||||
.links {
|
||||
float: right;
|
||||
text-align: right;
|
||||
margin-top: 1em;
|
||||
margin-inline-end: 1em;
|
||||
|
||||
a, span {
|
||||
margin-left: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
.dashboard-title {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.mempoolStatus {
|
||||
width: 100%;
|
||||
height: 270px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.hostLink {
|
||||
|
|
|
@ -26,7 +26,7 @@ export class ServerStatusComponent implements OnInit, OnDestroy {
|
|||
ngOnInit(): void {
|
||||
this.hostSubscription = this.stateService.serverHealth$.pipe(
|
||||
map((hosts) => {
|
||||
const subpath = window.location.pathname.slice(0, -8);
|
||||
const subpath = window.location.pathname.slice(0, -6);
|
||||
for (const host of hosts) {
|
||||
let statusUrl = '';
|
||||
let linkHost = '';
|
||||
|
@ -66,7 +66,7 @@ export class ServerStatusComponent implements OnInit, OnDestroy {
|
|||
})
|
||||
).subscribe();
|
||||
this.tip$ = this.stateService.chainTip$;
|
||||
this.websocketService.want(['blocks', 'tomahawk']);
|
||||
this.websocketService.want(['mempool-blocks', 'stats', 'blocks', 'tomahawk']);
|
||||
}
|
||||
|
||||
trackByFn(index: number, host: HealthCheckHost): string {
|
||||
|
|
|
@ -132,6 +132,7 @@ export interface HealthCheckHost {
|
|||
outOfSync: boolean;
|
||||
unreachable: boolean;
|
||||
checked: boolean;
|
||||
lastChecked: number;
|
||||
link?: string;
|
||||
statusPage?: SafeResourceUrl;
|
||||
flag?: string;
|
||||
|
|
|
@ -101,12 +101,12 @@ const routes: Routes = [
|
|||
|
||||
if (window['__env']?.OFFICIAL_MEMPOOL_SPACE) {
|
||||
routes[0].children.push({
|
||||
path: 'nodes',
|
||||
path: 'monitoring',
|
||||
data: { networks: ['bitcoin', 'liquid'] },
|
||||
component: ServerHealthComponent
|
||||
});
|
||||
routes[0].children.push({
|
||||
path: 'network',
|
||||
path: 'nodes',
|
||||
data: { networks: ['bitcoin', 'liquid'] },
|
||||
component: ServerStatusComponent
|
||||
});
|
||||
|
|
Loading…
Add table
Reference in a new issue