@@ -41,7 +39,7 @@ import { DependencyInfo } from '../types/dependency-info'
],
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
- imports: [EmverPipesModule, TuiIcon],
+ imports: [ExverPipesModule, TuiIcon],
})
export class ServiceDependencyComponent {
@Input({ required: true, alias: 'serviceDependency' })
diff --git a/web/projects/ui/src/app/routes/portal/routes/service/components/health-checks.component.ts b/web/projects/ui/src/app/routes/portal/routes/service/components/health-checks.component.ts
index b82d127a4..e559e8a28 100644
--- a/web/projects/ui/src/app/routes/portal/routes/service/components/health-checks.component.ts
+++ b/web/projects/ui/src/app/routes/portal/routes/service/components/health-checks.component.ts
@@ -33,5 +33,5 @@ export class ServiceHealthChecksComponent {
@Input({ required: true })
checks: readonly T.HealthCheckResult[] = []
- readonly connected$ = inject(ConnectionService).connected$
+ readonly connected$ = inject(ConnectionService)
}
diff --git a/web/projects/ui/src/app/routes/portal/routes/service/components/interface-list-item.component.ts b/web/projects/ui/src/app/routes/portal/routes/service/components/interface-list-item.component.ts
index 26d64536d..334ea7e14 100644
--- a/web/projects/ui/src/app/routes/portal/routes/service/components/interface-list-item.component.ts
+++ b/web/projects/ui/src/app/routes/portal/routes/service/components/interface-list-item.component.ts
@@ -9,6 +9,7 @@ import {
} from '@angular/core'
import { map, timer } from 'rxjs'
import { ConfigService } from 'src/app/services/config.service'
+import { PackageDataEntry } from 'src/app/services/patch-db/data-model'
import { ExtendedInterfaceInfo } from '../pipes/interface-info.pipe'
@Component({
@@ -56,9 +57,12 @@ import { ExtendedInterfaceInfo } from '../pipes/interface-info.pipe'
export class ServiceInterfaceListItemComponent {
private readonly config = inject(ConfigService)
- @Input({ required: true, alias: 'serviceInterfaceListItem' })
+ @Input({ required: true })
info!: ExtendedInterfaceInfo
+ @Input({ required: true })
+ pkg!: PackageDataEntry
+
@Input()
disabled = false
@@ -68,6 +72,8 @@ export class ServiceInterfaceListItemComponent {
)
get href(): string | null {
- return this.disabled ? null : this.config.launchableAddress(this.info)
+ return this.disabled
+ ? null
+ : this.config.launchableAddress(this.info, this.pkg.hosts)
}
}
diff --git a/web/projects/ui/src/app/routes/portal/routes/service/components/interface-list.component.ts b/web/projects/ui/src/app/routes/portal/routes/service/components/interface-list.component.ts
index acd13ec08..de14745d6 100644
--- a/web/projects/ui/src/app/routes/portal/routes/service/components/interface-list.component.ts
+++ b/web/projects/ui/src/app/routes/portal/routes/service/components/interface-list.component.ts
@@ -11,7 +11,9 @@ import { ServiceInterfaceListItemComponent } from './interface-list-item.compone
@for (info of pkg | interfaceInfo; track $index) {
diff --git a/web/projects/ui/src/app/routes/portal/routes/service/components/status.component.ts b/web/projects/ui/src/app/routes/portal/routes/service/components/status.component.ts
index ed24b70a6..196115c91 100644
--- a/web/projects/ui/src/app/routes/portal/routes/service/components/status.component.ts
+++ b/web/projects/ui/src/app/routes/portal/routes/service/components/status.component.ts
@@ -1,4 +1,3 @@
-import { TuiLoader, TuiIcon } from '@taiga-ui/core'
import { CommonModule } from '@angular/common'
import {
ChangeDetectionStrategy,
@@ -6,10 +5,10 @@ import {
HostBinding,
Input,
} from '@angular/core'
+import { TuiIcon, TuiLoader } from '@taiga-ui/core'
+import { InstallingInfo } from 'src/app/services/patch-db/data-model'
import { StatusRendering } from 'src/app/services/pkg-status-rendering.service'
import { InstallingProgressDisplayPipe } from '../pipes/install-progress.pipe'
-import { InstallingInfo } from 'src/app/services/patch-db/data-model'
-import { UnitConversionPipesModule } from '@start9labs/shared'
@Component({
selector: 'service-status',
@@ -27,9 +26,6 @@ import { UnitConversionPipesModule } from '@start9labs/shared'
@if (rendering.showDots) {
}
- @if (sigtermTimeout && (sigtermTimeout | durationToSeconds) > 30) {
-
This may take a while
- }
}
`,
styles: [
@@ -58,13 +54,7 @@ import { UnitConversionPipesModule } from '@start9labs/shared'
],
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
- imports: [
- CommonModule,
- InstallingProgressDisplayPipe,
- UnitConversionPipesModule,
- TuiIcon,
- TuiLoader,
- ],
+ imports: [CommonModule, InstallingProgressDisplayPipe, TuiIcon, TuiLoader],
})
export class ServiceStatusComponent {
@Input({ required: true })
@@ -76,8 +66,6 @@ export class ServiceStatusComponent {
@Input()
connected = false
- @Input() sigtermTimeout?: string | null = null
-
@HostBinding('class')
get class(): string | null {
if (!this.connected) return null
diff --git a/web/projects/ui/src/app/routes/portal/routes/service/pipes/interface-info.pipe.ts b/web/projects/ui/src/app/routes/portal/routes/service/pipes/interface-info.pipe.ts
index 9a831ad7a..2f1604732 100644
--- a/web/projects/ui/src/app/routes/portal/routes/service/pipes/interface-info.pipe.ts
+++ b/web/projects/ui/src/app/routes/portal/routes/service/pipes/interface-info.pipe.ts
@@ -2,7 +2,7 @@ import { Pipe, PipeTransform } from '@angular/core'
import { T } from '@start9labs/start-sdk'
import { PackageDataEntry } from 'src/app/services/patch-db/data-model'
-export interface ExtendedInterfaceInfo extends T.ServiceInterfaceWithHostInfo {
+export interface ExtendedInterfaceInfo extends T.ServiceInterface {
id: string
icon: string
color: string
diff --git a/web/projects/ui/src/app/routes/portal/routes/service/pipes/to-additional.pipe.ts b/web/projects/ui/src/app/routes/portal/routes/service/pipes/to-additional.pipe.ts
index dfaa39140..a78837f6b 100644
--- a/web/projects/ui/src/app/routes/portal/routes/service/pipes/to-additional.pipe.ts
+++ b/web/projects/ui/src/app/routes/portal/routes/service/pipes/to-additional.pipe.ts
@@ -74,11 +74,7 @@ export class ToAdditionalPipe implements PipeTransform {
label: 'License',
size: 'l',
data: {
- content: from(
- this.api.getStatic(
- `/public/package-data/${id}/${version}/LICENSE.md`,
- ),
- ),
+ content: from(this.api.getStaticInstalled(id, 'LICENSE.md')),
},
})
.subscribe()
diff --git a/web/projects/ui/src/app/routes/portal/routes/service/pipes/to-menu.pipe.ts b/web/projects/ui/src/app/routes/portal/routes/service/pipes/to-menu.pipe.ts
index cd438ddcc..132cbc860 100644
--- a/web/projects/ui/src/app/routes/portal/routes/service/pipes/to-menu.pipe.ts
+++ b/web/projects/ui/src/app/routes/portal/routes/service/pipes/to-menu.pipe.ts
@@ -98,13 +98,13 @@ export class ToMenuPipe implements PipeTransform {
})
.subscribe(),
},
- pkg.marketplaceUrl
+ pkg.registry
? {
icon: '@tui.shopping-bag',
name: 'Marketplace Listing',
description: `View ${manifest.title} on the Marketplace`,
routerLink: `/portal/system/marketplace`,
- params: { url: pkg.marketplaceUrl, id: manifest.id },
+ params: { url: pkg.registry, id: manifest.id },
}
: {
icon: '@tui.shopping-bag',
@@ -114,7 +114,7 @@ export class ToMenuPipe implements PipeTransform {
]
}
- private showInstructions({ title, id, version }: T.Manifest) {
+ private showInstructions({ title, id }: T.Manifest) {
this.api
.setDbValue
(['ack-instructions', id], true)
.catch(e => console.error('Failed to mark instructions as seen', e))
@@ -124,11 +124,7 @@ export class ToMenuPipe implements PipeTransform {
label: `${title} instructions`,
size: 'l',
data: {
- content: from(
- this.api.getStatic(
- `/public/package-data/${id}/${version}/INSTRUCTIONS.md`,
- ),
- ),
+ content: from(this.api.getStaticInstalled(id, 'instructions.md')),
},
})
.subscribe()
diff --git a/web/projects/ui/src/app/routes/portal/routes/service/routes/interface.component.ts b/web/projects/ui/src/app/routes/portal/routes/service/routes/interface.component.ts
index 48d0a8f05..ad92d4553 100644
--- a/web/projects/ui/src/app/routes/portal/routes/service/routes/interface.component.ts
+++ b/web/projects/ui/src/app/routes/portal/routes/service/routes/interface.component.ts
@@ -3,17 +3,17 @@ import { ChangeDetectionStrategy, Component, inject } from '@angular/core'
import { ActivatedRoute } from '@angular/router'
import { getPkgId } from '@start9labs/shared'
import { PatchDB } from 'patch-db-client'
-import { map } from 'rxjs'
+import { combineLatest, map } from 'rxjs'
import { InterfaceComponent } from 'src/app/routes/portal/components/interfaces/interface.component'
import { DataModel } from 'src/app/services/patch-db/data-model'
-import { getAddresses } from '../../../components/interfaces/interface.utils'
+import { getMultihostAddresses } from '../../../components/interfaces/interface.utils'
@Component({
template: `
`,
changeDetection: ChangeDetectionStrategy.OnPush,
@@ -22,23 +22,25 @@ import { getAddresses } from '../../../components/interfaces/interface.utils'
})
export class ServiceInterfaceRoute {
private readonly route = inject(ActivatedRoute)
+ private readonly patch = inject>(PatchDB)
readonly context = {
packageId: getPkgId(this.route),
interfaceId: this.route.snapshot.paramMap.get('interfaceId') || '',
}
- readonly interfaceInfo$ = inject(PatchDB)
- .watch$(
+ readonly interfacesWithAddresses$ = combineLatest([
+ this.patch.watch$(
'packageData',
this.context.packageId,
'serviceInterfaces',
this.context.interfaceId,
- )
- .pipe(
- map(info => ({
- ...info,
- addresses: getAddresses(info),
- })),
- )
+ ),
+ this.patch.watch$('packageData', this.context.packageId, 'hosts'),
+ ]).pipe(
+ map(([iFace, hosts]) => ({
+ ...iFace,
+ addresses: getMultihostAddresses(iFace, hosts[iFace.addressInfo.hostId]),
+ })),
+ )
}
diff --git a/web/projects/ui/src/app/routes/portal/routes/service/routes/outlet.component.ts b/web/projects/ui/src/app/routes/portal/routes/service/routes/outlet.component.ts
index 66fdd7c08..0193747d7 100644
--- a/web/projects/ui/src/app/routes/portal/routes/service/routes/outlet.component.ts
+++ b/web/projects/ui/src/app/routes/portal/routes/service/routes/outlet.component.ts
@@ -16,7 +16,7 @@ import { DataModel } from 'src/app/services/patch-db/data-model'
imports: [CommonModule, RouterOutlet],
})
export class ServiceOutletComponent {
- private readonly patch = inject(PatchDB)
+ private readonly patch = inject>(PatchDB)
private readonly route = inject(ActivatedRoute)
private readonly router = inject(Router)
diff --git a/web/projects/ui/src/app/routes/portal/routes/service/routes/service.component.ts b/web/projects/ui/src/app/routes/portal/routes/service/routes/service.component.ts
index 3372dc146..d35320cb1 100644
--- a/web/projects/ui/src/app/routes/portal/routes/service/routes/service.component.ts
+++ b/web/projects/ui/src/app/routes/portal/routes/service/routes/service.component.ts
@@ -47,11 +47,6 @@ import { DependencyInfo } from '../types/dependency-info'
[connected]="!!(connected$ | async)"
[installingInfo]="service.pkg.stateInfo.installingInfo"
[rendering]="getRendering(service.status)"
- [sigtermTimeout]="
- service.pkg.status.main.status === 'stopping'
- ? service.pkg.status.main.timeout
- : null
- "
/>
@if (isInstalled(service) && (connected$ | async)) {
@@ -164,14 +159,14 @@ import { DependencyInfo } from '../types/dependency-info'
],
})
export class ServiceRoute {
- private readonly patch = inject(PatchDB)
+ private readonly patch = inject>(PatchDB)
private readonly pkgId$ = inject(ActivatedRoute).paramMap.pipe(
map(params => params.get('pkgId')!),
)
private readonly depErrorService = inject(DepErrorService)
private readonly router = inject(Router)
private readonly formDialog = inject(FormDialogService)
- readonly connected$ = inject(ConnectionService).connected$
+ readonly connected$ = inject(ConnectionService)
readonly service$ = this.pkgId$.pipe(
switchMap(pkgId =>
@@ -232,11 +227,11 @@ export class ServiceRoute {
depErrors,
)
- const { title, icon, versionSpec } = pkg.currentDependencies[depId]
+ const { title, icon, versionRange } = pkg.currentDependencies[depId]
return {
id: depId,
- version: versionSpec,
+ version: versionRange,
title,
icon,
errorText: errorText
@@ -322,7 +317,7 @@ export class ServiceRoute {
const dependentInfo: DependentInfo = {
id: manifest.id,
title: manifest.title,
- version: pkg.currentDependencies[depId].versionSpec,
+ version: pkg.currentDependencies[depId].versionRange,
}
const navigationExtras: NavigationExtras = {
// @TODO state not being used by marketplace component. Maybe it is not important to use.
diff --git a/web/projects/ui/src/app/routes/portal/routes/service/types/dependency-info.ts b/web/projects/ui/src/app/routes/portal/routes/service/types/dependency-info.ts
index a28c44a24..cedf01ebc 100644
--- a/web/projects/ui/src/app/routes/portal/routes/service/types/dependency-info.ts
+++ b/web/projects/ui/src/app/routes/portal/routes/service/types/dependency-info.ts
@@ -1,7 +1,7 @@
export interface DependencyInfo {
id: string
- title: string
- icon: string
+ title: string | null
+ icon: string | null
version: string
errorText: string
actionText: string
diff --git a/web/projects/ui/src/app/routes/portal/routes/system/backups/components/status.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/backups/components/status.component.ts
index db296a08f..541be1833 100644
--- a/web/projects/ui/src/app/routes/portal/routes/system/backups/components/status.component.ts
+++ b/web/projects/ui/src/app/routes/portal/routes/system/backups/components/status.component.ts
@@ -4,7 +4,7 @@ import {
inject,
Input,
} from '@angular/core'
-import { Emver } from '@start9labs/shared'
+import { Exver } from '@start9labs/shared'
import { TuiIcon } from '@taiga-ui/core'
import { BackupTarget } from 'src/app/services/api/api.types'
import { BackupType } from '../types/backup-type'
@@ -21,8 +21,9 @@ import { BackupType } from '../types/backup-type'
imports: [TuiIcon],
})
export class BackupsStatusComponent {
- private readonly emver = inject(Emver)
+ private readonly exver = inject(Exver)
+ @Input({ required: true }) serverId!: string
@Input({ required: true }) type!: BackupType
@Input({ required: true }) target!: BackupTarget
@@ -62,8 +63,11 @@ export class BackupsStatusComponent {
private get hasBackup(): boolean {
return (
- !!this.target.startOs &&
- this.emver.compare(this.target.startOs.version, '0.3.0') !== -1
+ this.target.startOs[this.serverId] &&
+ this.exver.compareOsVersion(
+ this.target.startOs[this.serverId].version,
+ '0.3.6',
+ ) !== 'less'
)
}
}
diff --git a/web/projects/ui/src/app/routes/portal/routes/system/backups/components/upcoming.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/backups/components/upcoming.component.ts
index 375a0b827..af5978170 100644
--- a/web/projects/ui/src/app/routes/portal/routes/system/backups/components/upcoming.component.ts
+++ b/web/projects/ui/src/app/routes/portal/routes/system/backups/components/upcoming.component.ts
@@ -95,7 +95,7 @@ import { GetBackupIconPipe } from '../pipes/get-backup-icon.pipe'
})
export class BackupsUpcomingComponent {
readonly current = toSignal(
- inject(PatchDB)
+ inject>(PatchDB)
.watch$('serverInfo', 'statusInfo', 'currentBackup', 'job')
.pipe(map(job => job || {})),
)
diff --git a/web/projects/ui/src/app/routes/portal/routes/system/backups/modals/backup.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/backups/modals/backup.component.ts
index 7387f2361..3c792878e 100644
--- a/web/projects/ui/src/app/routes/portal/routes/system/backups/modals/backup.component.ts
+++ b/web/projects/ui/src/app/routes/portal/routes/system/backups/modals/backup.component.ts
@@ -77,7 +77,7 @@ interface Package {
imports: [FormsModule, TuiButton, TuiGroup, TuiLoader, TuiBlock, TuiCheckbox],
})
export class BackupsBackupModal {
- private readonly patch = inject(PatchDB)
+ private readonly patch = inject>(PatchDB)
readonly context =
inject>(
POLYMORPHEUS_CONTEXT,
diff --git a/web/projects/ui/src/app/routes/portal/routes/system/backups/modals/recover.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/backups/modals/recover.component.ts
index 6017365f0..77c726154 100644
--- a/web/projects/ui/src/app/routes/portal/routes/system/backups/modals/recover.component.ts
+++ b/web/projects/ui/src/app/routes/portal/routes/system/backups/modals/recover.component.ts
@@ -81,7 +81,7 @@ export class BackupsRecoverModal {
private readonly context =
inject>(POLYMORPHEUS_CONTEXT)
- readonly packageData$ = inject(PatchDB)
+ readonly packageData$ = inject>(PatchDB)
.watch$('packageData')
.pipe(take(1))
@@ -118,12 +118,13 @@ export class BackupsRecoverModal {
const ids = options.filter(({ checked }) => !!checked).map(({ id }) => id)
const loader = this.loader.open('Initializing...').subscribe()
- const { targetId, password } = this.context.data
+ const { targetId, serverId, password } = this.context.data
try {
await this.api.restorePackages({
ids,
targetId,
+ serverId,
password,
})
diff --git a/web/projects/ui/src/app/routes/portal/routes/system/backups/modals/servers.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/backups/modals/servers.component.ts
new file mode 100644
index 000000000..aee420d52
--- /dev/null
+++ b/web/projects/ui/src/app/routes/portal/routes/system/backups/modals/servers.component.ts
@@ -0,0 +1,30 @@
+import { Component, inject } from '@angular/core'
+import { ServerComponent, StartOSDiskInfo } from '@start9labs/shared'
+import { TuiDialogContext } from '@taiga-ui/core'
+import {
+ POLYMORPHEUS_CONTEXT,
+ PolymorpheusComponent,
+} from '@taiga-ui/polymorpheus'
+
+interface Data {
+ servers: StartOSDiskInfo[]
+}
+
+@Component({
+ standalone: true,
+ template: `
+ @for (server of context.data.servers; track $index) {
+
+ }
+ `,
+ imports: [ServerComponent],
+})
+export class ServersComponent {
+ readonly context =
+ inject>(POLYMORPHEUS_CONTEXT)
+}
+
+export const SERVERS = new PolymorpheusComponent(ServersComponent)
diff --git a/web/projects/ui/src/app/routes/portal/routes/system/backups/modals/target.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/backups/modals/target.component.ts
index 91aae471a..4506716de 100644
--- a/web/projects/ui/src/app/routes/portal/routes/system/backups/modals/target.component.ts
+++ b/web/projects/ui/src/app/routes/portal/routes/system/backups/modals/target.component.ts
@@ -23,6 +23,9 @@ import { BackupsStatusComponent } from '../components/status.component'
import { GetDisplayInfoPipe } from '../pipes/get-display-info.pipe'
import { BackupType } from '../types/backup-type'
import { TARGETS } from './targets.component'
+import { getServerInfo } from 'src/app/utils/get-server-info'
+import { PatchDB } from 'patch-db-client'
+import { DataModel } from 'src/app/services/patch-db/data-model'
@Component({
template: `
@@ -40,7 +43,11 @@ import { TARGETS } from './targets.component'
{{ displayInfo.name }}
-
+
{{ displayInfo.description }}
@@ -69,6 +76,7 @@ export class BackupsTargetModal {
private readonly dialogs = inject(TuiDialogService)
private readonly errorService = inject(ErrorService)
private readonly api = inject(ApiService)
+ private readonly patch = inject
>(PatchDB)
readonly context =
inject>(
@@ -81,10 +89,12 @@ export class BackupsTargetModal {
? 'Loading Backup Targets'
: 'Loading Backup Sources'
+ serverId = ''
targets: BackupTarget[] = []
async ngOnInit() {
try {
+ this.serverId = (await getServerInfo(this.patch)).id
this.targets = (await this.api.getBackupTargets({})).saved
} catch (e: any) {
this.errorService.handleError(e)
diff --git a/web/projects/ui/src/app/routes/portal/routes/system/backups/modals/targets.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/backups/modals/targets.component.ts
index 28e68eb45..4d9fdc98a 100644
--- a/web/projects/ui/src/app/routes/portal/routes/system/backups/modals/targets.component.ts
+++ b/web/projects/ui/src/app/routes/portal/routes/system/backups/modals/targets.component.ts
@@ -171,8 +171,8 @@ export class BackupsTargetsModal implements OnInit {
text: 'Save',
handler: ({ type }: BackupConfig) =>
this.add(
- type[CT.unionSelectKey] === 'cifs' ? 'cifs' : 'cloud',
- type[CT.unionValueKey],
+ type.selection === 'cifs' ? 'cifs' : 'cloud',
+ type.value,
),
},
],
diff --git a/web/projects/ui/src/app/routes/portal/routes/system/backups/pipes/to-options.pipe.ts b/web/projects/ui/src/app/routes/portal/routes/system/backups/pipes/to-options.pipe.ts
index a7fdbd99e..d9d5f900d 100644
--- a/web/projects/ui/src/app/routes/portal/routes/system/backups/pipes/to-options.pipe.ts
+++ b/web/projects/ui/src/app/routes/portal/routes/system/backups/pipes/to-options.pipe.ts
@@ -26,7 +26,7 @@ export class ToOptionsPipe implements PipeTransform {
id,
installed: !!packageData[id],
checked: false,
- newerOS: this.compare(packageBackups[id].osVersion),
+ newerStartOs: this.compare(packageBackups[id].osVersion),
}))
.sort((a, b) =>
b.title.toLowerCase() > a.title.toLowerCase() ? -1 : 1,
diff --git a/web/projects/ui/src/app/routes/portal/routes/system/backups/services/restore.service.ts b/web/projects/ui/src/app/routes/portal/routes/system/backups/services/restore.service.ts
index 6574737bd..84534cc69 100644
--- a/web/projects/ui/src/app/routes/portal/routes/system/backups/services/restore.service.ts
+++ b/web/projects/ui/src/app/routes/portal/routes/system/backups/services/restore.service.ts
@@ -1,7 +1,11 @@
import { inject, Injectable } from '@angular/core'
import { Router } from '@angular/router'
import * as argon2 from '@start9labs/argon2'
-import { ErrorService, LoadingService } from '@start9labs/shared'
+import {
+ ErrorService,
+ LoadingService,
+ StartOSDiskInfo,
+} from '@start9labs/shared'
import { TuiDialogOptions, TuiDialogService } from '@taiga-ui/core'
import {
catchError,
@@ -18,10 +22,11 @@ import {
PROMPT,
PromptOptions,
} from 'src/app/routes/portal/modals/prompt.component'
-import { ApiService } from 'src/app/services/api/embassy-api.service'
import { BackupTarget } from 'src/app/services/api/api.types'
-import { TARGET, TARGET_RESTORE } from '../modals/target.component'
+import { ApiService } from 'src/app/services/api/embassy-api.service'
import { RECOVER } from '../modals/recover.component'
+import { SERVERS } from '../modals/servers.component'
+import { TARGET, TARGET_RESTORE } from '../modals/target.component'
import { RecoverData } from '../types/recover-data'
@Injectable({
@@ -38,23 +43,33 @@ export class BackupsRestoreService {
this.dialogs
.open(TARGET, TARGET_RESTORE)
.pipe(
+ // @TODO Alex implement servers
switchMap(target =>
- this.dialogs.open(PROMPT, PROMPT_OPTIONS).pipe(
- exhaustMap(password =>
- this.getRecoverData(
- target.id,
- password,
- target.startOs?.passwordHash || '',
+ this.dialogs
+ .open(SERVERS, {
+ data: { servers: [] },
+ })
+ .pipe(
+ switchMap(({ id, passwordHash }) =>
+ this.dialogs.open(PROMPT, PROMPT_OPTIONS).pipe(
+ exhaustMap(password =>
+ this.getRecoverData(
+ target.id,
+ id,
+ password,
+ passwordHash || '',
+ ),
+ ),
+ take(1),
+ switchMap(data =>
+ this.dialogs.open(RECOVER, {
+ label: 'Select Services to Restore',
+ data,
+ }),
+ ),
+ ),
),
),
- take(1),
- switchMap(data =>
- this.dialogs.open(RECOVER, {
- label: 'Select Services to Restore',
- data,
- }),
- ),
- ),
),
)
.subscribe(() => {
@@ -64,6 +79,7 @@ export class BackupsRestoreService {
private getRecoverData(
targetId: string,
+ serverId: string,
password: string,
hash: string,
): Observable {
@@ -81,7 +97,7 @@ export class BackupsRestoreService {
return EMPTY
}),
- map(backupInfo => ({ targetId, password, backupInfo })),
+ map(backupInfo => ({ targetId, password, backupInfo, serverId })),
)
}
}
diff --git a/web/projects/ui/src/app/routes/portal/routes/system/backups/types/backup-config.ts b/web/projects/ui/src/app/routes/portal/routes/system/backups/types/backup-config.ts
index a3c3ea0b3..3ccfd0aaf 100644
--- a/web/projects/ui/src/app/routes/portal/routes/system/backups/types/backup-config.ts
+++ b/web/projects/ui/src/app/routes/portal/routes/system/backups/types/backup-config.ts
@@ -1,16 +1,15 @@
-import { CT } from '@start9labs/start-sdk'
import { RR } from 'src/app/services/api/api.types'
export type BackupConfig =
| {
type: {
- [CT.unionSelectKey]: 'dropbox' | 'google-drive'
- [CT.unionValueKey]: RR.AddCloudBackupTargetReq
+ selection: 'dropbox' | 'google-drive'
+ value: RR.AddCloudBackupTargetReq
}
}
| {
type: {
- [CT.unionSelectKey]: 'cifs'
- [CT.unionValueKey]: RR.AddCifsBackupTargetReq
+ selection: 'cifs'
+ value: RR.AddCifsBackupTargetReq
}
}
diff --git a/web/projects/ui/src/app/routes/portal/routes/system/backups/types/recover-data.ts b/web/projects/ui/src/app/routes/portal/routes/system/backups/types/recover-data.ts
index 7823451ac..71103a0c3 100644
--- a/web/projects/ui/src/app/routes/portal/routes/system/backups/types/recover-data.ts
+++ b/web/projects/ui/src/app/routes/portal/routes/system/backups/types/recover-data.ts
@@ -2,6 +2,7 @@ import { BackupInfo } from 'src/app/services/api/api.types'
export interface RecoverData {
targetId: string
+ serverId: string
backupInfo: BackupInfo
password: string
}
diff --git a/web/projects/ui/src/app/routes/portal/routes/system/marketplace/components/controls.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/marketplace/components/controls.component.ts
index 2e4717016..2587691a5 100644
--- a/web/projects/ui/src/app/routes/portal/routes/system/marketplace/components/controls.component.ts
+++ b/web/projects/ui/src/app/routes/portal/routes/system/marketplace/components/controls.component.ts
@@ -12,12 +12,12 @@ import {
MarketplacePkg,
} from '@start9labs/marketplace'
import {
- Emver,
+ Exver,
ErrorService,
isEmptyObject,
LoadingService,
sameUrl,
- EmverPipesModule,
+ ExverPipesModule,
} from '@start9labs/shared'
import { PatchDB } from 'patch-db-client'
import { firstValueFrom } from 'rxjs'
@@ -41,7 +41,7 @@ import { ToManifestPipe } from 'src/app/routes/portal/pipes/to-manifest'
localPkg.stateInfo.state === 'installed' && (localPkg | toManifest);
as localManifest
) {
- @switch (localManifest.version | compareEmver: pkg.manifest.version) {
+ @switch (localManifest.version | compareExver: pkg.version) {
@case (1) {
diff --git a/web/projects/ui/src/app/routes/portal/routes/system/marketplace/components/tile.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/marketplace/components/tile.component.ts
index 8e6e51996..5e3fb7da9 100644
--- a/web/projects/ui/src/app/routes/portal/routes/system/marketplace/components/tile.component.ts
+++ b/web/projects/ui/src/app/routes/portal/routes/system/marketplace/components/tile.component.ts
@@ -22,11 +22,11 @@ import { MarketplaceControlsComponent } from './controls.component'
@@ -45,7 +45,7 @@ import { MarketplaceControlsComponent } from './controls.component'
slot="controls"
class="controls-wrapper"
[pkg]="pkg"
- [localPkg]="pkg.manifest.id | toLocal | async"
+ [localPkg]="pkg.id | toLocal | async"
/>
@@ -125,7 +125,7 @@ export class MarketplaceTileComponent {
toggle(open: boolean) {
this.router.navigate([], {
- queryParams: { id: open ? this.pkg.manifest.id : null },
+ queryParams: { id: open ? this.pkg.id : null },
})
}
}
diff --git a/web/projects/ui/src/app/routes/portal/routes/system/marketplace/modals/preview.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/marketplace/modals/preview.component.ts
index 5f90da116..6fd9cc1c1 100644
--- a/web/projects/ui/src/app/routes/portal/routes/system/marketplace/modals/preview.component.ts
+++ b/web/projects/ui/src/app/routes/portal/routes/system/marketplace/modals/preview.component.ts
@@ -6,6 +6,8 @@ import {
Input,
TemplateRef,
} from '@angular/core'
+import { FormsModule } from '@angular/forms'
+import { Router } from '@angular/router'
import {
AboutModule,
AbstractMarketplaceService,
@@ -14,21 +16,17 @@ import {
MarketplaceDependenciesComponent,
MarketplacePackageHeroComponent,
MarketplacePkg,
- ReleaseNotesModule,
- StoreIdentity,
} from '@start9labs/marketplace'
-import { displayEmver, Emver, SharedPipesModule } from '@start9labs/shared'
-import { BehaviorSubject, filter, switchMap, tap } from 'rxjs'
+import { Exver, SharedPipesModule } from '@start9labs/shared'
import {
+ TuiButton,
TuiDialogContext,
TuiDialogService,
- TuiLoader,
TuiIcon,
- TuiButton,
+ TuiLoader,
} from '@taiga-ui/core'
import { TuiRadioList, TuiStringifyContentPipe } from '@taiga-ui/kit'
-import { FormsModule } from '@angular/forms'
-import { Router } from '@angular/router'
+import { BehaviorSubject, filter, switchMap, tap } from 'rxjs'
@Component({
selector: 'marketplace-preview',
@@ -44,13 +42,12 @@ import { Router } from '@angular/router'
- @if (!(pkg.manifest.dependencies | empty)) {
+ @if (!(pkg.dependencyMetadata | empty)) {
}
-
-
+
- @if (!(package.manifest.dependencies | empty)) {
+ @if (!(package.dependencyMetadata | empty)) {
}
@@ -93,18 +93,18 @@ export class SideloadPackageComponent {
private readonly errorService = inject(ErrorService)
private readonly router = inject(Router)
private readonly alerts = inject(TuiAlertService)
- private readonly emver = inject(Emver)
+ private readonly exver = inject(Exver)
readonly button$ = combineLatest([
inject(ClientStorageService).showDevTools$,
- inject(PatchDB)
+ inject>(PatchDB)
.watch$('packageData')
.pipe(
map(local =>
- local[this.package.manifest.id]
- ? this.emver.compare(
- getManifest(local[this.package.manifest.id]).version,
- this.package.manifest.version,
+ local[this.package.id]
+ ? this.exver.compareExver(
+ getManifest(local[this.package.id]).version,
+ this.package.version,
)
: null,
),
@@ -132,14 +132,12 @@ export class SideloadPackageComponent {
async upload() {
const loader = this.loader.open('Uploading package').subscribe()
- const { manifest, icon } = this.package
- const { size } = this.file
try {
- const pkg = await this.api.sideloadPackage({ manifest, icon, size })
+ const { upload } = await this.api.sideloadPackage()
- await this.api.uploadPackage(pkg, this.file)
- await this.router.navigate(['/portal/service', manifest.id])
+ await this.api.uploadPackage(upload, this.file).catch(console.error)
+ await this.router.navigate(['/portal/service', this.package.id])
this.alerts
.open('Package uploaded successfully', { status: 'success' })
diff --git a/web/projects/ui/src/app/routes/portal/routes/system/sideload/sideload.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/sideload/sideload.component.ts
index 24aece782..932cd9d19 100644
--- a/web/projects/ui/src/app/routes/portal/routes/system/sideload/sideload.component.ts
+++ b/web/projects/ui/src/app/routes/portal/routes/system/sideload/sideload.component.ts
@@ -12,8 +12,6 @@ import { Subject } from 'rxjs'
import { ConfigService } from 'src/app/services/config.service'
import { SideloadPackageComponent } from './package.component'
-import { parseS9pk, validateS9pk } from './sideload.utils'
-
@Component({
template: `
@@ -105,13 +103,14 @@ export default class SideloadComponent {
this.package = null
}
+ // @TODO Alex refactor sideload
async onFile(file: File | null) {
- if (!file || !(await validateS9pk(file))) {
- this.invalid = true
- } else {
- this.package = await parseS9pk(file)
- this.file = file
- }
+ // if (!file || !(await validateS9pk(file))) {
+ // this.invalid = true
+ // } else {
+ // this.package = await parseS9pk(file)
+ // this.file = file
+ // }
this.refresh$.next()
}
diff --git a/web/projects/ui/src/app/routes/portal/routes/system/sideload/sideload.utils.ts b/web/projects/ui/src/app/routes/portal/routes/system/sideload/sideload.utils.ts
deleted file mode 100644
index 027cb52fd..000000000
--- a/web/projects/ui/src/app/routes/portal/routes/system/sideload/sideload.utils.ts
+++ /dev/null
@@ -1,158 +0,0 @@
-import { MarketplacePkg } from '@start9labs/marketplace'
-import cbor from 'cbor'
-
-interface Positions {
- [key: string]: [bigint, bigint] // [position, length]
-}
-
-const MAGIC = new Uint8Array([59, 59])
-const VERSION = new Uint8Array([1])
-
-export async function validateS9pk(file: File): Promise {
- const magic = new Uint8Array(await blobToBuffer(file.slice(0, 2)))
- const version = new Uint8Array(await blobToBuffer(file.slice(2, 3)))
-
- return compare(magic, MAGIC) && compare(version, VERSION)
-}
-
-export async function parseS9pk(file: File): Promise {
- const positions: Positions = {}
- // magic=2bytes, version=1bytes, pubkey=32bytes, signature=64bytes, toc_length=4bytes = 103byte is starting point
- let start = 103
- let end = start + 1 // 104
- const tocLength = new DataView(
- await blobToBuffer(file.slice(99, 103) ?? new Blob()),
- ).getUint32(0, false)
- await getPositions(start, end, file, positions, tocLength as any)
-
- const manifest = await getAsset(positions, file, 'manifest')
- const [icon] = await Promise.all([
- await getIcon(positions, file),
- // getAsset(positions, file, 'license'),
- // getAsset(positions, file, 'instructions'),
- ])
-
- return {
- manifest,
- icon,
- license: '',
- instructions: '',
- categories: [],
- versions: [],
- dependencyMetadata: {},
- publishedAt: '',
- }
-}
-
-async function getPositions(
- initialStart: number,
- initialEnd: number,
- file: Blob,
- positions: Positions,
- tocLength: number,
-) {
- let start = initialStart
- let end = initialEnd
- const titleLength = new Uint8Array(
- await blobToBuffer(file.slice(start, end)),
- )[0]
- const tocTitle = await file.slice(end, end + titleLength).text()
- start = end + titleLength
- end = start + 8
- const chapterPosition = new DataView(
- await blobToBuffer(file.slice(start, end)),
- ).getBigUint64(0, false)
- start = end
- end = start + 8
- const chapterLength = new DataView(
- await blobToBuffer(file.slice(start, end)),
- ).getBigUint64(0, false)
-
- positions[tocTitle] = [chapterPosition, chapterLength]
- start = end
- end = start + 1
- if (end <= tocLength + (initialStart - 1)) {
- await getPositions(start, end, file, positions, tocLength)
- }
-}
-
-async function readBlobAsDataURL(
- f: Blob | File,
-): Promise {
- const reader = new FileReader()
- return new Promise((resolve, reject) => {
- reader.onloadend = () => {
- resolve(reader.result)
- }
- reader.readAsDataURL(f)
- reader.onerror = _ => reject(new Error('error reading blob'))
- })
-}
-
-async function blobToDataURL(data: Blob | File): Promise {
- const res = await readBlobAsDataURL(data)
- if (res instanceof ArrayBuffer) {
- throw new Error('readBlobAsDataURL response should not be an array buffer')
- }
- if (res == null) {
- throw new Error('readBlobAsDataURL response should not be null')
- }
- if (typeof res === 'string') return res
- throw new Error('no possible blob to data url resolution found')
-}
-
-async function blobToBuffer(data: Blob | File): Promise {
- const res = await readBlobToArrayBuffer(data)
- if (res instanceof String) {
- throw new Error('readBlobToArrayBuffer response should not be a string')
- }
- if (res == null) {
- throw new Error('readBlobToArrayBuffer response should not be null')
- }
- if (res instanceof ArrayBuffer) return res
- throw new Error('no possible blob to array buffer resolution found')
-}
-
-async function readBlobToArrayBuffer(
- f: Blob | File,
-): Promise {
- const reader = new FileReader()
- return new Promise((resolve, reject) => {
- reader.onloadend = () => {
- resolve(reader.result)
- }
- reader.readAsArrayBuffer(f)
- reader.onerror = _ => reject(new Error('error reading blob'))
- })
-}
-
-function compare(a: Uint8Array, b: Uint8Array) {
- for (let i = 0; i < a.length; i++) {
- if (a[i] !== b[i]) return false
- }
- return true
-}
-
-async function getAsset(
- positions: Positions,
- file: Blob,
- asset: 'manifest' | 'license' | 'instructions',
-): Promise {
- const data = await blobToBuffer(
- file.slice(
- Number(positions[asset][0]),
- Number(positions[asset][0]) + Number(positions[asset][1]),
- ),
- )
- return cbor.decode(data, true)
-}
-
-async function getIcon(positions: Positions, file: Blob): Promise {
- const contentType = '' // @TODO
- const data = file.slice(
- Number(positions['icon'][0]),
- Number(positions['icon'][0]) + Number(positions['icon'][1]),
- contentType,
- )
- return blobToDataURL(data)
-}
diff --git a/web/projects/ui/src/app/routes/portal/routes/system/updates/filter-updates.pipe.ts b/web/projects/ui/src/app/routes/portal/routes/system/updates/filter-updates.pipe.ts
index af244f801..0e257a1d8 100644
--- a/web/projects/ui/src/app/routes/portal/routes/system/updates/filter-updates.pipe.ts
+++ b/web/projects/ui/src/app/routes/portal/routes/system/updates/filter-updates.pipe.ts
@@ -1,5 +1,5 @@
import { inject, Pipe, PipeTransform } from '@angular/core'
-import { Emver } from '@start9labs/shared'
+import { Exver } from '@start9labs/shared'
import { MarketplacePkg } from '@start9labs/marketplace'
import {
InstalledState,
@@ -12,7 +12,7 @@ import {
standalone: true,
})
export class FilterUpdatesPipe implements PipeTransform {
- private readonly emver = inject(Emver)
+ private readonly exver = inject(Exver)
transform(
pkgs?: MarketplacePkg[],
@@ -20,10 +20,10 @@ export class FilterUpdatesPipe implements PipeTransform {
): MarketplacePkg[] | null {
return (
pkgs?.filter(
- ({ manifest }) =>
- this.emver.compare(
- manifest.version,
- local?.[manifest.id]?.stateInfo.manifest.version,
+ ({ version, id }) =>
+ this.exver.compareExver(
+ version,
+ local?.[id]?.stateInfo.manifest.version || '',
) === 1,
) || null
)
diff --git a/web/projects/ui/src/app/routes/portal/routes/system/updates/item.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/updates/item.component.ts
index 0b72e3344..60e30260a 100644
--- a/web/projects/ui/src/app/routes/portal/routes/system/updates/item.component.ts
+++ b/web/projects/ui/src/app/routes/portal/routes/system/updates/item.component.ts
@@ -5,7 +5,6 @@ import {
MarketplacePkg,
} from '@start9labs/marketplace'
import {
- EmverPipesModule,
MarkdownPipeModule,
SafeLinksDirective,
SharedPipesModule,
@@ -45,12 +44,12 @@ import { hasCurrentDeps } from 'src/app/utils/has-deps'
-
{{ marketplacePkg.manifest.title }}
+
{{ marketplacePkg.title }}
- {{ localPkg.stateInfo.manifest.version | displayEmver }}
+ {{ localPkg.stateInfo.manifest.version }}
- {{ marketplacePkg.manifest.version | displayEmver }}
+ {{ marketplacePkg.version }}
{{ errors }}
@@ -84,16 +83,13 @@ import { hasCurrentDeps } from 'src/app/utils/has-deps'
What's new
View listing
@@ -115,7 +111,6 @@ import { hasCurrentDeps } from 'src/app/utils/has-deps'
standalone: true,
imports: [
RouterLink,
- EmverPipesModule,
MarkdownPipeModule,
NgDompurifyModule,
SafeLinksDirective,
@@ -132,7 +127,7 @@ import { hasCurrentDeps } from 'src/app/utils/has-deps'
})
export class UpdatesItemComponent {
private readonly dialogs = inject(TuiDialogService)
- private readonly patch = inject(PatchDB
)
+ private readonly patch = inject>(PatchDB)
private readonly marketplace = inject(
AbstractMarketplaceService,
) as MarketplaceService
@@ -147,7 +142,7 @@ export class UpdatesItemComponent {
url!: string
get pkgId(): string {
- return this.marketplacePkg.manifest.id
+ return this.marketplacePkg.id
}
get errors(): string {
@@ -159,7 +154,7 @@ export class UpdatesItemComponent {
}
async onClick() {
- const { id } = this.marketplacePkg.manifest
+ const { id } = this.marketplacePkg
delete this.marketplace.updateErrors[id]
this.marketplace.updateQueue[id] = true
@@ -178,7 +173,7 @@ export class UpdatesItemComponent {
}
private async update() {
- const { id, version } = this.marketplacePkg.manifest
+ const { id, version } = this.marketplacePkg
try {
await this.marketplace.installPackage(id, version, this.url)
diff --git a/web/projects/ui/src/app/routes/portal/routes/system/updates/updates.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/updates/updates.component.ts
index 00e2f0f81..774976c0f 100644
--- a/web/projects/ui/src/app/routes/portal/routes/system/updates/updates.component.ts
+++ b/web/projects/ui/src/app/routes/portal/routes/system/updates/updates.component.ts
@@ -35,7 +35,7 @@ import { isInstalled, isUpdating } from 'src/app/utils/get-package-data'
@for (pkg of pkgs; track pkg) {
} @empty {
@@ -76,7 +76,7 @@ export default class UpdatesComponent {
readonly data$ = combineLatest({
hosts: this.service.getKnownHosts$(true),
mp: this.service.getMarketplace$(),
- local: inject(PatchDB)
+ local: inject>(PatchDB)
.watch$('packageData')
.pipe(
map(pkgs =>
diff --git a/web/projects/ui/src/app/routing.module.ts b/web/projects/ui/src/app/routing.module.ts
index cea864793..0c6c0c89a 100644
--- a/web/projects/ui/src/app/routing.module.ts
+++ b/web/projects/ui/src/app/routing.module.ts
@@ -1,19 +1,19 @@
import { NgModule } from '@angular/core'
import { PreloadAllModules, RouterModule, Routes } from '@angular/router'
+import { stateNot } from 'src/app/services/state.service'
import { AuthGuard } from './guards/auth.guard'
import { UnauthGuard } from './guards/unauth.guard'
const routes: Routes = [
{
path: 'diagnostic',
- loadChildren: () =>
- import('./routes/diagnostic/diagnostic.module').then(
- m => m.DiagnosticModule,
- ),
+ canActivate: [stateNot(['initializing', 'running'])],
+ loadChildren: () => import('./routes/diagnostic/diagnostic.module'),
},
{
- path: 'loading',
- loadComponent: () => import('./routes/loading/loading.page'),
+ path: 'initializing',
+ canActivate: [stateNot(['error', 'running'])],
+ loadComponent: () => import('./routes/initializing/initializing.page'),
},
{
path: 'login',
@@ -23,8 +23,7 @@ const routes: Routes = [
},
{
path: 'portal',
- canActivate: [AuthGuard],
- canActivateChild: [AuthGuard],
+ canActivate: [AuthGuard, stateNot(['error', 'initializing'])],
loadChildren: () => import('./routes/portal/portal.routes'),
},
{
diff --git a/web/projects/ui/src/app/services/actions.service.ts b/web/projects/ui/src/app/services/actions.service.ts
index 8bc2e2ebc..d2228ef4b 100644
--- a/web/projects/ui/src/app/services/actions.service.ts
+++ b/web/projects/ui/src/app/services/actions.service.ts
@@ -24,7 +24,7 @@ export class ActionsService {
private readonly loader = inject(LoadingService)
private readonly api = inject(ApiService)
private readonly formDialog = inject(FormDialogService)
- private readonly patch = inject(PatchDB)
+ private readonly patch = inject>(PatchDB)
configure(manifest: T.Manifest): void {
this.formDialog.open(ConfigModal, {
diff --git a/web/projects/ui/src/app/services/api/api.fixures.ts b/web/projects/ui/src/app/services/api/api.fixures.ts
index a2c9c7d94..bcc5a1837 100644
--- a/web/projects/ui/src/app/services/api/api.fixures.ts
+++ b/web/projects/ui/src/app/services/api/api.fixures.ts
@@ -3,12 +3,7 @@ import {
PackageDataEntry,
ServerStatusInfo,
} from 'src/app/services/patch-db/data-model'
-import {
- Metrics,
- NotificationLevel,
- RR,
- ServerNotifications,
-} from './api.types'
+import { RR, ServerMetrics, ServerNotifications } from './api.types'
import { BTC_ICON, LND_ICON, PROXY_ICON, REGISTRY_ICON } from './api-icons'
import { Log } from '@start9labs/shared'
import { configBuilderToSpec } from 'src/app/utils/configBuilderToSpec'
@@ -748,7 +743,7 @@ export module Mock {
packageId: null,
createdAt: '2019-12-26T14:20:30.872Z',
code: 1,
- level: NotificationLevel.Success,
+ level: 'success',
title: 'Backup Complete',
message: 'StartOS and services have been successfully backed up.',
data: {
@@ -769,7 +764,7 @@ export module Mock {
packageId: null,
createdAt: '2019-12-26T14:20:30.872Z',
code: 2,
- level: NotificationLevel.Warning,
+ level: 'warning',
title: 'SSH Key Added',
message: 'A new SSH key was added. If you did not do this, shit is bad.',
data: null,
@@ -780,7 +775,7 @@ export module Mock {
packageId: null,
createdAt: '2019-12-26T14:20:30.872Z',
code: 3,
- level: NotificationLevel.Info,
+ level: 'info',
title: 'SSH Key Removed',
message: 'A SSH key was removed.',
data: null,
@@ -791,7 +786,7 @@ export module Mock {
packageId: 'bitcoind',
createdAt: '2019-12-26T14:20:30.872Z',
code: 4,
- level: NotificationLevel.Error,
+ level: 'error',
title: 'Service Crashed',
message: new Array(3)
.fill(
@@ -806,7 +801,7 @@ export module Mock {
},
]
- export function getMetrics(): Metrics {
+ export function getMetrics(): ServerMetrics {
return {
general: {
temperature: {
@@ -1020,8 +1015,16 @@ export module Mock {
path: '/Desktop/embassy-backups',
username: 'TestUser',
mountable: false,
- // @TODO Matt Provide mock for startOs
- startOs: null,
+ startOs: {
+ abcdefgh: {
+ hostname: 'adjective-noun.local',
+ version: '0.3.6',
+ timestamp: new Date().toISOString(),
+ passwordHash:
+ '$argon2d$v=19$m=1024,t=1,p=1$YXNkZmFzZGZhc2RmYXNkZg$Ceev1I901G6UwU+hY0sHrFZ56D+o+LNJ',
+ wrappedKey: '',
+ },
+ },
},
{
id: 'ftcvewdnkemfksdm',
@@ -1030,7 +1033,7 @@ export module Mock {
provider: 'dropbox',
path: '/Home/backups',
mountable: true,
- startOs: null,
+ startOs: {},
},
{
id: 'csgashbdjkasnd',
@@ -1040,7 +1043,7 @@ export module Mock {
path: '/Desktop/embassy-backups-2',
username: 'TestUser',
mountable: true,
- startOs: null,
+ startOs: {},
},
{
id: 'powjefhjbnwhdva',
@@ -1054,8 +1057,16 @@ export module Mock {
vendor: 'SSK',
mountable: true,
path: '/HomeFolder/Documents',
- // @TODO Matt Provide mock for startOs
- startOs: {},
+ startOs: {
+ 'different-server': {
+ hostname: 'different-server.local',
+ version: '0.3.6',
+ timestamp: new Date().toISOString(),
+ passwordHash:
+ '$argon2d$v=19$m=1024,t=1,p=1$YXNkZmFzZGZhc2RmYXNkZg$Ceev1I901G6UwU+hY0sHrFZ56D+o+LNJ',
+ wrappedKey: '',
+ },
+ },
},
],
}
diff --git a/web/projects/ui/src/app/services/api/api.types.ts b/web/projects/ui/src/app/services/api/api.types.ts
index f1bf71b9c..5efcf2bbf 100644
--- a/web/projects/ui/src/app/services/api/api.types.ts
+++ b/web/projects/ui/src/app/services/api/api.types.ts
@@ -61,7 +61,7 @@ export module RR {
// init
- export type InitGetProgressRes = {
+ export type InitFollowProgressRes = {
progress: T.FullProgress
guid: string
}
@@ -88,10 +88,10 @@ export module RR {
guid: string
}
- export type GetServerMetricsReq = {} // server.metrics
- export type GetServerMetricsRes = {
+ export type FollowServerMetricsReq = {} // server.metrics.follow
+ export type FollowServerMetricsRes = {
guid: string
- metrics: Metrics
+ metrics: ServerMetrics
}
export type UpdateServerReq = { registry: string } // server.update
@@ -339,9 +339,11 @@ export module RR {
export type FollowPackageLogsReq = FollowServerLogsReq & { id: string } // package.logs.follow
export type FollowPackageLogsRes = FollowServerLogsRes
- export type GetPackageMetricsReq = { id: string } // package.metrics
- // @TODO Matt create package metrics type
- export type GetPackageMetricsRes = any
+ export type FollowPackageMetricsReq = { id: string } // package.metrics.follow
+ export type FollowPackageMetricsRes = {
+ guid: string
+ metrics: AppMetrics
+ }
export type InstallPackageReq = T.InstallParams
export type InstallPackageRes = null
@@ -415,24 +417,6 @@ export module RR {
} // package.proxy.set-outbound
export type SetServiceOutboundProxyRes = null
- // marketplace
-
- export type GetMarketplaceInfoReq = { serverId: string }
- export type GetMarketplaceInfoRes = StoreInfo
-
- export type GetMarketplaceEosReq = { serverId: string }
- // @TODO Matt fix type
- export type GetMarketplaceEosRes = any
-
- export type GetMarketplacePackagesReq = {
- ids?: { id: string; version: string }[]
- // iff !ids
- category?: string
- query?: string
- page?: number
- perPage?: number
- }
-
// registry
/** these are returned in ASCENDING order. the newest available version will be the LAST in the object */
@@ -442,34 +426,34 @@ export module RR {
export type CheckOSUpdateRes = OSUpdate
}
-export interface OSUpdate {
+export type OSUpdate = {
version: string
headline: string
releaseNotes: { [version: string]: string }
}
-export interface Breakages {
+export type Breakages = {
[id: string]: TaggedDependencyError
}
-export interface TaggedDependencyError {
+export type TaggedDependencyError = {
dependency: string
error: DependencyError
}
-export interface ActionResponse {
+export type ActionResponse = {
message: string
value: string | null
copyable: boolean
qr: boolean
}
-interface MetricData {
+type MetricData = {
value: string
unit: string
}
-export interface Metrics {
+export type ServerMetrics = {
general: {
temperature: MetricData | null
}
@@ -497,14 +481,28 @@ export interface Metrics {
}
}
-export interface Session {
+export type AppMetrics = {
+ memory: {
+ percentageUsed: MetricData
+ used: MetricData
+ }
+ cpu: {
+ percentageUsed: MetricData
+ }
+ disk: {
+ percentageUsed: MetricData
+ used: MetricData
+ }
+}
+
+export type Session = {
loggedIn: string
lastActive: string
userAgent: string
metadata: SessionMetadata
}
-export interface SessionMetadata {
+export type SessionMetadata = {
platforms: PlatformType[]
}
@@ -564,7 +562,7 @@ export interface CloudBackupTarget extends BaseBackupTarget {
provider: 'dropbox' | 'google-drive'
}
-export interface BackupRun {
+export type BackupRun = {
id: string
startedAt: string
completedAt: string
@@ -573,7 +571,7 @@ export interface BackupRun {
report: BackupReport
}
-export interface BackupJob {
+export type BackupJob = {
id: string
name: string
target: BackupTarget
@@ -581,7 +579,7 @@ export interface BackupJob {
packageIds: string[]
}
-export interface BackupInfo {
+export type BackupInfo = {
version: string
timestamp: string
packageBackups: {
@@ -589,18 +587,18 @@ export interface BackupInfo {
}
}
-export interface PackageBackupInfo {
+export type PackageBackupInfo = {
title: string
version: string
osVersion: string
timestamp: string
}
-export interface ServerSpecs {
+export type ServerSpecs = {
[key: string]: string | number
}
-export interface SSHKey {
+export type SSHKey = {
createdAt: string
alg: string
hostname: string
@@ -609,32 +607,25 @@ export interface SSHKey {
export type ServerNotifications = ServerNotification[]
-export interface ServerNotification {
+export type ServerNotification = {
id: number
packageId: string | null
createdAt: string
code: T
- level: NotificationLevel
+ level: 'success' | 'info' | 'warning' | 'error'
title: string
message: string
data: NotificationData
read: boolean
}
-export enum NotificationLevel {
- Success = 'success',
- Info = 'info',
- Warning = 'warning',
- Error = 'error',
-}
-
export type NotificationData = T extends 0
? null
: T extends 1
? BackupReport
: any
-export interface BackupReport {
+export type BackupReport = {
server: {
attempted: boolean
error: string | null
@@ -646,7 +637,7 @@ export interface BackupReport {
}
}
-export interface AvailableWifi {
+export type AvailableWifi = {
ssid: string
strength: number
security: string[]
@@ -681,29 +672,29 @@ export type DependencyError =
| DependencyErrorHealthChecksFailed
| DependencyErrorTransitive
-export interface DependencyErrorNotInstalled {
+export type DependencyErrorNotInstalled = {
type: 'notInstalled'
}
-export interface DependencyErrorNotRunning {
+export type DependencyErrorNotRunning = {
type: 'notRunning'
}
-export interface DependencyErrorIncorrectVersion {
+export type DependencyErrorIncorrectVersion = {
type: 'incorrectVersion'
expected: string // version range
received: string // version
}
-export interface DependencyErrorConfigUnsatisfied {
+export type DependencyErrorConfigUnsatisfied = {
type: 'configUnsatisfied'
}
-export interface DependencyErrorHealthChecksFailed {
+export type DependencyErrorHealthChecksFailed = {
type: 'healthChecksFailed'
check: T.HealthCheckResult
}
-export interface DependencyErrorTransitive {
+export type DependencyErrorTransitive = {
type: 'transitive'
}
diff --git a/web/projects/ui/src/app/services/api/embassy-api.service.ts b/web/projects/ui/src/app/services/api/embassy-api.service.ts
index 103620697..45ae8ce10 100644
--- a/web/projects/ui/src/app/services/api/embassy-api.service.ts
+++ b/web/projects/ui/src/app/services/api/embassy-api.service.ts
@@ -31,7 +31,7 @@ export abstract class ApiService {
abstract openWebsocket$(
guid: string,
- config: RR.WebsocketConfig,
+ config?: RR.WebsocketConfig,
): Observable
// state
@@ -77,7 +77,7 @@ export abstract class ApiService {
// init
- abstract initGetProgress(): Promise
+ abstract initFollowProgress(): Promise
abstract initFollowLogs(
params: RR.FollowServerLogsReq,
@@ -111,9 +111,9 @@ export abstract class ApiService {
params: RR.FollowServerLogsReq,
): Promise
- abstract getServerMetrics(
- params: RR.GetServerMetricsReq,
- ): Promise
+ abstract followServerMetrics(
+ params: RR.FollowServerMetricsReq,
+ ): Promise
abstract updateServer(url?: string): Promise
diff --git a/web/projects/ui/src/app/services/api/embassy-live-api.service.ts b/web/projects/ui/src/app/services/api/embassy-live-api.service.ts
index e45d7867a..615d11743 100644
--- a/web/projects/ui/src/app/services/api/embassy-live-api.service.ts
+++ b/web/projects/ui/src/app/services/api/embassy-live-api.service.ts
@@ -95,7 +95,7 @@ export class LiveApiService extends ApiService {
openWebsocket$(
guid: string,
- config: RR.WebsocketConfig,
+ config: RR.WebsocketConfig = {},
): Observable {
const { location } = this.document.defaultView!
const protocol = location.protocol === 'http:' ? 'ws' : 'wss'
@@ -199,7 +199,7 @@ export class LiveApiService extends ApiService {
// init
- async initGetProgress(): Promise {
+ async initFollowProgress(): Promise {
return this.rpcRequest({ method: 'init.subscribe', params: {} })
}
@@ -251,9 +251,9 @@ export class LiveApiService extends ApiService {
return this.rpcRequest({ method: 'net.tor.logs.follow', params })
}
- async getServerMetrics(
- params: RR.GetServerMetricsReq,
- ): Promise {
+ async followServerMetrics(
+ params: RR.FollowServerMetricsReq,
+ ): Promise {
return this.rpcRequest({ method: 'server.metrics', params })
}
diff --git a/web/projects/ui/src/app/services/api/embassy-mock-api.service.ts b/web/projects/ui/src/app/services/api/embassy-mock-api.service.ts
index 19c46ea5e..43c012b8f 100644
--- a/web/projects/ui/src/app/services/api/embassy-mock-api.service.ts
+++ b/web/projects/ui/src/app/services/api/embassy-mock-api.service.ts
@@ -1,10 +1,5 @@
import { Injectable } from '@angular/core'
-import {
- pauseFor,
- Log,
- RPCErrorDetails,
- RPCOptions,
-} from '@start9labs/shared'
+import { pauseFor, Log, RPCErrorDetails, RPCOptions } from '@start9labs/shared'
import { ApiService } from './embassy-api.service'
import {
Operation,
@@ -20,7 +15,7 @@ import {
StateInfo,
UpdatingState,
} from 'src/app/services/patch-db/data-model'
-import { BackupTargetType, Metrics, RR } from './api.types'
+import { BackupTargetType, RR } from './api.types'
import { Mock } from './api.fixures'
import {
from,
@@ -40,7 +35,6 @@ import {
GetPackagesRes,
MarketplacePkg,
} from '@start9labs/marketplace'
-import { WebSocketSubjectConfig } from 'rxjs/webSocket'
import markdown from 'raw-loader!../../../../../shared/assets/markdown/md-sample.md'
const PROGRESS: T.FullProgress = {
@@ -89,8 +83,8 @@ export class MockApiService extends ApiService {
async uploadPackage(guid: string, body: Blob): Promise {
await pauseFor(2000)
- // @TODO Matt what should this return?
- return ''
+ // @TODO Aiden confirm this is correct
+ return 'success'
}
async uploadFile(body: Blob): Promise {
@@ -263,7 +257,7 @@ export class MockApiService extends ApiService {
// init
- async initGetProgress(): Promise {
+ async initFollowProgress(): Promise {
await pauseFor(250)
return {
progress: PROGRESS,
@@ -281,19 +275,6 @@ export class MockApiService extends ApiService {
// server
- openMetricsWebsocket$(
- config: WebSocketSubjectConfig,
- ): Observable {
- return interval(2000).pipe(
- map((_, index) => {
- // mock fire open observer
- if (index === 0) config.openObserver?.next(new Event(''))
- if (index === 4) throw new Error('HAHAHA')
- return Mock.getMetrics()
- }),
- )
- }
-
async getSystemTime(
params: RR.GetSystemTimeReq,
): Promise {
@@ -380,9 +361,9 @@ export class MockApiService extends ApiService {
return logs
}
- async getServerMetrics(
- params: RR.GetServerMetricsReq,
- ): Promise {
+ async followServerMetrics(
+ params: RR.FollowServerMetricsReq,
+ ): Promise {
await pauseFor(2000)
return {
guid: 'iqudh37um-i38u3-34-a51b-jkhd783ein',
diff --git a/web/projects/ui/src/app/services/api/mock-patch.ts b/web/projects/ui/src/app/services/api/mock-patch.ts
index 8a3cf90f5..6759590a2 100644
--- a/web/projects/ui/src/app/services/api/mock-patch.ts
+++ b/web/projects/ui/src/app/services/api/mock-patch.ts
@@ -130,8 +130,7 @@ export const mockPatchData: DataModel = {
password: '',
},
platform: 'x86_64-nonfree',
- // @TODO Matt zram needs to be added?
- // zram: true,
+ zram: true,
governor: 'performance',
passwordHash:
'$argon2d$v=19$m=1024,t=1,p=1$YXNkZmFzZGZhc2RmYXNkZg$Ceev1I901G6UwU+hY0sHrFZ56D+o+LNJ',
diff --git a/web/projects/ui/src/app/services/badge.service.ts b/web/projects/ui/src/app/services/badge.service.ts
index c71163292..28917c6e8 100644
--- a/web/projects/ui/src/app/services/badge.service.ts
+++ b/web/projects/ui/src/app/services/badge.service.ts
@@ -1,6 +1,6 @@
import { inject, Injectable } from '@angular/core'
import { AbstractMarketplaceService } from '@start9labs/marketplace'
-import { Emver } from '@start9labs/shared'
+import { Exver } from '@start9labs/shared'
import { PatchDB } from 'patch-db-client'
import {
combineLatest,
@@ -26,8 +26,8 @@ import { getManifest } from 'src/app/utils/get-package-data'
})
export class BadgeService {
private readonly notifications = inject(NotificationService)
- private readonly emver = inject(Emver)
- private readonly patch = inject(PatchDB)
+ private readonly exver = inject(Exver)
+ private readonly patch = inject>(PatchDB)
private readonly settings$ = combineLatest([
this.patch.watch$('serverInfo', 'ntpSynced'),
inject(EOSService).updateAvailable$,
@@ -36,7 +36,7 @@ export class BadgeService {
AbstractMarketplaceService,
) as MarketplaceService
- private readonly local$ = inject(ConnectionService).connected$.pipe(
+ private readonly local$ = inject(ConnectionService).pipe(
filter(Boolean),
switchMap(() => this.patch.watch$('packageData').pipe(first())),
switchMap(outer =>
@@ -67,10 +67,12 @@ export class BadgeService {
Object.entries(marketplace).reduce(
(list, [_, store]) =>
store?.packages.reduce(
- (result, { manifest: { id, version } }) =>
+ (result, { id, version }) =>
local[id] &&
- this.emver.compare(version, getManifest(local[id]).version) ===
- 1
+ this.exver.compareExver(
+ version,
+ getManifest(local[id]).version,
+ ) === 1
? result.add(id)
: result,
list,
diff --git a/web/projects/ui/src/app/services/breadcrumbs.service.ts b/web/projects/ui/src/app/services/breadcrumbs.service.ts
index 193ebf0c0..d499ea158 100644
--- a/web/projects/ui/src/app/services/breadcrumbs.service.ts
+++ b/web/projects/ui/src/app/services/breadcrumbs.service.ts
@@ -20,7 +20,7 @@ export interface Breadcrumb {
providedIn: 'root',
})
export class BreadcrumbsService extends BehaviorSubject {
- private readonly patch = inject(PatchDB)
+ private readonly patch = inject>(PatchDB)
constructor() {
super([])
diff --git a/web/projects/ui/src/app/services/config.service.ts b/web/projects/ui/src/app/services/config.service.ts
index efb0b5dcd..fc69f6fa2 100644
--- a/web/projects/ui/src/app/services/config.service.ts
+++ b/web/projects/ui/src/app/services/config.service.ts
@@ -82,17 +82,14 @@ export class ConfigService {
/** ${scheme}://${username}@${host}:${externalPort}${suffix} */
launchableAddress(
- interfaces: PackageDataEntry['serviceInterfaces'],
+ ui: T.ServiceInterface,
hosts: PackageDataEntry['hosts'],
): string {
- const ui = Object.values(interfaces).find(
- i =>
- i.type === 'ui' &&
- (i.addressInfo.scheme === 'http' ||
- i.addressInfo.sslScheme === 'https'),
- ) // TODO select if multiple
-
- if (!ui) return ''
+ if (
+ ui.type !== 'ui' ||
+ (ui.addressInfo.scheme !== 'http' && ui.addressInfo.sslScheme !== 'https')
+ )
+ return ''
const hostnameInfo =
hosts[ui.addressInfo.hostId]?.hostnameInfo[ui.addressInfo.internalPort]
diff --git a/web/projects/ui/src/app/services/dep-error.service.ts b/web/projects/ui/src/app/services/dep-error.service.ts
index 9b1301148..20cce6d40 100644
--- a/web/projects/ui/src/app/services/dep-error.service.ts
+++ b/web/projects/ui/src/app/services/dep-error.service.ts
@@ -38,7 +38,7 @@ export class DepErrorService {
),
),
distinctUntilChanged(deepEqual),
- shareReplay({ bufferSize: 1, refCount: true }),
+ shareReplay(1),
)
constructor(
diff --git a/web/projects/ui/src/app/services/marketplace.service.ts b/web/projects/ui/src/app/services/marketplace.service.ts
index a3f5bffb7..f4d11f475 100644
--- a/web/projects/ui/src/app/services/marketplace.service.ts
+++ b/web/projects/ui/src/app/services/marketplace.service.ts
@@ -76,7 +76,7 @@ export class MarketplaceService implements AbstractMarketplaceService {
map(({ selectedUrl: url, knownHosts: hosts }) =>
toStoreIdentity(url, hosts[url]),
),
- shareReplay({ bufferSize: 1, refCount: true }),
+ shareReplay(1),
)
private readonly marketplace$ = this.knownHosts$.pipe(
@@ -102,7 +102,7 @@ export class MarketplaceService implements AbstractMarketplaceService {
},
{},
),
- shareReplay({ bufferSize: 1, refCount: true }),
+ shareReplay(1),
)
private readonly filteredMarketplace$ = combineLatest([
@@ -168,21 +168,11 @@ export class MarketplaceService implements AbstractMarketplaceService {
this.marketplace$.pipe(
map(m => m[url]),
filter(Boolean),
- map(({ info, packages }) => {
- const categories = new Set()
- if (info.categories.includes('featured')) categories.add('featured')
- categories.add('all')
- info.categories.forEach(c => categories.add(c))
-
- return {
- url,
- info: {
- ...info,
- categories: Array.from(categories),
- },
- packages,
- }
- }),
+ map(({ info, packages }) => ({
+ url,
+ info,
+ packages,
+ })),
),
),
)
diff --git a/web/projects/ui/src/app/services/network.service.ts b/web/projects/ui/src/app/services/network.service.ts
index e1568603d..9623a5216 100644
--- a/web/projects/ui/src/app/services/network.service.ts
+++ b/web/projects/ui/src/app/services/network.service.ts
@@ -1,11 +1,11 @@
import { inject, Injectable } from '@angular/core'
-import { WINDOW } from '@ng-web-apis/common'
+import { WA_WINDOW } from '@ng-web-apis/common'
import { fromEvent, merge, Observable, shareReplay } from 'rxjs'
import { distinctUntilChanged, map, startWith } from 'rxjs/operators'
@Injectable({ providedIn: 'root' })
export class NetworkService extends Observable {
- private readonly win = inject(WINDOW)
+ private readonly win = inject(WA_WINDOW)
private readonly stream$ = merge(
fromEvent(this.win, 'online'),
fromEvent(this.win, 'offline'),
diff --git a/web/projects/ui/src/app/services/notification.service.ts b/web/projects/ui/src/app/services/notification.service.ts
index f3fd635d8..d9ce24897 100644
--- a/web/projects/ui/src/app/services/notification.service.ts
+++ b/web/projects/ui/src/app/services/notification.service.ts
@@ -2,7 +2,6 @@ import { inject, Injectable } from '@angular/core'
import { ErrorService } from '@start9labs/shared'
import { TuiDialogService } from '@taiga-ui/core'
import {
- NotificationLevel,
ServerNotification,
ServerNotifications,
} from 'src/app/services/api/api.types'
@@ -14,7 +13,7 @@ import { DataModel } from 'src/app/services/patch-db/data-model'
@Injectable({ providedIn: 'root' })
export class NotificationService {
- private readonly patch = inject(PatchDB)
+ private readonly patch = inject>(PatchDB)
private readonly errorService = inject(ErrorService)
private readonly api = inject(ApiService)
private readonly dialogs = inject(TuiDialogService)
@@ -63,13 +62,13 @@ export class NotificationService {
getColor(notification: ServerNotification): string {
switch (notification.level) {
- case NotificationLevel.Info:
+ case 'info':
return 'var(--tui-status-info)'
- case NotificationLevel.Success:
+ case 'success':
return 'var(--tui-status-positive)'
- case NotificationLevel.Warning:
+ case 'warning':
return 'var(--tui-status-warning)'
- case NotificationLevel.Error:
+ case 'error':
return 'var(--tui-status-negative)'
default:
return ''
@@ -78,12 +77,12 @@ export class NotificationService {
getIcon(notification: ServerNotification): string {
switch (notification.level) {
- case NotificationLevel.Info:
+ case 'info':
return '@tui.info'
- case NotificationLevel.Success:
+ case 'success':
return '@tui.circle-check'
- case NotificationLevel.Warning:
- case NotificationLevel.Error:
+ case 'warning':
+ case 'error':
return '@tui.circle-alert'
default:
return ''
diff --git a/web/projects/ui/src/app/services/patch-db/data-model.ts b/web/projects/ui/src/app/services/patch-db/data-model.ts
index d5a3f66f5..bffd5af36 100644
--- a/web/projects/ui/src/app/services/patch-db/data-model.ts
+++ b/web/projects/ui/src/app/services/patch-db/data-model.ts
@@ -56,6 +56,7 @@ export type ServerInfo = {
platform: string
arch: string
governor: string | null
+ zram: boolean
}
export type NetworkInfo = {
diff --git a/web/projects/ui/src/app/services/patch-db/patch-db.providers.ts b/web/projects/ui/src/app/services/patch-db/patch-db.providers.ts
deleted file mode 100644
index e47f1a9a5..000000000
--- a/web/projects/ui/src/app/services/patch-db/patch-db.providers.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import { PatchDB } from 'patch-db-client'
-import { Injector } from '@angular/core'
-import { PATCH_SOURCE, sourceFactory } from './patch-db.factory'
-
-export const PATCH_DB_PROVIDERS = [
- {
- provide: PATCH_SOURCE,
- deps: [Injector],
- useFactory: sourceFactory,
- },
- {
- provide: PatchDB,
- deps: [PATCH_SOURCE],
- useClass: PatchDB,
- },
-]
diff --git a/web/projects/ui/src/app/services/state.service.ts b/web/projects/ui/src/app/services/state.service.ts
index 9eb8caf0a..bffa373f2 100644
--- a/web/projects/ui/src/app/services/state.service.ts
+++ b/web/projects/ui/src/app/services/state.service.ts
@@ -1,7 +1,7 @@
import { inject, Injectable } from '@angular/core'
import { CanActivateFn, IsActiveMatchOptions, Router } from '@angular/router'
-import { ALWAYS_TRUE_HANDLER } from '@taiga-ui/cdk'
-import { TuiAlertService, TuiNotification } from '@taiga-ui/core'
+import { TUI_TRUE_HANDLER } from '@taiga-ui/cdk'
+import { TuiAlertService } from '@taiga-ui/core'
import {
BehaviorSubject,
combineLatest,
@@ -94,8 +94,8 @@ export class StateService extends Observable {
this.alerts
.open('Trying to reach server', {
label: 'State unknown',
- autoClose: false,
- status: TuiNotification.Error,
+ autoClose: 0,
+ status: 'error',
})
.pipe(
takeUntil(
@@ -106,7 +106,7 @@ export class StateService extends Observable {
),
this.alerts.open('Connection restored', {
label: 'Server reached',
- status: TuiNotification.Success,
+ status: 'success',
}),
),
),
@@ -131,6 +131,6 @@ export function stateNot(state: RR.ServerState[]): CanActivateFn {
return () =>
inject(StateService).pipe(
filter(current => !current || !state.includes(current)),
- map(ALWAYS_TRUE_HANDLER),
+ map(TUI_TRUE_HANDLER),
)
}
diff --git a/web/projects/ui/src/app/services/theme-switcher.service.ts b/web/projects/ui/src/app/services/theme-switcher.service.ts
index cdb215457..30cc3e16f 100644
--- a/web/projects/ui/src/app/services/theme-switcher.service.ts
+++ b/web/projects/ui/src/app/services/theme-switcher.service.ts
@@ -1,5 +1,5 @@
import { Inject, Injectable } from '@angular/core'
-import { WINDOW } from '@ng-web-apis/common'
+import { WA_WINDOW } from '@ng-web-apis/common'
import { PatchDB } from 'patch-db-client'
import { filter, take, BehaviorSubject } from 'rxjs'
import { ApiService } from './api/embassy-api.service'
@@ -12,7 +12,7 @@ export class ThemeSwitcherService extends BehaviorSubject {
constructor(
private readonly patch: PatchDB,
private readonly embassyApi: ApiService,
- @Inject(WINDOW) private readonly windowRef: Window,
+ @Inject(WA_WINDOW) private readonly windowRef: Window,
) {
super('Dark')
diff --git a/web/projects/ui/src/app/services/time.service.ts b/web/projects/ui/src/app/services/time.service.ts
index 3ca9d286f..2f30ae5f3 100644
--- a/web/projects/ui/src/app/services/time.service.ts
+++ b/web/projects/ui/src/app/services/time.service.ts
@@ -16,7 +16,7 @@ import { DataModel } from 'src/app/services/patch-db/data-model'
providedIn: 'root',
})
export class TimeService {
- private readonly patch = inject(PatchDB)
+ private readonly patch = inject>(PatchDB)
private readonly time$ = defer(() =>
inject(ApiService).getSystemTime({}),
).pipe(
diff --git a/web/projects/ui/tsconfig.json b/web/projects/ui/tsconfig.json
index 6978bd46f..ea09595fb 100644
--- a/web/projects/ui/tsconfig.json
+++ b/web/projects/ui/tsconfig.json
@@ -3,6 +3,6 @@
"compilerOptions": {
"baseUrl": "./"
},
- "files": ["src/main.ts"],
+ "files": ["src/main.ts", "src/polyfills.ts"],
"include": ["src/**/*.d.ts"]
}