From 3b669193f6060ee5b2d1bc50d33b6ba152ea85c5 Mon Sep 17 00:00:00 2001 From: Matt Hill Date: Sun, 24 Mar 2024 12:12:55 -0600 Subject: [PATCH] refactor downstream for 036 changes (#2577) refactor codebase for 036 changes --- .../install-wizard/src/app/app.module.ts | 2 - .../release-notes.component.html | 4 +- .../show/additional/additional.component.ts | 1 - .../src/pages/show/hero/hero.component.ts | 7 +- .../src/app/components/cifs.component.ts | 2 +- .../src/app/components/password.component.ts | 2 +- .../src/app/components/recover.component.ts | 4 +- .../setup-wizard/src/app/pages/home.page.ts | 4 +- .../src/app/pages/recover.page.ts | 2 +- .../src/app/pages/success.page.ts | 6 +- .../src/app/services/mock-api.service.ts | 2 +- .../src/directives/drag-scroller.directive.ts | 1 - .../shared/src/mocks/get-setup-status.ts | 4 +- .../shared/src/services/setup.service.ts | 4 +- .../src/app/apps/diagnostic/logs/logs.page.ts | 2 +- .../apps/portal/components/card.component.ts | 270 +++++++++--------- .../apps/portal/components/form.component.ts | 2 +- .../components/header/about.component.ts | 6 +- .../components/header/connection.component.ts | 6 +- .../components/header/header.component.ts | 2 +- .../header/notifications.component.ts | 10 +- .../interface-addresses.component.ts | 87 ++++++ .../interface-clearnet.component.ts | 46 ++- .../interfaces/interface-local.component.ts | 67 ++--- .../interfaces/interface-tor.component.ts | 51 +++- .../interfaces/interface.component.ts | 100 +++---- .../components/interfaces/interface.utils.ts | 100 ++++++- .../interfaces/interfaces.component.ts | 55 ---- .../components/logs/logs-fetch.directive.ts | 2 +- .../apps/portal/components/logs/logs.pipe.ts | 2 +- .../apps/portal/modals/config.component.ts | 41 +-- .../ui/src/app/apps/portal/pipes/to-badge.ts | 15 - .../src/app/apps/portal/pipes/to-manifest.ts | 14 + .../routes/dashboard/controls.component.ts | 66 ++--- .../routes/dashboard/service.component.ts | 37 ++- .../routes/dashboard/services.component.ts | 10 +- .../routes/dashboard/status.component.ts | 36 +-- .../portal/routes/dashboard/ui.component.ts | 43 +-- .../service/components/actions.component.ts | 35 +-- .../components/additional.component.ts | 4 +- .../components/health-check.component.ts | 17 +- ...nt.ts => interface-list-item.component.ts} | 6 +- .../components/interface-list.component.ts | 36 +++ .../components/interfaces.component.ts | 35 --- .../service/components/menu.component.ts | 19 +- .../service/components/progress.component.ts | 29 +- ...ial.component.ts => property.component.ts} | 4 +- .../service/components/status.component.ts | 25 +- ...s.component.ts => properties.component.ts} | 22 +- .../service/pipes/group-actions.pipe.ts | 7 +- .../service/pipes/install-progress.pipe.ts | 2 +- .../service/pipes/interface-info.pipe.ts | 17 +- .../service/pipes/to-additional.pipe.ts | 20 +- .../routes/service/pipes/to-menu.pipe.ts | 39 +-- .../service/routes/actions.component.ts | 28 +- .../service/routes/interface.component.ts | 31 +- .../routes/service/routes/outlet.component.ts | 2 +- .../service/routes/service.component.ts | 164 +++++------ .../backups/components/status.component.ts | 4 +- .../backups/components/upcoming.component.ts | 6 +- .../system/backups/modals/backup.component.ts | 20 +- .../system/backups/modals/edit.component.ts | 8 +- .../backups/modals/history.component.ts | 8 +- .../system/backups/modals/jobs.component.ts | 10 +- .../backups/modals/recover.component.ts | 20 +- .../system/backups/modals/target.component.ts | 2 +- .../backups/modals/targets.component.ts | 12 +- .../system/backups/pipes/to-options.pipe.ts | 2 +- .../system/backups/services/create.service.ts | 2 +- .../backups/services/restore.service.ts | 4 +- .../system/backups/types/backup-config.ts | 2 +- .../system/backups/types/recover-option.ts | 2 +- .../routes/system/backups/types/target.ts | 6 +- .../system/backups/utils/job-builder.ts | 12 +- .../components/controls.component.ts | 28 +- .../components/notification.component.ts | 1 - .../marketplace/modals/registry.component.ts | 2 +- .../system/marketplace/pipes/to-local.pipe.ts | 2 +- .../marketplace/services/alerts.service.ts | 4 +- .../system/marketplace/utils/registry.ts | 2 +- .../system/notifications/item.component.ts | 7 +- .../settings/components/menu.component.ts | 14 +- .../settings/modals/update.component.ts | 2 +- .../settings/routes/domains/constants.ts | 40 +-- .../routes/domains/domains.component.ts | 20 +- .../settings/routes/email/email.component.ts | 6 +- .../experimental/experimental.component.ts | 31 -- .../routes/interfaces/interfaces.component.ts | 22 -- .../routes/interfaces/ui.component.ts | 70 +++++ .../settings/routes/proxies/constants.ts | 10 +- .../settings/routes/proxies/menu.component.ts | 47 +-- .../routes/proxies/proxies.component.ts | 4 +- .../routes/proxies/table.component.ts | 16 +- .../settings/routes/router/primary-ip.pipe.ts | 12 +- .../routes/router/router.component.ts | 4 +- .../settings/routes/router/table.component.ts | 1 - .../routes/sessions/sessions.component.ts | 4 +- .../routes/sessions/table.component.ts | 4 +- .../settings/routes/ssh/table.component.ts | 2 +- .../system/settings/routes/wifi/utils.ts | 4 +- .../settings/routes/wifi/wifi.component.ts | 2 +- .../system/settings/routes/wifi/wifi.const.ts | 2 +- .../routes/system/settings/settings.routes.ts | 13 +- .../system/settings/settings.service.ts | 60 +++- .../routes/system/settings/settings.types.ts | 4 +- .../system/sideload/package.component.ts | 5 +- .../routes/system/sideload/sideload.utils.ts | 6 +- .../updates/components/item.component.ts | 46 +-- .../updates/pipes/filter-updates.pipe.ts | 11 +- .../updates/pipes/install-progress.pipe.ts | 16 -- .../system/updates/updates.component.ts | 27 +- .../apps/portal/services/actions.service.ts | 30 +- .../app/apps/portal/services/badge.service.ts | 21 +- .../portal/services/breadcrumbs.service.ts | 10 +- .../portal/services/notification.service.ts | 4 +- .../apps/portal/services/services.service.ts | 8 +- .../apps/portal/utils/to-navigation-item.ts | 3 +- .../ui/src/app/common/form/control.ts | 2 +- .../form/form-array/form-array.component.ts | 2 +- .../form/form-color/form-color.component.ts | 2 +- .../form-control/form-control.component.ts | 2 +- .../form-control/form-control.providers.ts | 2 +- .../form-datetime/form-datetime.component.ts | 2 +- .../form/form-file/form-file.component.ts | 2 +- .../form/form-group/form-group.component.ts | 2 +- .../form-multiselect.component.ts | 2 +- .../form/form-number/form-number.component.ts | 2 +- .../form/form-object/form-object.component.ts | 2 +- .../form/form-select/form-select.component.ts | 2 +- .../form/form-text/form-text.component.ts | 2 +- .../form-textarea/form-textarea.component.ts | 2 +- .../form/form-toggle/form-toggle.component.ts | 2 +- .../form/form-union/form-union.component.ts | 4 +- .../ui/src/app/common/form/hint.pipe.ts | 2 +- .../notifications-toast.component.ts | 2 +- .../refresh-alert.component.ts | 2 +- .../toast-container/update-toast.component.ts | 2 +- .../ui/src/app/services/api/api.fixures.ts | 19 +- .../ui/src/app/services/api/api.types.ts | 25 +- .../app/services/api/embassy-api.service.ts | 6 +- .../services/api/embassy-live-api.service.ts | 8 +- .../services/api/embassy-mock-api.service.ts | 17 +- .../ui/src/app/services/api/mock-patch.ts | 6 +- .../ui/src/app/services/config.service.ts | 15 +- .../ui/src/app/services/form.service.ts | 2 +- .../src/app/services/patch-db/data-model.ts | 31 +- .../services/pkg-status-rendering.service.ts | 5 - .../ui/src/app/services/proxy.service.ts | 116 ++------ .../ui/src/app/util/clearnetAddress.ts | 11 - .../ui/src/app/util/config-utilities.ts | 2 +- .../ui/src/app/util/configBuilderToSpec.ts | 2 +- .../ui/src/app/util/get-package-info.ts | 1 - 152 files changed, 1360 insertions(+), 1352 deletions(-) create mode 100644 web/projects/ui/src/app/apps/portal/components/interfaces/interface-addresses.component.ts delete mode 100644 web/projects/ui/src/app/apps/portal/components/interfaces/interfaces.component.ts delete mode 100644 web/projects/ui/src/app/apps/portal/pipes/to-badge.ts create mode 100644 web/projects/ui/src/app/apps/portal/pipes/to-manifest.ts rename web/projects/ui/src/app/apps/portal/routes/service/components/{interface.component.ts => interface-list-item.component.ts} (89%) create mode 100644 web/projects/ui/src/app/apps/portal/routes/service/components/interface-list.component.ts delete mode 100644 web/projects/ui/src/app/apps/portal/routes/service/components/interfaces.component.ts rename web/projects/ui/src/app/apps/portal/routes/service/components/{credential.component.ts => property.component.ts} (94%) rename web/projects/ui/src/app/apps/portal/routes/service/modals/{credentials.component.ts => properties.component.ts} (72%) delete mode 100644 web/projects/ui/src/app/apps/portal/routes/system/settings/routes/interfaces/interfaces.component.ts create mode 100644 web/projects/ui/src/app/apps/portal/routes/system/settings/routes/interfaces/ui.component.ts delete mode 100644 web/projects/ui/src/app/apps/portal/routes/system/updates/pipes/install-progress.pipe.ts delete mode 100644 web/projects/ui/src/app/util/clearnetAddress.ts diff --git a/web/projects/install-wizard/src/app/app.module.ts b/web/projects/install-wizard/src/app/app.module.ts index 3164d5d99..242742fda 100644 --- a/web/projects/install-wizard/src/app/app.module.ts +++ b/web/projects/install-wizard/src/app/app.module.ts @@ -5,7 +5,6 @@ import { DriveComponent, LoadingModule, RELATIVE_URL, - UnitConversionPipesModule, WorkspaceConfig, } from '@start9labs/shared' import { TuiDialogModule, TuiRootModule } from '@taiga-ui/core' @@ -42,7 +41,6 @@ const { TuiIconModule, TuiSurfaceModule, TuiTitleModule, - UnitConversionPipesModule, ], providers: [ { diff --git a/web/projects/marketplace/src/pages/release-notes/release-notes.component.html b/web/projects/marketplace/src/pages/release-notes/release-notes.component.html index 635dacd0b..be9537114 100644 --- a/web/projects/marketplace/src/pages/release-notes/release-notes.component.html +++ b/web/projects/marketplace/src/pages/release-notes/release-notes.component.html @@ -2,7 +2,7 @@

What's new

-

+

Latest Release  -  @@ -16,7 +16,7 @@

- - - {{ title }} - - - - - } - `, - styles: [ - ` - :host { - display: flex; - height: 5.5rem; - width: 12.5rem; - border-radius: var(--tui-radius-l); - overflow: hidden; - box-shadow: 0 0.25rem 0.25rem rgb(0 0 0 / 25%); - // TODO: Theme - background: rgb(111 109 109); - } +// @Component({ +// selector: '[appCard]', +// template: ` +// +// +// @if (badge) { +// +// {{ badge }} +// +// } +// @if (icon?.startsWith('tuiIcon')) { +// +// } @else { +// +// } +// +// +// +// @if (isService) { +// +// +// +// +// +// {{ title }} +// +// +// +// +// } +// `, +// styles: [ +// ` +// :host { +// display: flex; +// height: 5.5rem; +// width: 12.5rem; +// border-radius: var(--tui-radius-l); +// overflow: hidden; +// box-shadow: 0 0.25rem 0.25rem rgb(0 0 0 / 25%); +// // TODO: Theme +// background: rgb(111 109 109); +// } - .link { - display: flex; - flex: 1; - flex-direction: column; - align-items: center; - justify-content: center; - color: white; - gap: 0.25rem; - padding: 0 0.5rem; - font: var(--tui-font-text-m); - white-space: nowrap; - overflow: hidden; - } +// .link { +// display: flex; +// flex: 1; +// flex-direction: column; +// align-items: center; +// justify-content: center; +// color: white; +// gap: 0.25rem; +// padding: 0 0.5rem; +// font: var(--tui-font-text-m); +// white-space: nowrap; +// overflow: hidden; +// } - .icon { - width: 2.5rem; - height: 2.5rem; - border-radius: 100%; - color: var(--tui-text-01-night); - } +// .icon { +// width: 2.5rem; +// height: 2.5rem; +// border-radius: 100%; +// color: var(--tui-text-01-night); +// } - .side { - width: 3rem; - display: flex; - align-items: center; - justify-content: center; - box-shadow: 0 0.25rem 0.25rem rgb(0 0 0 / 25%); - // TODO: Theme - background: #4b4a4a; - } - `, - ], - standalone: true, - changeDetection: ChangeDetectionStrategy.OnPush, - imports: [ - CommonModule, - RouterLink, - TuiButtonModule, - TuiHostedDropdownModule, - TuiDataListModule, - TuiIconModule, - TickerModule, - TuiBadgedContentModule, - TuiBadgeNotificationModule, - ActionsComponent, - ], -}) -export class CardComponent { - @Input({ required: true }) - id!: string +// .side { +// width: 3rem; +// display: flex; +// align-items: center; +// justify-content: center; +// box-shadow: 0 0.25rem 0.25rem rgb(0 0 0 / 25%); +// // TODO: Theme +// background: #4b4a4a; +// } +// `, +// ], +// standalone: true, +// changeDetection: ChangeDetectionStrategy.OnPush, +// imports: [ +// CommonModule, +// RouterLink, +// TuiButtonModule, +// TuiHostedDropdownModule, +// TuiDataListModule, +// TuiIconModule, +// TickerModule, +// TuiBadgedContentModule, +// TuiBadgeNotificationModule, +// ActionsComponent, +// ], +// }) +// export class CardComponent { +// @Input({ required: true }) +// id!: string - @Input({ required: true }) - icon!: string +// @Input({ required: true }) +// icon!: string - @Input({ required: true }) - title!: string +// @Input({ required: true }) +// title!: string - @Input() - actions: Record = {} +// @Input() +// actions: Record = {} - @Input() - badge: number | null = null +// @Input() +// badge: number | null = null - get isService(): boolean { - return !this.id.includes('/') - } +// get isService(): boolean { +// return !this.id.includes('/') +// } - // Prevents Firefox from starting a native drag - @HostListener('pointerdown.prevent') - onDown() {} -} +// // Prevents Firefox from starting a native drag +// @HostListener('pointerdown.prevent') +// onDown() {} +// } diff --git a/web/projects/ui/src/app/apps/portal/components/form.component.ts b/web/projects/ui/src/app/apps/portal/components/form.component.ts index 9c7295520..d77a493a3 100644 --- a/web/projects/ui/src/app/apps/portal/components/form.component.ts +++ b/web/projects/ui/src/app/apps/portal/components/form.component.ts @@ -8,7 +8,7 @@ import { } from '@angular/core' import { FormGroup, ReactiveFormsModule } from '@angular/forms' import { RouterModule } from '@angular/router' -import { InputSpec } from '@start9labs/start-sdk/lib/config/configTypes' +import { InputSpec } from '@start9labs/start-sdk/cjs/sdk/lib/config/configTypes' import { tuiMarkControlAsTouchedAndValidate, TuiValueChangesModule, diff --git a/web/projects/ui/src/app/apps/portal/components/header/about.component.ts b/web/projects/ui/src/app/apps/portal/components/header/about.component.ts index 7d5aa2369..17f108198 100644 --- a/web/projects/ui/src/app/apps/portal/components/header/about.component.ts +++ b/web/projects/ui/src/app/apps/portal/components/header/about.component.ts @@ -37,13 +37,13 @@ import { ConfigService } from 'src/app/services/config.service'
CA fingerprint -
{{ server['ca-fingerprint'] }}
+
{{ server.caFingerprint }}
@@ -62,7 +62,7 @@ import { ConfigService } from 'src/app/services/config.service' ], }) export class AboutComponent { - readonly server$ = inject(PatchDB).watch$('server-info') + readonly server$ = inject(PatchDB).watch$('serverInfo') readonly copyService = inject(CopyService) readonly gitHash = inject(ConfigService).gitHash } diff --git a/web/projects/ui/src/app/apps/portal/components/header/connection.component.ts b/web/projects/ui/src/app/apps/portal/components/header/connection.component.ts index 3bf249263..c49d48420 100644 --- a/web/projects/ui/src/app/apps/portal/components/header/connection.component.ts +++ b/web/projects/ui/src/app/apps/portal/components/header/connection.component.ts @@ -50,8 +50,8 @@ export class HeaderConnectionComponent { inject(ConnectionService).networkConnected$, inject(ConnectionService).websocketConnected$.pipe(startWith(false)), inject(PatchDB) - .watch$('server-info', 'status-info') - .pipe(startWith({ restarting: false, 'shutting-down': false })), + .watch$('serverInfo', 'statusInfo') + .pipe(startWith({ restarting: false, shuttingDown: false })), ]).pipe( map(([network, websocket, status]) => { if (!network) @@ -68,7 +68,7 @@ export class HeaderConnectionComponent { icon: 'tuiIconCloudOff', status: 'warning', } - if (status['shutting-down']) + if (status.shuttingDown) return { message: 'Shutting Down', color: 'var(--tui-neutral-fill)', diff --git a/web/projects/ui/src/app/apps/portal/components/header/header.component.ts b/web/projects/ui/src/app/apps/portal/components/header/header.component.ts index b12380702..4e8a9cbf9 100644 --- a/web/projects/ui/src/app/apps/portal/components/header/header.component.ts +++ b/web/projects/ui/src/app/apps/portal/components/header/header.component.ts @@ -169,7 +169,7 @@ export class HeaderComponent { 'ui', 'gaming', 'snake', - 'high-score', + 'highScore', ) } diff --git a/web/projects/ui/src/app/apps/portal/components/header/notifications.component.ts b/web/projects/ui/src/app/apps/portal/components/header/notifications.component.ts index e0e219738..91c8c3da7 100644 --- a/web/projects/ui/src/app/apps/portal/components/header/notifications.component.ts +++ b/web/projects/ui/src/app/apps/portal/components/header/notifications.component.ts @@ -46,7 +46,7 @@ import { NotificationService } from '../../services/notification.service' tuiCell [notification]="not" > - + {{ $any(packageData[pkgId])?.manifest.title || pkgId }} View Service @@ -104,7 +104,7 @@ export class HeaderNotificationsComponent { private readonly patch = inject(PatchDB) private readonly service = inject(NotificationService) - readonly packageData$ = this.patch.watch$('package-data').pipe(first()) + readonly packageData$ = this.patch.watch$('packageData').pipe(first()) readonly notifications$ = new Subject() @@ -112,7 +112,7 @@ export class HeaderNotificationsComponent { ngAfterViewInit() { this.patch - .watch$('server-info', 'unreadNotifications', 'recent') + .watch$('serverInfo', 'unreadNotifications', 'recent') .pipe( tap(recent => this.notifications$.next(recent)), first(), diff --git a/web/projects/ui/src/app/apps/portal/components/interfaces/interface-addresses.component.ts b/web/projects/ui/src/app/apps/portal/components/interfaces/interface-addresses.component.ts new file mode 100644 index 000000000..c0d603068 --- /dev/null +++ b/web/projects/ui/src/app/apps/portal/components/interfaces/interface-addresses.component.ts @@ -0,0 +1,87 @@ +import { NgIf } from '@angular/common' +import { + ChangeDetectionStrategy, + Component, + inject, + Input, +} from '@angular/core' +import { WINDOW } from '@ng-web-apis/common' +import { CopyService } from '@start9labs/shared' +import { TuiDialogService } from '@taiga-ui/core' +import { + TuiButtonModule, + TuiCellModule, + TuiTitleModule, +} from '@taiga-ui/experimental' +import { PolymorpheusComponent } from '@tinkoff/ng-polymorpheus' +import { QRComponent } from 'src/app/common/qr.component' +import { mask } from 'src/app/util/mask' + +@Component({ + standalone: true, + selector: 'app-interface-address', + template: ` +
+

+ {{ isMasked ? mask : address }} +

+ + {{ label }} + + + + +
+ `, + imports: [NgIf, TuiCellModule, TuiTitleModule, TuiButtonModule], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class InterfaceAddressComponent { + private readonly window = inject(WINDOW) + private readonly dialogs = inject(TuiDialogService) + readonly copyService = inject(CopyService) + + @Input() label?: string + @Input({ required: true }) address!: string + @Input({ required: true }) isMasked!: boolean + @Input({ required: true }) isUi!: boolean + + get mask(): string { + return mask(this.address, 64) + } + + launch(url: string): void { + this.window.open(url, '_blank', 'noreferrer') + } + + showQR(data: string) { + this.dialogs + .open(new PolymorpheusComponent(QRComponent), { + size: 'auto', + data, + }) + .subscribe() + } +} diff --git a/web/projects/ui/src/app/apps/portal/components/interfaces/interface-clearnet.component.ts b/web/projects/ui/src/app/apps/portal/components/interfaces/interface-clearnet.component.ts index 551713d2d..8b30037e1 100644 --- a/web/projects/ui/src/app/apps/portal/components/interfaces/interface-clearnet.component.ts +++ b/web/projects/ui/src/app/apps/portal/components/interfaces/interface-clearnet.component.ts @@ -1,4 +1,4 @@ -import { NgIf } from '@angular/common' +import { NgForOf, NgIf } from '@angular/common' import { ChangeDetectionStrategy, Component, @@ -20,10 +20,9 @@ import { } from 'src/app/apps/portal/components/interfaces/interface.utils' import { ApiService } from 'src/app/services/api/embassy-api.service' import { FormDialogService } from 'src/app/services/form-dialog.service' -import { DomainInfo, NetworkInfo } from 'src/app/services/patch-db/data-model' -import { getClearnetAddress } from 'src/app/util/clearnetAddress' +import { NetworkInfo } from 'src/app/services/patch-db/data-model' +import { InterfaceAddressComponent } from './interface-addresses.component' import { InterfaceComponent } from './interface.component' -import { InterfacesComponent } from './interfaces.component' type ClearnetForm = { domain: string @@ -45,32 +44,36 @@ type ClearnetForm = { -
-
- + `, - imports: [InterfaceComponent, NgIf, TuiButtonModule], + imports: [NgForOf, InterfaceAddressComponent, NgIf, TuiButtonModule], changeDetection: ChangeDetectionStrategy.OnPush, }) export class InterfaceClearnetComponent { @@ -79,21 +82,14 @@ export class InterfaceClearnetComponent { private readonly errorService = inject(ErrorService) private readonly api = inject(ApiService) private readonly dialogs = inject(TuiDialogService) - readonly interfaces = inject(InterfacesComponent) + readonly interface = inject(InterfaceComponent) @Input({ required: true }) network!: NetworkInfo - getClearnet(clearnet: DomainInfo): string { - return getClearnetAddress('https', clearnet) - } - async add() { - const { domainInfo } = this.interfaces.addressInfo - const { domain = '', subdomain = '' } = domainInfo || {} const options: Partial>> = { label: 'Select Domain/Subdomain', data: { - value: { domain, subdomain }, spec: await getClearnetSpec(this.network), buttons: [ { @@ -118,9 +114,9 @@ export class InterfaceClearnetComponent { const loader = this.loader.open('Removing...').subscribe() try { - if (this.interfaces.packageContext) { + if (this.interface.packageContext) { await this.api.setInterfaceClearnetAddress({ - ...this.interfaces.packageContext, + ...this.interface.packageContext, domainInfo: null, }) } else { @@ -138,9 +134,9 @@ export class InterfaceClearnetComponent { const loader = this.loader.open('Saving...').subscribe() try { - if (this.interfaces.packageContext) { + if (this.interface.packageContext) { await this.api.setInterfaceClearnetAddress({ - ...this.interfaces.packageContext, + ...this.interface.packageContext, domainInfo, }) } else { diff --git a/web/projects/ui/src/app/apps/portal/components/interfaces/interface-local.component.ts b/web/projects/ui/src/app/apps/portal/components/interfaces/interface-local.component.ts index 73a21a1d5..4b2ad8d11 100644 --- a/web/projects/ui/src/app/apps/portal/components/interfaces/interface-local.component.ts +++ b/web/projects/ui/src/app/apps/portal/components/interfaces/interface-local.component.ts @@ -1,8 +1,8 @@ -import { CommonModule } from '@angular/common' +import { NgForOf, NgIf } from '@angular/common' import { ChangeDetectionStrategy, Component, inject } from '@angular/core' import { TuiButtonModule } from '@taiga-ui/experimental' -import { InterfacesComponent } from './interfaces.component' import { InterfaceComponent } from './interface.component' +import { InterfaceAddressComponent } from './interface-addresses.component' @Component({ standalone: true, @@ -19,41 +19,44 @@ import { InterfaceComponent } from './interface.component' View instructions - - Download Root CA - - + - - + +
+ +
+ + + `, - imports: [InterfaceComponent, CommonModule, TuiButtonModule], + imports: [NgForOf, NgIf, InterfaceAddressComponent, TuiButtonModule], changeDetection: ChangeDetectionStrategy.OnPush, }) export class InterfaceLocalComponent { - readonly interfaces = inject(InterfacesComponent) + readonly interface = inject(InterfaceComponent) + + async add() {} + + async remove() {} } diff --git a/web/projects/ui/src/app/apps/portal/components/interfaces/interface-tor.component.ts b/web/projects/ui/src/app/apps/portal/components/interfaces/interface-tor.component.ts index a15f93e2a..6614687d2 100644 --- a/web/projects/ui/src/app/apps/portal/components/interfaces/interface-tor.component.ts +++ b/web/projects/ui/src/app/apps/portal/components/interfaces/interface-tor.component.ts @@ -1,6 +1,7 @@ import { ChangeDetectionStrategy, Component, inject } from '@angular/core' +import { InterfaceAddressComponent } from './interface-addresses.component' import { InterfaceComponent } from './interface.component' -import { InterfacesComponent } from './interfaces.component' +import { NgForOf, NgIf } from '@angular/common' @Component({ standalone: true, @@ -17,15 +18,49 @@ import { InterfacesComponent } from './interfaces.component' View instructions - + + + +
+ +
+
+ + + + + `, - imports: [InterfaceComponent], + imports: [NgForOf, NgIf, InterfaceAddressComponent], changeDetection: ChangeDetectionStrategy.OnPush, }) export class InterfaceTorComponent { - readonly interfaces = inject(InterfacesComponent) + readonly interface = inject(InterfaceComponent) + + async add() {} + + async remove() {} } diff --git a/web/projects/ui/src/app/apps/portal/components/interfaces/interface.component.ts b/web/projects/ui/src/app/apps/portal/components/interfaces/interface.component.ts index 2f0f362b5..f76828afb 100644 --- a/web/projects/ui/src/app/apps/portal/components/interfaces/interface.component.ts +++ b/web/projects/ui/src/app/apps/portal/components/interfaces/interface.component.ts @@ -1,79 +1,61 @@ -import { NgIf } from '@angular/common' +import { CommonModule } from '@angular/common' import { ChangeDetectionStrategy, Component, inject, Input, } from '@angular/core' -import { WINDOW } from '@ng-web-apis/common' -import { CopyService } from '@start9labs/shared' -import { TuiDialogService } from '@taiga-ui/core' -import { - TuiButtonModule, - TuiCellModule, - TuiTitleModule, -} from '@taiga-ui/experimental' -import { PolymorpheusComponent } from '@tinkoff/ng-polymorpheus' -import { QRComponent } from 'src/app/common/qr.component' +import { T } from '@start9labs/start-sdk' +import { TuiCardModule, TuiSurfaceModule } from '@taiga-ui/experimental' +import { PatchDB } from 'patch-db-client' +import { InterfaceClearnetComponent } from 'src/app/apps/portal/components/interfaces/interface-clearnet.component' +import { InterfaceLocalComponent } from 'src/app/apps/portal/components/interfaces/interface-local.component' +import { InterfaceTorComponent } from 'src/app/apps/portal/components/interfaces/interface-tor.component' +import { DataModel } from 'src/app/services/patch-db/data-model' +import { AddressDetails } from './interface.utils' @Component({ standalone: true, selector: 'app-interface', template: ` -
-

- {{ label }} - {{ hostname }} -

- - - -
+

Clearnet

+ + +

Tor

+ + +

Local

+ `, - imports: [NgIf, TuiCellModule, TuiTitleModule, TuiButtonModule], changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + CommonModule, + InterfaceTorComponent, + InterfaceLocalComponent, + InterfaceClearnetComponent, + TuiCardModule, + TuiSurfaceModule, + ], }) export class InterfaceComponent { - private readonly window = inject(WINDOW) - private readonly dialogs = inject(TuiDialogService) - readonly copyService = inject(CopyService) + readonly network$ = inject(PatchDB).watch$('serverInfo', 'network') - @Input({ required: true }) label = '' - @Input({ required: true }) hostname = '' - @Input({ required: true }) isUi = false - - launch(url: string): void { - this.window.open(url, '_blank', 'noreferrer') + @Input() packageContext?: { + packageId: string + interfaceId: string } + @Input({ required: true }) serviceInterface!: ServiceInterfaceWithAddresses +} - showQR(data: string) { - this.dialogs - .open(new PolymorpheusComponent(QRComponent), { - size: 'auto', - data, - }) - .subscribe() +export type ServiceInterfaceWithAddresses = T.ServiceInterface & { + addresses: { + clearnet: AddressDetails[] + local: AddressDetails[] + tor: AddressDetails[] } } diff --git a/web/projects/ui/src/app/apps/portal/components/interfaces/interface.utils.ts b/web/projects/ui/src/app/apps/portal/components/interfaces/interface.utils.ts index 461b1a984..19b03852b 100644 --- a/web/projects/ui/src/app/apps/portal/components/interfaces/interface.utils.ts +++ b/web/projects/ui/src/app/apps/portal/components/interfaces/interface.utils.ts @@ -1,6 +1,7 @@ -import { Config } from '@start9labs/start-sdk/lib/config/builder/config' -import { Value } from '@start9labs/start-sdk/lib/config/builder/value' -import { InputSpec } from '@start9labs/start-sdk/lib/config/configTypes' +import { T } from '@start9labs/start-sdk' +import { Config } from '@start9labs/start-sdk/cjs/sdk/lib/config/builder/config' +import { Value } from '@start9labs/start-sdk/cjs/sdk/lib/config/builder/value' +import { InputSpec } from '@start9labs/start-sdk/cjs/sdk/lib/config/configTypes' import { TuiDialogOptions } from '@taiga-ui/core' import { TuiPromptData } from '@taiga-ui/kit' import { NetworkInfo } from 'src/app/services/patch-db/data-model' @@ -44,3 +45,96 @@ export function getClearnetSpec({ }), ) } + +export type AddressDetails = { + label?: string + url: string +} + +export function getAddresses( + serviceInterface: T.ServiceInterfaceWithHostInfo, +): { + clearnet: AddressDetails[] + local: AddressDetails[] + tor: AddressDetails[] +} { + const host = serviceInterface.hostInfo + const addressInfo = serviceInterface.addressInfo + const username = addressInfo.username ? addressInfo.username + '@' : '' + const suffix = addressInfo.suffix || '' + + const hostnames = + host.kind === 'multi' + ? host.hostnames + : host.hostname + ? [host.hostname] + : [] + + const clearnet: AddressDetails[] = [] + const local: AddressDetails[] = [] + const tor: AddressDetails[] = [] + + hostnames.forEach(h => { + let scheme = '' + let port = '' + + if (h.hostname.sslPort) { + port = h.hostname.sslPort === 443 ? '' : `:${h.hostname.sslPort}` + scheme = addressInfo.bindOptions.addSsl?.scheme + ? `${addressInfo.bindOptions.addSsl.scheme}://` + : '' + } + + if (h.hostname.port) { + port = h.hostname.port === 80 ? '' : `:${h.hostname.port}` + scheme = addressInfo.bindOptions.scheme + ? `${addressInfo.bindOptions.scheme}://` + : '' + } + + if (h.kind === 'onion') { + tor.push({ + label: h.hostname.sslPort ? 'HTTPS' : 'HTTP', + url: toHref(scheme, username, h.hostname.value, port, suffix), + }) + } else { + const hostnameKind = h.hostname.kind + + if (hostnameKind === 'domain') { + tor.push({ + url: toHref( + scheme, + username, + `${h.hostname.subdomain}.${h.hostname.domain}`, + port, + suffix, + ), + }) + } else { + local.push({ + label: + hostnameKind === 'local' + ? 'Local' + : `${h.networkInterfaceId} (${hostnameKind})`, + url: toHref(scheme, username, h.hostname.value, port, suffix), + }) + } + } + }) + + return { + clearnet, + local, + tor, + } +} + +function toHref( + scheme: string, + username: string, + hostname: string, + port: string, + suffix: string, +): string { + return `${scheme}${username}${hostname}${port}${suffix}` +} diff --git a/web/projects/ui/src/app/apps/portal/components/interfaces/interfaces.component.ts b/web/projects/ui/src/app/apps/portal/components/interfaces/interfaces.component.ts deleted file mode 100644 index 635f62486..000000000 --- a/web/projects/ui/src/app/apps/portal/components/interfaces/interfaces.component.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { CommonModule } from '@angular/common' -import { - ChangeDetectionStrategy, - Component, - inject, - Input, -} from '@angular/core' -import { TuiCardModule, TuiSurfaceModule } from '@taiga-ui/experimental' -import { PatchDB } from 'patch-db-client' -import { InterfaceClearnetComponent } from 'src/app/apps/portal/components/interfaces/interface-clearnet.component' -import { InterfaceLocalComponent } from 'src/app/apps/portal/components/interfaces/interface-local.component' -import { InterfaceTorComponent } from 'src/app/apps/portal/components/interfaces/interface-tor.component' -import { AddressInfo, DataModel } from 'src/app/services/patch-db/data-model' - -@Component({ - standalone: true, - selector: 'app-interfaces', - template: ` -

Clearnet

- - -

Tor

- - -

Local

- - `, - changeDetection: ChangeDetectionStrategy.OnPush, - imports: [ - CommonModule, - InterfaceTorComponent, - InterfaceLocalComponent, - InterfaceClearnetComponent, - TuiCardModule, - TuiSurfaceModule, - ], -}) -export class InterfacesComponent { - readonly network$ = inject(PatchDB).watch$( - 'server-info', - 'network', - ) - - @Input() packageContext?: { - packageId: string - interfaceId: string - } - @Input({ required: true }) addressInfo!: AddressInfo - @Input({ required: true }) isUi!: boolean -} diff --git a/web/projects/ui/src/app/apps/portal/components/logs/logs-fetch.directive.ts b/web/projects/ui/src/app/apps/portal/components/logs/logs-fetch.directive.ts index 17128ff47..7a4e52a93 100644 --- a/web/projects/ui/src/app/apps/portal/components/logs/logs-fetch.directive.ts +++ b/web/projects/ui/src/app/apps/portal/components/logs/logs-fetch.directive.ts @@ -25,7 +25,7 @@ export class LogsFetchDirective { }), ), ), - tap(res => this.component.setCursor(res['start-cursor'])), + tap(res => this.component.setCursor(res.startCursor)), map(({ entries }) => convertAnsi(entries)), catchError(e => { this.errors.handleError(e) diff --git a/web/projects/ui/src/app/apps/portal/components/logs/logs.pipe.ts b/web/projects/ui/src/app/apps/portal/components/logs/logs.pipe.ts index 4c3df9c4c..2c4e28224 100644 --- a/web/projects/ui/src/app/apps/portal/components/logs/logs.pipe.ts +++ b/web/projects/ui/src/app/apps/portal/components/logs/logs.pipe.ts @@ -43,7 +43,7 @@ export class LogsPipe implements PipeTransform { map(() => getMessage(true)), ), defer(() => followLogs(this.options)).pipe( - tap(r => this.logs.setCursor(r['start-cursor'])), + tap(r => this.logs.setCursor(r.startCursor)), switchMap(r => this.api.openLogsWebsocket$(this.toConfig(r.guid))), bufferTime(1000), filter(logs => !!logs.length), diff --git a/web/projects/ui/src/app/apps/portal/modals/config.component.ts b/web/projects/ui/src/app/apps/portal/modals/config.component.ts index d56e0e6f8..b75d9d927 100644 --- a/web/projects/ui/src/app/apps/portal/modals/config.component.ts +++ b/web/projects/ui/src/app/apps/portal/modals/config.component.ts @@ -6,7 +6,7 @@ import { isEmptyObject, LoadingService, } from '@start9labs/shared' -import { InputSpec } from '@start9labs/start-sdk/lib/config/configTypes' +import { InputSpec } from '@start9labs/start-sdk/cjs/sdk/lib/config/configTypes' import { TuiButtonModule } from '@taiga-ui/experimental' import { TuiDialogContext, @@ -27,7 +27,11 @@ import { PackageDataEntry, } from 'src/app/services/patch-db/data-model' import { hasCurrentDeps } from 'src/app/util/has-deps' -import { getAllPackages, getPackage } from 'src/app/util/get-package-data' +import { + getAllPackages, + getManifest, + getPackage, +} from 'src/app/util/get-package-data' import { Breakages } from 'src/app/services/api/api.types' import { InvalidService } from 'src/app/common/form/invalid.service' import { @@ -35,6 +39,7 @@ import { FormComponent, } from 'src/app/apps/portal/components/form.component' import { DependentInfo } from 'src/app/types/dependent-info' +import { ToManifestPipe } from '../pipes/to-manifest' export interface PackageConfigData { readonly pkgId: string @@ -52,23 +57,26 @@ export interface PackageConfigData {
- + - {{ pkg.manifest.title }} has been automatically configured with - recommended defaults. Make whatever changes you want, then click "Save". + {{ manifest.title }} has been automatically configured with recommended + defaults. Make whatever changes you want, then click "Save". - - No config options for {{ pkg.manifest.title }} - {{ pkg.manifest.version }}. + + No config options for {{ manifest.title }} {{ manifest.version }}. `
  • ${packages[id].manifest.title}
  • `, + id => `
  • ${getManifest(packages[id]).title}
  • `, )}` const data: TuiPromptData = { content, yes: 'Continue', no: 'Cancel' } diff --git a/web/projects/ui/src/app/apps/portal/pipes/to-badge.ts b/web/projects/ui/src/app/apps/portal/pipes/to-badge.ts deleted file mode 100644 index 04aad4ec1..000000000 --- a/web/projects/ui/src/app/apps/portal/pipes/to-badge.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { inject, Pipe, PipeTransform } from '@angular/core' -import { BadgeService } from '../services/badge.service' -import { Observable } from 'rxjs' - -@Pipe({ - name: 'toBadge', - standalone: true, -}) -export class ToBadgePipe implements PipeTransform { - readonly badge = inject(BadgeService) - - transform(id: string): Observable { - return this.badge.getCount(id) - } -} diff --git a/web/projects/ui/src/app/apps/portal/pipes/to-manifest.ts b/web/projects/ui/src/app/apps/portal/pipes/to-manifest.ts new file mode 100644 index 000000000..404c1bbd3 --- /dev/null +++ b/web/projects/ui/src/app/apps/portal/pipes/to-manifest.ts @@ -0,0 +1,14 @@ +import { Pipe, PipeTransform } from '@angular/core' +import { PackageDataEntry } from 'src/app/services/patch-db/data-model' +import { Manifest } from '@start9labs/marketplace' +import { getManifest } from 'src/app/util/get-package-data' + +@Pipe({ + name: 'toManifest', + standalone: true, +}) +export class ToManifestPipe implements PipeTransform { + transform(pkg: PackageDataEntry): Manifest { + return getManifest(pkg) + } +} \ No newline at end of file diff --git a/web/projects/ui/src/app/apps/portal/routes/dashboard/controls.component.ts b/web/projects/ui/src/app/apps/portal/routes/dashboard/controls.component.ts index ed037df29..195924ec4 100644 --- a/web/projects/ui/src/app/apps/portal/routes/dashboard/controls.component.ts +++ b/web/projects/ui/src/app/apps/portal/routes/dashboard/controls.component.ts @@ -10,24 +10,23 @@ import { TuiButtonModule, tuiButtonOptionsProvider, } from '@taiga-ui/experimental' -import { map, of } from 'rxjs' -import { UIComponent } from 'src/app/apps/portal/routes/dashboard/ui.component' +import { map, Observable } from 'rxjs' +import { UILaunchComponent } from 'src/app/apps/portal/routes/dashboard/ui.component' import { ActionsService } from 'src/app/apps/portal/services/actions.service' import { DepErrorService } from 'src/app/services/dep-error.service' -import { - PackageDataEntry, - PackageMainStatus, -} from 'src/app/services/patch-db/data-model' +import { PackageDataEntry } from 'src/app/services/patch-db/data-model' +import { getManifest } from 'src/app/util/get-package-data' +import { Manifest } from '@start9labs/marketplace' @Component({ standalone: true, selector: 'fieldset[appControls]', template: ` - @if (isRunning) { + @if (pkg.status.main.status === 'running') { @@ -35,17 +34,17 @@ import { } @else { @@ -53,48 +52,39 @@ import { } - + `, changeDetection: ChangeDetectionStrategy.OnPush, - imports: [TuiButtonModule, UIComponent, TuiLetModule, AsyncPipe], + imports: [TuiButtonModule, UILaunchComponent, TuiLetModule, AsyncPipe], providers: [tuiButtonOptionsProvider({ size: 's', appearance: 'none' })], }) export class ControlsComponent { private readonly errors = inject(DepErrorService) @Input() - appControls!: PackageDataEntry + pkg!: PackageDataEntry + + get manifest(): Manifest { + return getManifest(this.pkg) + } readonly actions = inject(ActionsService) - get isRunning(): boolean { - return ( - this.appControls.installed?.status.main.status === - PackageMainStatus.Running + @tuiPure + hasUnmet(pkg: PackageDataEntry): Observable { + const id = getManifest(pkg).id + return this.errors.getPkgDepErrors$(id).pipe( + map(errors => + Object.keys(pkg.currentDependencies) + .map(id => !!(errors[id] as any)?.[id]) // @TODO fix + .some(Boolean), + ), ) } - - get isConfigured(): boolean { - return !!this.appControls.installed?.status.configured - } - - @tuiPure - hasUnmet({ installed, manifest }: PackageDataEntry) { - return installed - ? this.errors.getPkgDepErrors$(manifest.id).pipe( - map(errors => - Object.keys(installed['current-dependencies']) - .filter(id => !!manifest.dependencies[id]) - .map(id => !!(errors[manifest.id] as any)?.[id]) // @TODO fix - .some(Boolean), - ), - ) - : of(false) - } } diff --git a/web/projects/ui/src/app/apps/portal/routes/dashboard/service.component.ts b/web/projects/ui/src/app/apps/portal/routes/dashboard/service.component.ts index 299578bc8..eb6c7f54b 100644 --- a/web/projects/ui/src/app/apps/portal/routes/dashboard/service.component.ts +++ b/web/projects/ui/src/app/apps/portal/routes/dashboard/service.component.ts @@ -11,29 +11,26 @@ import { ControlsComponent } from 'src/app/apps/portal/routes/dashboard/controls import { StatusComponent } from 'src/app/apps/portal/routes/dashboard/status.component' import { ConnectionService } from 'src/app/services/connection.service' import { PkgDependencyErrors } from 'src/app/services/dep-error.service' -import { - PackageDataEntry, - PackageState, -} from 'src/app/services/patch-db/data-model' -import { renderPkgStatus } from 'src/app/services/pkg-status-rendering.service' +import { PackageDataEntry } from 'src/app/services/patch-db/data-model' +import { getManifest } from 'src/app/util/get-package-data' @Component({ standalone: true, selector: 'tr[appService]', template: ` - logo + logo - {{ appService.manifest.title }} + {{ manifest.title }} - {{ appService.manifest.version }} - + {{ manifest.version }} +
    `, @@ -57,19 +54,19 @@ import { renderPkgStatus } from 'src/app/services/pkg-status-rendering.service' }) export class ServiceComponent { @Input() - appService!: PackageDataEntry + pkg!: PackageDataEntry @Input() - appServiceError?: PkgDependencyErrors + depErrors?: PkgDependencyErrors readonly connected$ = inject(ConnectionService).connected$ - get routerLink() { - return `/portal/service/${this.appService.manifest.id}` + get manifest() { + return getManifest(this.pkg) } - get installed(): boolean { - return this.appService.state === PackageState.Installed + get routerLink() { + return `/portal/service/${this.manifest.id}` } @tuiPure diff --git a/web/projects/ui/src/app/apps/portal/routes/dashboard/services.component.ts b/web/projects/ui/src/app/apps/portal/routes/dashboard/services.component.ts index 8769ae101..c40212420 100644 --- a/web/projects/ui/src/app/apps/portal/routes/dashboard/services.component.ts +++ b/web/projects/ui/src/app/apps/portal/routes/dashboard/services.component.ts @@ -3,6 +3,7 @@ import { ChangeDetectionStrategy, Component, inject } from '@angular/core' import { ServiceComponent } from 'src/app/apps/portal/routes/dashboard/service.component' import { ServicesService } from 'src/app/apps/portal/services/services.service' import { DepErrorService } from 'src/app/services/dep-error.service' +import { ToManifestPipe } from '../../pipes/to-manifest' @Component({ standalone: true, @@ -21,10 +22,11 @@ import { DepErrorService } from 'src/app/services/dep-error.service' @if (errors$ | async; as errors) { - @for (service of services$ | async; track $index) { + @for (pkg of services$ | async; track $index) { } @empty { @@ -78,7 +80,7 @@ import { DepErrorService } from 'src/app/services/dep-error.service' } `, changeDetection: ChangeDetectionStrategy.OnPush, - imports: [ServiceComponent, AsyncPipe], + imports: [ServiceComponent, AsyncPipe, ToManifestPipe], }) export class ServicesComponent { readonly services$ = inject(ServicesService) diff --git a/web/projects/ui/src/app/apps/portal/routes/dashboard/status.component.ts b/web/projects/ui/src/app/apps/portal/routes/dashboard/status.component.ts index d398385e9..96f6be5c4 100644 --- a/web/projects/ui/src/app/apps/portal/routes/dashboard/status.component.ts +++ b/web/projects/ui/src/app/apps/portal/routes/dashboard/status.component.ts @@ -2,16 +2,13 @@ import { ChangeDetectionStrategy, Component, Input } from '@angular/core' import { tuiPure } from '@taiga-ui/cdk' import { TuiLoaderModule } from '@taiga-ui/core' import { TuiIconModule } from '@taiga-ui/experimental' -import { - PackageDataEntry, - PackageState, -} from 'src/app/services/patch-db/data-model' +import { PackageDataEntry } from 'src/app/services/patch-db/data-model' import { HealthStatus, PrimaryStatus, renderPkgStatus, } from 'src/app/services/pkg-status-rendering.service' -import { packageLoadingProgress } from 'src/app/util/package-loading-progress' +import { InstallingProgressDisplayPipe } from '../service/pipes/install-progress.pipe' @Component({ standalone: true, @@ -41,27 +38,23 @@ import { packageLoadingProgress } from 'src/app/util/package-loading-progress' }) export class StatusComponent { @Input() - appStatus!: PackageDataEntry + pkg!: PackageDataEntry @Input() - appStatusError = false + hasDepErrors = false get healthy(): boolean { - const status = this.getStatus(this.appStatus) + const status = this.getStatus(this.pkg) return ( - !this.appStatusError && // no deps error - !!this.appStatus.installed?.status.configured && // no config needed - status.primary !== PackageState.NeedsUpdate && // no update needed + !this.hasDepErrors && // no deps error + !!this.pkg.status.configured && // no config needed status.health !== HealthStatus.Failure // no health issues ) } get loading(): boolean { - return ( - !!this.appStatus['install-progress'] || - this.color === 'var(--tui-info-fill)' - ) + return !!this.pkg.stateInfo || this.color === 'var(--tui-info-fill)' } @tuiPure @@ -70,17 +63,15 @@ export class StatusComponent { } get status(): string { - if (this.appStatus['install-progress']) { - return `Installing... ${packageLoadingProgress(this.appStatus['install-progress'])?.totalProgress || 0}%` + if (this.pkg.stateInfo.installingInfo) { + return `Installing...${new InstallingProgressDisplayPipe().transform(this.pkg.stateInfo.installingInfo.progress.overall)}` } - switch (this.getStatus(this.appStatus).primary) { + switch (this.getStatus(this.pkg).primary) { case PrimaryStatus.Running: return 'Running' case PrimaryStatus.Stopped: return 'Stopped' - case PackageState.NeedsUpdate: - return 'Needs Update' case PrimaryStatus.NeedsConfig: return 'Needs Config' case PrimaryStatus.Updating: @@ -103,14 +94,13 @@ export class StatusComponent { } get color(): string { - if (this.appStatus['install-progress']) { + if (this.pkg.stateInfo.installingInfo) { return 'var(--tui-info-fill)' } - switch (this.getStatus(this.appStatus).primary) { + switch (this.getStatus(this.pkg).primary) { case PrimaryStatus.Running: return 'var(--tui-success-fill)' - case PackageState.NeedsUpdate: case PrimaryStatus.NeedsConfig: return 'var(--tui-warning-fill)' case PrimaryStatus.Updating: diff --git a/web/projects/ui/src/app/apps/portal/routes/dashboard/ui.component.ts b/web/projects/ui/src/app/apps/portal/routes/dashboard/ui.component.ts index 265f799bf..5d917b949 100644 --- a/web/projects/ui/src/app/apps/portal/routes/dashboard/ui.component.ts +++ b/web/projects/ui/src/app/apps/portal/routes/dashboard/ui.component.ts @@ -4,20 +4,19 @@ import { inject, Input, } from '@angular/core' +import { T } from '@start9labs/start-sdk' import { tuiPure } from '@taiga-ui/cdk' import { TuiDataListModule, TuiHostedDropdownModule } from '@taiga-ui/core' import { TuiButtonModule } from '@taiga-ui/experimental' import { ConfigService } from 'src/app/services/config.service' import { - InstalledPackageInfo, - InterfaceInfo, PackageDataEntry, PackageMainStatus, } from 'src/app/services/patch-db/data-model' @Component({ standalone: true, - selector: 'app-ui', + selector: 'app-ui-launch', template: ` @if (interfaces.length > 1) { @@ -26,7 +25,7 @@ import { iconLeft="tuiIconExternalLink" [disabled]="!isRunning" > - Interfaces + Launch UI @@ -44,42 +43,44 @@ import { } @else { - - {{ interfaces[0]?.name }} - + @if (interfaces[0]; as info) { + + {{ info.name }} + + } } `, changeDetection: ChangeDetectionStrategy.OnPush, imports: [TuiButtonModule, TuiHostedDropdownModule, TuiDataListModule], }) -export class UIComponent { +export class UILaunchComponent { private readonly config = inject(ConfigService) @Input() pkg!: PackageDataEntry - get interfaces(): readonly InterfaceInfo[] { - return this.getInterfaces(this.pkg.installed) + get interfaces(): readonly T.ServiceInterfaceWithHostInfo[] { + return this.getInterfaces(this.pkg) } get isRunning(): boolean { - return this.pkg.installed?.status.main.status === PackageMainStatus.Running + return this.pkg.status.main.status === PackageMainStatus.Running } @tuiPure - getInterfaces(info?: InstalledPackageInfo): InterfaceInfo[] { - return info - ? Object.values(info.interfaceInfo).filter(({ type }) => type === 'ui') + getInterfaces(pkg?: PackageDataEntry): T.ServiceInterfaceWithHostInfo[] { + return pkg + ? Object.values(pkg.serviceInterfaces).filter(({ type }) => type === 'ui') : [] } - getHref(info?: InterfaceInfo): string | null { + getHref(info?: T.ServiceInterfaceWithHostInfo): string | null { return info && this.isRunning ? this.config.launchableAddress(info) : null } } diff --git a/web/projects/ui/src/app/apps/portal/routes/service/components/actions.component.ts b/web/projects/ui/src/app/apps/portal/routes/service/components/actions.component.ts index f385cd0b3..be3f11dcf 100644 --- a/web/projects/ui/src/app/apps/portal/routes/service/components/actions.component.ts +++ b/web/projects/ui/src/app/apps/portal/routes/service/components/actions.component.ts @@ -8,20 +8,19 @@ import { tuiPure } from '@taiga-ui/cdk' import { TuiButtonModule } from '@taiga-ui/experimental' import { DependencyInfo } from 'src/app/apps/portal/routes/service/types/dependency-info' import { ActionsService } from 'src/app/apps/portal/services/actions.service' -import { - PackageDataEntry, - PackageMainStatus, -} from 'src/app/services/patch-db/data-model' +import { PackageDataEntry } from 'src/app/services/patch-db/data-model' +import { Manifest } from '@start9labs/marketplace' +import { getManifest } from 'src/app/util/get-package-data' @Component({ selector: 'service-actions', template: ` - @if (isRunning) { + @if (pkg.status.main.status === 'running') { @@ -30,17 +29,17 @@ import { tuiButton appearance="secondary" iconLeft="tuiIconRotateCw" - (click)="actions.restart(service)" + (click)="actions.restart(manifest)" > Restart } - @if (isStopped && isConfigured) { + @if (pkg.status.main.status === 'stopped' && isConfigured) { @@ -51,7 +50,7 @@ import { tuiButton appearance="secondary-warning" iconLeft="tuiIconTool" - (click)="actions.configure(service)" + (click)="actions.configure(manifest)" > Configure @@ -64,7 +63,7 @@ import { }) export class ServiceActionsComponent { @Input({ required: true }) - service!: PackageDataEntry + pkg!: PackageDataEntry @Input({ required: true }) dependencies: readonly DependencyInfo[] = [] @@ -72,19 +71,11 @@ export class ServiceActionsComponent { readonly actions = inject(ActionsService) get isConfigured(): boolean { - return this.service.installed!.status.configured + return this.pkg.status.configured } - get isRunning(): boolean { - return ( - this.service.installed?.status.main.status === PackageMainStatus.Running - ) - } - - get isStopped(): boolean { - return ( - this.service.installed?.status.main.status === PackageMainStatus.Stopped - ) + get manifest(): Manifest { + return getManifest(this.pkg) } @tuiPure diff --git a/web/projects/ui/src/app/apps/portal/routes/service/components/additional.component.ts b/web/projects/ui/src/app/apps/portal/routes/service/components/additional.component.ts index db233569d..8678b9cc0 100644 --- a/web/projects/ui/src/app/apps/portal/routes/service/components/additional.component.ts +++ b/web/projects/ui/src/app/apps/portal/routes/service/components/additional.component.ts @@ -8,7 +8,7 @@ import { ServiceAdditionalItemComponent } from './additional-item.component' selector: 'service-additional', template: `

    Additional Info

    - +
    @@ -35,10 +35,10 @@ import { ExtendedInterfaceInfo } from '../pipes/interface-info.pipe' standalone: true, imports: [TuiButtonModule, CommonModule, TuiSvgModule], }) -export class ServiceInterfaceComponent { +export class ServiceInterfaceListItemComponent { private readonly config = inject(ConfigService) - @Input({ required: true, alias: 'serviceInterface' }) + @Input({ required: true, alias: 'serviceInterfaceListItem' }) info!: ExtendedInterfaceInfo @Input() diff --git a/web/projects/ui/src/app/apps/portal/routes/service/components/interface-list.component.ts b/web/projects/ui/src/app/apps/portal/routes/service/components/interface-list.component.ts new file mode 100644 index 000000000..c83258c0b --- /dev/null +++ b/web/projects/ui/src/app/apps/portal/routes/service/components/interface-list.component.ts @@ -0,0 +1,36 @@ +import { NgForOf } from '@angular/common' +import { ChangeDetectionStrategy, Component, Input } from '@angular/core' +import { PackageStatus } from 'src/app/services/pkg-status-rendering.service' +import { InterfaceInfoPipe } from '../pipes/interface-info.pipe' +import { ServiceInterfaceListItemComponent } from './interface-list-item.component' +import { RouterLink } from '@angular/router' +import { PackageDataEntry } from 'src/app/services/patch-db/data-model' + +@Component({ + selector: 'service-interface-list', + template: ` +

    Service Interfaces

    +
    + `, + changeDetection: ChangeDetectionStrategy.OnPush, + standalone: true, + imports: [ + NgForOf, + RouterLink, + InterfaceInfoPipe, + ServiceInterfaceListItemComponent, + ], +}) +export class ServiceInterfaceListComponent { + @Input({ required: true }) + pkg!: PackageDataEntry + + @Input({ required: true }) + status!: PackageStatus +} diff --git a/web/projects/ui/src/app/apps/portal/routes/service/components/interfaces.component.ts b/web/projects/ui/src/app/apps/portal/routes/service/components/interfaces.component.ts deleted file mode 100644 index 993a1f9a8..000000000 --- a/web/projects/ui/src/app/apps/portal/routes/service/components/interfaces.component.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { NgForOf } from '@angular/common' -import { ChangeDetectionStrategy, Component, Input } from '@angular/core' -import { PackagePlus } from 'src/app/services/patch-db/data-model' -import { - PackageStatus, - PrimaryStatus, -} from 'src/app/services/pkg-status-rendering.service' -import { InterfaceInfoPipe } from '../pipes/interface-info.pipe' -import { ServiceInterfaceComponent } from './interface.component' -import { RouterLink } from '@angular/router' - -@Component({ - selector: 'service-interfaces', - template: ` -

    Interfaces

    - - `, - changeDetection: ChangeDetectionStrategy.OnPush, - standalone: true, - imports: [NgForOf, RouterLink, InterfaceInfoPipe, ServiceInterfaceComponent], -}) -export class ServiceInterfacesComponent { - @Input({ required: true }) - service!: PackagePlus - - isRunning({ primary }: PackageStatus): boolean { - return primary === PrimaryStatus.Running - } -} diff --git a/web/projects/ui/src/app/apps/portal/routes/service/components/menu.component.ts b/web/projects/ui/src/app/apps/portal/routes/service/components/menu.component.ts index da4d4c3ff..e1fa250ae 100644 --- a/web/projects/ui/src/app/apps/portal/routes/service/components/menu.component.ts +++ b/web/projects/ui/src/app/apps/portal/routes/service/components/menu.component.ts @@ -8,7 +8,7 @@ import { RouterLink } from '@angular/router' selector: 'service-menu', template: `

    Menu

    - @for (menu of service | toMenu; track $index) { + @for (menu of pkg | toMenu; track $index) { @if (menu.routerLink) { @if (menu.name === 'Outbound Proxy') { -
    {{ proxy }}
    +
    {{ pkg.outboundProxy || 'None' }}
    } } @@ -35,22 +35,11 @@ import { RouterLink } from '@angular/router' }) export class ServiceMenuComponent { @Input({ required: true }) - service!: PackageDataEntry + pkg!: PackageDataEntry get color(): string { - return this.service.installed?.outboundProxy + return this.pkg.outboundProxy ? 'var(--tui-success-fill)' : 'var(--tui-warning-fill)' } - - get proxy(): string { - switch (this.service.installed?.outboundProxy) { - case 'primary': - return 'System Primary' - case 'mirror': - return 'Mirror P2P' - default: - return this.service.installed?.outboundProxy?.proxyId || 'None' - } - } } diff --git a/web/projects/ui/src/app/apps/portal/routes/service/components/progress.component.ts b/web/projects/ui/src/app/apps/portal/routes/service/components/progress.component.ts index 9fda9bf54..2e15f4f02 100644 --- a/web/projects/ui/src/app/apps/portal/routes/service/components/progress.component.ts +++ b/web/projects/ui/src/app/apps/portal/routes/service/components/progress.component.ts @@ -1,27 +1,30 @@ import { ChangeDetectionStrategy, Component, Input } from '@angular/core' import { TuiProgressModule } from '@taiga-ui/kit' +import { Progress } from 'src/app/services/patch-db/data-model' +import { InstallingProgressPipe } from '../pipes/install-progress.pipe' @Component({ selector: '[progress]', template: ` - : {{ progress }}% - + @if (progress | installingProgress; as decimal) { + : {{ decimal * 100 }}% + + } `, styles: [':host { line-height: 2rem }'], changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, - imports: [TuiProgressModule], + imports: [TuiProgressModule, InstallingProgressPipe], }) export class ServiceProgressComponent { - @Input({ required: true }) - progress = 0 + @Input({ required: true }) progress!: Progress } diff --git a/web/projects/ui/src/app/apps/portal/routes/service/components/credential.component.ts b/web/projects/ui/src/app/apps/portal/routes/service/components/property.component.ts similarity index 94% rename from web/projects/ui/src/app/apps/portal/routes/service/components/credential.component.ts rename to web/projects/ui/src/app/apps/portal/routes/service/components/property.component.ts index 30b8f1514..67cde13e2 100644 --- a/web/projects/ui/src/app/apps/portal/routes/service/components/credential.component.ts +++ b/web/projects/ui/src/app/apps/portal/routes/service/components/property.component.ts @@ -10,7 +10,7 @@ import { TuiLabelModule } from '@taiga-ui/core' import { TuiButtonModule } from '@taiga-ui/experimental' @Component({ - selector: 'service-credential', + selector: 'service-property', template: `
    -
    + } `, changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, - imports: [CommonModule, ServiceActionComponent, GroupActionsPipe], + imports: [CommonModule, ServiceActionComponent, GroupActionsPipe, ToManifestPipe], }) export class ServiceActionsRoute { private readonly id = getPkgId(inject(ActivatedRoute)) readonly pkg$ = this.patch - .watch$('package-data', this.id) - .pipe(filter(pkg => pkg.state === PackageState.Installed)) + .watch$('packageData', this.id) + .pipe(filter(pkg => pkg.stateInfo.state === PackageState.Installed)) readonly action = { icon: 'tuiIconTrash2Large', @@ -85,7 +87,7 @@ export class ServiceActionsRoute { private readonly formDialog: FormDialogService, ) {} - async handleAction(action: WithId) { + async handleAction(action: WithId) { if (action.disabled) { this.dialogs .open(action.disabled, { @@ -94,11 +96,11 @@ export class ServiceActionsRoute { }) .subscribe() } else { - if (action['input-spec'] && !isEmptyObject(action['input-spec'])) { + if (action.input && !isEmptyObject(action.input)) { this.formDialog.open(FormComponent, { label: action.name, data: { - spec: action['input-spec'], + spec: action.input, buttons: [ { text: 'Execute', @@ -128,13 +130,13 @@ export class ServiceActionsRoute { } async tryUninstall(pkg: PackageDataEntry): Promise { - const { title, alerts } = pkg.manifest + const { title, alerts, id } = getManifest(pkg) let content = alerts.uninstall || `Uninstalling ${title} will permanently delete its data` - if (hasCurrentDeps(pkg)) { + if (hasCurrentDeps(id, await getAllPackages(this.patch))) { content = `${content}. Services that depend on ${title} will no longer work properly and may crash` } @@ -177,7 +179,7 @@ export class ServiceActionsRoute { try { const data = await this.embassyApi.executePackageAction({ id: this.id, - 'action-id': actionId, + actionId, input, }) diff --git a/web/projects/ui/src/app/apps/portal/routes/service/routes/interface.component.ts b/web/projects/ui/src/app/apps/portal/routes/service/routes/interface.component.ts index f63f07300..57b5228dc 100644 --- a/web/projects/ui/src/app/apps/portal/routes/service/routes/interface.component.ts +++ b/web/projects/ui/src/app/apps/portal/routes/service/routes/interface.component.ts @@ -3,21 +3,22 @@ import { ChangeDetectionStrategy, Component, inject } from '@angular/core' import { ActivatedRoute } from '@angular/router' import { getPkgId } from '@start9labs/shared' import { PatchDB } from 'patch-db-client' -import { InterfacesComponent } from 'src/app/apps/portal/components/interfaces/interfaces.component' +import { map } from 'rxjs' +import { InterfaceComponent } from 'src/app/apps/portal/components/interfaces/interface.component' import { DataModel } from 'src/app/services/patch-db/data-model' +import { getAddresses } from '../../../components/interfaces/interface.utils' @Component({ template: ` - `, changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, - imports: [CommonModule, InterfacesComponent], + imports: [CommonModule, InterfaceComponent], }) export class ServiceInterfaceRoute { private readonly route = inject(ActivatedRoute) @@ -27,11 +28,17 @@ export class ServiceInterfaceRoute { interfaceId: this.route.snapshot.paramMap.get('interfaceId') || '', } - readonly interfaceInfo$ = inject(PatchDB).watch$( - 'package-data', - this.context.packageId, - 'state-info', - 'interfaceInfo', - this.context.interfaceId, - ) + readonly interfaceInfo$ = inject(PatchDB) + .watch$( + 'packageData', + this.context.packageId, + 'serviceInterfaces', + this.context.interfaceId, + ) + .pipe( + map(info => ({ + ...info, + addresses: getAddresses(info), + })), + ) } diff --git a/web/projects/ui/src/app/apps/portal/routes/service/routes/outlet.component.ts b/web/projects/ui/src/app/apps/portal/routes/service/routes/outlet.component.ts index 733f796f2..66fdd7c08 100644 --- a/web/projects/ui/src/app/apps/portal/routes/service/routes/outlet.component.ts +++ b/web/projects/ui/src/app/apps/portal/routes/service/routes/outlet.component.ts @@ -24,7 +24,7 @@ export class ServiceOutletComponent { map(() => this.route.firstChild?.snapshot.paramMap?.get('pkgId')), filter(Boolean), distinctUntilChanged(), - switchMap(id => this.patch.watch$('package-data', id)), + switchMap(id => this.patch.watch$('packageData', id)), tap(pkg => { // if package disappears, navigate to list page if (!pkg) { diff --git a/web/projects/ui/src/app/apps/portal/routes/service/routes/service.component.ts b/web/projects/ui/src/app/apps/portal/routes/service/routes/service.component.ts index 152767ede..f9dd999dd 100644 --- a/web/projects/ui/src/app/apps/portal/routes/service/routes/service.component.ts +++ b/web/projects/ui/src/app/apps/portal/routes/service/routes/service.component.ts @@ -2,7 +2,7 @@ import { CommonModule } from '@angular/common' import { ChangeDetectionStrategy, Component, inject } from '@angular/core' import { ActivatedRoute, NavigationExtras, Router } from '@angular/router' import { Manifest } from '@start9labs/marketplace' -import { getPkgId, isEmptyObject } from '@start9labs/shared' +import { isEmptyObject } from '@start9labs/shared' import { PatchDB } from 'patch-db-client' import { combineLatest, map, switchMap } from 'rxjs' import { ConnectionService } from 'src/app/services/connection.service' @@ -15,15 +15,12 @@ import { FormDialogService } from 'src/app/services/form-dialog.service' import { DataModel, HealthCheckResult, - InstalledPackageInfo, MainStatus, PackageDataEntry, - PackageState, } from 'src/app/services/patch-db/data-model' import { PackageStatus, PrimaryRendering, - PrimaryStatus, renderPkgStatus, StatusRendering, } from 'src/app/services/pkg-status-rendering.service' @@ -32,7 +29,7 @@ import { ServiceActionsComponent } from '../components/actions.component' import { ServiceAdditionalComponent } from '../components/additional.component' import { ServiceDependenciesComponent } from '../components/dependencies.component' import { ServiceHealthChecksComponent } from '../components/health-checks.component' -import { ServiceInterfacesComponent } from '../components/interfaces.component' +import { ServiceInterfaceListComponent } from '../components/interface-list.component' import { ServiceMenuComponent } from '../components/menu.component' import { ServiceProgressComponent } from '../components/progress.component' import { ServiceStatusComponent } from '../components/status.component' @@ -40,43 +37,60 @@ import { PackageConfigData, ServiceConfigModal, } from 'src/app/apps/portal/modals/config.component' -import { ProgressDataPipe } from '../pipes/progress-data.pipe' import { DependencyInfo } from '../types/dependency-info' - -const STATES = [ - PackageState.Installing, - PackageState.Updating, - PackageState.Restoring, -] +import { getManifest } from 'src/app/util/get-package-data' +import { InstallingProgressPipe } from 'src/app/apps/portal/routes/service/pipes/install-progress.pipe' @Component({ template: ` @if (service$ | async; as service) { - @if (showProgress(service.pkg)) { - @if (service.pkg | progressData; as progress) { -

    Downloading

    -

    Validating

    -

    Unpacking

    - } +

    Status

    + + + @if ( + service.pkg.stateInfo.state === 'installing' || + service.pkg.stateInfo.state === 'updating' || + service.pkg.stateInfo.state === 'restoring' + ) { +

    + {{ phase.name }} +

    } @else { -

    Status

    - + @if ( + service.pkg.stateInfo.state === 'installed' && + service.status.primary !== 'backing-up' + ) { + @if (connected$ | async) { + + } - @if (isInstalled(service.pkg) && (connected$ | async)) { - - } - @if (isInstalled(service.pkg) && !isBackingUp(service.status)) { - - - @if (isRunning(service.status) && (health$ | async); as checks) { + @if ( + service.status.primary === 'running' && (health$ | async); + as checks + ) { } @@ -84,8 +98,8 @@ const STATES = [ } - - + + } } } @@ -94,17 +108,15 @@ const STATES = [ standalone: true, imports: [ CommonModule, - ServiceProgressComponent, ServiceStatusComponent, ServiceActionsComponent, - ServiceInterfacesComponent, + ServiceInterfaceListComponent, ServiceHealthChecksComponent, ServiceDependenciesComponent, ServiceMenuComponent, ServiceAdditionalComponent, - - ProgressDataPipe, + InstallingProgressPipe, ], }) export class ServiceRoute { @@ -115,12 +127,12 @@ export class ServiceRoute { private readonly depErrorService = inject(DepErrorService) private readonly router = inject(Router) private readonly formDialog = inject(FormDialogService) - readonly connected$ = inject(ConnectionService).connected$ + readonly service$ = this.pkgId$.pipe( switchMap(pkgId => combineLatest([ - this.patch.watch$('package-data', pkgId), + this.patch.watch$('packageData', pkgId), this.depErrorService.getPkgDepErrors$(pkgId), ]), ), @@ -132,9 +144,10 @@ export class ServiceRoute { } }), ) + readonly health$ = this.pkgId$.pipe( switchMap(pkgId => - this.patch.watch$('package-data', pkgId, 'installed', 'status', 'main'), + this.patch.watch$('packageData', pkgId, 'status', 'main'), ), map(toHealthCheck), ) @@ -143,53 +156,35 @@ export class ServiceRoute { return PrimaryRendering[primary] } - isInstalled({ state }: PackageDataEntry): boolean { - return state === PackageState.Installed - } - - isRunning({ primary }: PackageStatus): boolean { - return primary === PrimaryStatus.Running - } - - isBackingUp({ primary }: PackageStatus): boolean { - return primary === PrimaryStatus.BackingUp - } - - showProgress({ state }: PackageDataEntry): boolean { - return STATES.includes(state) - } - private getDepInfo( - { installed, manifest }: PackageDataEntry, + pkg: PackageDataEntry, depErrors: PkgDependencyErrors, ): DependencyInfo[] { - return installed - ? Object.keys(installed['current-dependencies']) - .filter(depId => !!manifest.dependencies[depId]) - .map(depId => - this.getDepValues(installed, manifest, depId, depErrors), - ) - : [] + const manifest = getManifest(pkg) + + return Object.keys(pkg.currentDependencies) + .filter(id => !!manifest.dependencies[id]) + .map(id => this.getDepValues(pkg, manifest, id, depErrors)) } private getDepValues( - pkgInstalled: InstalledPackageInfo, + pkg: PackageDataEntry, pkgManifest: Manifest, depId: string, depErrors: PkgDependencyErrors, ): DependencyInfo { const { errorText, fixText, fixAction } = this.getDepErrors( - pkgInstalled, + pkg, pkgManifest, depId, depErrors, ) - const depInfo = pkgInstalled['dependency-info'][depId] + const depInfo = pkg.dependencyInfo[depId] return { id: depId, - version: pkgManifest.dependencies[depId].version, // do we want this version range? + version: pkg.currentDependencies[depId].versionRange, title: depInfo?.title || depId, icon: depInfo?.icon || '', errorText: errorText @@ -205,12 +200,12 @@ export class ServiceRoute { } private getDepErrors( - pkgInstalled: InstalledPackageInfo, + pkg: PackageDataEntry, pkgManifest: Manifest, depId: string, depErrors: PkgDependencyErrors, ) { - const depError = (depErrors[pkgManifest.id] as any)?.[depId] // @TODO fix + const depError = depErrors[pkgManifest.id] let errorText: string | null = null let fixText: string | null = null @@ -220,18 +215,15 @@ export class ServiceRoute { if (depError.type === DependencyErrorType.NotInstalled) { errorText = 'Not installed' fixText = 'Install' - fixAction = () => - this.fixDep(pkgInstalled, pkgManifest, 'install', depId) + fixAction = () => this.fixDep(pkg, pkgManifest, 'install', depId) } else if (depError.type === DependencyErrorType.IncorrectVersion) { errorText = 'Incorrect version' fixText = 'Update' - fixAction = () => - this.fixDep(pkgInstalled, pkgManifest, 'update', depId) + fixAction = () => this.fixDep(pkg, pkgManifest, 'update', depId) } else if (depError.type === DependencyErrorType.ConfigUnsatisfied) { errorText = 'Config not satisfied' fixText = 'Auto config' - fixAction = () => - this.fixDep(pkgInstalled, pkgManifest, 'configure', depId) + fixAction = () => this.fixDep(pkg, pkgManifest, 'configure', depId) } else if (depError.type === DependencyErrorType.NotRunning) { errorText = 'Not running' fixText = 'Start' @@ -250,7 +242,7 @@ export class ServiceRoute { } async fixDep( - pkgInstalled: InstalledPackageInfo, + pkg: PackageDataEntry, pkgManifest: Manifest, action: 'install' | 'update' | 'configure', depId: string, @@ -258,10 +250,10 @@ export class ServiceRoute { switch (action) { case 'install': case 'update': - return this.installDep(pkgManifest, depId) + return this.installDep(pkg, pkgManifest, depId) case 'configure': return this.formDialog.open(ServiceConfigModal, { - label: `${pkgInstalled!['dependency-info'][depId].title} config`, + label: `${pkg.dependencyInfo[depId].title} config`, data: { pkgId: depId, dependentInfo: pkgManifest, @@ -270,13 +262,15 @@ export class ServiceRoute { } } - private async installDep(manifest: Manifest, depId: string): Promise { - const version = manifest.dependencies[depId].version - + private async installDep( + pkg: PackageDataEntry, + manifest: Manifest, + depId: string, + ): Promise { const dependentInfo: DependentInfo = { id: manifest.id, title: manifest.title, - version, + version: pkg.currentDependencies[depId].versionRange, } const navigationExtras: NavigationExtras = { state: { dependentInfo }, diff --git a/web/projects/ui/src/app/apps/portal/routes/system/backups/components/status.component.ts b/web/projects/ui/src/app/apps/portal/routes/system/backups/components/status.component.ts index a021438f3..d8c58b8aa 100644 --- a/web/projects/ui/src/app/apps/portal/routes/system/backups/components/status.component.ts +++ b/web/projects/ui/src/app/apps/portal/routes/system/backups/components/status.component.ts @@ -62,8 +62,8 @@ export class BackupsStatusComponent { private get hasBackup(): boolean { return ( - !!this.target['embassy-os'] && - this.emver.compare(this.target['embassy-os'].version, '0.3.0') !== -1 + !!this.target.startOs && + this.emver.compare(this.target.startOs.version, '0.3.0') !== -1 ) } } diff --git a/web/projects/ui/src/app/apps/portal/routes/system/backups/components/upcoming.component.ts b/web/projects/ui/src/app/apps/portal/routes/system/backups/components/upcoming.component.ts index 855231d76..55d67b043 100644 --- a/web/projects/ui/src/app/apps/portal/routes/system/backups/components/upcoming.component.ts +++ b/web/projects/ui/src/app/apps/portal/routes/system/backups/components/upcoming.component.ts @@ -30,7 +30,7 @@ import { GetBackupIconPipe } from '../pipes/get-backup-icon.pipe' Running - {{ job.next | date : 'MMM d, y, h:mm a' }} + {{ job.next | date: 'MMM d, y, h:mm a' }} {{ job.name }} @@ -38,7 +38,7 @@ import { GetBackupIconPipe } from '../pipes/get-backup-icon.pipe' {{ job.target.name }} - Packages: {{ job['package-ids'].length }} + Packages: {{ job.packageIds.length }} You have no active or upcoming backup jobs @@ -56,7 +56,7 @@ import { GetBackupIconPipe } from '../pipes/get-backup-icon.pipe' }) export class BackupsUpcomingComponent { readonly current$ = inject(PatchDB) - .watch$('server-info', 'status-info', 'current-backup', 'job') + .watch$('serverInfo', 'statusInfo', 'currentBackup', 'job') .pipe(map(job => job || {})) readonly upcoming$ = from(inject(ApiService).getBackupJobs({})).pipe( diff --git a/web/projects/ui/src/app/apps/portal/routes/system/backups/modals/backup.component.ts b/web/projects/ui/src/app/apps/portal/routes/system/backups/modals/backup.component.ts index a061f5c28..833fedf48 100644 --- a/web/projects/ui/src/app/apps/portal/routes/system/backups/modals/backup.component.ts +++ b/web/projects/ui/src/app/apps/portal/routes/system/backups/modals/backup.component.ts @@ -17,6 +17,7 @@ import { import { PatchDB } from 'patch-db-client' import { firstValueFrom, map } from 'rxjs' import { DataModel, PackageState } from 'src/app/services/patch-db/data-model' +import { getManifest } from 'src/app/util/get-package-data' interface Package { id: string @@ -90,16 +91,19 @@ export class BackupsBackupModal { async ngOnInit() { this.pkgs = await firstValueFrom( - this.patch.watch$('package-data').pipe( + this.patch.watch$('packageData').pipe( map(pkgs => Object.values(pkgs) - .map(({ manifest: { id, title }, icon, state }) => ({ - id, - title, - icon, - disabled: state !== PackageState.Installed, - checked: false, - })) + .map(pkg => { + const { id, title } = getManifest(pkg) + return { + id, + title, + icon: pkg.icon, + disabled: pkg.stateInfo.state !== PackageState.Installed, + checked: false, + } + }) .sort((a, b) => b.title.toLowerCase() > a.title.toLowerCase() ? -1 : 1, ), diff --git a/web/projects/ui/src/app/apps/portal/routes/system/backups/modals/edit.component.ts b/web/projects/ui/src/app/apps/portal/routes/system/backups/modals/edit.component.ts index e6219b6b7..5bdd6c7b9 100644 --- a/web/projects/ui/src/app/apps/portal/routes/system/backups/modals/edit.component.ts +++ b/web/projects/ui/src/app/apps/portal/routes/system/backups/modals/edit.component.ts @@ -53,10 +53,8 @@ import { ToHumanCronPipe } from '../pipes/to-human-cron.pipe' (click)="selectPackages()" > Packages - - {{ job['package-ids'].length + ' selected' }} + + {{ job.packageIds.length + ' selected' }} @@ -145,7 +143,7 @@ export class BackupsEditModal { selectPackages() { this.dialogs.open(BACKUP, BACKUP_OPTIONS).subscribe(id => { - this.job['package-ids'] = id + this.job.packageIds = id }) } } diff --git a/web/projects/ui/src/app/apps/portal/routes/system/backups/modals/history.component.ts b/web/projects/ui/src/app/apps/portal/routes/system/backups/modals/history.component.ts index fd3a59d8d..2506249c1 100644 --- a/web/projects/ui/src/app/apps/portal/routes/system/backups/modals/history.component.ts +++ b/web/projects/ui/src/app/apps/portal/routes/system/backups/modals/history.component.ts @@ -62,10 +62,8 @@ import { GetBackupIconPipe } from '../pipes/get-backup-icon.pipe' [style.background]="selected[index] ? 'var(--tui-clear)' : ''" > - {{ run['started-at'] | date : 'medium' }} - - {{ run['started-at'] | duration : run['completed-at'] }} Minutes - + {{ run.startedAt | date: 'medium' }} + {{ run.startedAt | duration: run.completedAt }} Minutes - Scheduling automatic backups is an excellent way to ensure your Embassy - data is safely backed up. Your Embassy will issue a notification whenever - one of your scheduled backups succeeds or fails. + Scheduling automatic backups is an excellent way to ensure your StartOS + data is safely backed up. StartOS will issue a notification whenever one + of your scheduled backups succeeds or fails.
    {{ job.target.name }} - Packages: {{ job['package-ids'].length }} + Packages: {{ job.packageIds.length }} {{ (job.cron | toHumanCron).message }} - @if (installed) { - @switch (localVersion | compareEmver: pkg.manifest.version) { + @if ( + localPkg.stateInfo.state === 'installed' && (localPkg | toManifest); + as localManifest + ) { + @switch (localManifest.version | compareEmver: pkg.manifest.version) { @case (1) {
    @@ -76,7 +70,7 @@ export class SettingsMenuComponent { private readonly clientStorageService = inject(ClientStorageService) private readonly alerts = inject(TuiAlertService) - readonly server$ = inject(PatchDB).watch$('server-info') + readonly server$ = inject(PatchDB).watch$('serverInfo') readonly service = inject(SettingsService) manageClicks = 0 diff --git a/web/projects/ui/src/app/apps/portal/routes/system/settings/modals/update.component.ts b/web/projects/ui/src/app/apps/portal/routes/system/settings/modals/update.component.ts index f300c5b6e..b0da46104 100644 --- a/web/projects/ui/src/app/apps/portal/routes/system/settings/modals/update.component.ts +++ b/web/projects/ui/src/app/apps/portal/routes/system/settings/modals/update.component.ts @@ -48,7 +48,7 @@ import { EOSService } from 'src/app/services/eos.service' ], }) export class SettingsUpdateModal { - readonly versions = Object.entries(this.eosService.eos?.['release-notes']!) + readonly versions = Object.entries(this.eosService.eos?.releaseNotes!) .sort(([a], [b]) => a.localeCompare(b)) .reverse() .map(([version, notes]) => ({ diff --git a/web/projects/ui/src/app/apps/portal/routes/system/settings/routes/domains/constants.ts b/web/projects/ui/src/app/apps/portal/routes/system/settings/routes/domains/constants.ts index 2d93ab758..d56278b1e 100644 --- a/web/projects/ui/src/app/apps/portal/routes/system/settings/routes/domains/constants.ts +++ b/web/projects/ui/src/app/apps/portal/routes/system/settings/routes/domains/constants.ts @@ -1,6 +1,6 @@ -import { Config } from '@start9labs/start-sdk/lib/config/builder/config' -import { Value } from '@start9labs/start-sdk/lib/config/builder/value' -import { Variants } from '@start9labs/start-sdk/lib/config/builder/variants' +import { Config } from '@start9labs/start-sdk/cjs/sdk/lib/config/builder/config' +import { Value } from '@start9labs/start-sdk/cjs/sdk/lib/config/builder/value' +import { Variants } from '@start9labs/start-sdk/cjs/sdk/lib/config/builder/variants' import { Proxy } from 'src/app/services/patch-db/data-model' import { configBuilderToSpec } from 'src/app/util/configBuilderToSpec' @@ -59,38 +59,18 @@ function getStrategyUnion(proxies: Proxy[]) { proxy: { name: 'Proxy', spec: Config.of({ - proxyStrategy: Value.union( - { - name: 'Proxy Strategy', - required: { default: 'primary' }, - description: `
    Primary
    Use the Primary Inbound proxy from your proxy settings. If you do not have any inbound proxies, no proxy will be used -
    Other
    Use a specific proxy from your proxy settings -`, - }, - Variants.of({ - primary: { - name: 'Primary', - spec: Config.of({}), - }, - other: { - name: 'Specific', - spec: Config.of({ - proxyId: Value.select({ - name: 'Select Proxy', - required: { default: null }, - values: inboundProxies, - }), - }), - }, - }), - ), + proxyId: Value.select({ + name: 'Select Proxy', + required: { default: null }, + values: inboundProxies, + }), }), }, }), ) } -export async function getStart9ToSpec(proxies: Proxy[]) { +export function getStart9ToSpec(proxies: Proxy[]) { return configBuilderToSpec( Config.of({ strategy: getStrategyUnion(proxies), @@ -98,7 +78,7 @@ export async function getStart9ToSpec(proxies: Proxy[]) { ) } -export async function getCustomSpec(proxies: Proxy[]) { +export function getCustomSpec(proxies: Proxy[]) { return configBuilderToSpec( Config.of({ hostname: Value.text({ diff --git a/web/projects/ui/src/app/apps/portal/routes/system/settings/routes/domains/domains.component.ts b/web/projects/ui/src/app/apps/portal/routes/system/settings/routes/domains/domains.component.ts index 0ec33bbf9..7780babf7 100644 --- a/web/projects/ui/src/app/apps/portal/routes/system/settings/routes/domains/domains.component.ts +++ b/web/projects/ui/src/app/apps/portal/routes/system/settings/routes/domains/domains.component.ts @@ -69,7 +69,7 @@ export class SettingsDomainsComponent { private readonly api = inject(ApiService) private readonly dialogs = inject(TuiDialogService) - readonly domains$ = this.patch.watch$('server-info', 'network').pipe( + readonly domains$ = this.patch.watch$('serverInfo', 'network').pipe( map(network => { const start9ToSubdomain = network.start9ToSubdomain const start9To = !start9ToSubdomain @@ -103,7 +103,7 @@ export class SettingsDomainsComponent { async add() { const proxies = await firstValueFrom( - this.patch.watch$('server-info', 'network', 'proxies'), + this.patch.watch$('serverInfo', 'network', 'proxies'), ) const options: Partial>> = { @@ -128,7 +128,7 @@ export class SettingsDomainsComponent { async claim() { const proxies = await firstValueFrom( - this.patch.watch$('server-info', 'network', 'proxies'), + this.patch.watch$('serverInfo', 'network', 'proxies'), ) const options: Partial>> = { @@ -150,13 +150,11 @@ export class SettingsDomainsComponent { this.formDialog.open(FormComponent, options) } - + // @TODO figure out how to get types here private getNetworkStrategy(strategy: any) { - const { ipStrategy, proxyStrategy = {} } = strategy.unionValueKey - const { unionSelectKey, unionValueKey = {} } = proxyStrategy - const proxyId = unionSelectKey === 'primary' ? null : unionValueKey.proxyId - - return strategy.unionSelectKey === 'local' ? { ipStrategy } : { proxyId } + return strategy.unionSelectKey === 'local' + ? { ipStrategy: strategy.unionValueKey.ipStrategy } + : { proxy: strategy.unionValueKey.proxyId } } private async deleteDomain(hostname?: string) { @@ -174,7 +172,7 @@ export class SettingsDomainsComponent { loader.unsubscribe() } } - + // @TODO figure out how to get types here private async claimDomain({ strategy }: any): Promise { const loader = this.loader.open('Saving...').subscribe() const networkStrategy = this.getNetworkStrategy(strategy) @@ -189,7 +187,7 @@ export class SettingsDomainsComponent { loader.unsubscribe() } } - + // @TODO figure out how to get types here private async save({ provider, strategy, hostname }: any): Promise { const loader = this.loader.open('Saving...').subscribe() const name = provider.unionSelectKey diff --git a/web/projects/ui/src/app/apps/portal/routes/system/settings/routes/email/email.component.ts b/web/projects/ui/src/app/apps/portal/routes/system/settings/routes/email/email.component.ts index c29a9213b..bb9267d86 100644 --- a/web/projects/ui/src/app/apps/portal/routes/system/settings/routes/email/email.component.ts +++ b/web/projects/ui/src/app/apps/portal/routes/system/settings/routes/email/email.component.ts @@ -9,8 +9,6 @@ import { TuiDialogService } from '@taiga-ui/core' import { TuiButtonModule } from '@taiga-ui/experimental' import { TuiInputModule } from '@taiga-ui/kit' import { ErrorService, LoadingService } from '@start9labs/shared' -import { InputSpec } from '@start9labs/start-sdk/lib/config/configTypes' -import { customSmtp } from '@start9labs/start-sdk/lib/config/configConstants' import { PatchDB } from 'patch-db-client' import { switchMap } from 'rxjs' import { FormModule } from 'src/app/common/form/form.module' @@ -19,6 +17,8 @@ import { DataModel } from 'src/app/services/patch-db/data-model' import { ApiService } from 'src/app/services/api/embassy-api.service' import { FormService } from 'src/app/services/form.service' import { EmailInfoComponent } from './info.component' +import { InputSpec } from '@start9labs/start-sdk/cjs/sdk/lib/config/configTypes' +import { customSmtp } from '@start9labs/start-sdk/cjs/sdk/lib/config/configConstants' @Component({ template: ` @@ -84,7 +84,7 @@ export class SettingsEmailComponent { testAddress = '' readonly spec: Promise = configBuilderToSpec(customSmtp) readonly form$ = this.patch - .watch$('server-info', 'smtp') + .watch$('serverInfo', 'smtp') .pipe( switchMap(async value => this.formService.createForm(await this.spec, value), diff --git a/web/projects/ui/src/app/apps/portal/routes/system/settings/routes/experimental/experimental.component.ts b/web/projects/ui/src/app/apps/portal/routes/system/settings/routes/experimental/experimental.component.ts index e70b1b875..bffc72bc8 100644 --- a/web/projects/ui/src/app/apps/portal/routes/system/settings/routes/experimental/experimental.component.ts +++ b/web/projects/ui/src/app/apps/portal/routes/system/settings/routes/experimental/experimental.component.ts @@ -93,37 +93,6 @@ export class SettingsExperimentalComponent { .subscribe(() => this.resetTor(this.wipe)) } - zram(enabled: boolean) { - this.dialogs - .open(TUI_PROMPT, { - label: 'Confirm', - data: { - content: enabled - ? 'Are you sure you want to disable zram? It provides significant performance benefits on low RAM devices.' - : 'Enable zram? It will only make a difference on lower RAM devices.', - yes: enabled ? 'Disable' : 'Enable', - no: 'Cancel', - }, - }) - .pipe(filter(Boolean)) - .subscribe(() => this.toggleZram(enabled)) - } - - private async toggleZram(enabled: boolean) { - const loader = this.loader - .open(enabled ? 'Disabling zram...' : 'Enabling zram...') - .subscribe() - - try { - await this.api.toggleZram({ enable: !enabled }) - this.alerts.open(`Zram ${enabled ? 'disabled' : 'enabled'}`).subscribe() - } catch (e: any) { - this.errorService.handleError(e) - } finally { - loader.unsubscribe() - } - } - private async resetTor(wipeState: boolean) { const loader = this.loader.open('Resetting Tor...').subscribe() diff --git a/web/projects/ui/src/app/apps/portal/routes/system/settings/routes/interfaces/interfaces.component.ts b/web/projects/ui/src/app/apps/portal/routes/system/settings/routes/interfaces/interfaces.component.ts deleted file mode 100644 index 53c4d4853..000000000 --- a/web/projects/ui/src/app/apps/portal/routes/system/settings/routes/interfaces/interfaces.component.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { CommonModule } from '@angular/common' -import { ChangeDetectionStrategy, Component, inject } from '@angular/core' -import { PatchDB } from 'patch-db-client' -import { InterfacesComponent } from 'src/app/apps/portal/components/interfaces/interfaces.component' -import { DataModel } from 'src/app/services/patch-db/data-model' - -@Component({ - template: ` - - `, - changeDetection: ChangeDetectionStrategy.OnPush, - standalone: true, - imports: [CommonModule, InterfacesComponent], -}) -export class SettingsInterfacesComponent { - readonly ui$ = inject(PatchDB).watch$('server-info', 'ui') -} diff --git a/web/projects/ui/src/app/apps/portal/routes/system/settings/routes/interfaces/ui.component.ts b/web/projects/ui/src/app/apps/portal/routes/system/settings/routes/interfaces/ui.component.ts new file mode 100644 index 000000000..84271ac19 --- /dev/null +++ b/web/projects/ui/src/app/apps/portal/routes/system/settings/routes/interfaces/ui.component.ts @@ -0,0 +1,70 @@ +import { CommonModule } from '@angular/common' +import { ChangeDetectionStrategy, Component, inject } from '@angular/core' +import { T } from '@start9labs/start-sdk' +import { PatchDB } from 'patch-db-client' +import { Observable, map } from 'rxjs' +import { + InterfaceComponent, + ServiceInterfaceWithAddresses, +} from 'src/app/apps/portal/components/interfaces/interface.component' +import { getAddresses } from 'src/app/apps/portal/components/interfaces/interface.utils' +import { DataModel } from 'src/app/services/patch-db/data-model' + +@Component({ + template: ` + + `, + changeDetection: ChangeDetectionStrategy.OnPush, + standalone: true, + imports: [CommonModule, InterfaceComponent], +}) +export class StartOsUiComponent { + readonly ui$: Observable = inject( + PatchDB, + ) + .watch$('serverInfo', 'ui') + .pipe( + map(hosts => { + const serviceInterface: T.ServiceInterfaceWithHostInfo = { + id: 'startos-ui', + name: 'StartOS UI', + description: 'The primary web user interface for StartOS', + type: 'ui', + hasPrimary: false, + disabled: false, + masked: false, + addressInfo: { + hostId: '', + username: null, + suffix: '', + bindOptions: { + scheme: 'http', + preferredExternalPort: 80, + addSsl: { + scheme: 'https', + preferredExternalPort: 443, + addXForwardedHeaders: null, + }, + secure: { + ssl: false, + }, + }, + }, + hostInfo: { + id: 'start-os-ui-host', + kind: 'multi', + hostnames: hosts, + }, + } + + return { + ...serviceInterface, + addresses: getAddresses(serviceInterface), + } + }), + ) +} diff --git a/web/projects/ui/src/app/apps/portal/routes/system/settings/routes/proxies/constants.ts b/web/projects/ui/src/app/apps/portal/routes/system/settings/routes/proxies/constants.ts index c2149c963..b7f68aef6 100644 --- a/web/projects/ui/src/app/apps/portal/routes/system/settings/routes/proxies/constants.ts +++ b/web/projects/ui/src/app/apps/portal/routes/system/settings/routes/proxies/constants.ts @@ -1,5 +1,5 @@ -import { Config } from '@start9labs/start-sdk/lib/config/builder/config' -import { Value } from '@start9labs/start-sdk/lib/config/builder/value' +import { Config } from '@start9labs/start-sdk/cjs/sdk/lib/config/builder/config' +import { Value } from '@start9labs/start-sdk/cjs/sdk/lib/config/builder/value' import { TuiDialogOptions } from '@taiga-ui/core' import { TuiPromptData } from '@taiga-ui/kit' @@ -27,8 +27,6 @@ export const wireguardSpec = Config.of({ }) export type WireguardSpec = typeof wireguardSpec.validator._TYPE -export type ProxyUpdate = Partial<{ +export type ProxyUpdate = { name: string - primaryInbound: true - primaryOutbound: true -}> +} diff --git a/web/projects/ui/src/app/apps/portal/routes/system/settings/routes/proxies/menu.component.ts b/web/projects/ui/src/app/apps/portal/routes/system/settings/routes/proxies/menu.component.ts index bff56bfcc..fa2867e6f 100644 --- a/web/projects/ui/src/app/apps/portal/routes/system/settings/routes/proxies/menu.component.ts +++ b/web/projects/ui/src/app/apps/portal/routes/system/settings/routes/proxies/menu.component.ts @@ -6,7 +6,6 @@ import { Input, } from '@angular/core' import { ErrorService, LoadingService } from '@start9labs/shared' -import { Value } from '@start9labs/start-sdk/lib/config/builder/value' import { TuiButtonModule } from '@taiga-ui/experimental' import { TuiDataListModule, @@ -25,6 +24,7 @@ import { Proxy } from 'src/app/services/patch-db/data-model' import { ApiService } from 'src/app/services/api/embassy-api.service' import { FormDialogService } from 'src/app/services/form-dialog.service' import { DELETE_OPTIONS, ProxyUpdate } from './constants' +import { Value } from '@start9labs/start-sdk/cjs/sdk/lib/config/builder/value' @Component({ selector: 'proxies-menu', @@ -45,23 +45,6 @@ import { DELETE_OPTIONS, ProxyUpdate } from './constants' - - @@ -105,20 +88,6 @@ export class ProxiesMenuComponent { }) } - async update(value: ProxyUpdate): Promise { - const loader = this.loader.open('Saving...').subscribe() - - try { - await this.api.updateProxy(value) - return true - } catch (e: any) { - this.errorService.handleError(e) - return false - } finally { - loader.unsubscribe() - } - } - async rename() { const spec = { name: 'Name', required: { default: this.proxy.name } } const name = await Value.text(spec).build({} as any) @@ -137,4 +106,18 @@ export class ProxiesMenuComponent { this.formDialog.open(FormComponent, options) } + + private async update(value: ProxyUpdate): Promise { + const loader = this.loader.open('Saving...').subscribe() + + try { + await this.api.updateProxy(value) + return true + } catch (e: any) { + this.errorService.handleError(e) + return false + } finally { + loader.unsubscribe() + } + } } diff --git a/web/projects/ui/src/app/apps/portal/routes/system/settings/routes/proxies/proxies.component.ts b/web/projects/ui/src/app/apps/portal/routes/system/settings/routes/proxies/proxies.component.ts index 3dfc3ae29..a1af1ec9c 100644 --- a/web/projects/ui/src/app/apps/portal/routes/system/settings/routes/proxies/proxies.component.ts +++ b/web/projects/ui/src/app/apps/portal/routes/system/settings/routes/proxies/proxies.component.ts @@ -1,7 +1,7 @@ import { CommonModule } from '@angular/common' import { ChangeDetectionStrategy, Component, inject } from '@angular/core' import { ErrorService, LoadingService } from '@start9labs/shared' -import { TuiDialogOptions, TuiDialogService } from '@taiga-ui/core' +import { TuiDialogOptions } from '@taiga-ui/core' import { TuiButtonModule } from '@taiga-ui/experimental' import { PatchDB } from 'patch-db-client' import { @@ -42,7 +42,7 @@ export class SettingsProxiesComponent { private readonly formDialog = inject(FormDialogService) readonly proxies$ = inject(PatchDB).watch$( - 'server-info', + 'serverInfo', 'network', 'proxies', ) diff --git a/web/projects/ui/src/app/apps/portal/routes/system/settings/routes/proxies/table.component.ts b/web/projects/ui/src/app/apps/portal/routes/system/settings/routes/proxies/table.component.ts index de31f515e..418e8e71a 100644 --- a/web/projects/ui/src/app/apps/portal/routes/system/settings/routes/proxies/table.component.ts +++ b/web/projects/ui/src/app/apps/portal/routes/system/settings/routes/proxies/table.component.ts @@ -20,7 +20,6 @@ import { ProxiesMenuComponent } from './menu.component' Name Created Type - Primary Used By @@ -28,21 +27,8 @@ import { ProxiesMenuComponent } from './menu.component' {{ proxy.name }} - {{ proxy.createdAt | date : 'short' }} + {{ proxy.createdAt | date: 'short' }} {{ proxy.type }} - - - Inbound - - - Outbound - -