From 7576b30a6b778894f40da9b47db6779469b56717 Mon Sep 17 00:00:00 2001 From: Drew Ansbacher Date: Mon, 1 Nov 2021 15:07:43 -0600 Subject: [PATCH] guid and new alert flow --- .../src/app/pages/password/password.page.html | 2 + .../src/app/pages/password/password.page.ts | 9 +- .../prod-key-modal/prod-key-modal.page.html | 1 + .../prod-key-modal/prod-key-modal.page.ts | 5 +- .../pages/product-key/product-key.page.html | 2 +- .../app/pages/product-key/product-key.page.ts | 9 +- .../src/app/pages/recover/recover.page.html | 64 +++++++------- .../src/app/pages/recover/recover.page.ts | 88 +++++++++++++------ .../src/app/services/api/api.service.ts | 2 + .../src/app/services/api/live-api.service.ts | 7 ++ .../src/app/services/api/mock-api.service.ts | 15 +++- .../src/app/services/state.service.ts | 8 ++ .../backup-drives/backup.service.ts | 20 ++--- ui/src/app/services/api/api.fixures.ts | 4 +- ui/src/app/services/api/api.types.ts | 2 +- 15 files changed, 157 insertions(+), 81 deletions(-) diff --git a/setup-wizard/src/app/pages/password/password.page.html b/setup-wizard/src/app/pages/password/password.page.html index 4bb43db1c..264113861 100644 --- a/setup-wizard/src/app/pages/password/password.page.html +++ b/setup-wizard/src/app/pages/password/password.page.html @@ -25,6 +25,7 @@ [class]="pwError ? 'error-border' : password && !!storageDrive ? 'success-border' : ''" > this.elem.setFocus(), 400) + } + async verifyPw () { if (!this.recoveryPartition || !this.recoveryPartition['embassy-os']) this.pwError = 'No recovery drive' // unreachable diff --git a/setup-wizard/src/app/pages/prod-key-modal/prod-key-modal.page.html b/setup-wizard/src/app/pages/prod-key-modal/prod-key-modal.page.html index 7e9ce9c55..aa0d991ce 100644 --- a/setup-wizard/src/app/pages/prod-key-modal/prod-key-modal.page.html +++ b/setup-wizard/src/app/pages/prod-key-modal/prod-key-modal.page.html @@ -17,6 +17,7 @@ [class]="error ? 'error-border' : ''" > Product Key

- +

{{ error }}

diff --git a/setup-wizard/src/app/pages/product-key/product-key.page.ts b/setup-wizard/src/app/pages/product-key/product-key.page.ts index dea2195d8..16a0b28cd 100644 --- a/setup-wizard/src/app/pages/product-key/product-key.page.ts +++ b/setup-wizard/src/app/pages/product-key/product-key.page.ts @@ -1,5 +1,5 @@ -import { Component } from '@angular/core' -import { LoadingController, NavController } from '@ionic/angular' +import { Component, ViewChild } from '@angular/core' +import { IonInput, LoadingController, NavController } from '@ionic/angular' import { ApiService } from 'src/app/services/api/api.service' import { HttpService } from 'src/app/services/api/http.service' import { StateService } from 'src/app/services/state.service' @@ -10,6 +10,7 @@ import { StateService } from 'src/app/services/state.service' styleUrls: ['product-key.page.scss'], }) export class ProductKeyPage { + @ViewChild('focusInput', { static: false }) elem: IonInput productKey: string error: string @@ -21,6 +22,10 @@ export class ProductKeyPage { private readonly httpService: HttpService, ) { } + ngAfterViewInit () { + setTimeout(() => this.elem.setFocus(), 400) + } + async submit () { if (!this.productKey) return this.error = 'Must enter product key' diff --git a/setup-wizard/src/app/pages/recover/recover.page.html b/setup-wizard/src/app/pages/recover/recover.page.html index 1e6a5dbf7..e12095b2d 100644 --- a/setup-wizard/src/app/pages/recover/recover.page.html +++ b/setup-wizard/src/app/pages/recover/recover.page.html @@ -15,7 +15,7 @@ - +

No recovery drives found

Please connect a recovery drive to your Embassy and refresh the page.

- - - + + + + + + - - - - + + + + - - - - -

{{ p.partition.logicalname }} - {{ p.partition.label }}

-

- {{ p.vendor }} - - - {{ p.model }} -

-

Embassy version: {{p.partition['embassy-os'].version}}

-
- - + +
+ + {{ drive.vendor || 'Unknown Vendor' }} - {{ drive.model || 'Unknown Model' }} - {{ drive.capacity | convertBytes }} + + + + +

{{ partition.label || partition.logicalname }}

+

{{ drive.capacity | convertBytes }}

+

+ + Embassy backups detected + +

+
+
+ + +
- - - Next - +
diff --git a/setup-wizard/src/app/pages/recover/recover.page.ts b/setup-wizard/src/app/pages/recover/recover.page.ts index 2616b7d10..38fc22958 100644 --- a/setup-wizard/src/app/pages/recover/recover.page.ts +++ b/setup-wizard/src/app/pages/recover/recover.page.ts @@ -1,6 +1,6 @@ import { Component } from '@angular/core' -import { ModalController, NavController } from '@ionic/angular' -import { ApiService, PartitionInfo } from 'src/app/services/api/api.service' +import { AlertController, LoadingController, ModalController, NavController } from '@ionic/angular' +import { ApiService, DiskInfo, PartitionInfo } from 'src/app/services/api/api.service' import { ErrorToastService } from 'src/app/services/error-toast.service' import { StateService } from 'src/app/services/state.service' import { PasswordPage } from '../password/password.page' @@ -12,39 +12,60 @@ import { ProdKeyModal } from '../prod-key-modal/prod-key-modal.page' styleUrls: ['recover.page.scss'], }) export class RecoverPage { - passwords = { } - prodKeys = { } - recoveryPartitions: { partition: PartitionInfo, model: string, vendor: string }[] = [] selectedPartition: PartitionInfo = null loading = true + drives: DiskInfo[] = [] + hasShownGuidAlert = false constructor ( private readonly apiService: ApiService, private readonly navCtrl: NavController, private readonly modalController: ModalController, + private readonly alertCtrl: AlertController, + private readonly loadingCtrl: LoadingController, readonly stateService: StateService, private readonly errorToastService: ErrorToastService, ) { } async ngOnInit () { - await this.getPartitions() + await this.getDrives() } async refresh () { - this.recoveryPartitions = [] this.selectedPartition = null this.loading = true - await this.getPartitions() + await this.getDrives() } - async getPartitions () { - try { - let drives = await this.apiService.getDrives() + partitionClickable (partition: PartitionInfo) { + return partition['embassy-os']?.full && ((!this.stateService.hasProductKey && partition['embassy-os']?.version.startsWith('0.2') ) || this.stateService.hasProductKey) + } - this.recoveryPartitions = drives.map(d => d.partitions.map(p => ({ partition: p, vendor: d.vendor, model: d.model})).filter(p => p.partition['embassy-os']?.full)).flat() - // if theres no product key, only show 0.2s - if (!this.stateService.hasProductKey) { - this.recoveryPartitions = this.recoveryPartitions.filter(p => p.partition['embassy-os']?.version.startsWith('0.2')) + async getDrives () { + try { + this.drives = await this.apiService.getDrives() + + const importableDrive = this.drives.filter(d => !!d.guid)[0] + if (!!importableDrive && !this.hasShownGuidAlert) { + const alert = await this.alertCtrl.create({ + header: 'Warning', + subHeader: 'Drive contains data!', + message: 'All data stored on this drive will be permanently deleted.', + buttons: [ + { + role: 'cancel', + text: 'Dismiss', + }, + { + text: 'Use', + handler: async () => { + await this.importDrive(importableDrive.guid) + }, + }, + ], + }) + await alert.present() + this.hasShownGuidAlert = true } } catch (e) { this.errorToastService.present(`${e.message}: ${e.data}`) @@ -53,15 +74,27 @@ export class RecoverPage { } } - async choosePartition (partition: PartitionInfo) { - if (this.selectedPartition?.logicalname === partition.logicalname) { - this.selectedPartition = null - return - } else { - this.selectedPartition = partition + async importDrive (guid: string) { + const loader = await this.loadingCtrl.create({ + message: 'Importing Drive', + }) + await loader.present() + try { + await this.stateService.importDrive(guid) + await this.navCtrl.navigateForward(`/success`) + } catch (e) { + this.errorToastService.present(`${e.message}: ${e.data}`) + } finally { + loader.dismiss() } + } - if ((partition['embassy-os'].version.startsWith('0.2') && this.stateService.hasProductKey) || this.passwords[partition.logicalname] || this.prodKeys[partition.logicalname]) return + async choosePartition (partition: PartitionInfo) { + this.selectedPartition = partition + + if (partition['embassy-os'].version.startsWith('0.2')) { + return this.selectRecoveryPartition() + } if (this.stateService.hasProductKey) { const modal = await this.modalController.create({ @@ -75,7 +108,7 @@ export class RecoverPage { if (!ret.data) { this.selectedPartition = null } else if (ret.data.password) { - this.passwords[partition.logicalname] = ret.data.password + this.selectRecoveryPartition(ret.data.password) } }) @@ -92,7 +125,7 @@ export class RecoverPage { if (!ret.data) { this.selectedPartition = null } else if (ret.data.productKey) { - this.prodKeys[partition.logicalname] = ret.data.productKey + this.selectRecoveryPartition() } }) @@ -100,11 +133,10 @@ export class RecoverPage { } } - async selectRecoveryPartition () { + async selectRecoveryPartition (password?: string) { this.stateService.recoveryPartition = this.selectedPartition - const pw = this.passwords[this.selectedPartition.logicalname] - if (pw) { - this.stateService.recoveryPassword = pw + if (password) { + this.stateService.recoveryPassword = password } await this.navCtrl.navigateForward(`/embassy`) } diff --git a/setup-wizard/src/app/services/api/api.service.ts b/setup-wizard/src/app/services/api/api.service.ts index 361ad3658..02e41f00e 100644 --- a/setup-wizard/src/app/services/api/api.service.ts +++ b/setup-wizard/src/app/services/api/api.service.ts @@ -8,6 +8,7 @@ export abstract class ApiService { // encrypted abstract verifyProductKey (): Promise // echo - throws error if invalid abstract verify03XPassword (logicalname: string, password: string): Promise // setup.recovery.test-password + abstract importDrive (guid: string): Promise // setup.execute abstract setupEmbassy (setupInfo: SetupEmbassyReq): Promise // setup.execute } @@ -35,6 +36,7 @@ export interface DiskInfo { model: string | null, partitions: PartitionInfo[], capacity: number, + guid: string | null, // cant back up if guid exists } export interface RecoveryStatusRes { diff --git a/setup-wizard/src/app/services/api/live-api.service.ts b/setup-wizard/src/app/services/api/live-api.service.ts index 1a1c63f87..745f7537f 100644 --- a/setup-wizard/src/app/services/api/live-api.service.ts +++ b/setup-wizard/src/app/services/api/live-api.service.ts @@ -57,6 +57,13 @@ export class LiveApiService extends ApiService { }) } + async importDrive (guid: string) { + return this.http.rpcRequest({ + method: 'setup.execute', + params: { guid }, + }) + } + async setupEmbassy (setupInfo: SetupEmbassyReq) { return this.http.rpcRequest({ method: 'setup.execute', diff --git a/setup-wizard/src/app/services/api/mock-api.service.ts b/setup-wizard/src/app/services/api/mock-api.service.ts index e67218ba6..0d5152d35 100644 --- a/setup-wizard/src/app/services/api/mock-api.service.ts +++ b/setup-wizard/src/app/services/api/mock-api.service.ts @@ -30,6 +30,7 @@ export class MockApiService extends ApiService { vendor: 'Vendor', model: 'Model', logicalname: '/dev/sda', + guid: 'theguid', partitions: [ { logicalname: 'sda1', @@ -54,11 +55,13 @@ export class MockApiService extends ApiService { logicalname: 'dev/sdb', partitions: [], capacity: 1600.01234, + guid: null, }, { vendor: 'Vendor', model: 'Model', logicalname: 'dev/sdc', + guid: null, partitions: [ { logicalname: 'sdc1', @@ -100,7 +103,8 @@ export class MockApiService extends ApiService { vendor: 'Vendor', model: 'Model', logicalname: '/dev/sdd', - partitions: [ + guid: null, + partitions: [ { logicalname: 'sdd1', label: null, @@ -143,6 +147,15 @@ export class MockApiService extends ApiService { return password.length > 8 } + async importDrive (guid: string) { + await pauseFor(3000) + return { + 'tor-address': 'asdfasdfasdf.onion', + 'lan-address': 'embassy-dfasdf.local', + 'root-ca': rootCA, + } + } + async setupEmbassy (setupInfo: SetupEmbassyReq) { await pauseFor(3000) return { diff --git a/setup-wizard/src/app/services/state.service.ts b/setup-wizard/src/app/services/state.service.ts index 6df7147ea..0398581e9 100644 --- a/setup-wizard/src/app/services/state.service.ts +++ b/setup-wizard/src/app/services/state.service.ts @@ -59,6 +59,14 @@ export class StateService { this.pollDataTransferProgress() } + + async importDrive (guid: string) : Promise { + const ret = await this.apiService.importDrive(guid) + this.torAddress = 'http://' + ret['tor-address'] + this.lanAddress = 'https://' + ret['lan-address'] + this.cert = ret['root-ca'] + } + async setupEmbassy () : Promise { const ret = await this.apiService.setupEmbassy({ 'embassy-logicalname': this.storageDrive.logicalname, diff --git a/ui/src/app/components/backup-drives/backup.service.ts b/ui/src/app/components/backup-drives/backup.service.ts index e3415ad88..d47596d76 100644 --- a/ui/src/app/components/backup-drives/backup.service.ts +++ b/ui/src/app/components/backup-drives/backup.service.ts @@ -24,19 +24,19 @@ export class BackupService { try { const drives = await this.embassyApi.getDrives({ }) this.drives = drives - .filter(d => !d.internal) - .map(d => { - const partionInfo: MappedPartitionInfo[] = d.partitions.map(p => { + .filter(d => !d.guid) + .map(d => { + const partionInfo: MappedPartitionInfo[] = d.partitions.map(p => { + return { + ...p, + hasBackup: [0, 1].includes(this.emver.compare(p['embassy-os']?.version, '0.3.0')), + } + }) return { - ...p, - hasBackup: [0, 1].includes(this.emver.compare(p['embassy-os']?.version, '0.3.0')), + ...d, + partitions: partionInfo, } }) - return { - ...d, - partitions: partionInfo, - } - }) } catch (e) { this.loadingError = getErrorMessage(e) } finally { diff --git a/ui/src/app/services/api/api.fixures.ts b/ui/src/app/services/api/api.fixures.ts index c251cbe2d..0747e6a8a 100644 --- a/ui/src/app/services/api/api.fixures.ts +++ b/ui/src/app/services/api/api.fixures.ts @@ -990,7 +990,7 @@ export module Mock { }, ], capacity: 1000000000000, - internal: true, + guid: 'asdfasdf', }, { logicalname: '/dev/sdb', @@ -1016,7 +1016,7 @@ export module Mock { }, ], capacity: 10000000000, - internal: false, + guid: null, }, ] diff --git a/ui/src/app/services/api/api.types.ts b/ui/src/app/services/api/api.types.ts index f19145bed..f97c2dc0e 100644 --- a/ui/src/app/services/api/api.types.ts +++ b/ui/src/app/services/api/api.types.ts @@ -300,7 +300,7 @@ export interface DriveInfo { model: string | null partitions: PartitionInfo[] capacity: number - internal: boolean + guid: string | null } export interface PartitionInfo {