disable ws for Consulate and better messaging for 0.2.x update

This commit is contained in:
Matt Hill
2021-12-08 21:32:52 -07:00
committed by Aiden McClelland
parent 45f0adde18
commit cfa4055053
13 changed files with 66 additions and 69 deletions

View File

@@ -1,6 +1,6 @@
import { Component } from '@angular/core' import { Component } from '@angular/core'
import { AlertController, LoadingController, ModalController } from '@ionic/angular' import { AlertController, LoadingController, ModalController } from '@ionic/angular'
import { ApiService, BackupTarget, CifsBackupTarget, EmbassyOSRecoveryInfo } from 'src/app/services/api/api.service' import { ApiService, CifsBackupTarget, EmbassyOSRecoveryInfo } from 'src/app/services/api/api.service'
import { PasswordPage } from '../password/password.page' import { PasswordPage } from '../password/password.page'
@Component({ @Component({
@@ -47,7 +47,6 @@ export class CifsModal {
private async presentModalPassword (embassyOS: EmbassyOSRecoveryInfo): Promise<void> { private async presentModalPassword (embassyOS: EmbassyOSRecoveryInfo): Promise<void> {
const target: CifsBackupTarget = { const target: CifsBackupTarget = {
type: 'cifs',
...this.cifs, ...this.cifs,
mountable: true, mountable: true,
'embassy-os': embassyOS, 'embassy-os': embassyOS,

View File

@@ -1,6 +1,6 @@
import { Component, Input, ViewChild } from '@angular/core' import { Component, Input, ViewChild } from '@angular/core'
import { IonInput, ModalController } from '@ionic/angular' import { IonInput, ModalController } from '@ionic/angular'
import { BackupTarget, DiskInfo } from 'src/app/services/api/api.service' import { DiskInfo, CifsBackupTarget, DiskBackupTarget } from 'src/app/services/api/api.service'
import * as argon2 from '@start9labs/argon2' import * as argon2 from '@start9labs/argon2'
@Component({ @Component({
@@ -10,7 +10,7 @@ import * as argon2 from '@start9labs/argon2'
}) })
export class PasswordPage { export class PasswordPage {
@ViewChild('focusInput') elem: IonInput @ViewChild('focusInput') elem: IonInput
@Input() target: BackupTarget @Input() target: CifsBackupTarget | DiskBackupTarget
@Input() storageDrive: DiskInfo @Input() storageDrive: DiskInfo
pwError = '' pwError = ''

View File

@@ -25,13 +25,13 @@
</ng-container> </ng-container>
<ion-item-group *ngIf="storageDrives.length"> <ion-item-group *ngIf="storageDrives.length">
<ion-item (click)="chooseDrive(drive)" class="ion-margin-bottom" button lines="none" *ngFor="let drive of storageDrives"> <ion-item (click)="chooseDrive(drive)" class="ion-margin-bottom" [disabled]="tooSmall(drive)" button lines="none" *ngFor="let drive of storageDrives">
<ion-icon slot="start" name="save-outline" size="large" color="light"></ion-icon> <ion-icon slot="start" name="save-outline" size="large" color="light"></ion-icon>
<ion-label class="ion-text-wrap"> <ion-label class="ion-text-wrap">
<h1>{{ drive.vendor || 'Unknown Vendor' }} - {{ drive.model || 'Unknown Model' }}</h1> <h1>{{ drive.vendor || 'Unknown Vendor' }} - {{ drive.model || 'Unknown Model' }}</h1>
<h2>{{ drive.logicalname }} - {{ drive.capacity | convertBytes }}</h2> <h2>{{ drive.logicalname }} - {{ drive.capacity | convertBytes }}</h2>
<p *ngIf="drive.capacity < 34359738368"> <p *ngIf=tooSmall(drive)>
<ion-text> <ion-text color="danger">
Drive capacity too small. Drive capacity too small.
</ion-text> </ion-text>
</p> </p>

View File

@@ -28,6 +28,10 @@ export class EmbassyPage {
await this.getDrives() await this.getDrives()
} }
tooSmall (drive: DiskInfo) {
return drive.capacity < 34359738368
}
async refresh () { async refresh () {
this.loading = true this.loading = true
await this.getDrives() await this.getDrives()

View File

@@ -1,10 +1,14 @@
<div class="inline"> <div class="inline">
<h2 *ngIf="hasValidBackup"> <!-- has backup -->
<h2 *ngIf="hasValidBackup; else noBackup">
<ion-icon name="cloud-done" color="success"></ion-icon> <ion-icon name="cloud-done" color="success"></ion-icon>
Embassy backup detected {{ is02x ? 'Embassy 0.2.x backup detected' : 'Embassy backup detected' }}
</h2>
<h2 *ngIf="!hasValidBackup">
<ion-icon name="cloud-offline" color="danger"></ion-icon>
No Embassy backup
</h2> </h2>
<!-- no backup -->
<ng-template #noBackup>
<h2>
<ion-icon name="cloud-offline" color="danger"></ion-icon>
No Embassy backup
</h2>
</ng-template>
</div> </div>

View File

@@ -45,12 +45,12 @@
To restore from a physical drive, please follow the <a href="https://docs.start9.com/user-manual/general/backups.html" target="blank" noreferrer>instructions</a>. To restore from a physical drive, please follow the <a href="https://docs.start9.com/user-manual/general/backups.html" target="blank" noreferrer>instructions</a>.
</p> </p>
<ng-container *ngFor="let target of driveTargets"> <ng-container *ngFor="let mapped of mappedDrives">
<ion-item button *ngIf="target.drive as drive" [disabled]="!driveClickable(drive)" (click)="select(drive)"> <ion-item button *ngIf="mapped.drive as drive" [disabled]="!driveClickable(mapped)" (click)="select(drive)">
<ion-icon slot="start" name="save-outline" size="large" color="light"></ion-icon> <ion-icon slot="start" name="save-outline" size="large" color="light"></ion-icon>
<ion-label> <ion-label>
<h1>{{ drive.label || drive.logicalname }}</h1> <h1>{{ drive.label || drive.logicalname }}</h1>
<drive-status [hasValidBackup]="target.hasValidBackup"></drive-status> <drive-status [hasValidBackup]="mapped.hasValidBackup" [is02x]="mapped.is02x"></drive-status>
<p>{{ drive.vendor || 'Unknown Vendor' }} - {{ drive.model || 'Unknown Model' }}</p> <p>{{ drive.vendor || 'Unknown Vendor' }} - {{ drive.model || 'Unknown Model' }}</p>
<p>Capacity: {{ drive.capacity | convertBytes }}</p> <p>Capacity: {{ drive.capacity | convertBytes }}</p>
</ion-label> </ion-label>

View File

@@ -1,10 +1,9 @@
import { Component, Input } from '@angular/core' import { Component, Input } from '@angular/core'
import { AlertController, LoadingController, ModalController, NavController } from '@ionic/angular' import { AlertController, LoadingController, ModalController, NavController } from '@ionic/angular'
import { CifsModal } from 'src/app/modals/cifs-modal/cifs-modal.page' import { CifsModal } from 'src/app/modals/cifs-modal/cifs-modal.page'
import { ApiService, CifsBackupTarget, DiskBackupTarget, DiskRecoverySource, RecoverySource } from 'src/app/services/api/api.service' import { ApiService, DiskBackupTarget } from 'src/app/services/api/api.service'
import { ErrorToastService } from 'src/app/services/error-toast.service' import { ErrorToastService } from 'src/app/services/error-toast.service'
import { StateService } from 'src/app/services/state.service' import { StateService } from 'src/app/services/state.service'
import { MappedDisk } from 'src/app/util/misc.util'
import { PasswordPage } from '../../modals/password/password.page' import { PasswordPage } from '../../modals/password/password.page'
import { ProdKeyModal } from '../../modals/prod-key-modal/prod-key-modal.page' import { ProdKeyModal } from '../../modals/prod-key-modal/prod-key-modal.page'
@@ -15,7 +14,7 @@ import { ProdKeyModal } from '../../modals/prod-key-modal/prod-key-modal.page'
}) })
export class RecoverPage { export class RecoverPage {
loading = true loading = true
driveTargets: MappedDisk[] = [] mappedDrives: MappedDisk[] = []
hasShownGuidAlert = false hasShownGuidAlert = false
constructor ( constructor (
@@ -38,29 +37,30 @@ export class RecoverPage {
await this.getDrives() await this.getDrives()
} }
driveClickable (drive: DiskBackupTarget) { driveClickable (mapped: MappedDisk) {
return drive['embassy-os']?.full && (this.stateService.hasProductKey || this.is02x(drive)) return mapped.drive['embassy-os']?.full && (this.stateService.hasProductKey || mapped.is02x)
} }
async getDrives () { async getDrives () {
this.driveTargets = [] this.mappedDrives = []
try { try {
const drives = await this.apiService.getDrives() const drives = await this.apiService.getDrives()
drives.filter(d => d.partitions.length).forEach(d => { drives.filter(d => d.partitions.length).forEach(d => {
d.partitions.forEach(p => { d.partitions.forEach(p => {
this.driveTargets.push( const drive: DiskBackupTarget = {
vendor: d.vendor,
model: d.model,
logicalname: p.logicalname,
label: p.label,
capacity: p.capacity,
used: p.used,
'embassy-os': p['embassy-os'],
}
this.mappedDrives.push(
{ {
hasValidBackup: p['embassy-os']?.full, hasValidBackup: p['embassy-os']?.full,
drive: { is02x: drive['embassy-os']?.version.startsWith('0.2'),
type: 'disk', drive,
vendor: d.vendor,
model: d.model,
logicalname: p.logicalname,
label: p.label,
capacity: p.capacity,
used: p.used,
'embassy-os': p['embassy-os'],
},
}, },
) )
}) })
@@ -102,7 +102,6 @@ export class RecoverPage {
if (res.role === 'success') { if (res.role === 'success') {
const { hostname, path, username, password } = res.data.cifs const { hostname, path, username, password } = res.data.cifs
this.stateService.recoverySource = { this.stateService.recoverySource = {
type: 'cifs',
hostname, hostname,
path, path,
username, username,
@@ -166,16 +165,11 @@ export class RecoverPage {
private async selectRecoverySource (logicalname: string, password?: string) { private async selectRecoverySource (logicalname: string, password?: string) {
this.stateService.recoverySource = { this.stateService.recoverySource = {
type: 'disk',
logicalname, logicalname,
} }
this.stateService.recoveryPassword = password this.stateService.recoveryPassword = password
this.navCtrl.navigateForward(`/embassy`) this.navCtrl.navigateForward(`/embassy`)
} }
private is02x (drive: DiskBackupTarget): boolean {
return !this.stateService.hasProductKey && drive['embassy-os']?.version.startsWith('0.2')
}
} }
@@ -186,4 +180,12 @@ export class RecoverPage {
}) })
export class DriveStatusComponent { export class DriveStatusComponent {
@Input() hasValidBackup: boolean @Input() hasValidBackup: boolean
@Input() is02x: boolean
}
interface MappedDisk {
is02x: boolean
hasValidBackup: boolean
drive: DiskBackupTarget
} }

View File

@@ -6,7 +6,7 @@ export abstract class ApiService {
abstract getRecoveryStatus (): Promise<RecoveryStatusRes> // setup.recovery.status abstract getRecoveryStatus (): Promise<RecoveryStatusRes> // setup.recovery.status
// encrypted // encrypted
abstract verifyCifs (cifs: VerifyCifs): Promise<EmbassyOSRecoveryInfo> // setup.cifs.verify abstract verifyCifs (cifs: CifsRecoverySource): Promise<EmbassyOSRecoveryInfo> // setup.cifs.verify
abstract verifyProductKey (): Promise<void> // echo - throws error if invalid abstract verifyProductKey (): Promise<void> // echo - throws error if invalid
abstract importDrive (guid: string): Promise<SetupEmbassyRes> // setup.execute abstract importDrive (guid: string): Promise<SetupEmbassyRes> // setup.execute
abstract setupEmbassy (setupInfo: SetupEmbassyReq): Promise<SetupEmbassyRes> // setup.execute abstract setupEmbassy (setupInfo: SetupEmbassyReq): Promise<SetupEmbassyRes> // setup.execute
@@ -18,12 +18,10 @@ export interface GetStatusRes {
migrating: boolean migrating: boolean
} }
export type VerifyCifs = Omit<CifsRecoverySource, 'type'>
export interface SetupEmbassyReq { export interface SetupEmbassyReq {
'embassy-logicalname': string 'embassy-logicalname': string
'embassy-password': string 'embassy-password': string
'recovery-source': RecoverySource | null 'recovery-source': CifsRecoverySource | DiskRecoverySource | null
'recovery-password': string | null 'recovery-password': string | null
} }
@@ -33,8 +31,6 @@ export interface SetupEmbassyRes {
'root-ca': string 'root-ca': string
} }
export type BackupTarget = DiskBackupTarget | CifsBackupTarget
export interface EmbassyOSRecoveryInfo { export interface EmbassyOSRecoveryInfo {
version: string version: string
full: boolean full: boolean
@@ -43,7 +39,6 @@ export interface EmbassyOSRecoveryInfo {
} }
export interface DiskBackupTarget { export interface DiskBackupTarget {
type: 'disk'
vendor: string | null vendor: string | null
model: string | null model: string | null
logicalname: string | null logicalname: string | null
@@ -54,7 +49,6 @@ export interface DiskBackupTarget {
} }
export interface CifsBackupTarget { export interface CifsBackupTarget {
type: 'cifs'
hostname: string hostname: string
path: string path: string
username: string username: string
@@ -62,15 +56,11 @@ export interface CifsBackupTarget {
'embassy-os': EmbassyOSRecoveryInfo | null 'embassy-os': EmbassyOSRecoveryInfo | null
} }
export type RecoverySource = DiskRecoverySource | CifsRecoverySource
export interface DiskRecoverySource { export interface DiskRecoverySource {
type: 'disk'
logicalname: string // partition logicalname logicalname: string // partition logicalname
} }
export interface CifsRecoverySource { export interface CifsRecoverySource {
type: 'cifs'
hostname: string hostname: string
path: string path: string
username: string username: string

View File

@@ -1,5 +1,5 @@
import { Injectable } from '@angular/core' import { Injectable } from '@angular/core'
import { ApiService, DiskInfo, EmbassyOSRecoveryInfo, GetStatusRes, RecoverySource, RecoveryStatusRes, SetupEmbassyReq, SetupEmbassyRes, VerifyCifs } from './api.service' import { ApiService, CifsRecoverySource, DiskInfo, DiskRecoverySource, EmbassyOSRecoveryInfo, GetStatusRes, RecoveryStatusRes, SetupEmbassyReq, SetupEmbassyRes } from './api.service'
import { HttpService } from './http.service' import { HttpService } from './http.service'
@Injectable({ @Injectable({
@@ -43,11 +43,11 @@ export class LiveApiService extends ApiService {
// ** ENCRYPTED ** // ** ENCRYPTED **
async verifyCifs (params: VerifyCifs) { async verifyCifs (source: CifsRecoverySource) {
params.path = params.path.replace('/\\/g', '/') source.path = source.path.replace('/\\/g', '/')
return this.http.rpcRequest<EmbassyOSRecoveryInfo>({ return this.http.rpcRequest<EmbassyOSRecoveryInfo>({
method: 'setup.cifs.verify', method: 'setup.cifs.verify',
params, params: source as any,
}) })
} }
@@ -71,7 +71,7 @@ export class LiveApiService extends ApiService {
} }
async setupEmbassy (setupInfo: SetupEmbassyReq) { async setupEmbassy (setupInfo: SetupEmbassyReq) {
if (setupInfo['recovery-source']?.type === 'cifs') { if (isCifsSource(setupInfo['recovery-source'])) {
setupInfo['recovery-source'].path = setupInfo['recovery-source'].path.replace('/\\/g', '/') setupInfo['recovery-source'].path = setupInfo['recovery-source'].path.replace('/\\/g', '/')
} }
@@ -93,3 +93,7 @@ export class LiveApiService extends ApiService {
}) })
} }
} }
function isCifsSource (source: CifsRecoverySource | DiskRecoverySource | undefined): source is CifsRecoverySource {
return !!(source as CifsRecoverySource)?.hostname
}

View File

@@ -1,6 +1,6 @@
import { Injectable } from '@angular/core' import { Injectable } from '@angular/core'
import { pauseFor } from 'src/app/util/misc.util' import { pauseFor } from 'src/app/util/misc.util'
import { ApiService, RecoverySource, SetupEmbassyReq, VerifyCifs } from './api.service' import { ApiService, CifsRecoverySource, SetupEmbassyReq } from './api.service'
let tries = 0 let tries = 0
@@ -137,13 +137,13 @@ export class MockApiService extends ApiService {
return { return {
'bytes-transferred': tries, 'bytes-transferred': tries,
'total-bytes': 4, 'total-bytes': 4,
complete: tries === 4 complete: tries === 4,
} }
} }
// ** ENCRYPTED ** // ** ENCRYPTED **
async verifyCifs (params: VerifyCifs) { async verifyCifs (params: CifsRecoverySource) {
await pauseFor(1000) await pauseFor(1000)
return { return {
version: '0.3.0', version: '0.3.0',

View File

@@ -1,6 +1,6 @@
import { Injectable } from '@angular/core' import { Injectable } from '@angular/core'
import { BehaviorSubject } from 'rxjs' import { BehaviorSubject } from 'rxjs'
import { ApiService, RecoverySource } from './api/api.service' import { ApiService, CifsRecoverySource, DiskRecoverySource } from './api/api.service'
import { ErrorToastService } from './error-toast.service' import { ErrorToastService } from './error-toast.service'
import { pauseFor } from '../util/misc.util' import { pauseFor } from '../util/misc.util'
@@ -14,7 +14,7 @@ export class StateService {
polling = false polling = false
embassyLoaded = false embassyLoaded = false
recoverySource: RecoverySource recoverySource: CifsRecoverySource | DiskRecoverySource
recoveryPassword: string recoveryPassword: string
dataTransferProgress: { bytesTransferred: number, totalBytes: number, complete: boolean } | null dataTransferProgress: { bytesTransferred: number, totalBytes: number, complete: boolean } | null

View File

@@ -1,10 +1,3 @@
import { DiskBackupTarget } from '../services/api/api.service'
export interface MappedDisk {
hasValidBackup: boolean
drive: DiskBackupTarget
}
export const pauseFor = (ms: number) => { export const pauseFor = (ms: number) => {
return new Promise(resolve => setTimeout(resolve, ms)) return new Promise(resolve => setTimeout(resolve, ms))
} }

View File

@@ -37,7 +37,8 @@ export class ConfigService {
mocks = mocks mocks = mocks
skipStartupAlerts = mocks.enabled && mocks.skipStartupAlerts skipStartupAlerts = mocks.enabled && mocks.skipStartupAlerts
supportsWebSockets = 'WebSocket' in window || 'MozWebSocket' in window isConsulate = window['platform'] === 'ios'
supportsWebSockets = !!window.WebSocket || this.isConsulate
isTor (): boolean { isTor (): boolean {
return (mocks.enabled && mocks.maskAs === 'tor') || this.origin.endsWith('.onion') return (mocks.enabled && mocks.maskAs === 'tor') || this.origin.endsWith('.onion')