import { Component, inject } from '@angular/core' import { Router } from '@angular/router' import { ErrorService, i18nPipe } from '@start9labs/shared' import { T } from '@start9labs/start-sdk' import { TuiButton, TuiDataList, TuiDialogService, TuiDropdown, TuiIcon, TuiLoader, TuiOptGroup, TuiTitle, } from '@taiga-ui/core' import { TuiCardLarge, TuiHeader } from '@taiga-ui/layout' import { PolymorpheusComponent } from '@taiga-ui/polymorpheus' import { ApiService } from '../services/api.service' import { StateService } from '../services/state.service' import { StartOSDiskInfoFull, StartOSDiskInfoWithId } from '../types' import { CIFS, CifsResult } from '../components/cifs.component' import { SELECT_NETWORK_BACKUP } from '../components/select-network-backup.dialog' import { UnlockPasswordDialog } from '../components/unlock-password.dialog' @Component({ template: ` {{ 'Select Backup' | i18n }} {{ 'Select the StartOS backup you want to restore' | i18n }} {{ 'Refresh' | i18n }} @if (loading) { } @else { {{ 'Select Backup' | i18n }} {{ 'Open Network Backup' | i18n }} @for (server of physicalServers; track server.id) { {{ server.id }} {{ server.drive.vendor }} {{ server.drive.model }} ยท {{ server.partition.logicalname }} } @empty { {{ 'No physical backups' | i18n }} } } `, styles: ` .refresh { display: inline-flex; align-items: center; gap: 0.25rem; cursor: pointer; color: var(--tui-text-action); tui-icon { font-size: 0.875rem; } } .server-item { display: flex; flex-direction: column; small { opacity: 0.7; } } .no-items { padding: 0.5rem 0.75rem; color: var(--tui-text-secondary); font-style: italic; } `, imports: [ TuiButton, TuiCardLarge, TuiDataList, TuiDropdown, TuiLoader, TuiIcon, TuiOptGroup, TuiTitle, TuiHeader, i18nPipe, ], }) export default class RestorePage { private readonly api = inject(ApiService) private readonly router = inject(Router) private readonly dialogs = inject(TuiDialogService) private readonly errorService = inject(ErrorService) private readonly stateService = inject(StateService) private readonly i18n = inject(i18nPipe) loading = true open = false physicalServers: StartOSDiskInfoFull[] = [] async ngOnInit() { await this.loadDrives() } async refresh() { this.loading = true await this.loadDrives() } openCifs() { this.open = false this.dialogs .open(CIFS, { label: this.i18n.transform('Connect Network Folder'), size: 's', }) .subscribe(result => { if (result) { this.handleCifsResult(result) } }) } selectPhysicalBackup(server: StartOSDiskInfoFull) { this.open = false this.showUnlockDialog(server.id, { type: 'disk', logicalname: server.partition.logicalname, }) } private handleCifsResult(result: CifsResult) { if (result.servers.length === 1) { this.showUnlockDialog(result.servers[0]!.id, { type: 'cifs', ...result.cifs, }) } else if (result.servers.length > 1) { this.showSelectNetworkBackupDialog(result.cifs, result.servers) } } private showSelectNetworkBackupDialog( cifs: T.Cifs, servers: StartOSDiskInfoWithId[], ) { this.dialogs .open(SELECT_NETWORK_BACKUP, { label: this.i18n.transform('Select Network Backup'), size: 's', data: { servers }, }) .subscribe(server => { if (server) { this.showUnlockDialog(server.id, { type: 'cifs', ...cifs }) } }) } private showUnlockDialog( serverId: string, target: { type: 'disk'; logicalname: string } | ({ type: 'cifs' } & T.Cifs), ) { this.dialogs .open(new PolymorpheusComponent(UnlockPasswordDialog), { label: this.i18n.transform('Unlock Backup'), size: 's', }) .subscribe(password => { if (password) { this.stateService.recoverySource = { type: 'backup', target, serverId, password, } this.router.navigate(['/password']) } }) } private async loadDrives() { this.physicalServers = [] try { const drives = await this.api.getDisks() this.physicalServers = drives.flatMap(drive => drive.partitions.flatMap(partition => Object.entries(partition.startOs).map(([id, val]) => ({ id, ...val, partition, drive, })), ), ) } catch (e: any) { this.errorService.handleError(e) } finally { this.loading = false } } }