mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-31 04:23:40 +00:00
v0.3.4
remove health checks from manifest remove "restarting" bool on "starting" status remove restarting attr
This commit is contained in:
@@ -3,7 +3,7 @@ import { CommonModule } from '@angular/common'
|
||||
import { Routes, RouterModule } from '@angular/router'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { AppShowPage } from './app-show.page'
|
||||
import { EmverPipesModule, ResponsiveColModule } from '@start9labs/shared'
|
||||
import { EmverPipesModule, ResponsiveColModule, SharedPipesModule } from '@start9labs/shared'
|
||||
import { StatusComponentModule } from 'src/app/components/status/status.component.module'
|
||||
import { AppConfigPageModule } from 'src/app/modals/app-config/app-config.module'
|
||||
import { LaunchablePipeModule } from 'src/app/pipes/launchable/launchable.module'
|
||||
@@ -16,7 +16,6 @@ import { AppShowMenuComponent } from './components/app-show-menu/app-show-menu.c
|
||||
import { AppShowHealthChecksComponent } from './components/app-show-health-checks/app-show-health-checks.component'
|
||||
import { AppShowAdditionalComponent } from './components/app-show-additional/app-show-additional.component'
|
||||
import { HealthColorPipe } from './pipes/health-color.pipe'
|
||||
import { ToHealthChecksPipe } from './pipes/to-health-checks.pipe'
|
||||
import { ToButtonsPipe } from './pipes/to-buttons.pipe'
|
||||
import { ToDependenciesPipe } from './pipes/to-dependencies.pipe'
|
||||
import { ToStatusPipe } from './pipes/to-status.pipe'
|
||||
@@ -34,7 +33,6 @@ const routes: Routes = [
|
||||
AppShowPage,
|
||||
HealthColorPipe,
|
||||
ProgressDataPipe,
|
||||
ToHealthChecksPipe,
|
||||
ToButtonsPipe,
|
||||
ToDependenciesPipe,
|
||||
ToStatusPipe,
|
||||
@@ -56,6 +54,7 @@ const routes: Routes = [
|
||||
LaunchablePipeModule,
|
||||
UiPipeModule,
|
||||
ResponsiveColModule,
|
||||
SharedPipesModule,
|
||||
],
|
||||
})
|
||||
export class AppShowPageModule {}
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
<!-- ** health checks ** -->
|
||||
<app-show-health-checks
|
||||
*ngIf="isRunning(status)"
|
||||
[pkg]="pkg"
|
||||
[pkgId]="pkgId"
|
||||
></app-show-health-checks>
|
||||
<!-- ** dependencies ** -->
|
||||
<app-show-dependencies
|
||||
|
||||
@@ -31,7 +31,7 @@ const STATES = [
|
||||
export class AppShowPage {
|
||||
readonly secure = this.config.isSecure()
|
||||
|
||||
private readonly pkgId = getPkgId(this.route)
|
||||
readonly pkgId = getPkgId(this.route)
|
||||
|
||||
readonly pkg$ = this.patch.watch$('package-data', this.pkgId).pipe(
|
||||
tap(pkg => {
|
||||
|
||||
@@ -1,92 +1,82 @@
|
||||
<ng-container
|
||||
*ngIf="pkg | toHealthChecks | async | keyvalue: asIsOrder as checks"
|
||||
>
|
||||
<ng-container *ngIf="checks.length">
|
||||
<ion-item-divider>Health Checks</ion-item-divider>
|
||||
<!-- connected -->
|
||||
<ng-container *ngIf="connected$ | async; else disconnected">
|
||||
<ion-item *ngFor="let health of checks">
|
||||
<!-- result -->
|
||||
<ng-container *ngIf="health.value?.result as result; else noResult">
|
||||
<ion-spinner
|
||||
*ngIf="isLoading(result)"
|
||||
class="icon-spinner"
|
||||
color="primary"
|
||||
slot="start"
|
||||
></ion-spinner>
|
||||
<ion-icon
|
||||
*ngIf="result === HealthResult.Success"
|
||||
slot="start"
|
||||
name="checkmark"
|
||||
color="success"
|
||||
></ion-icon>
|
||||
<ion-icon
|
||||
*ngIf="result === HealthResult.Failure"
|
||||
slot="start"
|
||||
name="warning-outline"
|
||||
color="warning"
|
||||
></ion-icon>
|
||||
<ion-icon
|
||||
*ngIf="result === HealthResult.Disabled"
|
||||
slot="start"
|
||||
name="remove"
|
||||
color="dark"
|
||||
></ion-icon>
|
||||
<ion-label>
|
||||
<h2 class="bold">
|
||||
{{ pkg.manifest['health-checks'][health.key].name }}
|
||||
</h2>
|
||||
<ion-text [color]="result | healthColor">
|
||||
<p>
|
||||
<span *ngIf="isReady(result)">{{ result | titlecase }}</span>
|
||||
<span *ngIf="result === HealthResult.Starting">...</span>
|
||||
<span *ngIf="result === HealthResult.Failure">
|
||||
{{ $any(health.value).error }}
|
||||
</span>
|
||||
<span *ngIf="result === HealthResult.Loading">
|
||||
{{ $any(health.value).message }}
|
||||
</span>
|
||||
<span
|
||||
*ngIf="
|
||||
result === HealthResult.Success &&
|
||||
pkg.manifest['health-checks'][health.key]['success-message']
|
||||
"
|
||||
>:
|
||||
{{
|
||||
pkg.manifest['health-checks'][health.key]['success-message']
|
||||
}}
|
||||
</span>
|
||||
</p>
|
||||
</ion-text>
|
||||
</ion-label>
|
||||
</ng-container>
|
||||
<!-- no result -->
|
||||
<ng-template #noResult>
|
||||
<ion-spinner
|
||||
class="icon-spinner"
|
||||
color="dark"
|
||||
slot="start"
|
||||
></ion-spinner>
|
||||
<ion-label>
|
||||
<h2 class="bold">
|
||||
{{ pkg.manifest['health-checks'][health.key].name }}
|
||||
</h2>
|
||||
<p class="primary">Awaiting result...</p>
|
||||
</ion-label>
|
||||
</ng-template>
|
||||
</ion-item>
|
||||
</ng-container>
|
||||
<!-- disconnected -->
|
||||
<ng-template #disconnected>
|
||||
<ion-item *ngFor="let health of checks">
|
||||
<ion-avatar slot="start">
|
||||
<ion-skeleton-text class="avatar"></ion-skeleton-text>
|
||||
</ion-avatar>
|
||||
<ng-container *ngIf="healthChecks$ | async as checks">
|
||||
<ion-item-divider>Health Checks</ion-item-divider>
|
||||
<!-- connected -->
|
||||
<ng-container *ngIf="connected$ | async; else disconnected">
|
||||
<ion-item *ngFor="let check of checks">
|
||||
<!-- result -->
|
||||
<ng-container *ngIf="check.result as result; else noResult">
|
||||
<ion-spinner
|
||||
*ngIf="isLoading(result)"
|
||||
class="icon-spinner"
|
||||
color="primary"
|
||||
slot="start"
|
||||
></ion-spinner>
|
||||
<ion-icon
|
||||
*ngIf="result === 'success'"
|
||||
slot="start"
|
||||
name="checkmark"
|
||||
color="success"
|
||||
></ion-icon>
|
||||
<ion-icon
|
||||
*ngIf="result === 'failure'"
|
||||
slot="start"
|
||||
name="warning-outline"
|
||||
color="warning"
|
||||
></ion-icon>
|
||||
<ion-icon
|
||||
*ngIf="result === 'disabled'"
|
||||
slot="start"
|
||||
name="remove"
|
||||
color="dark"
|
||||
></ion-icon>
|
||||
<ion-label>
|
||||
<ion-skeleton-text class="label"></ion-skeleton-text>
|
||||
<ion-skeleton-text class="description"></ion-skeleton-text>
|
||||
<h2 class="bold">
|
||||
{{ check.name }}
|
||||
</h2>
|
||||
<ion-text [color]="result | healthColor">
|
||||
<p>
|
||||
<span *ngIf="isReady(result)">{{ result | titlecase }}</span>
|
||||
<span *ngIf="result === 'starting'">...</span>
|
||||
<span *ngIf="result === 'failure'">
|
||||
{{ $any(check).error }}
|
||||
</span>
|
||||
<span *ngIf="result === 'loading'">
|
||||
{{ $any(check).message }}
|
||||
</span>
|
||||
<span *ngIf="result === 'success'"
|
||||
>:
|
||||
{{ $any(check).message }}
|
||||
</span>
|
||||
</p>
|
||||
</ion-text>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
<!-- no result -->
|
||||
<ng-template #noResult>
|
||||
<ion-spinner
|
||||
class="icon-spinner"
|
||||
color="dark"
|
||||
slot="start"
|
||||
></ion-spinner>
|
||||
<ion-label>
|
||||
<h2 class="bold">
|
||||
{{ check.name }}
|
||||
</h2>
|
||||
<p class="primary">Awaiting result...</p>
|
||||
</ion-label>
|
||||
</ng-template>
|
||||
</ion-item>
|
||||
</ng-container>
|
||||
<!-- disconnected -->
|
||||
<ng-template #disconnected>
|
||||
<ion-item *ngFor="let check of checks">
|
||||
<ion-avatar slot="start">
|
||||
<ion-skeleton-text class="avatar"></ion-skeleton-text>
|
||||
</ion-avatar>
|
||||
<ion-label>
|
||||
<ion-skeleton-text class="label"></ion-skeleton-text>
|
||||
<ion-skeleton-text class="description"></ion-skeleton-text>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core'
|
||||
import { PatchDB } from 'patch-db-client'
|
||||
import { map } from 'rxjs'
|
||||
import { ConnectionService } from 'src/app/services/connection.service'
|
||||
import {
|
||||
HealthResult,
|
||||
PackageDataEntry,
|
||||
} from 'src/app/services/patch-db/data-model'
|
||||
import { DataModel, HealthResult } from 'src/app/services/patch-db/data-model'
|
||||
import { isEmptyObject } from '@start9labs/shared'
|
||||
|
||||
@Component({
|
||||
selector: 'app-show-health-checks',
|
||||
@@ -12,14 +12,26 @@ import {
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class AppShowHealthChecksComponent {
|
||||
@Input()
|
||||
pkg!: PackageDataEntry
|
||||
|
||||
HealthResult = HealthResult
|
||||
@Input() pkgId!: string
|
||||
|
||||
readonly connected$ = this.connectionService.connected$
|
||||
|
||||
constructor(private readonly connectionService: ConnectionService) {}
|
||||
get healthChecks$() {
|
||||
return this.patch
|
||||
.watch$('package-data', this.pkgId, 'installed', 'status', 'main')
|
||||
.pipe(
|
||||
map(main => {
|
||||
if (main.status !== 'running' || isEmptyObject(main.health))
|
||||
return null
|
||||
return Object.values(main.health)
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
constructor(
|
||||
private readonly connectionService: ConnectionService,
|
||||
private readonly patch: PatchDB<DataModel>,
|
||||
) {}
|
||||
|
||||
isLoading(result: HealthResult): boolean {
|
||||
return result === HealthResult.Starting || result === HealthResult.Loading
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core'
|
||||
import {
|
||||
DataModel,
|
||||
HealthCheckResult,
|
||||
PackageDataEntry,
|
||||
PackageMainStatus,
|
||||
} from 'src/app/services/patch-db/data-model'
|
||||
import { isEmptyObject } from '@start9labs/shared'
|
||||
import { map, startWith } from 'rxjs/operators'
|
||||
import { PatchDB } from 'patch-db-client'
|
||||
import { Observable } from 'rxjs'
|
||||
|
||||
@Pipe({
|
||||
name: 'toHealthChecks',
|
||||
})
|
||||
export class ToHealthChecksPipe implements PipeTransform {
|
||||
constructor(private readonly patch: PatchDB<DataModel>) {}
|
||||
|
||||
transform(
|
||||
pkg: PackageDataEntry,
|
||||
): Observable<Record<string, HealthCheckResult | null>> | null {
|
||||
const healthChecks = Object.keys(pkg.manifest['health-checks']).reduce(
|
||||
(obj, key) => ({ ...obj, [key]: null }),
|
||||
{},
|
||||
)
|
||||
|
||||
const healthChecks$ = this.patch
|
||||
.watch$('package-data', pkg.manifest.id, 'installed', 'status', 'main')
|
||||
.pipe(
|
||||
map(main => {
|
||||
// Question: is this ok or do we have to use Object.keys
|
||||
// to maintain order and the keys initially present in pkg?
|
||||
return main.status === PackageMainStatus.Running &&
|
||||
!isEmptyObject(main.health)
|
||||
? main.health
|
||||
: healthChecks
|
||||
}),
|
||||
startWith(healthChecks),
|
||||
)
|
||||
|
||||
return isEmptyObject(healthChecks) ? null : healthChecks$
|
||||
}
|
||||
}
|
||||
@@ -87,7 +87,6 @@ export module Mock {
|
||||
'shm-size': '',
|
||||
'sigterm-timeout': '1ms',
|
||||
},
|
||||
'health-checks': {},
|
||||
config: {
|
||||
get: null,
|
||||
set: null,
|
||||
@@ -382,7 +381,6 @@ export module Mock {
|
||||
'shm-size': '',
|
||||
'sigterm-timeout': '10000µs',
|
||||
},
|
||||
'health-checks': {},
|
||||
config: {
|
||||
get: null,
|
||||
set: null,
|
||||
@@ -535,7 +533,6 @@ export module Mock {
|
||||
'shm-size': '',
|
||||
'sigterm-timeout': '1m',
|
||||
},
|
||||
'health-checks': {},
|
||||
config: { get: {} as any, set: {} as any },
|
||||
volumes: {},
|
||||
'min-os-version': '0.2.12',
|
||||
|
||||
@@ -727,7 +727,18 @@ export class MockApiService extends ApiService {
|
||||
await pauseFor(2000)
|
||||
|
||||
setTimeout(async () => {
|
||||
const patch2 = [
|
||||
if (params.id !== 'bitcoind') {
|
||||
const patch2 = [
|
||||
{
|
||||
op: PatchOp.REPLACE,
|
||||
path: path + '/health',
|
||||
value: {},
|
||||
},
|
||||
]
|
||||
this.mockRevision(patch2)
|
||||
}
|
||||
|
||||
const patch3 = [
|
||||
{
|
||||
op: PatchOp.REPLACE,
|
||||
path: path + '/status',
|
||||
@@ -739,52 +750,7 @@ export class MockApiService extends ApiService {
|
||||
value: new Date().toISOString(),
|
||||
},
|
||||
]
|
||||
this.mockRevision(patch2)
|
||||
|
||||
const patch3 = [
|
||||
{
|
||||
op: PatchOp.REPLACE,
|
||||
path: path + '/health',
|
||||
value: {
|
||||
'ephemeral-health-check': {
|
||||
result: 'starting',
|
||||
},
|
||||
'unnecessary-health-check': {
|
||||
result: 'disabled',
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
this.mockRevision(patch3)
|
||||
|
||||
await pauseFor(2000)
|
||||
|
||||
const patch4 = [
|
||||
{
|
||||
op: PatchOp.REPLACE,
|
||||
path: path + '/health',
|
||||
value: {
|
||||
'ephemeral-health-check': {
|
||||
result: 'starting',
|
||||
},
|
||||
'unnecessary-health-check': {
|
||||
result: 'disabled',
|
||||
},
|
||||
'chain-state': {
|
||||
result: 'loading',
|
||||
message: 'Bitcoin is syncing from genesis',
|
||||
},
|
||||
'p2p-interface': {
|
||||
result: 'success',
|
||||
},
|
||||
'rpc-interface': {
|
||||
result: 'failure',
|
||||
error: 'RPC interface unreachable.',
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
this.mockRevision(patch4)
|
||||
}, 2000)
|
||||
|
||||
const originalPatch = [
|
||||
@@ -896,11 +862,6 @@ export class MockApiService extends ApiService {
|
||||
path: path + '/status',
|
||||
value: PackageMainStatus.Stopping,
|
||||
},
|
||||
{
|
||||
op: PatchOp.REPLACE,
|
||||
path: path + '/health',
|
||||
value: {},
|
||||
},
|
||||
]
|
||||
|
||||
return this.withRevision(patch)
|
||||
|
||||
@@ -127,24 +127,6 @@ export const mockPatchData: DataModel = {
|
||||
'shm-size': '',
|
||||
'sigterm-timeout': '.49m',
|
||||
},
|
||||
'health-checks': {
|
||||
'chain-state': {
|
||||
name: 'Chain State',
|
||||
},
|
||||
'ephemeral-health-check': {
|
||||
name: 'Ephemeral Health Check',
|
||||
},
|
||||
'p2p-interface': {
|
||||
name: 'P2P Interface',
|
||||
'success-message': 'the health check ran succesfully',
|
||||
},
|
||||
'rpc-interface': {
|
||||
name: 'RPC Interface',
|
||||
},
|
||||
'unnecessary-health-check': {
|
||||
name: 'Unneccessary Health Check',
|
||||
},
|
||||
} as any,
|
||||
config: {
|
||||
get: {},
|
||||
set: {},
|
||||
@@ -243,9 +225,12 @@ export const mockPatchData: DataModel = {
|
||||
'Your reason for re-syncing. Why are you doing this?',
|
||||
nullable: false,
|
||||
masked: false,
|
||||
copyable: false,
|
||||
pattern: '^[a-zA-Z]+$',
|
||||
'pattern-description': 'Must contain only letters.',
|
||||
placeholder: null,
|
||||
textarea: false,
|
||||
warning: null,
|
||||
default: null,
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
@@ -253,14 +238,19 @@ export const mockPatchData: DataModel = {
|
||||
description: 'Tell the class your name.',
|
||||
nullable: true,
|
||||
masked: false,
|
||||
copyable: false,
|
||||
warning: 'You may loose all your money by providing your name.',
|
||||
placeholder: null,
|
||||
pattern: null,
|
||||
'pattern-description': null,
|
||||
textarea: false,
|
||||
default: null,
|
||||
},
|
||||
notifications: {
|
||||
name: 'Notification Preferences',
|
||||
type: 'list',
|
||||
subtype: 'enum',
|
||||
description: 'how you want to be notified',
|
||||
warning: null,
|
||||
range: '[1,3]',
|
||||
default: ['email'],
|
||||
spec: {
|
||||
@@ -282,6 +272,9 @@ export const mockPatchData: DataModel = {
|
||||
default: 100,
|
||||
range: '[0, 9999]',
|
||||
integral: true,
|
||||
units: null,
|
||||
placeholder: null,
|
||||
warning: null,
|
||||
},
|
||||
'top-speed': {
|
||||
type: 'number',
|
||||
@@ -291,6 +284,9 @@ export const mockPatchData: DataModel = {
|
||||
range: '[-1000, 1000]',
|
||||
integral: false,
|
||||
units: 'm/s',
|
||||
placeholder: null,
|
||||
warning: null,
|
||||
default: null,
|
||||
},
|
||||
testnet: {
|
||||
name: 'Testnet',
|
||||
@@ -318,22 +314,33 @@ export const mockPatchData: DataModel = {
|
||||
name: 'Emergency Contact',
|
||||
type: 'object',
|
||||
description: 'The person to contact in case of emergency.',
|
||||
warning: null,
|
||||
spec: {
|
||||
name: {
|
||||
type: 'string',
|
||||
name: 'Name',
|
||||
description: null,
|
||||
nullable: false,
|
||||
masked: false,
|
||||
copyable: false,
|
||||
pattern: '^[a-zA-Z]+$',
|
||||
'pattern-description': 'Must contain only letters.',
|
||||
placeholder: null,
|
||||
textarea: false,
|
||||
warning: null,
|
||||
default: null,
|
||||
},
|
||||
email: {
|
||||
type: 'string',
|
||||
name: 'Email',
|
||||
description: null,
|
||||
nullable: false,
|
||||
masked: false,
|
||||
copyable: true,
|
||||
placeholder: null,
|
||||
pattern: null,
|
||||
'pattern-description': null,
|
||||
textarea: false,
|
||||
warning: null,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -351,7 +358,7 @@ export const mockPatchData: DataModel = {
|
||||
pattern: '^[0-9]{1,3}([,.][0-9]{1,3})?$',
|
||||
'pattern-description': 'Must be a valid IP address',
|
||||
masked: false,
|
||||
copyable: false,
|
||||
placeholder: null,
|
||||
},
|
||||
},
|
||||
bitcoinNode: {
|
||||
@@ -375,7 +382,12 @@ export const mockPatchData: DataModel = {
|
||||
description: 'the lan address',
|
||||
nullable: true,
|
||||
masked: false,
|
||||
copyable: false,
|
||||
placeholder: null,
|
||||
pattern: null,
|
||||
'pattern-description': null,
|
||||
textarea: false,
|
||||
warning: null,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
external: {
|
||||
@@ -388,7 +400,9 @@ export const mockPatchData: DataModel = {
|
||||
pattern: '.*',
|
||||
'pattern-description': 'anything',
|
||||
masked: false,
|
||||
copyable: true,
|
||||
placeholder: null,
|
||||
textarea: false,
|
||||
warning: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -411,20 +425,26 @@ export const mockPatchData: DataModel = {
|
||||
started: '2021-06-14T20:49:17.774Z',
|
||||
health: {
|
||||
'ephemeral-health-check': {
|
||||
name: 'Ephemeral Health Check',
|
||||
result: HealthResult.Starting,
|
||||
},
|
||||
'chain-state': {
|
||||
name: 'Chain State',
|
||||
result: HealthResult.Loading,
|
||||
message: 'Bitcoin is syncing from genesis',
|
||||
},
|
||||
'p2p-interface': {
|
||||
name: 'P2P Interface',
|
||||
result: HealthResult.Success,
|
||||
message: 'the health check ran successfully',
|
||||
},
|
||||
'rpc-interface': {
|
||||
name: 'RPC Interface',
|
||||
result: HealthResult.Failure,
|
||||
error: 'RPC interface unreachable.',
|
||||
},
|
||||
'unnecessary-health-check': {
|
||||
name: 'Totally Unnecessary',
|
||||
result: HealthResult.Disabled,
|
||||
},
|
||||
},
|
||||
@@ -461,7 +481,7 @@ export const mockPatchData: DataModel = {
|
||||
manifest: {
|
||||
id: 'lnd',
|
||||
title: 'Lightning Network Daemon',
|
||||
version: '0.11.0',
|
||||
version: '0.11.1',
|
||||
description: {
|
||||
short: 'A bolt spec compliant client.',
|
||||
long: 'More info about LND. More info about LND. More info about LND.',
|
||||
@@ -501,7 +521,6 @@ export const mockPatchData: DataModel = {
|
||||
'shm-size': '',
|
||||
'sigterm-timeout': '0.5s',
|
||||
},
|
||||
'health-checks': {},
|
||||
config: {
|
||||
get: null,
|
||||
set: null,
|
||||
|
||||
@@ -155,10 +155,6 @@ export interface Manifest extends MarketplaceManifest<DependencyConfig | null> {
|
||||
scripts: string // path to scripts folder
|
||||
}
|
||||
main: ActionImpl
|
||||
'health-checks': Record<
|
||||
string,
|
||||
ActionImpl & { name: string; 'success-message': string | null }
|
||||
>
|
||||
config: ConfigActions | null
|
||||
volumes: Record<string, Volume>
|
||||
'min-os-version': string
|
||||
@@ -295,7 +291,6 @@ export interface MainStatusStopping {
|
||||
|
||||
export interface MainStatusStarting {
|
||||
status: PackageMainStatus.Starting
|
||||
restarting: boolean
|
||||
}
|
||||
|
||||
export interface MainStatusRunning {
|
||||
@@ -322,12 +317,13 @@ export enum PackageMainStatus {
|
||||
Restarting = 'restarting',
|
||||
}
|
||||
|
||||
export type HealthCheckResult =
|
||||
export type HealthCheckResult = { name: string } & (
|
||||
| HealthCheckResultStarting
|
||||
| HealthCheckResultLoading
|
||||
| HealthCheckResultDisabled
|
||||
| HealthCheckResultSuccess
|
||||
| HealthCheckResultFailure
|
||||
)
|
||||
|
||||
export enum HealthResult {
|
||||
Starting = 'starting',
|
||||
@@ -347,6 +343,7 @@ export interface HealthCheckResultDisabled {
|
||||
|
||||
export interface HealthCheckResultSuccess {
|
||||
result: HealthResult.Success
|
||||
message: string
|
||||
}
|
||||
|
||||
export interface HealthCheckResultLoading {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { isEmptyObject } from '@start9labs/shared'
|
||||
import {
|
||||
InstalledPackageDataEntry,
|
||||
MainStatusStarting,
|
||||
PackageDataEntry,
|
||||
PackageMainStatus,
|
||||
@@ -8,42 +9,39 @@ import {
|
||||
} from 'src/app/services/patch-db/data-model'
|
||||
|
||||
export interface PackageStatus {
|
||||
primary: PrimaryStatus
|
||||
primary: PrimaryStatus | PackageState | PackageMainStatus
|
||||
dependency: DependencyStatus | null
|
||||
health: HealthStatus | null
|
||||
}
|
||||
|
||||
export function renderPkgStatus(pkg: PackageDataEntry): PackageStatus {
|
||||
let primary: PrimaryStatus
|
||||
let primary: PrimaryStatus | PackageState | PackageMainStatus
|
||||
let dependency: DependencyStatus | null = null
|
||||
let health: HealthStatus | null = null
|
||||
const hasHealthChecks = !isEmptyObject(pkg.manifest['health-checks'])
|
||||
|
||||
if (pkg.state === PackageState.Installed && pkg.installed) {
|
||||
primary = getPrimaryStatus(pkg.installed.status)
|
||||
dependency = getDependencyStatus(pkg)
|
||||
health = getHealthStatus(pkg.installed.status, hasHealthChecks)
|
||||
dependency = getDependencyStatus(pkg.installed)
|
||||
health = getHealthStatus(pkg.installed.status)
|
||||
} else {
|
||||
primary = pkg.state as string as PrimaryStatus
|
||||
primary = pkg.state
|
||||
}
|
||||
|
||||
return { primary, dependency, health }
|
||||
}
|
||||
|
||||
function getPrimaryStatus(status: Status): PrimaryStatus {
|
||||
function getPrimaryStatus(status: Status): PrimaryStatus | PackageMainStatus {
|
||||
if (!status.configured) {
|
||||
return PrimaryStatus.NeedsConfig
|
||||
} else if ((status.main as MainStatusStarting).restarting) {
|
||||
return PrimaryStatus.Restarting
|
||||
} else {
|
||||
return status.main.status as any as PrimaryStatus
|
||||
return status.main.status
|
||||
}
|
||||
}
|
||||
|
||||
function getDependencyStatus(pkg: PackageDataEntry): DependencyStatus | null {
|
||||
const installed = pkg.installed
|
||||
if (!installed || isEmptyObject(installed['current-dependencies']))
|
||||
return null
|
||||
function getDependencyStatus(
|
||||
installed: InstalledPackageDataEntry,
|
||||
): DependencyStatus | null {
|
||||
if (isEmptyObject(installed['current-dependencies'])) return null
|
||||
|
||||
const depErrors = installed.status['dependency-errors']
|
||||
const depIds = Object.keys(depErrors).filter(key => !!depErrors[key])
|
||||
@@ -51,11 +49,8 @@ function getDependencyStatus(pkg: PackageDataEntry): DependencyStatus | null {
|
||||
return depIds.length ? DependencyStatus.Warning : DependencyStatus.Satisfied
|
||||
}
|
||||
|
||||
function getHealthStatus(
|
||||
status: Status,
|
||||
hasHealthChecks: boolean,
|
||||
): HealthStatus | null {
|
||||
if (status.main.status !== PackageMainStatus.Running || !status.main.health) {
|
||||
function getHealthStatus(status: Status): HealthStatus | null {
|
||||
if (status.main.status !== PackageMainStatus.Running) {
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -65,10 +60,6 @@ function getHealthStatus(
|
||||
return HealthStatus.Failure
|
||||
}
|
||||
|
||||
if (!values.length && hasHealthChecks) {
|
||||
return HealthStatus.Waiting
|
||||
}
|
||||
|
||||
if (values.some(h => h.result === 'loading')) {
|
||||
return HealthStatus.Loading
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user