From 8f34d1c555da7038352f9ae660879ad1ea477fbe Mon Sep 17 00:00:00 2001 From: waterplea Date: Tue, 27 Aug 2024 12:17:05 +0400 Subject: [PATCH] fix: final fixes --- .../routes/dashboard/controls.component.ts | 14 +++-- .../routes/dashboard/service.component.ts | 2 +- .../service/components/actions.component.ts | 42 +++++++++---- .../service/routes/service.component.ts | 5 +- .../backups/components/targets.component.ts | 25 ++++---- .../backups/components/upcoming.component.ts | 11 +++- .../system/backups/modals/edit.component.ts | 20 ++++-- .../backups/modals/history.component.ts | 19 ++++-- .../system/backups/modals/jobs.component.ts | 28 ++++++--- .../backups/modals/recover.component.ts | 8 +-- .../system/backups/modals/target.component.ts | 34 +++++----- .../backups/modals/targets.component.ts | 30 ++++++--- .../backups/pipes/get-backup-icon.pipe.ts | 2 +- .../system/backups/services/create.service.ts | 2 +- .../backups/services/restore.service.ts | 2 +- .../system/backups/utils/job-builder.ts | 30 +++++---- .../settings/routes/wifi/wifi.component.ts | 62 +++++++++---------- .../ui/src/app/services/api/api.fixures.ts | 20 +++--- .../ui/src/app/services/api/api.types.ts | 7 +-- .../services/api/embassy-mock-api.service.ts | 21 ++++--- .../ui/src/app/services/api/mock-patch.ts | 3 + .../src/app/services/patch-db/data-model.ts | 3 + .../services/pkg-status-rendering.service.ts | 2 +- 23 files changed, 235 insertions(+), 157 deletions(-) diff --git a/web/projects/ui/src/app/routes/portal/routes/dashboard/controls.component.ts b/web/projects/ui/src/app/routes/portal/routes/dashboard/controls.component.ts index 2429dae26..06c2309f2 100644 --- a/web/projects/ui/src/app/routes/portal/routes/dashboard/controls.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/dashboard/controls.component.ts @@ -13,13 +13,16 @@ import { UILaunchComponent } from 'src/app/routes/portal/routes/dashboard/ui.com import { ActionsService } from 'src/app/services/actions.service' import { DepErrorService } from 'src/app/services/dep-error.service' import { PackageDataEntry } from 'src/app/services/patch-db/data-model' +import { renderPkgStatus } from 'src/app/services/pkg-status-rendering.service' import { getManifest } from 'src/app/utils/get-package-data' +const RUNNING = ['running', 'starting', 'restarting'] + @Component({ standalone: true, selector: 'fieldset[appControls]', template: ` - @if (pkg().status.main.status === 'running') { + @if (running()) { + } + @if (canRestart) { } - @if (!isConfigured) { + @if (canConfigure) { @@ -59,7 +60,7 @@ import { GetBackupIconPipe } from '../pipes/get-backup-icon.pipe' size="xs" appearance="icon" iconStart="@tui.trash-2" - (click)="delete$.next(target.id)" + (click)="delete$.next(target.key)" > Delete @@ -132,7 +133,7 @@ import { GetBackupIconPipe } from '../pipes/get-backup-icon.pipe' `, changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, - imports: [TuiButton, GetBackupIconPipe, TuiIcon], + imports: [TuiButton, GetBackupIconPipe, TuiIcon, KeyValuePipe], }) export class BackupsTargetsComponent { private readonly dialogs = inject(TuiDialogService) @@ -140,10 +141,10 @@ export class BackupsTargetsComponent { readonly delete$ = new Subject() @Input() - backupsTargets: readonly BackupTarget[] | null = null + backupsTargets: Record | null = null @Output() - readonly update = new EventEmitter() + readonly update = new EventEmitter() @Output() readonly delete = this.delete$.pipe( 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 af5978170..002ea8edf 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 @@ -33,8 +33,10 @@ import { GetBackupIconPipe } from '../pipes/get-backup-icon.pipe' {{ job.name }} - - {{ job.target.name }} + @if (targets()?.saved?.[job.targetId]; as target) { + + {{ target.name }} + } Packages: {{ job.packageIds.length }} @@ -94,6 +96,9 @@ import { GetBackupIconPipe } from '../pipes/get-backup-icon.pipe' imports: [GetBackupIconPipe, DatePipe, TuiIcon], }) export class BackupsUpcomingComponent { + private readonly api = inject(ApiService) + + readonly targets = toSignal(from(this.api.getBackupTargets({}))) readonly current = toSignal( inject>(PatchDB) .watch$('serverInfo', 'statusInfo', 'currentBackup', 'job') @@ -101,7 +106,7 @@ export class BackupsUpcomingComponent { ) readonly upcoming = toSignal( - from(inject(ApiService).getBackupJobs({})).pipe( + from(this.api.getBackupJobs({})).pipe( map(jobs => jobs .map(job => { diff --git a/web/projects/ui/src/app/routes/portal/routes/system/backups/modals/edit.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/backups/modals/edit.component.ts index 082dd893a..2dd95c7d2 100644 --- a/web/projects/ui/src/app/routes/portal/routes/system/backups/modals/edit.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/system/backups/modals/edit.component.ts @@ -1,3 +1,4 @@ +import { toSignal } from '@angular/core/rxjs-interop' import { TuiWrapperModule, TuiInputModule, @@ -13,6 +14,7 @@ import { POLYMORPHEUS_CONTEXT, PolymorpheusComponent, } from '@taiga-ui/polymorpheus' +import { from, map } from 'rxjs' import { ApiService } from 'src/app/services/api/embassy-api.service' import { BackupJob, BackupTarget } from 'src/app/services/api/api.types' import { TARGET, TARGET_CREATE } from './target.component' @@ -36,8 +38,8 @@ import { ToHumanCronPipe } from '../pipes/to-human-cron.pipe' (click)="selectTarget()" > Target - - {{ job.target.type || 'Select target' }} + + {{ target()?.type || 'Select target' }} - - {{ run.job.target.name }} + @if (targets()?.saved?.[run.job.targetId]; as target) { + + {{ target.name }} + } } @empty { @if (runs()) { - No backups have been run yet. + + No backups have been run yet. + } @else { @for (row of ['', '']; track $index) { -
Loading
+ +
Loading
+ } } @@ -166,7 +174,8 @@ export class BackupsHistoryModal { private readonly errorService = inject(ErrorService) private readonly loader = inject(LoadingService) - runs = signal(null) + readonly targets = toSignal(from(this.api.getBackupTargets({}))) + readonly runs = signal(null) selected: boolean[] = [] get all(): boolean | null { diff --git a/web/projects/ui/src/app/routes/portal/routes/system/backups/modals/jobs.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/backups/modals/jobs.component.ts index 5d4f428d7..c25cb30cc 100644 --- a/web/projects/ui/src/app/routes/portal/routes/system/backups/modals/jobs.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/system/backups/modals/jobs.component.ts @@ -1,4 +1,5 @@ import { Component, inject, OnInit } from '@angular/core' +import { toSignal } from '@angular/core/rxjs-interop' import { ErrorService, LoadingService } from '@start9labs/shared' import { TuiDialogOptions, @@ -9,7 +10,7 @@ import { } from '@taiga-ui/core' import { TuiConfirmData, TUI_CONFIRM } from '@taiga-ui/kit' import { PolymorpheusComponent } from '@taiga-ui/polymorpheus' -import { BehaviorSubject, filter } from 'rxjs' +import { BehaviorSubject, filter, from } from 'rxjs' import { BackupJob } from 'src/app/services/api/api.types' import { ApiService } from 'src/app/services/api/embassy-api.service' import { GetBackupIconPipe } from '../pipes/get-backup-icon.pipe' @@ -52,8 +53,10 @@ import { EDIT } from './edit.component' {{ job.name }} - - {{ job.target.name }} + @if (targets()?.saved?.[job.targetId]; as target) { + + {{ target.name }} + } Packages: {{ job.packageIds.length }} {{ (job.cron | toHumanCron).message }} @@ -76,11 +79,15 @@ import { EDIT } from './edit.component' } @empty { @if (jobs) { - No jobs found. + + No jobs found. + } @else { @for (i of ['', '']; track $index) { -
Loading
+ +
Loading
+ } } @@ -147,6 +154,7 @@ export class BackupsJobsModal implements OnInit { private readonly api = inject(ApiService) readonly loading$ = new BehaviorSubject(true) + readonly targets = toSignal(from(this.api.getBackupTargets({}))) jobs?: BackupJob[] @@ -179,11 +187,11 @@ export class BackupsJobsModal implements OnInit { label: 'Edit Job', data: new BackupJobBuilder(data), }) - .subscribe(job => { - data.name = job.name - data.target = job.target - data.cron = job.cron - data.packageIds = job.packageIds + .subscribe(({ name, targetId, cron, packageIds }) => { + data.name = name + data.targetId = targetId + data.cron = cron + data.packageIds = packageIds }) } 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 77c726154..0e3b28156 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 @@ -85,17 +85,17 @@ export class BackupsRecoverModal { .watch$('packageData') .pipe(take(1)) - readonly toMessage = (option: RecoverOption) => { - if (option.newerStartOs) { + readonly toMessage = ({ newerStartOs, installed, title }: RecoverOption) => { + if (newerStartOs) { return { text: `Unavailable. Backup was made on a newer version of StartOS.`, color: 'var(--tui-status-negative)', } } - if (option.installed) { + if (installed) { return { - text: `Unavailable. ${option.title} is already installed.`, + text: `Unavailable. ${title} is already installed.`, color: 'var(--tui-status-warning)', } } 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 2878974c5..229d6d3d4 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 @@ -1,10 +1,11 @@ +import { KeyValuePipe } from '@angular/common' import { ChangeDetectionStrategy, Component, inject, signal, } from '@angular/core' -import { ErrorService, Exver, isEmptyObject } from '@start9labs/shared' +import { ErrorService, Exver } from '@start9labs/shared' import { TuiButton, TuiDialogContext, @@ -17,15 +18,15 @@ import { POLYMORPHEUS_CONTEXT, PolymorpheusComponent, } from '@taiga-ui/polymorpheus' +import { PatchDB } from 'patch-db-client' import { BackupTarget } from 'src/app/services/api/api.types' import { ApiService } from 'src/app/services/api/embassy-api.service' +import { DataModel } from 'src/app/services/patch-db/data-model' +import { getServerInfo } from 'src/app/utils/get-server-info' 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: ` @@ -33,20 +34,20 @@ import { DataModel } from 'src/app/services/patch-db/data-model' } @else {

Saved Targets

- @for (target of targets; track $index) { + @for (target of targets | keyvalue; track $index) {

- - - - + } @else { + + } + } + } @else { +

No wireless interface detected.

+ } `, changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [ - CommonModule, FormsModule, TuiButton, TuiSwitch, - TuiLet, TuiCardLarge, TuiLoader, - SharedPipesModule, + TuiAppearance, WifiInfoComponent, WifiTableComponent, ], @@ -97,14 +97,10 @@ export class SettingsWifiComponent { private readonly update$ = new Subject() private readonly formDialog = inject(FormDialogService) private readonly cdr = inject(ChangeDetectorRef) + private readonly patch = inject>(PatchDB) - readonly wifi$ = merge(this.getWifi$(), this.update$) - readonly enabled$ = inject>(PatchDB).watch$( - 'serverInfo', - 'network', - 'wifi', - 'enabled', - ) + readonly status = toSignal(this.patch.watch$('serverInfo', 'network', 'wifi')) + readonly wifi = toSignal(merge(this.getWifi$(), this.update$)) async onToggle(enable: boolean) { const loader = this.loader 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 f120f7628..14f890c98 100644 --- a/web/projects/ui/src/app/services/api/api.fixures.ts +++ b/web/projects/ui/src/app/services/api/api.fixures.ts @@ -1006,9 +1006,8 @@ export module Mock { startOs: {}, }, ], - saved: [ - { - id: 'hsbdjhasbasda', + saved: { + hsbdjhasbasda: { type: 'cifs', name: 'Embassy Backups', hostname: 'smb://192.169.10.0', @@ -1026,8 +1025,7 @@ export module Mock { }, }, }, - { - id: 'ftcvewdnkemfksdm', + ftcvewdnkemfksdm: { type: 'cloud', name: 'Dropbox 1', provider: 'dropbox', @@ -1035,8 +1033,7 @@ export module Mock { mountable: false, startOs: {}, }, - { - id: 'csgashbdjkasnd', + csgashbdjkasnd: { type: 'cifs', name: 'Network Folder 2', hostname: 'smb://192.169.10.0', @@ -1045,8 +1042,7 @@ export module Mock { mountable: true, startOs: {}, }, - { - id: 'powjefhjbnwhdva', + powjefhjbnwhdva: { type: 'disk', name: 'Physical Drive 1', logicalname: 'sdba1', @@ -1068,21 +1064,21 @@ export module Mock { }, }, }, - ], + }, } export const BackupJobs: RR.GetBackupJobsRes = [ { id: 'lalalalalala-babababababa', name: 'My Backup Job', - target: BackupTargets.saved[0], + targetId: Object.keys(BackupTargets.saved)[0], cron: '0 3 * * *', packageIds: ['bitcoind', 'lnd'], }, { id: 'hahahahaha-mwmwmwmwmwmw', name: 'Another Backup Job', - target: BackupTargets.saved[1], + targetId: Object.keys(BackupTargets.saved)[1], cron: '0 * * * *', packageIds: ['lnd'], }, 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 5efcf2bbf..e9ed0f781 100644 --- a/web/projects/ui/src/app/services/api/api.types.ts +++ b/web/projects/ui/src/app/services/api/api.types.ts @@ -256,7 +256,7 @@ export module RR { export type GetBackupTargetsReq = {} // backup.target.list export type GetBackupTargetsRes = { unknownDisks: UnknownDisk[] - saved: BackupTarget[] + saved: Record } export type AddCifsBackupTargetReq = { @@ -277,7 +277,7 @@ export module RR { name: string path: string } // backup.target.disk.add - export type AddBackupTargetRes = BackupTarget + export type AddBackupTargetRes = Record export type UpdateCifsBackupTargetReq = AddCifsBackupTargetReq & { id: string @@ -539,7 +539,6 @@ export interface UnknownDisk { } export interface BaseBackupTarget { - id: string type: BackupTargetType name: string mountable: boolean @@ -574,7 +573,7 @@ export type BackupRun = { export type BackupJob = { id: string name: string - target: BackupTarget + targetId: string cron: string // '* * * * * *' https://cloud.google.com/scheduler/docs/configuring/cron-job-schedules packageIds: string[] } 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 83fbd9829..5e0ccc385 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 @@ -822,14 +822,15 @@ export class MockApiService extends ApiService { await pauseFor(2000) const { path, name } = params return { - id: 'latfgvwdbhjsndmk', - name, - type: 'cifs', - hostname: 'mockhotname', - path: path.replace(/\\/g, '/'), - username: 'mockusername', - mountable: true, - startOs: {}, + latfgvwdbhjsndmk: { + name, + type: 'cifs', + hostname: 'mockhotname', + path: path.replace(/\\/g, '/'), + username: 'mockusername', + mountable: true, + startOs: {}, + }, } } @@ -838,7 +839,7 @@ export class MockApiService extends ApiService { params: RR.UpdateCifsBackupTargetReq | RR.UpdateCloudBackupTargetReq, ): Promise { await pauseFor(2000) - return Mock.BackupTargets.saved.find(b => b.id === params.id)! + return { [params.id]: Mock.BackupTargets.saved[params.id] } } async removeBackupTarget( @@ -862,7 +863,7 @@ export class MockApiService extends ApiService { return { id: 'hjdfbjsahdbn', name: params.name, - target: Mock.BackupTargets.saved[0], + targetId: Object.keys(Mock.BackupTargets.saved)[0], cron: params.cron, packageIds: params.packageIds, } 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 6759590a2..bcda83c6a 100644 --- a/web/projects/ui/src/app/services/api/mock-patch.ts +++ b/web/projects/ui/src/app/services/api/mock-patch.ts @@ -79,6 +79,9 @@ export const mockPatchData: DataModel = { wifi: { enabled: false, lastRegion: null, + interface: 'test', + ssids: [], + selected: null, }, wanConfig: { upnp: false, 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 bffd5af36..1550d1e7d 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 @@ -86,6 +86,9 @@ export type PortForward = { export type WiFiInfo = { enabled: boolean lastRegion: string | null + interface: string | null + ssids: Array + selected: string | null } export type Domain = { diff --git a/web/projects/ui/src/app/services/pkg-status-rendering.service.ts b/web/projects/ui/src/app/services/pkg-status-rendering.service.ts index 7abc42f65..a813adb92 100644 --- a/web/projects/ui/src/app/services/pkg-status-rendering.service.ts +++ b/web/projects/ui/src/app/services/pkg-status-rendering.service.ts @@ -10,7 +10,7 @@ export interface PackageStatus { export function renderPkgStatus( pkg: PackageDataEntry, - depErrors: PkgDependencyErrors, + depErrors: PkgDependencyErrors = {}, ): PackageStatus { let primary: PrimaryStatus let dependency: DependencyStatus | null = null