diff --git a/frontend/src/app/app-routing.module.ts b/frontend/src/app/app-routing.module.ts index 18949876e..e123a1525 100644 --- a/frontend/src/app/app-routing.module.ts +++ b/frontend/src/app/app-routing.module.ts @@ -6,7 +6,6 @@ import { EightBlocksComponent } from './components/eight-blocks/eight-blocks.com import { MempoolBlockViewComponent } from './components/mempool-block-view/mempool-block-view.component'; import { ClockComponent } from './components/clock/clock.component'; import { StatusViewComponent } from './components/status-view/status-view.component'; -import { ServerHealthComponent } from './components/server-health/server-health.component'; const browserWindow = window || {}; // @ts-ignore @@ -32,11 +31,6 @@ let routes: Routes = [ data: { networks: ['bitcoin', 'liquid'] }, component: StatusViewComponent }, - { - path: 'nodes', - data: { networks: ['bitcoin', 'liquid'] }, - component: ServerHealthComponent - }, { path: '', loadChildren: () => import('./bitcoin-graphs.module').then(m => m.BitcoinGraphsModule), @@ -72,11 +66,6 @@ let routes: Routes = [ data: { networks: ['bitcoin', 'liquid'] }, component: StatusViewComponent }, - { - path: 'nodes', - data: { networks: ['bitcoin', 'liquid'] }, - component: ServerHealthComponent - }, { path: '', loadChildren: () => import('./bitcoin-graphs.module').then(m => m.BitcoinGraphsModule), @@ -145,11 +134,6 @@ let routes: Routes = [ data: { networks: ['bitcoin', 'liquid'] }, component: StatusViewComponent }, - { - path: 'nodes', - data: { networks: ['bitcoin', 'liquid'] }, - component: ServerHealthComponent - }, { path: '', loadChildren: () => import('./bitcoin-graphs.module').then(m => m.BitcoinGraphsModule), @@ -189,11 +173,6 @@ if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid') { data: { networks: ['bitcoin', 'liquid'] }, component: StatusViewComponent }, - { - path: 'nodes', - data: { networks: ['bitcoin', 'liquid'] }, - component: ServerHealthComponent - }, { path: '', loadChildren: () => import('./liquid/liquid-graphs.module').then(m => m.LiquidGraphsModule), @@ -234,11 +213,6 @@ if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid') { data: { networks: ['bitcoin', 'liquid']}, component: StatusViewComponent }, - { - path: 'nodes', - data: { networks: ['bitcoin', 'liquid'] }, - component: ServerHealthComponent - }, { path: '', loadChildren: () => import('./liquid/liquid-graphs.module').then(m => m.LiquidGraphsModule), 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 dd8d61444..f376b4314 100644 --- a/frontend/src/app/components/server-health/server-health.component.html +++ b/frontend/src/app/components/server-health/server-health.component.html @@ -1,4 +1,10 @@ -
+
+ +

Node Status

+
@@ -9,7 +15,7 @@ - + @@ -19,11 +25,4 @@
RTT Height
⭐️ {{ host.host }} {{ host.rtt | number : '1.0-0' }} {{ host.rtt == null ? '' : 'ms'}} {{ !host.checked ? '⏳' : (host.unreachable ? '🔥' : '✅') }}
- - - - -
diff --git a/frontend/src/app/components/server-health/server-health.component.scss b/frontend/src/app/components/server-health/server-health.component.scss index e403e5824..ebc6a883c 100644 --- a/frontend/src/app/components/server-health/server-health.component.scss +++ b/frontend/src/app/components/server-health/server-health.component.scss @@ -1,4 +1,18 @@ .tomahawk { + .links { + float: right; + text-align: right; + margin-top: 1em; + + a, span { + margin-left: 1em; + } + } + + .dashboard-title { + text-align: left; + } + .status-panel { max-width: 720px; margin: auto; @@ -16,20 +30,5 @@ text-align: right; } } - - td { - cursor: pointer; - } - } - - .mempoolStatus { - width: 100%; - height: 270px; - } - - .hostLink { - text-align: center; - margin: auto; - margin-top: 1em; } } \ No newline at end of file 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 bd7cc57e8..b1008a4a5 100644 --- a/frontend/src/app/components/server-health/server-health.component.ts +++ b/frontend/src/app/components/server-health/server-health.component.ts @@ -1,6 +1,6 @@ -import { Component, OnInit, ChangeDetectionStrategy, SecurityContext, OnDestroy } from '@angular/core'; +import { Component, OnInit, ChangeDetectionStrategy, SecurityContext } from '@angular/core'; import { WebsocketService } from '../../services/websocket.service'; -import { Observable, Subject, map, tap } from 'rxjs'; +import { Observable, Subject, map } from 'rxjs'; import { StateService } from '../../services/state.service'; import { HealthCheckHost } from '../../interfaces/websocket.interface'; import { DomSanitizer } from '@angular/platform-browser'; @@ -11,10 +11,9 @@ import { DomSanitizer } from '@angular/platform-browser'; styleUrls: ['./server-health.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, }) -export class ServerHealthComponent implements OnInit, OnDestroy { +export class ServerHealthComponent implements OnInit { hosts$: Observable; tip$: Subject; - hosts: HealthCheckHost[] = []; constructor( private websocketService: WebsocketService, @@ -33,36 +32,17 @@ export class ServerHealthComponent implements OnInit, OnDestroy { statusUrl = window.location.host + subpath + '/status'; linkHost = window.location.host + subpath; } else { - statusUrl = host.host.slice(0, -4) + subpath + '/status'; - linkHost = host.host.slice(0, -4) + subpath; + const hostUrl = new URL(host.host); + statusUrl = 'https://' + hostUrl.hostname + subpath + '/status'; + linkHost = hostUrl.hostname + subpath; } host.statusPage = this.sanitizer.bypassSecurityTrustResourceUrl(this.sanitizer.sanitize(SecurityContext.URL, statusUrl)); host.link = linkHost; } return hosts; - }), - tap((hosts) => { - if (this.hosts.length !== hosts.length) { - this.hosts = hosts; - } }) ); this.tip$ = this.stateService.chainTip$; this.websocketService.want(['blocks', 'tomahawk']); } - - scrollTo(host: HealthCheckHost): void { - const el = document.getElementById(host.host); - if (el) { - el.scrollIntoView(); - } - } - - trackByFn(index: number, host: HealthCheckHost): string { - return host.host; - } - - ngOnDestroy(): void { - this.hosts = []; - } } diff --git a/frontend/src/app/components/server-health/server-status.component.html b/frontend/src/app/components/server-health/server-status.component.html new file mode 100644 index 000000000..484d774a0 --- /dev/null +++ b/frontend/src/app/components/server-health/server-status.component.html @@ -0,0 +1,14 @@ +
+ +

Live Network

+ + + + + +
diff --git a/frontend/src/app/components/server-health/server-status.component.scss b/frontend/src/app/components/server-health/server-status.component.scss new file mode 100644 index 000000000..09bebe040 --- /dev/null +++ b/frontend/src/app/components/server-health/server-status.component.scss @@ -0,0 +1,26 @@ +.tomahawk { + .links { + float: right; + text-align: right; + margin-top: 1em; + + a, span { + margin-left: 1em; + } + } + + .dashboard-title { + text-align: left; + } + + .mempoolStatus { + width: 100%; + height: 270px; + } + + .hostLink { + text-align: center; + margin: auto; + margin-top: 1em; + } +} \ No newline at end of file diff --git a/frontend/src/app/components/server-health/server-status.component.ts b/frontend/src/app/components/server-health/server-status.component.ts new file mode 100644 index 000000000..8c893988d --- /dev/null +++ b/frontend/src/app/components/server-health/server-status.component.ts @@ -0,0 +1,80 @@ +import { Component, OnInit, ChangeDetectionStrategy, SecurityContext, OnDestroy, ChangeDetectorRef } from '@angular/core'; +import { WebsocketService } from '../../services/websocket.service'; +import { Observable, Subject, Subscription, map, tap } from 'rxjs'; +import { StateService } from '../../services/state.service'; +import { HealthCheckHost } from '../../interfaces/websocket.interface'; +import { DomSanitizer } from '@angular/platform-browser'; + +@Component({ + selector: 'app-server-status', + templateUrl: './server-status.component.html', + styleUrls: ['./server-status.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class ServerStatusComponent implements OnInit, OnDestroy { + tip$: Subject; + hosts: HealthCheckHost[] = []; + hostSubscription: Subscription; + + constructor( + private websocketService: WebsocketService, + private stateService: StateService, + private cd: ChangeDetectorRef, + public sanitizer: DomSanitizer, + ) {} + + ngOnInit(): void { + this.hostSubscription = this.stateService.serverHealth$.pipe( + map((hosts) => { + const subpath = window.location.pathname.slice(0, -8); + for (const host of hosts) { + let statusUrl = ''; + let linkHost = ''; + if (host.socket) { + statusUrl = window.location.host + subpath + '/status'; + linkHost = window.location.host + subpath; + } else { + const hostUrl = new URL(host.host); + statusUrl = 'https://' + hostUrl.hostname + subpath + '/status'; + linkHost = hostUrl.hostname + subpath; + } + host.statusPage = this.sanitizer.bypassSecurityTrustResourceUrl(this.sanitizer.sanitize(SecurityContext.URL, statusUrl)); + host.link = linkHost; + } + return hosts; + }), + tap((hosts) => { + if (this.hosts.length !== hosts.length) { + this.hosts = hosts.sort((a,b) => { + const aParts = (a.host?.split('.') || []).reverse(); + const bParts = (b.host?.split('.') || []).reverse(); + let i = 0; + while (i < Math.max(aParts.length, bParts.length)) { + if (aParts[i] && !bParts[i]) { + return 1; + } else if (bParts[i] && !aParts[i]) { + return -1; + } else if (aParts[i] !== bParts[i]) { + return aParts[i].localeCompare(bParts[i]); + } + i++; + } + return 0; + }); + } + this.cd.markForCheck(); + }) + ).subscribe(); + this.tip$ = this.stateService.chainTip$; + this.websocketService.want(['blocks', 'tomahawk']); + } + + trackByFn(index: number, host: HealthCheckHost): string { + return host.host; + } + + ngOnDestroy(): void { + this.hosts = []; + this.hostSubscription.unsubscribe(); + } +} diff --git a/frontend/src/app/liquid/liquid-master-page.module.ts b/frontend/src/app/liquid/liquid-master-page.module.ts index 8988cb05c..4b8364ad5 100644 --- a/frontend/src/app/liquid/liquid-master-page.module.ts +++ b/frontend/src/app/liquid/liquid-master-page.module.ts @@ -19,6 +19,8 @@ import { RecentPegsListComponent } from '../components/liquid-reserves-audit/rec import { FederationWalletComponent } from '../components/liquid-reserves-audit/federation-wallet/federation-wallet.component'; import { FederationUtxosListComponent } from '../components/liquid-reserves-audit/federation-utxos-list/federation-utxos-list.component'; import { FederationAddressesListComponent } from '../components/liquid-reserves-audit/federation-addresses-list/federation-addresses-list.component'; +import { ServerHealthComponent } from '../components/server-health/server-health.component'; +import { ServerStatusComponent } from '../components/server-health/server-status.component'; const routes: Routes = [ { @@ -140,6 +142,19 @@ const routes: Routes = [ }, ]; +if (window['__env']?.OFFICIAL_MEMPOOL_SPACE) { + routes[0].children.push({ + path: 'nodes', + data: { networks: ['bitcoin', 'liquid'] }, + component: ServerHealthComponent + }); + routes[0].children.push({ + path: 'network', + data: { networks: ['bitcoin', 'liquid'] }, + component: ServerStatusComponent + }); +} + @NgModule({ imports: [ RouterModule.forChild(routes) diff --git a/frontend/src/app/master-page.module.ts b/frontend/src/app/master-page.module.ts index d7ec87030..ec3e08674 100644 --- a/frontend/src/app/master-page.module.ts +++ b/frontend/src/app/master-page.module.ts @@ -10,6 +10,8 @@ import { PushTransactionComponent } from './components/push-transaction/push-tra import { CalculatorComponent } from './components/calculator/calculator.component'; import { BlocksList } from './components/blocks-list/blocks-list.component'; import { RbfList } from './components/rbf-list/rbf-list.component'; +import { ServerHealthComponent } from './components/server-health/server-health.component'; +import { ServerStatusComponent } from './components/server-health/server-status.component'; const browserWindow = window || {}; // @ts-ignore @@ -96,6 +98,19 @@ const routes: Routes = [ } ]; +if (window['__env']?.OFFICIAL_MEMPOOL_SPACE) { + routes[0].children.push({ + path: 'nodes', + data: { networks: ['bitcoin', 'liquid'] }, + component: ServerHealthComponent + }); + routes[0].children.push({ + path: 'network', + data: { networks: ['bitcoin', 'liquid'] }, + component: ServerStatusComponent + }); +} + @NgModule({ imports: [ RouterModule.forChild(routes) diff --git a/frontend/src/app/shared/shared.module.ts b/frontend/src/app/shared/shared.module.ts index 6c43a019f..2f6496559 100644 --- a/frontend/src/app/shared/shared.module.ts +++ b/frontend/src/app/shared/shared.module.ts @@ -54,6 +54,7 @@ import { AssetsComponent } from '../components/assets/assets.component'; import { AssetsNavComponent } from '../components/assets/assets-nav/assets-nav.component'; import { StatusViewComponent } from '../components/status-view/status-view.component'; import { ServerHealthComponent } from '../components/server-health/server-health.component'; +import { ServerStatusComponent } from '../components/server-health/server-status.component'; import { FeesBoxComponent } from '../components/fees-box/fees-box.component'; import { DifficultyComponent } from '../components/difficulty/difficulty.component'; import { DifficultyTooltipComponent } from '../components/difficulty/difficulty-tooltip.component'; @@ -153,6 +154,7 @@ import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-dir AssetsComponent, StatusViewComponent, ServerHealthComponent, + ServerStatusComponent, FeesBoxComponent, DifficultyComponent, DifficultyMiningComponent, @@ -280,6 +282,7 @@ import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-dir AssetsComponent, StatusViewComponent, ServerHealthComponent, + ServerStatusComponent, FeesBoxComponent, DifficultyComponent, DifficultyMiningComponent,