mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 02:11:53 +00:00
Refactor/status info (#3066)
* refactor status info * wip fe * frontend changes and version bump * fix tests and motd * add registry workflow * better starttunnel instructions * placeholders for starttunnel tables --------- Co-authored-by: Aiden McClelland <me@drbonez.dev>
This commit is contained in:
@@ -19,7 +19,6 @@ import { PatchDB } from 'patch-db-client'
|
||||
import { filter, map } from 'rxjs'
|
||||
import { ApiService } from 'src/app/services/api/api.service'
|
||||
import { TunnelData } from 'src/app/services/patch-db/data-model'
|
||||
|
||||
import { DEVICES_ADD } from './add'
|
||||
import { DEVICES_CONFIG } from './config'
|
||||
import { MappedDevice, MappedSubnet } from './utils'
|
||||
@@ -84,6 +83,8 @@ import { MappedDevice, MappedSubnet } from './utils'
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
} @empty {
|
||||
<div class="placeholder">No devices</div>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -55,6 +55,8 @@ import { MappedDevice, MappedForward } from './utils'
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
} @empty {
|
||||
<div class="placeholder">No port forwards</div>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -67,6 +67,8 @@ import { SUBNETS_ADD } from './add'
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
} @empty {
|
||||
<div class="placeholder">No subnets</div>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -80,6 +80,12 @@ tui-dialog[new][data-appearance~='start-9'] {
|
||||
}
|
||||
}
|
||||
|
||||
.placeholder {
|
||||
padding: 1rem;
|
||||
font: var(--tui-font-text-l);
|
||||
color: var(--tui-text-tertiary);
|
||||
}
|
||||
|
||||
qr-code {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
@@ -15,7 +15,7 @@ import { getManifest } from 'src/app/utils/get-package-data'
|
||||
selector: 'service-error',
|
||||
template: `
|
||||
<header>{{ 'Service Launch Error' | i18n }}</header>
|
||||
<p class="error-message">{{ error?.message }}</p>
|
||||
<p class="error-message">{{ error?.details }}</p>
|
||||
<p>{{ error?.debug }}</p>
|
||||
<h4>
|
||||
{{ 'Actions' | i18n }}
|
||||
@@ -95,7 +95,7 @@ export class ServiceErrorComponent {
|
||||
overflow = false
|
||||
|
||||
get error() {
|
||||
return this.pkg.status.main === 'error' ? this.pkg.status : null
|
||||
return this.pkg.statusInfo.error
|
||||
}
|
||||
|
||||
rebuild() {
|
||||
@@ -108,7 +108,7 @@ export class ServiceErrorComponent {
|
||||
|
||||
show() {
|
||||
this.dialog
|
||||
.openAlert(this.error?.message as i18nKey, { label: 'Service error' })
|
||||
.openAlert(this.error?.details as i18nKey, { label: 'Service error' })
|
||||
.subscribe()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ import {
|
||||
<span class="loading-dots"></span>
|
||||
}
|
||||
|
||||
@if ($any(pkg().status)?.started; as started) {
|
||||
@if (pkg().statusInfo.started; as started) {
|
||||
<service-uptime [started]="started" />
|
||||
}
|
||||
</h3>
|
||||
|
||||
@@ -19,6 +19,7 @@ import { ServiceTasksComponent } from 'src/app/routes/portal/routes/services/com
|
||||
import { ActionService } from 'src/app/services/action.service'
|
||||
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||
import { PackageDataEntry } from 'src/app/services/patch-db/data-model'
|
||||
import { getInstalledBaseStatus } from 'src/app/services/pkg-status-rendering.service'
|
||||
import { getManifest } from 'src/app/utils/get-package-data'
|
||||
|
||||
@Component({
|
||||
@@ -161,7 +162,7 @@ export class ServiceTaskComponent {
|
||||
pkgInfo: {
|
||||
id: this.task().packageId,
|
||||
title,
|
||||
mainStatus: pkg.status.main,
|
||||
status: getInstalledBaseStatus(pkg.statusInfo),
|
||||
icon: pkg.icon,
|
||||
},
|
||||
actionInfo: { id: this.task().actionId, metadata },
|
||||
|
||||
@@ -114,11 +114,10 @@ import { distinctUntilChanged } from 'rxjs/operators'
|
||||
})
|
||||
export class ServiceUptimeComponent {
|
||||
protected readonly uptime$ = timer(0, 1000).pipe(
|
||||
map(() =>
|
||||
this.started()
|
||||
? Math.max(Date.now() - new Date(this.started()).getTime(), 0)
|
||||
: 0,
|
||||
),
|
||||
map(() => {
|
||||
const started = this.started()
|
||||
return started ? Math.max(Date.now() - new Date(started).getTime(), 0) : 0
|
||||
}),
|
||||
distinctUntilChanged(),
|
||||
map(delta => ({
|
||||
seconds: Math.floor(delta / 1000) % 60,
|
||||
@@ -128,5 +127,5 @@ export class ServiceUptimeComponent {
|
||||
})),
|
||||
)
|
||||
|
||||
readonly started = input('')
|
||||
readonly started = input<string | null>(null)
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ import { StatusComponent } from './status.component'
|
||||
></td>
|
||||
<td [style.grid-area]="'2 / 2'">{{ manifest.version }}</td>
|
||||
<td class="uptime">
|
||||
@if ($any(pkg.status)?.started; as started) {
|
||||
@if (pkg.statusInfo.started; as started) {
|
||||
<span>{{ 'Uptime' | i18n }}:</span>
|
||||
<service-uptime [started]="started" />
|
||||
} @else {
|
||||
|
||||
@@ -56,7 +56,7 @@ export class StatusComponent {
|
||||
const { primary, health } = this.getStatus(this.pkg)
|
||||
return (
|
||||
!this.hasDepErrors &&
|
||||
primary !== 'taskRequired' &&
|
||||
primary !== 'task-required' &&
|
||||
primary !== 'error' &&
|
||||
health !== 'failure'
|
||||
)
|
||||
@@ -80,7 +80,7 @@ export class StatusComponent {
|
||||
case 'updating':
|
||||
case 'stopping':
|
||||
case 'starting':
|
||||
case 'backingUp':
|
||||
case 'backing-up':
|
||||
case 'restarting':
|
||||
case 'removing':
|
||||
return true
|
||||
@@ -93,7 +93,7 @@ export class StatusComponent {
|
||||
switch (this.getStatus(this.pkg).primary) {
|
||||
case 'running':
|
||||
return 'var(--tui-status-positive)'
|
||||
case 'taskRequired':
|
||||
case 'task-required':
|
||||
return 'var(--tui-status-warning)'
|
||||
case 'error':
|
||||
return 'var(--tui-status-negative)'
|
||||
@@ -101,7 +101,7 @@ export class StatusComponent {
|
||||
case 'updating':
|
||||
case 'stopping':
|
||||
case 'starting':
|
||||
case 'backingUp':
|
||||
case 'backing-up':
|
||||
case 'restarting':
|
||||
case 'removing':
|
||||
case 'restoring':
|
||||
|
||||
@@ -11,6 +11,7 @@ import { tuiPure } from '@taiga-ui/cdk'
|
||||
import { TuiDataList, TuiDropdown, TuiButton } from '@taiga-ui/core'
|
||||
import { PackageDataEntry } from 'src/app/services/patch-db/data-model'
|
||||
import { InterfaceService } from '../../../components/interfaces/interface.service'
|
||||
import { getInstalledPrimaryStatus } from 'src/app/services/pkg-status-rendering.service'
|
||||
|
||||
@Component({
|
||||
selector: 'app-ui-launch',
|
||||
@@ -70,7 +71,7 @@ export class UILaunchComponent {
|
||||
}
|
||||
|
||||
get isRunning(): boolean {
|
||||
return this.pkg.status.main === 'running'
|
||||
return getInstalledPrimaryStatus(this.pkg) === 'running'
|
||||
}
|
||||
|
||||
@tuiPure
|
||||
|
||||
@@ -27,6 +27,7 @@ import { TaskInfoComponent } from 'src/app/routes/portal/modals/config-dep.compo
|
||||
import { ActionService } from 'src/app/services/action.service'
|
||||
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||
import { DataModel } from 'src/app/services/patch-db/data-model'
|
||||
import { BaseStatus } from 'src/app/services/pkg-status-rendering.service'
|
||||
import { getAllPackages, getManifest } from 'src/app/utils/get-package-data'
|
||||
|
||||
export type PackageActionData = {
|
||||
@@ -34,7 +35,7 @@ export type PackageActionData = {
|
||||
id: string
|
||||
title: string
|
||||
icon: string
|
||||
mainStatus: T.MainStatus['main']
|
||||
status: BaseStatus
|
||||
}
|
||||
actionInfo: {
|
||||
id: string
|
||||
|
||||
@@ -16,6 +16,10 @@ import { DataModel } from 'src/app/services/patch-db/data-model'
|
||||
import { StandardActionsService } from 'src/app/services/standard-actions.service'
|
||||
import { getManifest } from 'src/app/utils/get-package-data'
|
||||
import { ServiceActionComponent } from '../components/action.component'
|
||||
import {
|
||||
BaseStatus,
|
||||
getInstalledBaseStatus,
|
||||
} from 'src/app/services/pkg-status-rendering.service'
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
@@ -27,7 +31,7 @@ import { ServiceActionComponent } from '../components/action.component'
|
||||
<button
|
||||
tuiCell
|
||||
[action]="a"
|
||||
(click)="handle(pkg.mainStatus, pkg.icon, pkg.manifest, a)"
|
||||
(click)="handle(pkg.status, pkg.icon, pkg.manifest, a)"
|
||||
></button>
|
||||
}
|
||||
</section>
|
||||
@@ -77,7 +81,7 @@ export default class ServiceActionsRoute {
|
||||
? 'Other'
|
||||
: 'General'
|
||||
return {
|
||||
mainStatus: pkg.status.main,
|
||||
status: getInstalledBaseStatus(pkg.statusInfo),
|
||||
icon: pkg.icon,
|
||||
manifest: getManifest(pkg),
|
||||
actions: Object.entries(pkg.actions)
|
||||
@@ -131,13 +135,13 @@ export default class ServiceActionsRoute {
|
||||
}
|
||||
|
||||
handle(
|
||||
mainStatus: T.MainStatus['main'],
|
||||
status: BaseStatus,
|
||||
icon: string,
|
||||
{ id, title }: T.Manifest,
|
||||
action: T.ActionMetadata & { id: string },
|
||||
) {
|
||||
this.actions.present({
|
||||
pkgInfo: { id, title, icon, mainStatus },
|
||||
pkgInfo: { id, title, icon, status },
|
||||
actionInfo: { id: action.id, metadata: action },
|
||||
})
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
InterfaceService,
|
||||
} from '../../../components/interfaces/interface.service'
|
||||
import { GatewayService } from 'src/app/services/gateway.service'
|
||||
import { getInstalledBaseStatus } from 'src/app/services/pkg-status-rendering.service'
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
@@ -101,7 +102,8 @@ export default class ServiceInterfaceRoute {
|
||||
readonly pkg = toSignal(this.patch.watch$('packageData', this.pkgId))
|
||||
|
||||
readonly isRunning = computed(() => {
|
||||
return this.pkg()?.status.main === 'running'
|
||||
const pkg = this.pkg()
|
||||
return pkg ? getInstalledBaseStatus(pkg.statusInfo) === 'running' : false
|
||||
})
|
||||
|
||||
readonly serviceInterface = computed(() => {
|
||||
|
||||
@@ -25,7 +25,7 @@ const INACTIVE: PrimaryStatus[] = [
|
||||
'updating',
|
||||
'removing',
|
||||
'restoring',
|
||||
'backingUp',
|
||||
'backing-up',
|
||||
]
|
||||
|
||||
@Component({
|
||||
|
||||
@@ -34,7 +34,7 @@ import { ServiceUptimeComponent } from '../components/uptime.component'
|
||||
@Component({
|
||||
template: `
|
||||
@if (pkg(); as pkg) {
|
||||
@if (pkg.status.main === 'error') {
|
||||
@if (pkg.statusInfo.error) {
|
||||
<service-error [pkg]="pkg" />
|
||||
} @else if (installing()) {
|
||||
<service-install-progress [pkg]="pkg" />
|
||||
@@ -45,9 +45,9 @@ import { ServiceUptimeComponent } from '../components/uptime.component'
|
||||
}
|
||||
</service-status>
|
||||
|
||||
@if (status() !== 'backingUp') {
|
||||
@if (status() !== 'backing-up') {
|
||||
<service-health-checks [checks]="health()" />
|
||||
<service-uptime class="g-card" [started]="$any(pkg.status).started" />
|
||||
<service-uptime class="g-card" [started]="pkg.statusInfo.started" />
|
||||
<service-interfaces [pkg]="pkg" [disabled]="status() !== 'running'" />
|
||||
|
||||
@if (errors() | async; as errors) {
|
||||
@@ -179,7 +179,7 @@ export class ServiceRoute {
|
||||
protected readonly pkg = computed(() => this.services()[this.id() || ''])
|
||||
|
||||
protected readonly health = computed((pkg = this.pkg()) =>
|
||||
pkg ? toHealthCheck(pkg.status) : [],
|
||||
pkg ? toHealthCheck(pkg.statusInfo) : [],
|
||||
)
|
||||
|
||||
protected readonly status = computed((pkg = this.pkg()) =>
|
||||
@@ -202,8 +202,10 @@ export class ServiceRoute {
|
||||
)
|
||||
}
|
||||
|
||||
function toHealthCheck(status: T.MainStatus): T.NamedHealthCheckResult[] {
|
||||
return status.main !== 'running' || isEmptyObject(status.health)
|
||||
function toHealthCheck(statusInfo: T.StatusInfo): T.NamedHealthCheckResult[] {
|
||||
return statusInfo.desired.main !== 'running' ||
|
||||
!statusInfo.started ||
|
||||
isEmptyObject(statusInfo.health)
|
||||
? []
|
||||
: Object.values(status.health).filter(h => !!h)
|
||||
: Object.values(statusInfo.health).filter(h => !!h)
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ import { DataModel } from 'src/app/services/patch-db/data-model'
|
||||
<tui-icon icon="@tui.check" class="g-positive" />
|
||||
{{ 'complete' | i18n }}
|
||||
} @else {
|
||||
@if ((pkg.key | tuiMapper: toStatus | async) === 'backingUp') {
|
||||
@if ((pkg.key | tuiMapper: toStatus | async) === 'backing-up') {
|
||||
<tui-loader size="s" />
|
||||
{{ 'backing up' | i18n }}
|
||||
} @else {
|
||||
@@ -65,5 +65,5 @@ export class BackupProgressComponent {
|
||||
)
|
||||
|
||||
readonly toStatus = (pkgId: string) =>
|
||||
this.patch.watch$('packageData', pkgId, 'status', 'main')
|
||||
this.patch.watch$('packageData', pkgId, 'statusInfo', 'desired', 'main')
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ const allowedStatuses = {
|
||||
'restoring',
|
||||
'stopping',
|
||||
'starting',
|
||||
'backingUp',
|
||||
'backing-up',
|
||||
]),
|
||||
}
|
||||
|
||||
@@ -45,9 +45,7 @@ export class ActionService {
|
||||
const { pkgInfo, actionInfo } = data
|
||||
|
||||
if (
|
||||
allowedStatuses[actionInfo.metadata.allowedStatuses].has(
|
||||
pkgInfo.mainStatus,
|
||||
)
|
||||
allowedStatuses[actionInfo.metadata.allowedStatuses].has(pkgInfo.status)
|
||||
) {
|
||||
if (actionInfo.metadata.hasInput) {
|
||||
this.formDialog.open<PackageActionData>(ActionInputModal, {
|
||||
|
||||
@@ -1921,8 +1921,9 @@ export namespace Mock {
|
||||
s9pk: '/media/startos/data/package-data/archive/installed/asdfasdf.s9pk',
|
||||
icon: '/assets/img/service-icons/bitcoin-core.svg',
|
||||
lastBackup: null,
|
||||
status: {
|
||||
main: 'running',
|
||||
statusInfo: {
|
||||
error: null,
|
||||
desired: { main: 'running' },
|
||||
started: new Date().toISOString(),
|
||||
health: {},
|
||||
},
|
||||
@@ -2201,8 +2202,11 @@ export namespace Mock {
|
||||
s9pk: '/media/startos/data/package-data/archive/installed/asdfasdf.s9pk',
|
||||
icon: '/assets/img/service-icons/btc-rpc-proxy.png',
|
||||
lastBackup: null,
|
||||
status: {
|
||||
main: 'stopped',
|
||||
statusInfo: {
|
||||
desired: { main: 'stopped' },
|
||||
started: null,
|
||||
health: {},
|
||||
error: null,
|
||||
},
|
||||
actions: {},
|
||||
serviceInterfaces: {
|
||||
@@ -2246,8 +2250,11 @@ export namespace Mock {
|
||||
s9pk: '/media/startos/data/package-data/archive/installed/asdfasdf.s9pk',
|
||||
icon: '/assets/img/service-icons/lnd.png',
|
||||
lastBackup: null,
|
||||
status: {
|
||||
main: 'stopped',
|
||||
statusInfo: {
|
||||
desired: { main: 'stopped' },
|
||||
error: null,
|
||||
health: {},
|
||||
started: null,
|
||||
},
|
||||
actions: {
|
||||
config: {
|
||||
|
||||
@@ -762,12 +762,12 @@ export class MockApiService extends ApiService {
|
||||
setTimeout(async () => {
|
||||
for (let i = 0; i < ids.length; i++) {
|
||||
const id = ids[i]
|
||||
const appPath = `/packageData/${id}/status/main/`
|
||||
const appPatch: ReplaceOperation<T.MainStatus['main']>[] = [
|
||||
const appPath = `/packageData/${id}/statusInfo/desired/main`
|
||||
const appPatch: ReplaceOperation<T.DesiredStatus['main']>[] = [
|
||||
{
|
||||
op: PatchOp.REPLACE,
|
||||
path: appPath,
|
||||
value: 'backingUp',
|
||||
value: 'backing-up',
|
||||
},
|
||||
]
|
||||
this.mockRevision(appPatch)
|
||||
@@ -1073,17 +1073,18 @@ export class MockApiService extends ApiService {
|
||||
}
|
||||
|
||||
async startPackage(params: RR.StartPackageReq): Promise<RR.StartPackageRes> {
|
||||
const path = `/packageData/${params.id}/status`
|
||||
const path = `/packageData/${params.id}/statusInfo`
|
||||
|
||||
await pauseFor(2000)
|
||||
|
||||
setTimeout(async () => {
|
||||
const patch2: ReplaceOperation<T.MainStatus & { main: 'running' }>[] = [
|
||||
const patch2: ReplaceOperation<T.StatusInfo>[] = [
|
||||
{
|
||||
op: PatchOp.REPLACE,
|
||||
path,
|
||||
value: {
|
||||
main: 'running',
|
||||
error: null,
|
||||
desired: { main: 'running' },
|
||||
started: new Date().toISOString(),
|
||||
health: {
|
||||
'ephemeral-health-check': {
|
||||
@@ -1118,14 +1119,14 @@ export class MockApiService extends ApiService {
|
||||
this.mockRevision(patch2)
|
||||
}, 2000)
|
||||
|
||||
const originalPatch: ReplaceOperation<
|
||||
T.MainStatus & { main: 'starting' }
|
||||
>[] = [
|
||||
const originalPatch: ReplaceOperation<T.StatusInfo>[] = [
|
||||
{
|
||||
op: PatchOp.REPLACE,
|
||||
path,
|
||||
value: {
|
||||
main: 'starting',
|
||||
desired: { main: 'running' },
|
||||
started: null,
|
||||
error: null,
|
||||
health: {},
|
||||
},
|
||||
},
|
||||
@@ -1140,15 +1141,16 @@ export class MockApiService extends ApiService {
|
||||
params: RR.RestartPackageReq,
|
||||
): Promise<RR.RestartPackageRes> {
|
||||
await pauseFor(2000)
|
||||
const path = `/packageData/${params.id}/status`
|
||||
const path = `/packageData/${params.id}/statusInfo`
|
||||
|
||||
setTimeout(async () => {
|
||||
const patch2: ReplaceOperation<T.MainStatus & { main: 'running' }>[] = [
|
||||
const patch2: ReplaceOperation<T.StatusInfo>[] = [
|
||||
{
|
||||
op: PatchOp.REPLACE,
|
||||
path,
|
||||
value: {
|
||||
main: 'running',
|
||||
desired: { main: 'running' },
|
||||
error: null,
|
||||
started: new Date().toISOString(),
|
||||
health: {
|
||||
'ephemeral-health-check': {
|
||||
@@ -1183,12 +1185,15 @@ export class MockApiService extends ApiService {
|
||||
this.mockRevision(patch2)
|
||||
}, this.revertTime)
|
||||
|
||||
const patch: ReplaceOperation<T.MainStatus & { main: 'restarting' }>[] = [
|
||||
const patch: ReplaceOperation<T.StatusInfo>[] = [
|
||||
{
|
||||
op: PatchOp.REPLACE,
|
||||
path,
|
||||
value: {
|
||||
main: 'restarting',
|
||||
desired: { main: 'restarting' },
|
||||
started: null,
|
||||
error: null,
|
||||
health: {},
|
||||
},
|
||||
},
|
||||
]
|
||||
@@ -1200,24 +1205,34 @@ export class MockApiService extends ApiService {
|
||||
|
||||
async stopPackage(params: RR.StopPackageReq): Promise<RR.StopPackageRes> {
|
||||
await pauseFor(2000)
|
||||
const path = `/packageData/${params.id}/status`
|
||||
const path = `/packageData/${params.id}/statusInfo`
|
||||
|
||||
setTimeout(() => {
|
||||
const patch2: ReplaceOperation<T.MainStatus & { main: 'stopped' }>[] = [
|
||||
const patch2: ReplaceOperation<T.StatusInfo>[] = [
|
||||
{
|
||||
op: PatchOp.REPLACE,
|
||||
path: path,
|
||||
value: { main: 'stopped' },
|
||||
value: {
|
||||
desired: { main: 'stopped' },
|
||||
error: null,
|
||||
health: {},
|
||||
started: null,
|
||||
},
|
||||
},
|
||||
]
|
||||
this.mockRevision(patch2)
|
||||
}, this.revertTime)
|
||||
|
||||
const patch: ReplaceOperation<T.MainStatus & { main: 'stopping' }>[] = [
|
||||
const patch: ReplaceOperation<T.StatusInfo>[] = [
|
||||
{
|
||||
op: PatchOp.REPLACE,
|
||||
path: path,
|
||||
value: { main: 'stopping' },
|
||||
value: {
|
||||
desired: { main: 'stopped' },
|
||||
error: null,
|
||||
health: {},
|
||||
started: new Date().toISOString(),
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
@@ -232,8 +232,11 @@ export const mockPatchData: DataModel = {
|
||||
s9pk: '/media/startos/data/package-data/archive/installed/asdfasdf.s9pk',
|
||||
icon: '/assets/img/service-icons/bitcoin-core.svg',
|
||||
lastBackup: new Date(new Date().valueOf() - 604800001).toISOString(),
|
||||
status: {
|
||||
main: 'stopped',
|
||||
statusInfo: {
|
||||
desired: { main: 'stopped' },
|
||||
error: null,
|
||||
health: {},
|
||||
started: null,
|
||||
},
|
||||
// status: {
|
||||
// main: 'error',
|
||||
@@ -518,8 +521,11 @@ export const mockPatchData: DataModel = {
|
||||
s9pk: '/media/startos/data/package-data/archive/installed/asdfasdf.s9pk',
|
||||
icon: '/assets/img/service-icons/lnd.png',
|
||||
lastBackup: null,
|
||||
status: {
|
||||
main: 'stopped',
|
||||
statusInfo: {
|
||||
desired: { main: 'stopped' },
|
||||
error: null,
|
||||
health: {},
|
||||
started: null,
|
||||
},
|
||||
actions: {
|
||||
config: {
|
||||
|
||||
@@ -11,6 +11,7 @@ import deepEqual from 'fast-deep-equal'
|
||||
import { Observable } from 'rxjs'
|
||||
import { isInstalled } from 'src/app/utils/get-package-data'
|
||||
import { T } from '@start9labs/start-sdk'
|
||||
import { getInstalledBaseStatus } from './pkg-status-rendering.service'
|
||||
|
||||
export type AllDependencyErrors = Record<string, PkgDependencyErrors>
|
||||
export type PkgDependencyErrors = Record<string, DependencyError | null>
|
||||
@@ -153,7 +154,7 @@ export class DepErrorService {
|
||||
}
|
||||
}
|
||||
|
||||
const depStatus = dep.status.main
|
||||
const depStatus = getInstalledBaseStatus(dep.statusInfo)
|
||||
|
||||
// not running
|
||||
if (depStatus !== 'running' && depStatus !== 'starting') {
|
||||
@@ -165,7 +166,7 @@ export class DepErrorService {
|
||||
// health check failure
|
||||
if (depStatus === 'running' && currentDep?.kind === 'running') {
|
||||
for (let id of currentDep.healthChecks) {
|
||||
const check = dep.status.health[id]
|
||||
const check = dep.statusInfo.health[id]
|
||||
if (check && check?.result !== 'success') {
|
||||
return {
|
||||
type: 'healthChecksFailed',
|
||||
|
||||
@@ -13,7 +13,7 @@ export function renderPkgStatus(pkg: PackageDataEntry): PackageStatus {
|
||||
|
||||
if (pkg.stateInfo.state === 'installed') {
|
||||
primary = getInstalledPrimaryStatus(pkg)
|
||||
health = getHealthStatus(pkg.status)
|
||||
health = getHealthStatus(pkg.statusInfo)
|
||||
} else {
|
||||
primary = pkg.stateInfo.state
|
||||
}
|
||||
@@ -21,33 +21,43 @@ export function renderPkgStatus(pkg: PackageDataEntry): PackageStatus {
|
||||
return { primary, health }
|
||||
}
|
||||
|
||||
export function getInstalledPrimaryStatus({
|
||||
tasks,
|
||||
status,
|
||||
}: T.PackageDataEntry): PrimaryStatus {
|
||||
export function getInstalledBaseStatus(statusInfo: T.StatusInfo): BaseStatus {
|
||||
if (
|
||||
Object.values(tasks).some(t => t.active && t.task.severity === 'critical')
|
||||
) {
|
||||
return 'taskRequired'
|
||||
}
|
||||
|
||||
if (
|
||||
Object.values(status.main === 'running' && status.health)
|
||||
.filter(h => !!h)
|
||||
.some(h => h.result === 'starting')
|
||||
statusInfo.desired.main === 'running' &&
|
||||
(!statusInfo.started ||
|
||||
Object.values(statusInfo.health)
|
||||
.filter(h => !!h)
|
||||
.some(h => h.result === 'starting'))
|
||||
) {
|
||||
return 'starting'
|
||||
}
|
||||
|
||||
return status.main
|
||||
if (statusInfo.desired.main === 'stopped' && statusInfo.started) {
|
||||
return 'stopping'
|
||||
}
|
||||
|
||||
return statusInfo.desired.main
|
||||
}
|
||||
|
||||
function getHealthStatus(status: T.MainStatus): T.HealthStatus | null {
|
||||
if (status.main !== 'running' || !status.main) {
|
||||
export function getInstalledPrimaryStatus({
|
||||
tasks,
|
||||
statusInfo,
|
||||
}: T.PackageDataEntry): PrimaryStatus {
|
||||
if (
|
||||
Object.values(tasks).some(t => t.active && t.task.severity === 'critical')
|
||||
) {
|
||||
return 'task-required'
|
||||
}
|
||||
|
||||
return getInstalledBaseStatus(statusInfo)
|
||||
}
|
||||
|
||||
function getHealthStatus(statusInfo: T.StatusInfo): T.HealthStatus | null {
|
||||
if (statusInfo.desired.main !== 'running') {
|
||||
return null
|
||||
}
|
||||
|
||||
const values = Object.values(status.health).filter(h => !!h)
|
||||
const values = Object.values(statusInfo.health).filter(h => !!h)
|
||||
|
||||
if (values.some(h => h.result === 'failure')) {
|
||||
return 'failure'
|
||||
@@ -70,7 +80,7 @@ export interface StatusRendering {
|
||||
showDots?: boolean
|
||||
}
|
||||
|
||||
export type PrimaryStatus =
|
||||
export type BaseStatus =
|
||||
| 'installing'
|
||||
| 'updating'
|
||||
| 'removing'
|
||||
@@ -80,10 +90,11 @@ export type PrimaryStatus =
|
||||
| 'stopping'
|
||||
| 'restarting'
|
||||
| 'stopped'
|
||||
| 'backingUp'
|
||||
| 'taskRequired'
|
||||
| 'backing-up'
|
||||
| 'error'
|
||||
|
||||
export type PrimaryStatus = BaseStatus | 'task-required'
|
||||
|
||||
export type DependencyStatus = 'warning' | 'satisfied'
|
||||
|
||||
export const PrimaryRendering: Record<PrimaryStatus, StatusRendering> = {
|
||||
@@ -122,7 +133,7 @@ export const PrimaryRendering: Record<PrimaryStatus, StatusRendering> = {
|
||||
color: 'dark-shade',
|
||||
showDots: false,
|
||||
},
|
||||
backingUp: {
|
||||
'backing-up': {
|
||||
display: 'Backing Up',
|
||||
color: 'primary',
|
||||
showDots: true,
|
||||
@@ -137,7 +148,7 @@ export const PrimaryRendering: Record<PrimaryStatus, StatusRendering> = {
|
||||
color: 'success',
|
||||
showDots: false,
|
||||
},
|
||||
taskRequired: {
|
||||
'task-required': {
|
||||
display: 'Task Required',
|
||||
color: 'warning',
|
||||
showDots: false,
|
||||
|
||||
Reference in New Issue
Block a user