diff --git a/setup-wizard/src/app/pages/init/init.page.ts b/setup-wizard/src/app/pages/init/init.page.ts index 791554c6f..3a535e354 100644 --- a/setup-wizard/src/app/pages/init/init.page.ts +++ b/setup-wizard/src/app/pages/init/init.page.ts @@ -1,8 +1,7 @@ import { Component } from '@angular/core' import { NavController } from '@ionic/angular' import { interval, Observable } from 'rxjs' -import { finalize, skip, take } from 'rxjs/operators' -import { pauseFor } from 'src/app/util/misc.util' +import { finalize, take } from 'rxjs/operators' @Component({ selector: 'app-init', @@ -13,16 +12,14 @@ export class InitPage { progress: Observable constructor ( - private navCtrl: NavController, + private readonly navCtrl: NavController, ) { } ngOnInit () { this.progress = interval(500) .pipe( - skip(1), - take(100), - finalize(async () => { - await pauseFor(1000) + take(101), + finalize(() => { this.navCtrl.navigateForward('/success') }), ) diff --git a/setup-wizard/src/app/pages/loading/loading.page.html b/setup-wizard/src/app/pages/loading/loading.page.html index 12530c147..e26e1032c 100644 --- a/setup-wizard/src/app/pages/loading/loading.page.html +++ b/setup-wizard/src/app/pages/loading/loading.page.html @@ -15,6 +15,7 @@ +

After completion, you will be prompted to download a file from your Embassy. Save the file somewhere safe, it is the easiest way to recover your Embassy's addresses and SSL certificate in case you lose them.

diff --git a/setup-wizard/src/app/pages/password/password.page.html b/setup-wizard/src/app/pages/password/password.page.html index e56986993..e4d177214 100644 --- a/setup-wizard/src/app/pages/password/password.page.html +++ b/setup-wizard/src/app/pages/password/password.page.html @@ -6,17 +6,17 @@ - -
-
-
- -

Choose a password for your Embassy. Make it good. Write it down.

-

Losing your password can result in total loss of data.

-
-

Enter the password that was used to encrypt this drive.

-
- + +
+
+ +

Choose a password for your Embassy. Make it good. Write it down.

+

Losing your password can result in total loss of data.

+
+

Enter the password that was used to encrypt this drive.

+
+ +

Password:

@@ -61,9 +61,9 @@

{{ verError }}

-
- -
+ + +
diff --git a/setup-wizard/src/app/pages/password/password.page.ts b/setup-wizard/src/app/pages/password/password.page.ts index 70d3a9532..53d67d465 100644 --- a/setup-wizard/src/app/pages/password/password.page.ts +++ b/setup-wizard/src/app/pages/password/password.page.ts @@ -9,7 +9,7 @@ import * as argon2 from '@start9labs/argon2' styleUrls: ['password.page.scss'], }) export class PasswordPage { - @ViewChild('focusInput', { static: false }) elem: IonInput + @ViewChild('focusInput') elem: IonInput @Input() recoveryPartition: PartitionInfo @Input() storageDrive: DiskInfo 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 aa0d991ce..418992e52 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 @@ -12,24 +12,17 @@

Verify the product key for the chosen recovery drive.

- + - - - -

{{ error }}

+
+

{{ error }}

+
diff --git a/setup-wizard/src/app/pages/prod-key-modal/prod-key-modal.page.ts b/setup-wizard/src/app/pages/prod-key-modal/prod-key-modal.page.ts index 5d6bd1bdc..186c8ad39 100644 --- a/setup-wizard/src/app/pages/prod-key-modal/prod-key-modal.page.ts +++ b/setup-wizard/src/app/pages/prod-key-modal/prod-key-modal.page.ts @@ -9,7 +9,7 @@ import { HttpService } from 'src/app/services/api/http.service' styleUrls: ['prod-key-modal.page.scss'], }) export class ProdKeyModal { - @ViewChild('focusInput', { static: false }) elem: IonInput + @ViewChild('focusInput') elem: IonInput @Input() recoveryPartition: PartitionInfo error = '' @@ -23,6 +23,10 @@ export class ProdKeyModal { private readonly httpService: HttpService, ) { } + ngAfterViewInit () { + setTimeout(() => this.elem.setFocus(), 400) + } + async verifyProductKey () { if (!this.productKey) return diff --git a/setup-wizard/src/app/pages/product-key/product-key.page.html b/setup-wizard/src/app/pages/product-key/product-key.page.html index af39c0b2f..045bdeb95 100644 --- a/setup-wizard/src/app/pages/product-key/product-key.page.html +++ b/setup-wizard/src/app/pages/product-key/product-key.page.html @@ -18,7 +18,14 @@

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 16a0b28cd..b1de56ead 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 @@ -10,7 +10,7 @@ import { StateService } from 'src/app/services/state.service' styleUrls: ['product-key.page.scss'], }) export class ProductKeyPage { - @ViewChild('focusInput', { static: false }) elem: IonInput + @ViewChild('focusInput') elem: IonInput productKey: string error: string @@ -22,7 +22,7 @@ export class ProductKeyPage { private readonly httpService: HttpService, ) { } - ngAfterViewInit () { + ionViewDidEnter () { setTimeout(() => this.elem.setFocus(), 400) } diff --git a/setup-wizard/src/app/pages/recover/recover.page.html b/setup-wizard/src/app/pages/recover/recover.page.html index e12095b2d..c47a37c9b 100644 --- a/setup-wizard/src/app/pages/recover/recover.page.html +++ b/setup-wizard/src/app/pages/recover/recover.page.html @@ -1,14 +1,14 @@ - + -
+
- + Select Recovery Drive Select the drive containing the Embassy you want to recover. @@ -29,10 +29,8 @@ - - - - + + @@ -45,18 +43,18 @@ -
- +
+

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

+

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

-

{{ drive.capacity | convertBytes }}

+

{{ partition.capacity | convertBytes }}

- Embassy backups detected + Embassy backup detected

diff --git a/setup-wizard/src/app/pages/recover/recover.page.scss b/setup-wizard/src/app/pages/recover/recover.page.scss index 492de6532..3c60ba1c0 100644 --- a/setup-wizard/src/app/pages/recover/recover.page.scss +++ b/setup-wizard/src/app/pages/recover/recover.page.scss @@ -2,3 +2,19 @@ border: 4px solid var(--ion-color-secondary); box-shadow: 4px 4px 16px var(--ion-color-light); } + +.drive-label { + font-weight: bold; + padding-bottom: 6px; +} + +.skeleton-header { + width: 180px; + height: 18px; + --ion-text-color-rgb: var(--ion-color-light-rgb); + margin-bottom: 6px; +} + +ion-item { + padding-bottom: 6px; +} diff --git a/setup-wizard/src/app/pages/recover/recover.page.ts b/setup-wizard/src/app/pages/recover/recover.page.ts index 38fc22958..092c5eb5b 100644 --- a/setup-wizard/src/app/pages/recover/recover.page.ts +++ b/setup-wizard/src/app/pages/recover/recover.page.ts @@ -38,26 +38,25 @@ export class RecoverPage { } partitionClickable (partition: PartitionInfo) { - return partition['embassy-os']?.full && ((!this.stateService.hasProductKey && partition['embassy-os']?.version.startsWith('0.2') ) || this.stateService.hasProductKey) + return partition['embassy-os']?.full && (this.stateService.hasProductKey || this.is02x(partition)) } async getDrives () { try { - this.drives = await this.apiService.getDrives() + this.drives = (await this.apiService.getDrives()).filter(d => d.partitions.length) - const importableDrive = this.drives.filter(d => !!d.guid)[0] + const importableDrive = this.drives.find(d => !!d.guid) 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.', + header: 'Embassy Drive Detected', + message: 'A valid EmbassyOS data drive has been detected. To use this drive in its current state, simply click "Use Drive" below.', buttons: [ { role: 'cancel', - text: 'Dismiss', + text: 'Cancel', }, { - text: 'Use', + text: 'Use Drive', handler: async () => { await this.importDrive(importableDrive.guid) }, @@ -113,6 +112,7 @@ export class RecoverPage { }) await modal.present() + // if no product key, it means they are an upgrade kit user } else { const modal = await this.modalController.create({ component: ProdKeyModal, @@ -140,4 +140,8 @@ export class RecoverPage { } await this.navCtrl.navigateForward(`/embassy`) } + + private is02x (partition: PartitionInfo): boolean { + return !this.stateService.hasProductKey && partition['embassy-os']?.version.startsWith('0.2') + } } diff --git a/setup-wizard/src/app/pages/success/success.page.ts b/setup-wizard/src/app/pages/success/success.page.ts index a38699344..06bf38533 100644 --- a/setup-wizard/src/app/pages/success/success.page.ts +++ b/setup-wizard/src/app/pages/success/success.page.ts @@ -17,7 +17,7 @@ export class SuccessPage { ) { } ngAfterViewInit () { - this.download() + setTimeout(() => this.download(), 500) } async copy (address: string): Promise { 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 745f7537f..7a207b463 100644 --- a/setup-wizard/src/app/services/api/live-api.service.ts +++ b/setup-wizard/src/app/services/api/live-api.service.ts @@ -58,16 +58,26 @@ export class LiveApiService extends ApiService { } async importDrive (guid: string) { - return this.http.rpcRequest({ + const res = await this.http.rpcRequest({ method: 'setup.execute', params: { guid }, }) + + return { + ...res, + 'root-ca': btoa(res['root-ca']), + } } async setupEmbassy (setupInfo: SetupEmbassyReq) { - return this.http.rpcRequest({ + const res = await this.http.rpcRequest({ method: 'setup.execute', params: setupInfo as any, }) + + return { + ...res, + 'root-ca': btoa(res['root-ca']), + } } } 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 0d5152d35..281630814 100644 --- a/setup-wizard/src/app/services/api/mock-api.service.ts +++ b/setup-wizard/src/app/services/api/mock-api.service.ts @@ -66,7 +66,7 @@ export class MockApiService extends ApiService { { logicalname: 'sdc1', label: 'label 1', - capacity: null, + capacity: 0, used: null, 'embassy-os': { version: '0.3.3', @@ -77,18 +77,19 @@ export class MockApiService extends ApiService { { logicalname: 'sdc1MOCKTESTER', label: 'label 1', - capacity: null, + capacity: 0, used: null, 'embassy-os': { version: '0.3.6', full: true, + // password is 'asdfasdf' 'password-hash': '$argon2d$v=19$m=1024,t=1,p=1$YXNkZmFzZGZhc2RmYXNkZg$Ceev1I901G6UwU+hY0sHrFZ56D+o+LNJ', }, }, { logicalname: 'sdc1', label: 'label 1', - capacity: null, + capacity: 0, used: null, 'embassy-os': { version: '0.3.3', @@ -152,7 +153,7 @@ export class MockApiService extends ApiService { return { 'tor-address': 'asdfasdfasdf.onion', 'lan-address': 'embassy-dfasdf.local', - 'root-ca': rootCA, + 'root-ca': btoa(rootCA), } } @@ -161,7 +162,7 @@ export class MockApiService extends ApiService { return { 'tor-address': 'asdfasdfasdf.onion', 'lan-address': 'embassy-dfasdf.local', - 'root-ca': rootCA, + 'root-ca': btoa(rootCA), } } @@ -182,4 +183,26 @@ export class MockApiService extends ApiService { } } -const rootCA = 'LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURwekNDQW8rZ0F3SUJBZ0lSQUlJdU9hcmxRRVRsVVFFT1pKR1pZZEl3RFFZSktvWklodmNOQVFFTEJRQXcKYlRFTE1Ba0dBMVVFQmhNQ1ZWTXhGVEFUQmdOVkJBb01ERVY0WVcxd2JHVWdRMjl5Y0RFT01Bd0dBMVVFQ3d3RgpVMkZzWlhNeEN6QUpCZ05WQkFnTUFsZEJNUmd3RmdZRFZRUUREQTkzZDNjdVpYaGhiWEJzWlM1amIyMHhFREFPCkJnTlZCQWNNQjFObFlYUjBiR1V3SGhjTk1qRXdNekE0TVRVME5qSTNXaGNOTWpJd016QTRNVFkwTmpJM1dqQnQKTVFzd0NRWURWUVFHRXdKVlV6RVZNQk1HQTFVRUNnd01SWGhoYlhCc1pTQkRiM0p3TVE0d0RBWURWUVFMREFWVApZV3hsY3pFTE1Ba0dBMVVFQ0F3Q1YwRXhHREFXQmdOVkJBTU1EM2QzZHk1bGVHRnRjR3hsTG1OdmJURVFNQTRHCkExVUVCd3dIVTJWaGRIUnNaVENDQVNJd0RRWUpLb1pJaHZjTkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQkFNUDcKdDVBS0ZaUTdhYnFrZXlVanNCVklXUmE5dENoOG9nZTl1L0x2Q2J4VTczOEc0anNzVCtPdWQzV01haklqdU5vdwpjcGMrMFEvZTQyVUxPLzZnVE5yVHM2T0NPbzlsVjZHMERwcmYvZTkxRFdvS2dQYXRlbS9wVWpOeXJhaWZIWmZ1CmI1bUxIQ2ZhaGpXWFVRdGMvc2ptRFFhWlJLM0thcjZsamxVQkUvTGU5TkV5T0FJa1NMUHpEdFc4TFhtNGl3Y1UKQlpyYjgyOHJLZDFBdzlvSTErM2JmekI2eFhtelp4YzVSTFh2ZU9DRWhLR0QzMmpLWi9STkZTQzhBWkF3SmUreApiVHN5cy9sVU9ZRlR1VDhCbjBUR3hSOHg3WTRINzUrRjlCYXZZM3YrV2tMajRNK29sTjlkTVI3RXQ5Rk10NHU0CllSb2t2NXpwOHpJYjVpVG5lMWtDQXdFQUFhTkNNRUF3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEUKRmdRVWFXMytyMzI4dVRMb2tvZzJUa2xtb0JLK3l0NHdEZ1lEVlIwUEFRSC9CQVFEQWdHR01BMEdDU3FHU0liMwpEUUVCQ3dVQUE0SUJBUUFYamQvN1VaOFJERStQTFdTRE5HUWRMZW1PQlRjYXdGK3RLK1B6QTRFdmxtbjlWdU5jCmcreDNvWnZWWlNEUUJBTlV6MGI5b1BlbzU0YUUzOGRXMXpRbTJxZlRhYjg4MjJhcWVXTUx5SjFkTXNBZ3FZWDIKdDkrdTZ3M056UkN3OFB2ejE4VjY5K2RGRTVBZVhtTlAwWjUvZ2R6OEgvTlNwY3RqbHpvcGJTY1JaS0NTbFBpZApSZjNaT1BtOVFQOTJZcFd5WURrZkFVMDR4ZERvMXZSME1ZaktQa2w0TGpScVNVL3RjQ0puUE1iSml3cStiV3BYCjJXSm9FQlhCL3AxNUtuNkp4akkwemUyU25TSTQ4Slo4aXQ0ZnZ4cmhPbzBWb0xOSXVDdU5YSk93VTE3UmRsMVcKWUppZGFxN2plNmsxOEFkZ1BBMEtoOHkxWHRmVUgzZlRhVnc0Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0=' +const rootCA = +`-----BEGIN CERTIFICATE----- +MIIDpzCCAo+gAwIBAgIRAIIuOarlQETlUQEOZJGZYdIwDQYJKoZIhvcNAQELBQAw +bTELMAkGA1UEBhMCVVMxFTATBgNVBAoMDEV4YW1wbGUgQ29ycDEOMAwGA1UECwwF +U2FsZXMxCzAJBgNVBAgMAldBMRgwFgYDVQQDDA93d3cuZXhhbXBsZS5jb20xEDAO +BgNVBAcMB1NlYXR0bGUwHhcNMjEwMzA4MTU0NjI3WhcNMjIwMzA4MTY0NjI3WjBt +MQswCQYDVQQGEwJVUzEVMBMGA1UECgwMRXhhbXBsZSBDb3JwMQ4wDAYDVQQLDAVT +YWxlczELMAkGA1UECAwCV0ExGDAWBgNVBAMMD3d3dy5leGFtcGxlLmNvbTEQMA4G +A1UEBwwHU2VhdHRsZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMP7 +t5AKFZQ7abqkeyUjsBVIWRa9tCh8oge9u/LvCbxU738G4jssT+Oud3WMajIjuNow +cpc+0Q/e42ULO/6gTNrTs6OCOo9lV6G0Dprf/e91DWoKgPatem/pUjNyraifHZfu +b5mLHCfahjWXUQtc/sjmDQaZRK3Kar6ljlUBE/Le9NEyOAIkSLPzDtW8LXm4iwcU +BZrb828rKd1Aw9oI1+3bfzB6xXmzZxc5RLXveOCEhKGD32jKZ/RNFSC8AZAwJe+x +bTsys/lUOYFTuT8Bn0TGxR8x7Y4H75+F9BavY3v+WkLj4M+olN9dMR7Et9FMt4u4 +YRokv5zp8zIb5iTne1kCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E +FgQUaW3+r328uTLokog2TklmoBK+yt4wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3 +DQEBCwUAA4IBAQAXjd/7UZ8RDE+PLWSDNGQdLemOBTcawF+tK+PzA4Evlmn9VuNc +g+x3oZvVZSDQBANUz0b9oPeo54aE38dW1zQm2qfTab8822aqeWMLyJ1dMsAgqYX2 +t9+u6w3NzRCw8Pvz18V69+dFE5AeXmNP0Z5/gdz8H/NSpctjlzopbScRZKCSlPid +Rf3ZOPm9QP92YpWyYDkfAU04xdDo1vR0MYjKPkl4LjRqSU/tcCJnPMbJiwq+bWpX +2WJoEBXB/p15Kn6JxjI0ze2SnSI48JZ8it4fvxrhOo0VoLNIuCuNXJOwU17Rdl1W +YJidaq7je6k18AdgPA0Kh8y1XtfUH3fTaVw4 +-----END CERTIFICATE-----` diff --git a/setup-wizard/src/app/services/global-error-handler.service.ts b/setup-wizard/src/app/services/global-error-handler.service.ts index d7234f0b5..1c3475d28 100644 --- a/setup-wizard/src/app/services/global-error-handler.service.ts +++ b/setup-wizard/src/app/services/global-error-handler.service.ts @@ -3,10 +3,11 @@ import { ErrorHandler, Injectable } from '@angular/core' @Injectable() export class GlobalErrorHandler implements ErrorHandler { - handleError (error: any): void { + handleError (e: any): void { + console.error(e) const chunkFailedMessage = /Loading chunk [\d]+ failed/ - if (chunkFailedMessage.test(error.message)) { + if (chunkFailedMessage.test(e.message)) { window.location.reload() } } diff --git a/setup-wizard/src/global.scss b/setup-wizard/src/global.scss index edceae566..748ab485f 100644 --- a/setup-wizard/src/global.scss +++ b/setup-wizard/src/global.scss @@ -47,7 +47,6 @@ ion-button { ion-item { --highlight-color-valid: transparent; --highlight-color-invalid: transparent; - --highlight-color-focused: var(--ion-color-light); --border-radius: 4px; --border-style: solid; diff --git a/ui/src/app/modals/generic-input/generic-input.component.ts b/ui/src/app/modals/generic-input/generic-input.component.ts index 9b354c496..9b9fc05cc 100644 --- a/ui/src/app/modals/generic-input/generic-input.component.ts +++ b/ui/src/app/modals/generic-input/generic-input.component.ts @@ -8,7 +8,7 @@ import { getErrorMessage } from 'src/app/services/error-toast.service' styleUrls: ['./generic-input.component.scss'], }) export class GenericInputComponent { - @ViewChild('mainInput', { static: false }) elem: IonInput + @ViewChild('mainInput') elem: IonInput @Input() title: string @Input() message: string @Input() warning: string