better network monitoring

This commit is contained in:
Matt Hill
2021-07-01 16:18:49 -06:00
committed by Aiden McClelland
parent f0e108f87b
commit 330d5a08af
14 changed files with 295 additions and 244 deletions

View File

@@ -8,45 +8,43 @@
</ion-header>
<ion-content style="position: relative">
<ng-container *ngIf="patch.watch$('package-data') | ngrxPush as pkgs">
<div *ngIf="pkgs | empty; else list" class="ion-text-center ion-padding">
<div style="display: flex; flex-direction: column; justify-content: center; height: 40vh">
<h2>Welcome to your <span style="font-style: italic; color: var(--ion-color-start9)">Embassy</span></h2>
<p class="ion-text-wrap">Get started by installing your first service.</p>
</div>
<ion-button [routerLink]="['/marketplace']" style="width: 50%;" fill="outline">
<ion-icon slot="start" name="storefront-outline"></ion-icon>
Marketplace
</ion-button>
<div *ngIf="pkgs | empty; else list" class="ion-text-center ion-padding">
<div style="display: flex; flex-direction: column; justify-content: center; height: 40vh">
<h2>Welcome to your <span style="font-style: italic; color: var(--ion-color-start9)">Embassy</span></h2>
<p class="ion-text-wrap">Get started by installing your first service.</p>
</div>
<ion-button [routerLink]="['/marketplace']" style="width: 50%;" fill="outline">
<ion-icon slot="start" name="storefront-outline"></ion-icon>
Marketplace
</ion-button>
</div>
<ng-template #list>
<ion-grid>
<ion-row *ngIf="connectionService.monitor$() | ngrxPush as connection">
<ion-col *ngFor="let pkg of pkgs | keyvalue : asIsOrder" sizeXs="4" sizeSm="3" sizeLg="3" sizeXl="2">
<ion-card class="installed-card" style="position:relative" [routerLink]="['/services', (pkg.value | manifest).id]">
<div class="launch-container" *ngIf="pkg.value | hasUi">
<div class="launch-button-triangle" (click)="launchUi(pkg.value, $event)" [class.launch-disabled]="!(pkg.value | isLaunchable)">
<ion-icon name="rocket-outline"></ion-icon>
</div>
<ng-template #list>
<ion-grid>
<ion-row>
<ion-col *ngFor="let pkg of pkgs | keyvalue : asIsOrder" sizeXs="4" sizeSm="3" sizeLg="3" sizeXl="2">
<ion-card class="installed-card" style="position:relative" [routerLink]="['/services', (pkg.value | manifest).id]">
<div class="launch-container" *ngIf="pkg.value | hasUi">
<div class="launch-button-triangle" (click)="launchUi(pkg.value, $event)" [class.launch-disabled]="!(pkg.value | isLaunchable)">
<ion-icon name="rocket-outline"></ion-icon>
</div>
<img style="position: absolute" class="main-img" [src]="pkg.value['static-files'].icon" alt="icon" />
<img class="main-img" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=">
<img class="bulb-on" *ngIf="pkg.value | displayBulb: 'green' : connection" src="assets/img/running-bulb.png"/>
<img class="bulb-on" *ngIf="pkg.value | displayBulb: 'red' : connection" src="assets/img/issue-bulb.png"/>
<img class="bulb-on" *ngIf="pkg.value | displayBulb: 'yellow' : connection" src="assets/img/warning-bulb.png"/>
<img class="bulb-off" *ngIf="pkg.value | displayBulb: 'off' : connection" src="assets/img/off-bulb.png"/>
</div>
<img style="position: absolute" class="main-img" [src]="pkg.value['static-files'].icon" alt="icon" />
<img class="main-img" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=">
<img class="bulb-on" *ngIf="pkg.value | displayBulb: 'green' : connected" src="assets/img/running-bulb.png"/>
<img class="bulb-on" *ngIf="pkg.value | displayBulb: 'red' : connected" src="assets/img/issue-bulb.png"/>
<img class="bulb-on" *ngIf="pkg.value | displayBulb: 'yellow' : connected" src="assets/img/warning-bulb.png"/>
<img class="bulb-off" *ngIf="pkg.value | displayBulb: 'off' : connected" src="assets/img/off-bulb.png"/>
<ion-card-header>
<status [pkg]="pkg.value" [connection]="connection" size="calc(4px + .7vw)" weight="bold"></status>
<ion-card-title>{{ (pkg.value | manifest).title }}</ion-card-title>
</ion-card-header>
</ion-card>
</ion-col>
</ion-row>
</ion-grid>
</ng-template>
</ng-container>
<ion-card-header>
<status *ngIf="connected" [pkg]="pkg.value" size="calc(4px + .7vw)" weight="bold"></status>
<ion-card-title>{{ (pkg.value | manifest).title }}</ion-card-title>
</ion-card-header>
</ion-card>
</ion-col>
</ion-row>
</ion-grid>
</ng-template>
</ion-content>

