fix time display bug and type metrics (#2490)

* fix time display bug and type metrics

* change metrics response

* nullable temp

* rename percentage used

* match frontend types

---------

Co-authored-by: Aiden McClelland <me@drbonez.dev>
This commit is contained in:
Matt Hill
2023-11-01 16:30:54 -06:00
committed by GitHub
parent 9c6dcc4a43
commit b597d0366a
8 changed files with 284 additions and 210 deletions

View File

@@ -11,74 +11,155 @@
</ion-header>
<ion-content class="ion-padding with-widgets">
<skeleton-list *ngIf="loading" [groups]="2"></skeleton-list>
<div id="metricSection">
<ng-container *ngIf="!loading">
<ion-item-group>
<!-- <ion-item-divider>Time</ion-item-divider> -->
<ion-item *ngIf="now$ | async as now; else timeLoading">
<ion-label>
<h1>System Time</h1>
<h2>
<ion-text style="color: white">
<b>{{ now.value | date:'MMMM d, y, h:mm a z':'UTC' }}</b>
</ion-text>
</h2>
<p *ngIf="!now.synced">
<ion-text color="warning">
NTP not synced, time could be wrong
</ion-text>
</p>
</ion-label>
<ion-note slot="end" class="metric-note"></ion-note>
</ion-item>
<ng-template #timeLoading>
<ion-item>
<ion-label>
<h1>System Time</h1>
<p>Loading...</p>
</ion-label>
<ion-note slot="end" class="metric-note"></ion-note>
</ion-item>
</ng-template>
<ion-item>
<ion-label>
<h1>System Uptime</h1>
<h2>
<ion-text style="color: white">
<ng-container *ngIf="uptime$ | async as uptime">
<b>{{ uptime.days }}</b>
Days,
<b>{{ uptime.hours }}</b>
Hours,
<b>{{ uptime.minutes }}</b>
Minutes,
<b>{{ uptime.seconds }}</b>
Seconds
</ng-container>
</ion-text>
</h2>
</ion-label>
</ion-item>
</ion-item-group>
<ion-item-group
*ngFor="let metricGroup of metrics | keyvalue : asIsOrder"
>
<ion-item-divider>{{ metricGroup.key }}</ion-item-divider>
<ion-item
*ngFor="let metric of metricGroup.value | keyvalue : asIsOrder"
>
<ion-label>{{ metric.key }}</ion-label>
<ion-note *ngIf="metric.value" slot="end" class="metric-note">
<ion-item-group>
<ion-item>
<ion-label>
<h1>System Time</h1>
<ng-container *ngIf="now$ | async as now; else timeLoading">
<h2>
<ion-text style="color: white">
{{ metric.value.value }} {{ metric.value.unit }}
<b>{{ now.value | date:'MMMM d, y, h:mm a z':'UTC' }}</b>
</ion-text>
</ion-note>
</ion-item>
</ion-item-group>
</ng-container>
</div>
</h2>
<p *ngIf="!now.synced">
<ion-text color="warning">
NTP not synced, time could be wrong
</ion-text>
</p>
</ng-container>
<ng-template #timeLoading>
<h2>Loading...</h2>
</ng-template>
</ion-label>
<ion-note slot="end" class="metric-note"></ion-note>
</ion-item>
<ion-item>
<ion-label>
<h1>System Uptime</h1>
<h2>
<ion-text style="color: white">
<ng-container *ngIf="uptime$ | async as uptime; else uptimeLoading">
<b>{{ uptime.days }}</b>
Days,
<b>{{ uptime.hours }}</b>
Hours,
<b>{{ uptime.minutes }}</b>
Minutes,
<b>{{ uptime.seconds }}</b>
Seconds
</ng-container>
<ng-template #uptimeLoading>Loading...</ng-template>
</ion-text>
</h2>
</ion-label>
</ion-item>
</ion-item-group>
<ng-container *ngIf="metrics$ | async as metrics; else loading">
<ion-item-group *ngIf="metrics.general as general">
<ion-item-divider>General</ion-item-divider>
<ion-item>
<ion-label>Temperature</ion-label>
<ion-note slot="end">
<ng-container *ngIf="general.temperature; else noTemp">
{{ general.temperature.value }} &deg;C
</ng-container>
<ng-template #noTemp>N/A</ng-template>
</ion-note>
</ion-item>
</ion-item-group>
<ion-item-group *ngIf="metrics.memory as memory">
<ion-item-divider>Memory</ion-item-divider>
<ion-item>
<ion-label>Percentage Used</ion-label>
<ion-note slot="end">{{ memory['percentage-used'].value }} %</ion-note>
</ion-item>
<ion-item>
<ion-label>Total</ion-label>
<ion-note slot="end">
<ion-text>{{ memory.total.value }} MiB</ion-text>
</ion-note>
</ion-item>
<ion-item>
<ion-label>Used</ion-label>
<ion-note slot="end">
<ion-text>{{ memory.used.value }} MiB</ion-text>
</ion-note>
</ion-item>
<ion-item>
<ion-label>Available</ion-label>
<ion-note slot="end">{{ memory.available.value }} MiB</ion-note>
</ion-item>
<ion-item>
<ion-label>zram Used</ion-label>
<ion-note slot="end">{{ memory['zram-used'].value }} MiB</ion-note>
</ion-item>
<ion-item>
<ion-label>zram Total</ion-label>
<ion-note slot="end">{{ memory['zram-total'].value }} MiB</ion-note>
</ion-item>
<ion-item>
<ion-label>zram Available</ion-label>
<ion-note slot="end">{{ memory['zram-available'].value }} MiB</ion-note>
</ion-item>
</ion-item-group>
<ion-item-group *ngIf="metrics.cpu as cpu">
<ion-item-divider>CPU</ion-item-divider>
<ion-item>
<ion-label>Percentage Used</ion-label>
<ion-note slot="end">{{ cpu['percentage-used'].value }} %</ion-note>
</ion-item>
<ion-item>
<ion-label>User Space</ion-label>
<ion-note slot="end">
<ion-text>{{ cpu['user-space'].value }} %</ion-text>
</ion-note>
</ion-item>
<ion-item>
<ion-label>Kernel Space</ion-label>
<ion-note slot="end">
<ion-text>{{ cpu['kernel-space'].value }} %</ion-text>
</ion-note>
</ion-item>
<ion-item>
<ion-label>Idle</ion-label>
<ion-note slot="end">{{ cpu.idle.value }} %</ion-note>
</ion-item>
<ion-item>
<ion-label>I/O Wait</ion-label>
<ion-note slot="end">{{ cpu.wait.value }} %</ion-note>
</ion-item>
</ion-item-group>
<ion-item-group *ngIf="metrics.disk as disk">
<ion-item-divider>Disk</ion-item-divider>
<ion-item>
<ion-label>Percentage Used</ion-label>
<ion-note slot="end">{{ disk['percentage-used'].value }} %</ion-note>
</ion-item>
<ion-item>
<ion-label>Capacity</ion-label>
<ion-note slot="end">
<ion-text>{{ disk.capacity.value }} GB</ion-text>
</ion-note>
</ion-item>
<ion-item>
<ion-label>Used</ion-label>
<ion-note slot="end">
<ion-text>{{ disk.used.value }} GB</ion-text>
</ion-note>
</ion-item>
<ion-item>
<ion-label>Available</ion-label>
<ion-note slot="end">{{ disk.available.value }} GB</ion-note>
</ion-item>
</ion-item-group>
</ng-container>
<ng-template #loading>
<skeleton-list [groups]="4"></skeleton-list>
</ng-template>
</ion-content>

View File

@@ -1,3 +1,4 @@
.metric-note {
ion-note {
font-size: 16px;
color: white;
}

View File

@@ -3,6 +3,7 @@ import { Metrics } from 'src/app/services/api/api.types'
import { ApiService } from 'src/app/services/api/embassy-api.service'
import { TimeService } from 'src/app/services/time-service'
import { pauseFor, ErrorToastService } from '@start9labs/shared'
import { Subject } from 'rxjs'
@Component({
selector: 'server-metrics',
@@ -10,9 +11,8 @@ import { pauseFor, ErrorToastService } from '@start9labs/shared'
styleUrls: ['./server-metrics.page.scss'],
})
export class ServerMetricsPage {
loading = true
going = false
metrics: Metrics = {}
metrics$ = new Subject<Metrics>()
readonly now$ = this.timeService.now$
readonly uptime$ = this.timeService.uptime$
@@ -25,19 +25,7 @@ export class ServerMetricsPage {
async ngOnInit() {
await this.getMetrics()
let headersCount = 0
let rowsCount = 0
Object.values(this.metrics).forEach(groupVal => {
headersCount++
Object.keys(groupVal).forEach(_ => {
rowsCount++
})
})
const height = headersCount * 54 + rowsCount * 50 + 24 // extra 24 for room at the bottom
const elem = document.getElementById('metricSection')
if (elem) elem.style.height = `${height}px`
this.startDaemon()
this.loading = false
}
ngOnDestroy() {
@@ -59,7 +47,8 @@ export class ServerMetricsPage {
private async getMetrics(): Promise<void> {
try {
this.metrics = await this.embassyApi.getServerMetrics({})
const metrics = await this.embassyApi.getServerMetrics({})
this.metrics$.next(metrics)
} catch (e: any) {
this.errToast.present(e)
this.stopDaemon()

View File

@@ -835,85 +835,79 @@ export module Mock {
export function getServerMetrics() {
return {
Group1: {
Metric1: {
value: Math.random(),
unit: 'mi/b',
general: {
temperature: {
value: '66.8',
unit: '°C',
},
Metric2: {
value: Math.random(),
},
memory: {
'percentage-used': {
value: '30.7',
unit: '%',
},
Metric3: {
value: 10.1,
total: {
value: '31971.10',
unit: 'MiB',
},
available: {
value: '22150.66',
unit: 'MiB',
},
used: {
value: '8784.97',
unit: 'MiB',
},
'zram-total': {
value: '7992.00',
unit: 'MiB',
},
'zram-available': {
value: '7882.50',
unit: 'MiB',
},
'zram-used': {
value: '109.50',
unit: 'MiB',
},
},
cpu: {
'percentage-used': {
value: '8.4',
unit: '%',
},
'user-space': {
value: '7.0',
unit: '%',
},
'kernel-space': {
value: '1.4',
unit: '%',
},
wait: {
value: '0.5',
unit: '%',
},
idle: {
value: '91.1',
unit: '%',
},
},
Group2: {
Hmmmm1: {
value: 22.2,
unit: 'mi/b',
disk: {
capacity: {
value: '1851.60',
unit: 'GB',
},
Hmmmm2: {
value: 50,
unit: '%',
used: {
value: '859.02',
unit: 'GB',
},
Hmmmm3: {
value: 10.1,
unit: '%',
available: {
value: '992.59',
unit: 'GB',
},
},
Group3: {
Hmmmm1: {
value: Math.random(),
unit: 'mi/b',
},
Hmmmm2: {
value: 50,
unit: '%',
},
Hmmmm3: {
value: 10.1,
unit: '%',
},
},
Group4: {
Hmmmm1: {
value: Math.random(),
unit: 'mi/b',
},
Hmmmm2: {
value: 50,
unit: '%',
},
Hmmmm3: {
value: 10.1,
unit: '%',
},
},
Group5: {
Hmmmm1: {
value: Math.random(),
unit: 'mi/b',
},
Hmmmm2: {
value: 50,
unit: '%',
},
Hmmmm3: {
value: 10.1,
unit: '%',
},
Hmmmm4: {
value: Math.random(),
unit: 'mi/b',
},
Hmmmm5: {
value: 50,
unit: '%',
},
Hmmmm6: {
value: 10.1,
'percentage-used': {
value: '46.4',
unit: '%',
},
},

View File

@@ -301,12 +301,36 @@ export interface ActionResponse {
qr: boolean
}
interface MetricData {
value: string
unit: string
}
export interface Metrics {
[key: string]: {
[key: string]: {
value: string | number | null
unit?: string
}
general: {
temperature: MetricData | null
}
memory: {
total: MetricData
'percentage-used': MetricData
used: MetricData
available: MetricData
'zram-total': MetricData
'zram-used': MetricData
'zram-available': MetricData
}
cpu: {
'percentage-used': MetricData
idle: MetricData
'user-space': MetricData
'kernel-space': MetricData
wait: MetricData
}
disk: {
capacity: MetricData
'percentage-used': MetricData
used: MetricData
available: MetricData
}
}

View File

@@ -3,13 +3,14 @@ import { map, shareReplay, startWith, switchMap } from 'rxjs/operators'
import { PatchDB } from 'patch-db-client'
import { DataModel } from './patch-db/data-model'
import { ApiService } from './api/embassy-api.service'
import { combineLatest, from, interval } from 'rxjs'
import { combineLatest, interval, of } from 'rxjs'
@Injectable({
providedIn: 'root',
})
export class TimeService {
private readonly time$ = from(this.apiService.getSystemTime({})).pipe(
private readonly time$ = of({}).pipe(
switchMap(() => this.apiService.getSystemTime({})),
switchMap(({ now, uptime }) => {
const current = new Date(now).valueOf()
return interval(1000).pipe(