mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 02:11:53 +00:00
Add guid to partition type (#1932)
* add guid to partitions and implement pipe in shared to return guid for any disk * fix bug and clean up
This commit is contained in:
committed by
Aiden McClelland
parent
22b273b145
commit
45a6a930c9
@@ -5,7 +5,10 @@ import { IonicModule } from '@ionic/angular'
|
||||
import { FormsModule } from '@angular/forms'
|
||||
import { HomePage } from './home.page'
|
||||
import { SwiperModule } from 'swiper/angular'
|
||||
import { UnitConversionPipesModule } from '@start9labs/shared'
|
||||
import {
|
||||
UnitConversionPipesModule,
|
||||
GuidPipePipesModule,
|
||||
} from '@start9labs/shared'
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
@@ -22,6 +25,7 @@ const routes: Routes = [
|
||||
RouterModule.forChild(routes),
|
||||
SwiperModule,
|
||||
UnitConversionPipesModule,
|
||||
GuidPipePipesModule,
|
||||
],
|
||||
declarations: [HomePage],
|
||||
})
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
</ion-card-header>
|
||||
<ion-card-content class="ion-margin-bottom">
|
||||
<swiper (swiper)="setSwiperInstance($event)">
|
||||
<!-- SLIDE 1 -->
|
||||
<ng-template swiperSlide>
|
||||
<ion-item
|
||||
*ngFor="let disk of disks"
|
||||
@@ -51,43 +52,53 @@
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
</ng-template>
|
||||
|
||||
<!-- SLIDE 2 -->
|
||||
<ng-template swiperSlide>
|
||||
<ion-item
|
||||
*ngIf="!!selectedDisk?.guid"
|
||||
button
|
||||
(click)="tryInstall(false)"
|
||||
>
|
||||
<ion-icon
|
||||
color="dark"
|
||||
slot="start"
|
||||
size="large"
|
||||
name="medkit-outline"
|
||||
></ion-icon>
|
||||
<ion-label>
|
||||
<h1>
|
||||
<ion-text color="success">Re-Install embassyOS</ion-text>
|
||||
</h1>
|
||||
<h2>Will preserve existing embassyOS data</h2>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item button lines="none" (click)="tryInstall(true)">
|
||||
<ion-icon
|
||||
color="dark"
|
||||
slot="start"
|
||||
size="large"
|
||||
name="download-outline"
|
||||
></ion-icon>
|
||||
<ion-label>
|
||||
<h1>
|
||||
<ion-text
|
||||
[color]="!!selectedDisk?.guid ? 'danger' : 'success'"
|
||||
>{{ !!selectedDisk?.guid ? 'Factory Reset' : 'Install
|
||||
embassyOS' }}</ion-text
|
||||
>
|
||||
</h1>
|
||||
<h2>Will delete existing data on disk</h2>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ng-container *ngIf="selectedDisk">
|
||||
<!-- re-install -->
|
||||
<ion-item
|
||||
*ngIf="selectedDisk | guid"
|
||||
button
|
||||
(click)="tryInstall(false)"
|
||||
>
|
||||
<ion-icon
|
||||
color="dark"
|
||||
slot="start"
|
||||
size="large"
|
||||
name="medkit-outline"
|
||||
></ion-icon>
|
||||
<ion-label>
|
||||
<h1>
|
||||
<ion-text color="success"
|
||||
>Re-Install embassyOS</ion-text
|
||||
>
|
||||
</h1>
|
||||
<h2>Will preserve existing embassyOS data</h2>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
|
||||
<!-- fresh install -->
|
||||
<ion-item button lines="none" (click)="tryInstall(true)">
|
||||
<ion-icon
|
||||
color="dark"
|
||||
slot="start"
|
||||
size="large"
|
||||
name="download-outline"
|
||||
></ion-icon>
|
||||
<ion-label>
|
||||
<h1>
|
||||
<ion-text
|
||||
[color]="(selectedDisk | guid) ? 'danger' : 'success'"
|
||||
>
|
||||
{{ (selectedDisk | guid) ? 'Factory Reset' : 'Install
|
||||
embassyOS' }}
|
||||
</ion-text>
|
||||
</h1>
|
||||
<h2>Will delete existing data on disk</h2>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
</ng-container>
|
||||
</ng-template>
|
||||
</swiper>
|
||||
</ion-card-content>
|
||||
|
||||
@@ -24,6 +24,7 @@ export class MockApiService implements ApiService {
|
||||
'$argon2d$v=19$m=1024,t=1,p=1$YXNkZmFzZGZhc2RmYXNkZg$Ceev1I901G6UwU+hY0sHrFZ56D+o+LNJ',
|
||||
'wrapped-key': null,
|
||||
},
|
||||
guid: null,
|
||||
},
|
||||
],
|
||||
capacity: 123456789123,
|
||||
@@ -40,15 +41,39 @@ export class MockApiService implements ApiService {
|
||||
capacity: 73264762332,
|
||||
used: null,
|
||||
'embassy-os': {
|
||||
version: '0.3.1',
|
||||
version: '0.3.3',
|
||||
full: true,
|
||||
'password-hash':
|
||||
'$argon2d$v=19$m=1024,t=1,p=1$YXNkZmFzZGZhc2RmYXNkZg$Ceev1I901G6UwU+hY0sHrFZ56D+o+LNJ',
|
||||
'wrapped-key': null,
|
||||
},
|
||||
guid: null,
|
||||
},
|
||||
],
|
||||
capacity: 123456789123,
|
||||
capacity: 124456789123,
|
||||
guid: null,
|
||||
},
|
||||
{
|
||||
logicalname: 'wxyz',
|
||||
vendor: 'SanDisk',
|
||||
model: 'Specialness',
|
||||
partitions: [
|
||||
{
|
||||
logicalname: 'pbcba',
|
||||
label: null,
|
||||
capacity: 73264762332,
|
||||
used: null,
|
||||
'embassy-os': {
|
||||
version: '0.3.2',
|
||||
full: true,
|
||||
'password-hash':
|
||||
'$argon2d$v=19$m=1024,t=1,p=1$YXNkZmFzZGZhc2RmYXNkZg$Ceev1I901G6UwU+hY0sHrFZ56D+o+LNJ',
|
||||
'wrapped-key': null,
|
||||
},
|
||||
guid: 'guid-guid-guid-guid',
|
||||
},
|
||||
],
|
||||
capacity: 123459789123,
|
||||
guid: null,
|
||||
},
|
||||
]
|
||||
|
||||
@@ -8,6 +8,11 @@ const routes: Routes = [
|
||||
loadChildren: () =>
|
||||
import('./pages/home/home.module').then(m => m.HomePageModule),
|
||||
},
|
||||
{
|
||||
path: 'attach',
|
||||
loadChildren: () =>
|
||||
import('./pages/attach/attach.module').then(m => m.AttachPageModule),
|
||||
},
|
||||
{
|
||||
path: 'recover',
|
||||
loadChildren: () =>
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import { NgModule } from '@angular/core'
|
||||
import { RouterModule, Routes } from '@angular/router'
|
||||
import { AttachPage } from './attach.page'
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: AttachPage,
|
||||
},
|
||||
]
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class AttachPageRoutingModule {}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { NgModule } from '@angular/core'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import {
|
||||
GuidPipePipesModule,
|
||||
UnitConversionPipesModule,
|
||||
} from '@start9labs/shared'
|
||||
import { AttachPage } from './attach.page'
|
||||
import { AttachPageRoutingModule } from './attach-routing.module'
|
||||
|
||||
@NgModule({
|
||||
declarations: [AttachPage],
|
||||
imports: [
|
||||
CommonModule,
|
||||
IonicModule,
|
||||
AttachPageRoutingModule,
|
||||
UnitConversionPipesModule,
|
||||
GuidPipePipesModule,
|
||||
],
|
||||
})
|
||||
export class AttachPageModule {}
|
||||
@@ -0,0 +1,74 @@
|
||||
<ion-content>
|
||||
<ion-grid>
|
||||
<ion-row>
|
||||
<ion-col>
|
||||
<div style="padding-bottom: 32px" class="ion-text-center">
|
||||
<img src="assets/img/logo.png" style="max-width: 240px" />
|
||||
</div>
|
||||
|
||||
<ion-card color="dark">
|
||||
<ion-card-header class="ion-text-center">
|
||||
<ion-card-title>Use Existing Drive</ion-card-title>
|
||||
<ion-card-subtitle
|
||||
>Select the physical drive containing your Embassy
|
||||
data</ion-card-subtitle
|
||||
>
|
||||
</ion-card-header>
|
||||
|
||||
<ion-card-content class="ion-margin">
|
||||
<ion-spinner
|
||||
*ngIf="loading"
|
||||
class="center-spinner"
|
||||
name="lines"
|
||||
></ion-spinner>
|
||||
|
||||
<!-- loaded -->
|
||||
<ion-item-group *ngIf="!loading" class="ion-text-center">
|
||||
<!-- drives -->
|
||||
<h2 class="target-label">Available Drives</h2>
|
||||
|
||||
<p *ngIf="!drives.length">
|
||||
No valid Embassy data drives found. Please make sure the drive
|
||||
is a valid Embassy data drive (not a backup) and is firmly
|
||||
connected, then refresh the page.
|
||||
</p>
|
||||
|
||||
<ng-container *ngFor="let drive of drives">
|
||||
<ion-item
|
||||
*ngIf="drive | guid as guid"
|
||||
button
|
||||
(click)="select(guid)"
|
||||
lines="none"
|
||||
>
|
||||
<ion-icon
|
||||
slot="start"
|
||||
name="save-outline"
|
||||
size="large"
|
||||
color="light"
|
||||
></ion-icon>
|
||||
<ion-label>
|
||||
<h1>{{ drive.logicalname }}</h1>
|
||||
<p>
|
||||
{{ drive.vendor || 'Unknown Vendor' }} - {{ drive.model ||
|
||||
'Unknown Model' }}
|
||||
</p>
|
||||
<p>Capacity: {{ drive.capacity | convertBytes }}</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
</ng-container>
|
||||
<ion-button
|
||||
class="ion-margin-top"
|
||||
fill="clear"
|
||||
color="primary"
|
||||
(click)="refresh()"
|
||||
>
|
||||
<ion-icon slot="start" name="refresh"></ion-icon>
|
||||
Refresh
|
||||
</ion-button>
|
||||
</ion-item-group>
|
||||
</ion-card-content>
|
||||
</ion-card>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
</ion-grid>
|
||||
</ion-content>
|
||||
@@ -0,0 +1,4 @@
|
||||
.target-label {
|
||||
font-weight: bold;
|
||||
padding-bottom: 6px;
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
import { Component } from '@angular/core'
|
||||
import {
|
||||
LoadingController,
|
||||
ModalController,
|
||||
NavController,
|
||||
} from '@ionic/angular'
|
||||
import { ApiService } from 'src/app/services/api/api.service'
|
||||
import { DiskInfo, ErrorToastService } from '@start9labs/shared'
|
||||
import { StateService } from 'src/app/services/state.service'
|
||||
import { PasswordPage } from 'src/app/modals/password/password.page'
|
||||
|
||||
@Component({
|
||||
selector: 'app-attach',
|
||||
templateUrl: 'attach.page.html',
|
||||
styleUrls: ['attach.page.scss'],
|
||||
})
|
||||
export class AttachPage {
|
||||
loading = true
|
||||
drives: DiskInfo[] = []
|
||||
|
||||
constructor(
|
||||
private readonly apiService: ApiService,
|
||||
private readonly navCtrl: NavController,
|
||||
private readonly errToastService: ErrorToastService,
|
||||
private readonly stateService: StateService,
|
||||
private readonly modalCtrl: ModalController,
|
||||
private readonly loadingCtrl: LoadingController,
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
await this.getDrives()
|
||||
}
|
||||
|
||||
async refresh() {
|
||||
this.loading = true
|
||||
await this.getDrives()
|
||||
}
|
||||
|
||||
async getDrives() {
|
||||
try {
|
||||
const drives = await this.apiService.getDrives()
|
||||
this.drives = drives.filter(d => d.partitions.length)
|
||||
} catch (e: any) {
|
||||
this.errToastService.present(e)
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
}
|
||||
|
||||
async select(guid: string) {
|
||||
const modal = await this.modalCtrl.create({
|
||||
component: PasswordPage,
|
||||
componentProps: { storageDrive: true },
|
||||
})
|
||||
modal.onDidDismiss().then(res => {
|
||||
if (res.data && res.data.password) {
|
||||
this.attachDrive(guid, res.data.password)
|
||||
}
|
||||
})
|
||||
await modal.present()
|
||||
}
|
||||
|
||||
private async attachDrive(guid: string, password: string) {
|
||||
const loader = await this.loadingCtrl.create({
|
||||
message: 'Attaching Drive',
|
||||
})
|
||||
await loader.present()
|
||||
try {
|
||||
await this.stateService.importDrive(guid, password)
|
||||
await this.navCtrl.navigateForward(`/success`)
|
||||
} catch (e: any) {
|
||||
this.errToastService.present(e)
|
||||
} finally {
|
||||
loader.dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,10 @@ import { NgModule } from '@angular/core'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { FormsModule } from '@angular/forms'
|
||||
import { UnitConversionPipesModule } from '@start9labs/shared'
|
||||
import {
|
||||
GuidPipePipesModule,
|
||||
UnitConversionPipesModule,
|
||||
} from '@start9labs/shared'
|
||||
import { EmbassyPage } from './embassy.page'
|
||||
import { PasswordPageModule } from '../../modals/password/password.module'
|
||||
import { EmbassyPageRoutingModule } from './embassy-routing.module'
|
||||
@@ -15,6 +18,7 @@ import { EmbassyPageRoutingModule } from './embassy-routing.module'
|
||||
EmbassyPageRoutingModule,
|
||||
PasswordPageModule,
|
||||
UnitConversionPipesModule,
|
||||
GuidPipePipesModule,
|
||||
],
|
||||
declarations: [EmbassyPage],
|
||||
})
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
BackupRecoverySource,
|
||||
DiskRecoverySource,
|
||||
} from 'src/app/services/api/api.service'
|
||||
import { DiskInfo, ErrorToastService } from '@start9labs/shared'
|
||||
import { DiskInfo, ErrorToastService, GuidPipe } from '@start9labs/shared'
|
||||
import { StateService } from 'src/app/services/state.service'
|
||||
import { PasswordPage } from '../../modals/password/password.page'
|
||||
import { ActivatedRoute } from '@angular/router'
|
||||
@@ -19,6 +19,7 @@ import { ActivatedRoute } from '@angular/router'
|
||||
selector: 'app-embassy',
|
||||
templateUrl: 'embassy.page.html',
|
||||
styleUrls: ['embassy.page.scss'],
|
||||
providers: [GuidPipe],
|
||||
})
|
||||
export class EmbassyPage {
|
||||
storageDrives: DiskInfo[] = []
|
||||
@@ -32,6 +33,7 @@ export class EmbassyPage {
|
||||
private readonly stateService: StateService,
|
||||
private readonly loadingCtrl: LoadingController,
|
||||
private readonly errorToastService: ErrorToastService,
|
||||
private readonly guidPipe: GuidPipe,
|
||||
private route: ActivatedRoute,
|
||||
) {}
|
||||
|
||||
@@ -71,7 +73,10 @@ export class EmbassyPage {
|
||||
}
|
||||
|
||||
async chooseDrive(drive: DiskInfo) {
|
||||
if (!!drive.partitions.find(p => p.used) || !!drive.guid) {
|
||||
if (
|
||||
this.guidPipe.transform(drive) ||
|
||||
!!drive.partitions.find(p => p.used)
|
||||
) {
|
||||
const alert = await this.alertCtrl.create({
|
||||
header: 'Warning',
|
||||
subHeader: 'Drive contains data!',
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<ion-content *ngIf="loaded">
|
||||
<ion-content>
|
||||
<ion-grid>
|
||||
<ion-row>
|
||||
<ion-col class="ion-text-center">
|
||||
@@ -6,7 +6,7 @@
|
||||
<img src="assets/img/logo.png" style="max-width: 240px" />
|
||||
</div>
|
||||
|
||||
<ion-card color="dark">
|
||||
<ion-card *ngIf="!loading" color="dark">
|
||||
<ion-card-header>
|
||||
<ion-button
|
||||
*ngIf="swiper?.activeIndex === 1"
|
||||
@@ -23,8 +23,10 @@
|
||||
</ion-card-title>
|
||||
</ion-card-header>
|
||||
<ion-card-content class="ion-margin-bottom">
|
||||
<swiper [autoHeight]="true" (swiper)="setSwiperInstance($event)">
|
||||
<swiper (swiper)="setSwiperInstance($event)">
|
||||
<!-- SLIDE 1 -->
|
||||
<ng-template swiperSlide>
|
||||
<!-- fresh -->
|
||||
<ion-item
|
||||
button
|
||||
[disabled]="error"
|
||||
@@ -37,6 +39,8 @@
|
||||
<p>Get started with a brand new Embassy</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
|
||||
<!-- recover -->
|
||||
<ion-item
|
||||
button
|
||||
[disabled]="error"
|
||||
@@ -51,7 +55,10 @@
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
</ng-template>
|
||||
|
||||
<!-- SLIDE 2 -->
|
||||
<ng-template swiperSlide>
|
||||
<!-- restore from backup -->
|
||||
<ion-item button detail="true" routerLink="/recover">
|
||||
<ion-icon
|
||||
color="dark"
|
||||
@@ -62,10 +69,17 @@
|
||||
<h2>
|
||||
<ion-text color="warning">Restore From Backup</ion-text>
|
||||
</h2>
|
||||
<p>Recover an Embassy from an encrypted backup</p>
|
||||
<p>Restore an Embassy from an encrypted backup</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item button detail="true" lines="none" (click)="import()">
|
||||
|
||||
<!-- attach -->
|
||||
<ion-item
|
||||
button
|
||||
detail="true"
|
||||
lines="none"
|
||||
routerLink="/attach"
|
||||
>
|
||||
<ion-icon
|
||||
color="dark"
|
||||
slot="start"
|
||||
@@ -75,9 +89,13 @@
|
||||
<h2>
|
||||
<ion-text color="primary">Use Existing Drive</ion-text>
|
||||
</h2>
|
||||
<p>Attach and use a valid Embassy data drive</p>
|
||||
<p>
|
||||
Use an existing, valid Embassy data drive (not a backup)
|
||||
</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
|
||||
<!-- transfer -->
|
||||
<ion-item
|
||||
button
|
||||
detail="true"
|
||||
@@ -94,8 +112,9 @@
|
||||
<ion-text color="success">Transfer</ion-text>
|
||||
</h2>
|
||||
<p>
|
||||
Transfer data to a new drive<br />(e.g. upgrade to a
|
||||
larger drive or an Embassy Pro)
|
||||
Transfer data from an existing, valid Embassy data drive
|
||||
(not a backup) to a new drive<br />(e.g. in order to
|
||||
transfer data to another device)
|
||||
</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
|
||||
@@ -1,14 +1,6 @@
|
||||
import { Component } from '@angular/core'
|
||||
import {
|
||||
AlertController,
|
||||
IonicSlides,
|
||||
LoadingController,
|
||||
ModalController,
|
||||
NavController,
|
||||
} from '@ionic/angular'
|
||||
import { PasswordPage } from 'src/app/modals/password/password.page'
|
||||
import { IonicSlides } from '@ionic/angular'
|
||||
import { ApiService } from 'src/app/services/api/api.service'
|
||||
import { StateService } from 'src/app/services/state.service'
|
||||
import SwiperCore, { Swiper } from 'swiper'
|
||||
import { ErrorToastService } from '@start9labs/shared'
|
||||
|
||||
@@ -21,35 +13,26 @@ SwiperCore.use([IonicSlides])
|
||||
})
|
||||
export class HomePage {
|
||||
swiper?: Swiper
|
||||
guid?: string | null
|
||||
error = false
|
||||
loaded = false
|
||||
loading = true
|
||||
|
||||
constructor(
|
||||
private readonly api: ApiService,
|
||||
private readonly modalCtrl: ModalController,
|
||||
private readonly alertCtrl: AlertController,
|
||||
private readonly loadingCtrl: LoadingController,
|
||||
private readonly stateService: StateService,
|
||||
private readonly navCtrl: NavController,
|
||||
private readonly errToastService: ErrorToastService,
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
async ionViewDidEnter() {
|
||||
if (this.swiper) {
|
||||
this.swiper.allowTouchMove = false
|
||||
}
|
||||
|
||||
try {
|
||||
await this.api.getPubKey()
|
||||
const disks = await this.api.getDrives()
|
||||
this.guid = disks.find(d => !!d.guid)?.guid
|
||||
} catch (e: any) {
|
||||
this.error = true
|
||||
this.errToastService.present(e)
|
||||
}
|
||||
}
|
||||
|
||||
async ionViewDidEnter() {
|
||||
this.loaded = true // needed to accommodate autoHight="true" on swiper. Otherwise Swiper height might be 0 when navigating *to* this page from later page. Happens on refresh.
|
||||
if (this.swiper) {
|
||||
this.swiper.allowTouchMove = false
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,41 +47,4 @@ export class HomePage {
|
||||
previous() {
|
||||
this.swiper?.slidePrev(500)
|
||||
}
|
||||
|
||||
async import() {
|
||||
if (this.guid) {
|
||||
const modal = await this.modalCtrl.create({
|
||||
component: PasswordPage,
|
||||
componentProps: { storageDrive: true },
|
||||
})
|
||||
modal.onDidDismiss().then(res => {
|
||||
if (res.data && res.data.password) {
|
||||
this.importDrive(res.data.password)
|
||||
}
|
||||
})
|
||||
await modal.present()
|
||||
} else {
|
||||
const alert = await this.alertCtrl.create({
|
||||
header: 'Drive Not Found',
|
||||
message:
|
||||
'Please make sure the drive is a valid Embassy data drive (not a backup) and is firmly connected, then refresh the page.',
|
||||
})
|
||||
await alert.present()
|
||||
}
|
||||
}
|
||||
|
||||
private async importDrive(password: string) {
|
||||
const loader = await this.loadingCtrl.create({
|
||||
message: 'Importing Drive',
|
||||
})
|
||||
await loader.present()
|
||||
try {
|
||||
await this.stateService.importDrive(this.guid!, password)
|
||||
await this.navCtrl.navigateForward(`/success`)
|
||||
} catch (e: any) {
|
||||
this.errToastService.present(e)
|
||||
} finally {
|
||||
loader.dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import { NgModule } from '@angular/core'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { UnitConversionPipesModule } from '@start9labs/shared'
|
||||
import {
|
||||
GuidPipePipesModule,
|
||||
UnitConversionPipesModule,
|
||||
} from '@start9labs/shared'
|
||||
import { TransferPage } from './transfer.page'
|
||||
import { TransferPageRoutingModule } from './transfer-routing.module'
|
||||
|
||||
@@ -12,6 +15,7 @@ import { TransferPageRoutingModule } from './transfer-routing.module'
|
||||
IonicModule,
|
||||
TransferPageRoutingModule,
|
||||
UnitConversionPipesModule,
|
||||
GuidPipePipesModule,
|
||||
],
|
||||
})
|
||||
export class TransferPageModule {}
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<ion-card-header class="ion-text-center">
|
||||
<ion-card-title>Transfer</ion-card-title>
|
||||
<ion-card-subtitle
|
||||
>Select the physical drive containing your previous Embassy
|
||||
>Select the physical drive containing your Embassy
|
||||
data</ion-card-subtitle
|
||||
>
|
||||
</ion-card-header>
|
||||
@@ -28,7 +28,12 @@
|
||||
<h2 class="target-label">Available Drives</h2>
|
||||
|
||||
<ng-container *ngFor="let drive of drives">
|
||||
<ion-item button (click)="select(drive)" lines="none">
|
||||
<ion-item
|
||||
*ngIf="drive | guid as guid"
|
||||
button
|
||||
(click)="select(guid)"
|
||||
lines="none"
|
||||
>
|
||||
<ion-icon
|
||||
slot="start"
|
||||
name="save-outline"
|
||||
|
||||
@@ -32,8 +32,8 @@ export class TransferPage {
|
||||
|
||||
async getDrives() {
|
||||
try {
|
||||
const disks = await this.apiService.getDrives()
|
||||
this.drives = disks.filter(d => d.partitions.length && d.guid)
|
||||
const drives = await this.apiService.getDrives()
|
||||
this.drives = drives.filter(d => d.partitions.length)
|
||||
} catch (e: any) {
|
||||
this.errToastService.present(e)
|
||||
} finally {
|
||||
@@ -41,11 +41,7 @@ export class TransferPage {
|
||||
}
|
||||
}
|
||||
|
||||
async select(target: DiskInfo) {
|
||||
const { logicalname, guid } = target
|
||||
|
||||
if (!logicalname) return
|
||||
|
||||
async select(guid: string) {
|
||||
const alert = await this.alertCtrl.create({
|
||||
header: 'Warning',
|
||||
message:
|
||||
@@ -60,7 +56,7 @@ export class TransferPage {
|
||||
handler: () => {
|
||||
this.stateService.recoverySource = {
|
||||
type: 'migrate',
|
||||
guid: guid!,
|
||||
guid,
|
||||
}
|
||||
this.navCtrl.navigateForward(`/embassy`, {
|
||||
queryParams: { action: 'transfer' },
|
||||
|
||||
@@ -24,9 +24,8 @@ export class MockApiService extends ApiService {
|
||||
async getPubKey() {
|
||||
await pauseFor(1000)
|
||||
|
||||
const keystore = jose.JWK.createKeyStore()
|
||||
|
||||
// randomly generated
|
||||
// const keystore = jose.JWK.createKeyStore()
|
||||
// this.pubkey = await keystore.generate('EC', 'P-256')
|
||||
|
||||
// generated from backend
|
||||
@@ -58,6 +57,7 @@ export class MockApiService extends ApiService {
|
||||
'$argon2d$v=19$m=1024,t=1,p=1$YXNkZmFzZGZhc2RmYXNkZg$Ceev1I901G6UwU+hY0sHrFZ56D+o+LNJ',
|
||||
'wrapped-key': null,
|
||||
},
|
||||
guid: null,
|
||||
},
|
||||
],
|
||||
capacity: 123456789123,
|
||||
@@ -65,8 +65,8 @@ export class MockApiService extends ApiService {
|
||||
},
|
||||
{
|
||||
logicalname: 'dcba',
|
||||
vendor: 'Samsung',
|
||||
model: 'T5',
|
||||
vendor: 'Crucial',
|
||||
model: 'MX500',
|
||||
partitions: [
|
||||
{
|
||||
logicalname: 'pbcba',
|
||||
@@ -74,16 +74,40 @@ export class MockApiService extends ApiService {
|
||||
capacity: 73264762332,
|
||||
used: null,
|
||||
'embassy-os': {
|
||||
version: '0.3.1',
|
||||
version: '0.3.3',
|
||||
full: true,
|
||||
'password-hash':
|
||||
'$argon2d$v=19$m=1024,t=1,p=1$YXNkZmFzZGZhc2RmYXNkZg$Ceev1I901G6UwU+hY0sHrFZ56D+o+LNJ',
|
||||
'wrapped-key': null,
|
||||
},
|
||||
guid: null,
|
||||
},
|
||||
],
|
||||
capacity: 123456789123,
|
||||
guid: 'uuid-uuid-uuid-uuid',
|
||||
capacity: 124456789123,
|
||||
guid: null,
|
||||
},
|
||||
{
|
||||
logicalname: 'wxyz',
|
||||
vendor: 'SanDisk',
|
||||
model: 'Specialness',
|
||||
partitions: [
|
||||
{
|
||||
logicalname: 'pbcba',
|
||||
label: null,
|
||||
capacity: 73264762332,
|
||||
used: null,
|
||||
'embassy-os': {
|
||||
version: '0.3.2',
|
||||
full: true,
|
||||
'password-hash':
|
||||
'$argon2d$v=19$m=1024,t=1,p=1$YXNkZmFzZGZhc2RmYXNkZg$Ceev1I901G6UwU+hY0sHrFZ56D+o+LNJ',
|
||||
'wrapped-key': null,
|
||||
},
|
||||
guid: 'guid-guid-guid-guid',
|
||||
},
|
||||
],
|
||||
capacity: 123459789123,
|
||||
guid: null,
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
8
frontend/projects/shared/src/pipes/guid/guid.module.ts
Normal file
8
frontend/projects/shared/src/pipes/guid/guid.module.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { NgModule } from '@angular/core'
|
||||
import { GuidPipe } from './guid.pipe'
|
||||
|
||||
@NgModule({
|
||||
declarations: [GuidPipe],
|
||||
exports: [GuidPipe],
|
||||
})
|
||||
export class GuidPipePipesModule {}
|
||||
11
frontend/projects/shared/src/pipes/guid/guid.pipe.ts
Normal file
11
frontend/projects/shared/src/pipes/guid/guid.pipe.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core'
|
||||
import { DiskInfo } from '../../types/api'
|
||||
|
||||
@Pipe({
|
||||
name: 'guid',
|
||||
})
|
||||
export class GuidPipe implements PipeTransform {
|
||||
transform(disk: DiskInfo): string | null {
|
||||
return disk.guid || disk.partitions.find(p => p.guid)?.guid || null
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,8 @@ export * from './directives/safe-links/safe-links.module'
|
||||
|
||||
export * from './pipes/emver/emver.module'
|
||||
export * from './pipes/emver/emver.pipe'
|
||||
export * from './pipes/guid/guid.module'
|
||||
export * from './pipes/guid/guid.pipe'
|
||||
export * from './pipes/markdown/markdown.module'
|
||||
export * from './pipes/markdown/markdown.pipe'
|
||||
export * from './pipes/shared/shared.module'
|
||||
|
||||
@@ -32,6 +32,7 @@ export interface PartitionInfo {
|
||||
capacity: number
|
||||
used: number | null
|
||||
'embassy-os': EmbassyOSDiskInfo | null
|
||||
guid: string | null
|
||||
}
|
||||
|
||||
export type EmbassyOSDiskInfo = {
|
||||
|
||||
Reference in New Issue
Block a user