View File

@@ -3,6 +3,7 @@ import { ConfigService } from 'src/app/services/config.service'
import { ConnectionService } from 'src/app/services/connection.service'
import { PatchDbModel } from 'src/app/models/patch-db/patch-db-model'
import { PackageDataEntry } from 'src/app/models/patch-db/data-model'
import { Subscription } from 'rxjs'
@Component({
selector: 'app-list',
@@ -10,7 +11,9 @@ import { PackageDataEntry } from 'src/app/models/patch-db/data-model'
styleUrls: ['./app-list.page.scss'],
})
export class AppListPage {
pkgs: PackageDataEntry[] = []
pkgs: { [id: string]: PackageDataEntry } = { }
connected: boolean
subs: Subscription[] = []
constructor (
private readonly config: ConfigService,
@@ -18,6 +21,19 @@ export class AppListPage {
public readonly patch: PatchDbModel,
) { }
ngOnInit () {
this.subs = [
this.patch.watch$('package-data').subscribe(pkgs => {
this.pkgs = pkgs
}),
this.patch.connected$().subscribe(c => this.connected = c),
]
}
ngOnDestroy () {
this.subs.forEach(sub => sub.unsubscribe())
}
launchUi (pkg: PackageDataEntry, event: Event): void {
event.preventDefault()
event.stopPropagation()

View File

@@ -16,122 +16,120 @@
<ion-text class="ion-text-wrap" color="danger">{{ error }}</ion-text>
</ion-item>
<ng-container *ngrxLet="connectionService.monitor$() as connection">
<ng-container *ngIf="pkg | status : connection as status">
<!-- top plate -->
<div class="top-plate">
<ion-item class="no-cushion-item" lines="none">
<ion-label class="ion-text-wrap" style="
display: grid;
grid-template-columns: 80px auto;
margin: 0px;
margin-top: 15px;"
>
<ion-avatar style="justify-self: center; height: 55px; width: 55px" slot="start">
<img [src]="pkg['static-files'].icon" />
</ion-avatar>
<div style="display: flex; flex-direction: column;">
<ion-text style="font-family: 'Montserrat'; font-size: x-large; line-height: normal;" [class.less-large]="manifest.title.length > 20">
{{ manifest.title }}
</ion-text>
<ion-text style="margin-top: -5px; margin-left: 2px;">
{{ manifest.version | displayEmver }}
</ion-text>
</div>
</ion-label>
</ion-item>
<div class="status-readout">
<status size="large" weight="bold" [pkg]="pkg" [connection]="connection"></status>
<ion-button *ngIf="status === FeStatus.NeedsConfig" expand="block" fill="outline" [routerLink]="['config']">
Configure
</ion-button>
<ion-button *ngIf="[FeStatus.Running, FeStatus.StartingUp, FeStatus.NeedsAttention] | includes : status" expand="block" fill="outline" color="danger" (click)="stop()">
Stop
</ion-button>
<ion-button *ngIf="status === FeStatus.DependencyIssue" expand="block" fill="outline" (click)="scrollToRequirements()">
Fix
</ion-button>
<ion-button *ngIf="status === FeStatus.Stopped" expand="block" fill="outline" color="success" (click)="tryStart()">
Start
</ion-button>
</div>
<ion-button size="small" *ngIf="pkg | hasUi" [disabled]="!(pkg | isLaunchable)" class="launch-button" expand="block" (click)="launchUiTab()">
Launch Web Interface
<ion-icon slot="end" name="rocket-outline"></ion-icon>
<ng-container *ngIf="pkg">
<!-- top plate -->
<div class="top-plate">
<ion-item class="no-cushion-item" lines="none">
<ion-label class="ion-text-wrap" style="
display: grid;
grid-template-columns: 80px auto;
margin: 0px;
margin-top: 15px;"
>
<ion-avatar style="justify-self: center; height: 55px; width: 55px" slot="start">
<img [src]="pkg['static-files'].icon" />
</ion-avatar>
<div style="display: flex; flex-direction: column;">
<ion-text style="font-family: 'Montserrat'; font-size: x-large; line-height: normal;" [class.less-large]="manifest.title.length > 20">
{{ manifest.title }}
</ion-text>
<ion-text style="margin-top: -5px; margin-left: 2px;">
{{ manifest.version | displayEmver }}
</ion-text>
</div>
</ion-label>
</ion-item>
<div class="status-readout">
<status *ngIf="connected" size="large" weight="bold" [pkg]="pkg"></status>
<ion-button *ngIf="pkg.status === FeStatus.NeedsConfig" expand="block" fill="outline" [routerLink]="['config']">
Configure
</ion-button>
<ion-button *ngIf="[FeStatus.Running, FeStatus.StartingUp, FeStatus.NeedsAttention] | includes : pkg.status" expand="block" fill="outline" color="danger" (click)="stop()">
Stop
</ion-button>
<ion-button *ngIf="pkg.status === FeStatus.DependencyIssue" expand="block" fill="outline" (click)="scrollToRequirements()">
Fix
</ion-button>
<ion-button *ngIf="pkg.status === FeStatus.Stopped" expand="block" fill="outline" color="success" (click)="tryStart()">
Start
</ion-button>
</div>
<ion-button size="small" *ngIf="pkg | hasUi" [disabled]="!(pkg | isLaunchable)" class="launch-button" expand="block" (click)="launchUiTab()">
Launch Web Interface
<ion-icon slot="end" name="rocket-outline"></ion-icon>
</ion-button>
</div>
<ng-container *ngIf="!([FeStatus.Installing, FeStatus.Updating, FeStatus.Removing] | includes : status)">
<ion-grid class="ion-text-center" style="margin: 0 6px;">
<ion-row>
<ion-col *ngFor="let button of buttons" sizeMd="4" sizeSm="6" sizeXs="6">
<ion-button style="width: 100%; min-height: 120px;" color="light" [disabled]="button.disabled | includes : status" (click)="button.action()">
<div>
<ion-icon size="large" [name]="button.icon"></ion-icon>
<br/><br/>
{{ button.title }}
</div>
</ion-button>
</ion-col>
</ion-row>
</ion-grid>
<ng-container *ngIf="!([FeStatus.Installing, FeStatus.Updating, FeStatus.Removing] | includes : pkg.status)">
<ion-grid class="ion-text-center" style="margin: 0 6px;">
<ion-row>
<ion-col *ngFor="let button of buttons" sizeMd="4" sizeSm="6" sizeXs="6">
<ion-button style="width: 100%; min-height: 120px;" color="light" [disabled]="button.disabled | includes : pkg.status" (click)="button.action()">
<div>
<ion-icon size="large" [name]="button.icon"></ion-icon>
<br/><br/>
{{ button.title }}
</div>
</ion-button>
</ion-col>
</ion-row>
</ion-grid>
<ion-item-group class="ion-padding-bottom">
<!-- dependencies -->
<ng-container *ngIf="!(pkg.installed['current-dependencies'] | empty)">
<ion-card id="dependencies" class="dep-card">
<ion-card-header>
<ion-card-title>Dependencies</ion-card-title>
</ion-card-header>
<ion-card-content>
<!-- A current-dependency is a subset of the manifest.dependencies that is currently required as determined by the service config. -->
<div *ngFor="let dep of pkg.installed['current-dependencies'] | keyvalue">
<ion-item *ngrxLet="patch.watch$('package-data', dep.key) as localDep" class="dependency-item">
<ion-avatar slot="start" style="position: relative; height: 5vh; width: 5vh; margin: 0px;">
<div class="dep-badge" [class]="pkg.installed.status['dependency-errors'][dep.key] ? 'dep-issue' : 'dep-sat'"></div>
<img [src]="localDep ? localDep['static-files'].icon : pkg.installed.status['dependency-errors'][dep.key]?.icon" />
</ion-avatar>
<ion-label class="ion-text-wrap" style="padding: 1vh; padding-left: 2vh">
<h4 style="font-family: 'Montserrat'">{{ localDep ? (localDep | manifest).title : pkg.installed.status['dependency-errors'][dep.key]?.title }}</h4>
<p style="font-size: small">{{ manifest.dependencies[dep.key].version | displayEmver }}</p>
<p style="padding-top: 2px; position: relative; font-style: italic; font-size: smaller"><ion-text [color]="pkg.installed.status['dependency-errors'][dep.key] ? 'warning' : 'success'">{{ pkg.installed.status['dependency-errors'][dep.key] ? pkg.installed.status['dependency-errors'][dep.key].type : 'satisfied' }}</ion-text></p>
</ion-label>
<ion-item-group class="ion-padding-bottom">
<!-- dependencies -->
<ng-container *ngIf="!(pkg.installed['current-dependencies'] | empty)">
<ion-card id="dependencies" class="dep-card">
<ion-card-header>
<ion-card-title>Dependencies</ion-card-title>
</ion-card-header>
<ion-card-content>
<!-- A current-dependency is a subset of the manifest.dependencies that is currently required as determined by the service config. -->
<div *ngFor="let dep of pkg.installed['current-dependencies'] | keyvalue">
<ion-item *ngrxLet="patch.watch$('package-data', dep.key) as localDep" class="dependency-item">
<ion-avatar slot="start" style="position: relative; height: 5vh; width: 5vh; margin: 0px;">
<div class="dep-badge" [class]="pkg.installed.status['dependency-errors'][dep.key] ? 'dep-issue' : 'dep-sat'"></div>
<img [src]="localDep ? localDep['static-files'].icon : pkg.installed.status['dependency-errors'][dep.key]?.icon" />
</ion-avatar>
<ion-label class="ion-text-wrap" style="padding: 1vh; padding-left: 2vh">
<h4 style="font-family: 'Montserrat'">{{ localDep ? (localDep | manifest).title : pkg.installed.status['dependency-errors'][dep.key]?.title }}</h4>
<p style="font-size: small">{{ manifest.dependencies[dep.key].version | displayEmver }}</p>
<p style="padding-top: 2px; position: relative; font-style: italic; font-size: smaller"><ion-text [color]="pkg.installed.status['dependency-errors'][dep.key] ? 'warning' : 'success'">{{ pkg.installed.status['dependency-errors'][dep.key] ? pkg.installed.status['dependency-errors'][dep.key].type : 'satisfied' }}</ion-text></p>
</ion-label>
<ion-button *ngIf="!pkg.installed.status['dependency-errors'][dep.key] || (pkg.installed.status['dependency-errors'][dep.key] && [DependencyErrorType.InterfaceHealthChecksFailed, DependencyErrorType.HealthChecksFailed] | includes : pkg.installed.status['dependency-errors'][dep.key].type)" slot="end" size="small" [routerLink]="['/services', dep.key]" color="primary" fill="outline" style="font-size: x-small">
View
<ion-button *ngIf="!pkg.installed.status['dependency-errors'][dep.key] || (pkg.installed.status['dependency-errors'][dep.key] && [DependencyErrorType.InterfaceHealthChecksFailed, DependencyErrorType.HealthChecksFailed] | includes : pkg.installed.status['dependency-errors'][dep.key].type)" slot="end" size="small" [routerLink]="['/services', dep.key]" color="primary" fill="outline" style="font-size: x-small">
View
</ion-button>
<ng-container *ngIf="pkg.installed.status['dependency-errors'][dep.key]">
<ion-button *ngIf="!localDep" slot="end" size="small" (click)="fixDep('install', dep.key)" color="primary" fill="outline" style="font-size: x-small">
Install
</ion-button>
<ng-container *ngIf="pkg.installed.status['dependency-errors'][dep.key]">
<ion-button *ngIf="!localDep" slot="end" size="small" (click)="fixDep('install', dep.key)" color="primary" fill="outline" style="font-size: x-small">
Install
<ng-container *ngIf="localDep && localDep.state === PackageState.Installed">
<ion-button *ngIf="pkg.installed.status['dependency-errors'][dep.key].type === DependencyErrorType.NotRunning" slot="end" size="small" [routerLink]="['/services', dep.key]" color="primary" fill="outline" style="font-size: x-small">
Start
</ion-button>
<ion-button *ngIf="pkg.installed.status['dependency-errors'][dep.key].type === DependencyErrorType.IncorrectVersion" slot="end" size="small" (click)="fixDep('update', dep.key)" color="primary" fill="outline" style="font-size: x-small">
Update
</ion-button>
<ion-button *ngIf="pkg.installed.status['dependency-errors'][dep.key].type === DependencyErrorType.ConfigUnsatisfied" slot="end" size="small" (click)="fixDep('configure', dep.key)" color="primary" fill="outline" style="font-size: x-small">
Configure
</ion-button>
</ng-container>
<ng-container *ngIf="localDep && localDep.state === PackageState.Installed">
<ion-button *ngIf="pkg.installed.status['dependency-errors'][dep.key].type === DependencyErrorType.NotRunning" slot="end" size="small" [routerLink]="['/services', dep.key]" color="primary" fill="outline" style="font-size: x-small">
Start
</ion-button>
<ion-button *ngIf="pkg.installed.status['dependency-errors'][dep.key].type === DependencyErrorType.IncorrectVersion" slot="end" size="small" (click)="fixDep('update', dep.key)" color="primary" fill="outline" style="font-size: x-small">
Update
</ion-button>
<ion-button *ngIf="pkg.installed.status['dependency-errors'][dep.key].type === DependencyErrorType.ConfigUnsatisfied" slot="end" size="small" (click)="fixDep('configure', dep.key)" color="primary" fill="outline" style="font-size: x-small">
Configure
</ion-button>
</ng-container>
<div *ngIf="localDep && localDep.state !== PackageState.Installed" slot="end" class="spinner">
<ion-spinner [color]="localDep.state === PackageState.Removing ? 'danger' : 'primary'" style="height: 3vh; width: 3vh" name="dots"></ion-spinner>
</div>
<div *ngIf="localDep && localDep.state !== PackageState.Installed" slot="end" class="spinner">
<ion-spinner [color]="localDep.state === PackageState.Removing ? 'danger' : 'primary'" style="height: 3vh; width: 3vh" name="dots"></ion-spinner>
</div>
</ng-container>
</ion-item>
</div>
</ion-card-content>
</ion-card>
</ng-container>
</ion-item-group>
</ng-container>
</ng-container>
</ion-item>
</div>
</ion-card-content>
</ion-card>
</ng-container>
</ion-item-group>
</ng-container>
</ng-container>
</ion-content>

View File

@@ -23,10 +23,12 @@ export class AppShowPage {
error: string
pkgId: string
pkg: PackageDataEntry
pkgSub: Subscription
hideLAN: boolean
buttons: Button[] = []
manifest: Manifest = { } as Manifest
connected: boolean
subs: Subscription[] = []
FeStatus = FEStatus
PackageState = PackageState
@@ -49,10 +51,13 @@ export class AppShowPage {
async ngOnInit () {
this.pkgId = this.route.snapshot.paramMap.get('pkgId')
this.pkgSub = this.patch.watch$('package-data', this.pkgId).subscribe(pkg => {
this.pkg = pkg
this.manifest = getManifest(this.pkg)
})
this.subs = [
this.patch.watch$('package-data', this.pkgId).subscribe(pkg => {
this.pkg = pkg
this.manifest = getManifest(this.pkg)
}),
this.patch.connected$().subscribe(c => this.connected = c),
]
this.setButtons()
}
@@ -61,7 +66,7 @@ export class AppShowPage {
}
async ngOnDestroy () {
this.pkgSub.unsubscribe()
this.subs.forEach(sub => sub.unsubscribe())
}
launchUiTab (): void {