mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 02:11:53 +00:00
Wizard refactor 2 (#615)
* new flow and endpoints * functional * prod build errors addressed * little more cleanup * transfer progress fixed * tor address fix * remove eslint cause sucks * fix skeleton text color and wording Co-authored-by: Matt Hill <matthewonthemoon@gmail.com> Co-authored-by: Drew Ansbacher <drew.ansbacher@spiredigital.com>
This commit is contained in:
committed by
Aiden McClelland
parent
e58df7ec4a
commit
ed395699b3
@@ -1,21 +0,0 @@
|
||||
{
|
||||
"root": true,
|
||||
"ignorePatterns": ["projects/**/*"],
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.ts"],
|
||||
"parserOptions": {
|
||||
"project": ["tsconfig.json"],
|
||||
"createDefaultProgram": true
|
||||
},
|
||||
"rules": {
|
||||
"semi": [1, "never"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": ["*.html"],
|
||||
"extends": ["plugin:@angular-eslint/template/recommended"],
|
||||
"rules": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -16,7 +16,7 @@ setup.disk.list
|
||||
model : string | null,
|
||||
partitions : PartitionInfo[],
|
||||
capacity : number,
|
||||
embassy_os : EmbassyOsDiskInfo | null,
|
||||
embassy-os : EmbassyOsDiskInfo | null,
|
||||
}[]
|
||||
|
||||
setup.recovery.status
|
||||
|
||||
20285
setup-wizard/package-lock.json
generated
20285
setup-wizard/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -29,23 +29,14 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "^12.2.1",
|
||||
"@angular-eslint/builder": "^12.3.1",
|
||||
"@angular-eslint/eslint-plugin": "^12.3.1",
|
||||
"@angular-eslint/eslint-plugin-template": "^12.3.1",
|
||||
"@angular-eslint/template-parser": "^12.3.1",
|
||||
"@angular/cli": "^12.2.1",
|
||||
"@angular/compiler": "^12.2.1",
|
||||
"@angular/compiler-cli": "^12.2.1",
|
||||
"@angular/language-service": "^12.2.1",
|
||||
"@ionic/angular-toolkit": "^4.0.0",
|
||||
"@types/node": "^16.9.1",
|
||||
"@typescript-eslint/eslint-plugin": "^4.29.1",
|
||||
"@typescript-eslint/parser": "^4.29.1",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-plugin-import": "^2.24.0",
|
||||
"eslint-plugin-jsdoc": "^36.0.7",
|
||||
"eslint-plugin-prefer-arrow": "^1.2.3",
|
||||
"ts-node": "^10.2.0",
|
||||
"tslint": "^6.1.3",
|
||||
"typescript": "4.3.5"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,19 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { PreloadAllModules, RouterModule, Routes } from '@angular/router';
|
||||
import { NgModule } from '@angular/core'
|
||||
import { PreloadAllModules, RouterModule, Routes } from '@angular/router'
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: 'product-key',
|
||||
loadChildren: () => import('./pages/product-key/product-key.module').then( m => m.ProductKeyPageModule),
|
||||
},
|
||||
{
|
||||
path: 'home',
|
||||
loadChildren: () => import('./pages/home/home.module').then( m => m.HomePageModule),
|
||||
},
|
||||
{
|
||||
path: 'recover',
|
||||
loadChildren: () => import('./pages/recover/recover.module').then( m => m.RecoverPageModule),
|
||||
},
|
||||
{
|
||||
path: 'embassy',
|
||||
loadChildren: () => import('./pages/embassy/embassy.module').then( m => m.EmbassyPageModule),
|
||||
@@ -9,23 +22,11 @@ const routes: Routes = [
|
||||
path: 'loading',
|
||||
loadChildren: () => import('./pages/loading/loading.module').then( m => m.LoadingPageModule),
|
||||
},
|
||||
{
|
||||
path: 'product-key',
|
||||
loadChildren: () => import('./pages/product-key/product-key.module').then( m => m.ProductKeyPageModule),
|
||||
},
|
||||
{
|
||||
path: 'recover',
|
||||
loadChildren: () => import('./pages/recover/recover.module').then( m => m.RecoverPageModule),
|
||||
},
|
||||
{
|
||||
path: 'home',
|
||||
loadChildren: () => import('./pages/home/home.module').then( m => m.HomePageModule),
|
||||
},
|
||||
{
|
||||
path: 'success',
|
||||
loadChildren: () => import('./pages/success/success.module').then( m => m.SuccessPageModule),
|
||||
},
|
||||
];
|
||||
]
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@@ -33,8 +34,8 @@ const routes: Routes = [
|
||||
scrollPositionRestoration: 'enabled',
|
||||
preloadingStrategy: PreloadAllModules,
|
||||
useHash: true,
|
||||
})
|
||||
}),
|
||||
],
|
||||
exports: [RouterModule]
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class AppRoutingModule { }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
<ion-app>
|
||||
<ion-content class="has-header">
|
||||
<ion-router-outlet></ion-router-outlet>
|
||||
</ion-content>
|
||||
<ion-router-outlet></ion-router-outlet>
|
||||
</ion-app>
|
||||
|
||||
@@ -1,18 +1,36 @@
|
||||
import { Component, OnInit } from '@angular/core'
|
||||
import { Component } from '@angular/core'
|
||||
import { NavController } from '@ionic/angular'
|
||||
import { ApiService } from './services/api/api.service'
|
||||
import { ErrorToastService } from './services/error-toast.service'
|
||||
import { StateService } from './services/state.service'
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: 'app.component.html',
|
||||
styleUrls: ['app.component.scss'],
|
||||
})
|
||||
export class AppComponent implements OnInit {
|
||||
|
||||
constructor(
|
||||
export class AppComponent {
|
||||
constructor (
|
||||
private readonly apiService: ApiService,
|
||||
private readonly errorToastService: ErrorToastService,
|
||||
private readonly navCtrl: NavController,
|
||||
) {}
|
||||
private readonly stateService: StateService,
|
||||
) { }
|
||||
|
||||
async ngOnInit() {
|
||||
await this.navCtrl.navigateForward(`/product-key`)
|
||||
async ngOnInit () {
|
||||
try {
|
||||
const status = await this.apiService.getStatus()
|
||||
if (status.migrating || status['product-key']) {
|
||||
this.stateService.hasProductKey = true
|
||||
this.stateService.isMigrating = status.migrating
|
||||
await this.navCtrl.navigateForward(`/product-key`)
|
||||
} else {
|
||||
this.stateService.hasProductKey = false
|
||||
this.stateService.isMigrating = false
|
||||
await this.navCtrl.navigateForward(`/recover`)
|
||||
}
|
||||
} catch (e) {
|
||||
this.errorToastService.present(`${e.message}: ${e.details}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,16 +16,15 @@ const useMocks = require('../../config.json').useMocks as boolean
|
||||
entryComponents: [],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
IonicModule.forRoot(),
|
||||
IonicModule.forRoot({
|
||||
navAnimation: iosTransitionAnimation,
|
||||
}),
|
||||
AppRoutingModule,
|
||||
HttpClientModule,
|
||||
IonicModule.forRoot({
|
||||
navAnimation: iosTransitionAnimation,
|
||||
}),
|
||||
],
|
||||
providers: [
|
||||
{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy },
|
||||
{
|
||||
{
|
||||
provide: ApiService ,
|
||||
useFactory: (http: HttpService) => {
|
||||
if (useMocks) {
|
||||
@@ -34,9 +33,9 @@ const useMocks = require('../../config.json').useMocks as boolean
|
||||
return new LiveApiService(http)
|
||||
}
|
||||
},
|
||||
deps: [HttpService]
|
||||
deps: [HttpService],
|
||||
},
|
||||
],
|
||||
bootstrap: [AppComponent],
|
||||
})
|
||||
export class AppModule {}
|
||||
export class AppModule { }
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { EmbassyPage } from './embassy.page';
|
||||
import { NgModule } from '@angular/core'
|
||||
import { RouterModule, Routes } from '@angular/router'
|
||||
import { EmbassyPage } from './embassy.page'
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: EmbassyPage,
|
||||
}
|
||||
];
|
||||
},
|
||||
]
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule]
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class EmbassyPageRoutingModule {}
|
||||
export class EmbassyPageRoutingModule { }
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { IonicModule } from '@ionic/angular';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { EmbassyPage } from './embassy.page';
|
||||
import { PasswordPageModule } from '../password/password.module';
|
||||
import { EmbassyPageRoutingModule } from './embassy-routing.module';
|
||||
import { NgModule } from '@angular/core'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { FormsModule } from '@angular/forms'
|
||||
import { EmbassyPage } from './embassy.page'
|
||||
import { PasswordPageModule } from '../password/password.module'
|
||||
import { EmbassyPageRoutingModule } from './embassy-routing.module'
|
||||
import { PipesModule } from 'src/app/pipes/pipe.module'
|
||||
|
||||
@NgModule({
|
||||
@@ -16,6 +16,6 @@ import { PipesModule } from 'src/app/pipes/pipe.module'
|
||||
PasswordPageModule,
|
||||
PipesModule,
|
||||
],
|
||||
declarations: [EmbassyPage]
|
||||
declarations: [EmbassyPage],
|
||||
})
|
||||
export class EmbassyPageModule {}
|
||||
export class EmbassyPageModule { }
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
|
||||
<ion-card color="dark">
|
||||
<ion-card-header class="ion-text-center" style="padding-bottom: 8px;">
|
||||
<ion-card-title>{{ loading ? 'Loading Drives' : 'Select Storage Drive'}}</ion-card-title>
|
||||
<ion-card-subtitle>Select the drive where all your Embassy data will be stored.</ion-card-subtitle>
|
||||
<ion-card-title>Select Storage Drive</ion-card-title>
|
||||
<ion-card-subtitle>Select the drive where your Embassy data will be stored.</ion-card-subtitle>
|
||||
</ion-card-header>
|
||||
|
||||
<ion-card-content class="ion-margin">
|
||||
@@ -31,7 +31,6 @@
|
||||
<ion-label class="ion-text-wrap">
|
||||
<ion-skeleton-text style="width: 80%; margin: 13px 0;" animated></ion-skeleton-text>
|
||||
<ion-skeleton-text style="width: 60%; margin: 10px 0;" animated></ion-skeleton-text>
|
||||
<ion-skeleton-text style="width: 30%; margin: 8px 0;" animated></ion-skeleton-text>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
</ng-container>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Component } from '@angular/core'
|
||||
import { AlertController, iosTransitionAnimation, LoadingController, ModalController, NavController } from '@ionic/angular'
|
||||
import { AlertController, LoadingController, ModalController, NavController } from '@ionic/angular'
|
||||
import { ApiService, DiskInfo } from 'src/app/services/api/api.service'
|
||||
import { ErrorToastService } from 'src/app/services/error-toast.service'
|
||||
import { StateService } from 'src/app/services/state.service'
|
||||
@@ -15,7 +15,7 @@ export class EmbassyPage {
|
||||
selectedDrive: DiskInfo = null
|
||||
loading = true
|
||||
|
||||
constructor(
|
||||
constructor (
|
||||
private readonly apiService: ApiService,
|
||||
private readonly navCtrl: NavController,
|
||||
private readonly modalController: ModalController,
|
||||
@@ -61,9 +61,9 @@ export class EmbassyPage {
|
||||
text: 'Continue',
|
||||
handler: () => {
|
||||
this.presentModalPassword(drive)
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
await alert.present()
|
||||
} else {
|
||||
@@ -75,21 +75,21 @@ export class EmbassyPage {
|
||||
const modal = await this.modalController.create({
|
||||
component: PasswordPage,
|
||||
componentProps: {
|
||||
storageDrive: drive
|
||||
storageDrive: drive,
|
||||
},
|
||||
})
|
||||
modal.onDidDismiss().then(async ret => {
|
||||
if (!ret.data || !ret.data.password) return
|
||||
|
||||
const loader = await this.loadingCtrl.create({
|
||||
message: 'Setting up your Embassy!'
|
||||
message: 'Setting up your Embassy!',
|
||||
})
|
||||
|
||||
|
||||
await loader.present()
|
||||
|
||||
|
||||
this.stateService.storageDrive = drive
|
||||
this.stateService.embassyPassword = ret.data.password
|
||||
|
||||
|
||||
try {
|
||||
this.stateService.torAddress = (await this.stateService.setupEmbassy()).torAddress
|
||||
} catch (e) {
|
||||
@@ -98,10 +98,10 @@ export class EmbassyPage {
|
||||
console.error(e.details)
|
||||
} finally {
|
||||
loader.dismiss()
|
||||
if(!!this.stateService.recoveryDrive) {
|
||||
await this.navCtrl.navigateForward(`/loading`, { animationDirection: 'forward', animation: iosTransitionAnimation })
|
||||
if (!!this.stateService.recoveryDrive) {
|
||||
await this.navCtrl.navigateForward(`/loading`, { animationDirection: 'forward' })
|
||||
} else {
|
||||
await this.navCtrl.navigateForward(`/success`, { animationDirection: 'forward', animation: iosTransitionAnimation })
|
||||
await this.navCtrl.navigateForward(`/success`, { animationDirection: 'forward' })
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { HomePage } from './home.page';
|
||||
import { NgModule } from '@angular/core'
|
||||
import { RouterModule, Routes } from '@angular/router'
|
||||
import { HomePage } from './home.page'
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: HomePage,
|
||||
}
|
||||
];
|
||||
},
|
||||
]
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule]
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class HomePageRoutingModule {}
|
||||
export class HomePageRoutingModule { }
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { IonicModule } from '@ionic/angular';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { HomePage } from './home.page';
|
||||
import { PasswordPageModule } from '../password/password.module';
|
||||
import { NgModule } from '@angular/core'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { FormsModule } from '@angular/forms'
|
||||
import { HomePage } from './home.page'
|
||||
import { PasswordPageModule } from '../password/password.module'
|
||||
|
||||
import { HomePageRoutingModule } from './home-routing.module';
|
||||
import { HomePageRoutingModule } from './home-routing.module'
|
||||
|
||||
|
||||
@NgModule({
|
||||
@@ -16,6 +16,6 @@ import { HomePageRoutingModule } from './home-routing.module';
|
||||
HomePageRoutingModule,
|
||||
PasswordPageModule,
|
||||
],
|
||||
declarations: [HomePage]
|
||||
declarations: [HomePage],
|
||||
})
|
||||
export class HomePageModule {}
|
||||
export class HomePageModule { }
|
||||
|
||||
@@ -11,11 +11,10 @@
|
||||
<ion-card-content class="ion-margin">
|
||||
<!-- fresh -->
|
||||
<ion-card
|
||||
(click)="embassyNav()"
|
||||
routerLink="/embassy"
|
||||
button="true"
|
||||
color="light"
|
||||
style="text-align: center; background-color: #00919b !important; height: 160px; margin-bottom: 20px; box-shadow: 4px 4px 16px var(--ion-color-light);
|
||||
"
|
||||
style="text-align: center; background-color: #00919b !important; height: 160px; margin-bottom: 20px; box-shadow: 4px 4px 16px var(--ion-color-light);"
|
||||
>
|
||||
<ion-card-header>
|
||||
<ion-card-title style="font-size: 40px;">Start Fresh</ion-card-title>
|
||||
@@ -25,11 +24,10 @@
|
||||
<!-- recover -->
|
||||
</ion-card>
|
||||
<ion-card
|
||||
(click)="recoverNav()"
|
||||
routerLink="/recover"
|
||||
button="true"
|
||||
color="light"
|
||||
style="text-align: center; background-color: #bf5900 !important; height: 160px; box-shadow: 4px 4px 16px var(--ion-color-light);
|
||||
"
|
||||
style="text-align: center; background-color: #bf5900 !important; height: 160px; box-shadow: 4px 4px 16px var(--ion-color-light);"
|
||||
>
|
||||
<ion-card-header>
|
||||
<ion-card-title style="font-size: 40px;">Recover</ion-card-title>
|
||||
|
||||
@@ -1,22 +1,9 @@
|
||||
import { Component } from '@angular/core'
|
||||
import { iosTransitionAnimation, NavController } from '@ionic/angular'
|
||||
|
||||
@Component({
|
||||
selector: 'app-home',
|
||||
templateUrl: 'home.page.html',
|
||||
styleUrls: ['home.page.scss'],
|
||||
})
|
||||
export class HomePage {
|
||||
constructor(
|
||||
private readonly navCtrl: NavController,
|
||||
) {}
|
||||
|
||||
async recoverNav () {
|
||||
await this.navCtrl.navigateForward(`/recover`, { animationDirection: 'forward', animation: iosTransitionAnimation })
|
||||
}
|
||||
|
||||
async embassyNav () {
|
||||
await this.navCtrl.navigateForward(`/embassy`, { animationDirection: 'forward', animation: iosTransitionAnimation })
|
||||
}
|
||||
}
|
||||
export class HomePage { }
|
||||
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { LoadingPage } from './loading.page';
|
||||
import { NgModule } from '@angular/core'
|
||||
import { RouterModule, Routes } from '@angular/router'
|
||||
import { LoadingPage } from './loading.page'
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: LoadingPage,
|
||||
}
|
||||
];
|
||||
},
|
||||
]
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule]
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class LoadingPageRoutingModule {}
|
||||
export class LoadingPageRoutingModule { }
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { IonicModule } from '@ionic/angular';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { LoadingPage } from './loading.page';
|
||||
import { PasswordPageModule } from '../password/password.module';
|
||||
import { LoadingPageRoutingModule } from './loading-routing.module';
|
||||
import { NgModule } from '@angular/core'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { FormsModule } from '@angular/forms'
|
||||
import { LoadingPage } from './loading.page'
|
||||
import { PasswordPageModule } from '../password/password.module'
|
||||
import { LoadingPageRoutingModule } from './loading-routing.module'
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@@ -14,6 +14,6 @@ import { LoadingPageRoutingModule } from './loading-routing.module';
|
||||
LoadingPageRoutingModule,
|
||||
PasswordPageModule,
|
||||
],
|
||||
declarations: [LoadingPage]
|
||||
declarations: [LoadingPage],
|
||||
})
|
||||
export class LoadingPageModule {}
|
||||
export class LoadingPageModule { }
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Component } from '@angular/core'
|
||||
import { iosTransitionAnimation, NavController } from '@ionic/angular'
|
||||
import { NavController } from '@ionic/angular'
|
||||
import { StateService } from 'src/app/services/state.service'
|
||||
|
||||
@Component({
|
||||
@@ -8,20 +8,19 @@ import { StateService } from 'src/app/services/state.service'
|
||||
styleUrls: ['loading.page.scss'],
|
||||
})
|
||||
export class LoadingPage {
|
||||
constructor(
|
||||
constructor (
|
||||
public stateService: StateService,
|
||||
private navCtrl: NavController
|
||||
) {}
|
||||
private navCtrl: NavController,
|
||||
) { }
|
||||
|
||||
ngOnInit () {
|
||||
this.stateService.pollDataTransferProgress()
|
||||
const progSub = this.stateService.dataProgSubject.subscribe(async progress => {
|
||||
if(progress === 1) {
|
||||
if (progress === 1) {
|
||||
progSub.unsubscribe()
|
||||
await this.navCtrl.navigateForward(`/success`, { animationDirection: 'forward', animation: iosTransitionAnimation })
|
||||
await this.navCtrl.navigateForward(`/success`)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { PasswordPage } from './password.page';
|
||||
import { NgModule } from '@angular/core'
|
||||
import { RouterModule, Routes } from '@angular/router'
|
||||
import { PasswordPage } from './password.page'
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: PasswordPage,
|
||||
}
|
||||
];
|
||||
},
|
||||
]
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule]
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class PasswordPageRoutingModule {}
|
||||
export class PasswordPageRoutingModule { }
|
||||
|
||||
@@ -12,8 +12,8 @@ import { PasswordPageRoutingModule } from './password-routing.module'
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
IonicModule,
|
||||
PasswordPageRoutingModule
|
||||
PasswordPageRoutingModule,
|
||||
],
|
||||
declarations: [PasswordPage]
|
||||
declarations: [PasswordPage],
|
||||
})
|
||||
export class PasswordPageModule {}
|
||||
export class PasswordPageModule { }
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
[ngModelOptions]="{'standalone': true}"
|
||||
[type]="!unmasked1 ? 'password' : 'text'"
|
||||
placeholder="Enter Password"
|
||||
debounce="500"
|
||||
(ionChange)="validate()"
|
||||
maxlength="64"
|
||||
></ion-input>
|
||||
@@ -49,7 +48,6 @@
|
||||
[(ngModel)]="passwordVer"
|
||||
[ngModelOptions]="{'standalone': true}"
|
||||
[type]="!unmasked2 ? 'password' : 'text'"
|
||||
debounce="500"
|
||||
(ionChange)="checkVer()"
|
||||
maxlength="64"
|
||||
placeholder="Retype Password"
|
||||
|
||||
@@ -21,13 +21,13 @@ export class PasswordPage {
|
||||
|
||||
hasData: boolean
|
||||
|
||||
constructor(
|
||||
constructor (
|
||||
private modalController: ModalController,
|
||||
private apiService: ApiService,
|
||||
private loadingCtrl: LoadingController,
|
||||
) {}
|
||||
) { }
|
||||
|
||||
ngOnInit() {
|
||||
ngOnInit () {
|
||||
if (this.storageDrive && this.storageDrive.partitions.find(p => p.used)) {
|
||||
this.hasData = true
|
||||
}
|
||||
@@ -36,16 +36,16 @@ export class PasswordPage {
|
||||
async verifyPw () {
|
||||
if (!this.recoveryDrive) this.pwError = 'No recovery drive' // unreachable
|
||||
const loader = await this.loadingCtrl.create({
|
||||
message: 'Verifying Password'
|
||||
message: 'Verifying Password',
|
||||
})
|
||||
await loader.present()
|
||||
|
||||
try {
|
||||
const isCorrectPassword = await this.apiService.verifyRecoveryPassword(this.recoveryDrive.logicalname, this.password)
|
||||
if(isCorrectPassword) {
|
||||
const isCorrectPassword = await this.apiService.verify03XPassword(this.recoveryDrive.logicalname, this.password)
|
||||
if (isCorrectPassword) {
|
||||
this.modalController.dismiss({ password: this.password })
|
||||
} else {
|
||||
this.pwError = "Incorrect password provided"
|
||||
this.pwError = 'Incorrect password provided'
|
||||
}
|
||||
} catch (e) {
|
||||
this.pwError = 'Error connecting to Embassy'
|
||||
@@ -57,29 +57,29 @@ export class PasswordPage {
|
||||
async submitPw () {
|
||||
this.validate()
|
||||
if (this.password !== this.passwordVer) {
|
||||
this.verError="*passwords do not match"
|
||||
this.verError = '*passwords do not match'
|
||||
}
|
||||
|
||||
if(this.pwError || this.verError) return
|
||||
if (this.pwError || this.verError) return
|
||||
this.modalController.dismiss({ password: this.password })
|
||||
}
|
||||
|
||||
validate () {
|
||||
if(!!this.recoveryDrive) return this.pwError = ''
|
||||
if (!!this.recoveryDrive) return this.pwError = ''
|
||||
|
||||
if (this.passwordVer) {
|
||||
this.checkVer()
|
||||
}
|
||||
|
||||
if (this.password.length < 12) {
|
||||
this.pwError="*password must be 12 characters or greater"
|
||||
this.pwError = '*password must be 12 characters or greater'
|
||||
} else {
|
||||
this.pwError = ''
|
||||
}
|
||||
}
|
||||
|
||||
checkVer () {
|
||||
this.verError = this.password !== this.passwordVer ? "*passwords do not match" : ''
|
||||
this.verError = this.password !== this.passwordVer ? '*passwords do not match' : ''
|
||||
}
|
||||
|
||||
cancel () {
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import { NgModule } from '@angular/core'
|
||||
import { RouterModule, Routes } from '@angular/router'
|
||||
import { ProdKeyModal } from './prod-key-modal.page'
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: ProdKeyModal,
|
||||
},
|
||||
]
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class ProdKeyModalRoutingModule { }
|
||||
@@ -0,0 +1,18 @@
|
||||
import { NgModule } from '@angular/core'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { FormsModule } from '@angular/forms'
|
||||
import { ProdKeyModal } from './prod-key-modal.page'
|
||||
|
||||
import { ProdKeyModalRoutingModule } from './prod-key-modal-routing.module'
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
IonicModule,
|
||||
ProdKeyModalRoutingModule,
|
||||
],
|
||||
declarations: [ProdKeyModal],
|
||||
})
|
||||
export class ProdKeyModalModule { }
|
||||
@@ -0,0 +1,47 @@
|
||||
<ion-header>
|
||||
<ion-toolbar color="light">
|
||||
<ion-title>
|
||||
Verify Recovery Product Key
|
||||
</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content color="light">
|
||||
<form (ngSubmit)="verifyProductKey()">
|
||||
<div style="padding: 8px 24px;">
|
||||
<div style="padding-bottom: 16px;">
|
||||
<p>Verify the product key for the chosen recovery drive.</p>
|
||||
</div>
|
||||
<ion-item
|
||||
color="dark"
|
||||
[class]="error ? 'error-border' : ''"
|
||||
>
|
||||
<ion-input
|
||||
[(ngModel)]="productKey"
|
||||
[ngModelOptions]="{'standalone': true}"
|
||||
[type]="!unmasked ? 'password' : 'text'"
|
||||
placeholder="Enter Product Key"
|
||||
maxlength="64"
|
||||
maxlength="12"
|
||||
></ion-input>
|
||||
<ion-button fill="clear" color="light" (click)="unmasked = !unmasked">
|
||||
<ion-icon slot="icon-only" [name]="unmasked ? 'eye-off-outline' : 'eye-outline'" size="small"></ion-icon>
|
||||
</ion-button>
|
||||
</ion-item>
|
||||
<p style="color: var(--ion-color-danger);">{{ error }}</p>
|
||||
</div>
|
||||
<input type="submit" style="display: none" />
|
||||
</form>
|
||||
</ion-content>
|
||||
|
||||
<ion-footer>
|
||||
<ion-toolbar color="light">
|
||||
<ion-button class="ion-padding-end" slot="end" color="dark" fill="clear" (click)="cancel()">
|
||||
Cancel
|
||||
</ion-button>
|
||||
<ion-button class="ion-padding-end" slot="end" color="dark" fill="clear" strong="true" (click)="verifyProductKey()">
|
||||
Verify
|
||||
</ion-button>
|
||||
</ion-toolbar>
|
||||
</ion-footer>
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
.password-input {
|
||||
color: var(--ion-color-dark);
|
||||
margin-bottom: 6px;
|
||||
font-size: medium;
|
||||
font-weight: 500;
|
||||
* {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
.error-border {
|
||||
border: 2px solid var(--ion-color-danger);
|
||||
}
|
||||
|
||||
.success-border {
|
||||
border: 2px solid var(--ion-color-success);
|
||||
}
|
||||
|
||||
ion-input {
|
||||
font-weight: 500;
|
||||
--placeholder-font-weight: 400;
|
||||
width: 100%;
|
||||
background: var(--ion-color-dark);
|
||||
border-radius: 3px;
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import { Component, Input } from '@angular/core'
|
||||
import { LoadingController, ModalController } from '@ionic/angular'
|
||||
import { ApiService, DiskInfo } from 'src/app/services/api/api.service'
|
||||
import { HttpService } from 'src/app/services/api/http.service'
|
||||
|
||||
@Component({
|
||||
selector: 'prod-key-modal',
|
||||
templateUrl: 'prod-key-modal.page.html',
|
||||
styleUrls: ['prod-key-modal.page.scss'],
|
||||
})
|
||||
export class ProdKeyModal {
|
||||
@Input() recoveryDrive: DiskInfo
|
||||
|
||||
error = ''
|
||||
productKey = ''
|
||||
unmasked = false
|
||||
|
||||
constructor (
|
||||
private readonly modalController: ModalController,
|
||||
private readonly apiService: ApiService,
|
||||
private readonly loadingCtrl: LoadingController,
|
||||
private readonly httpService: HttpService,
|
||||
) { }
|
||||
|
||||
async verifyProductKey () {
|
||||
if (!this.productKey) return
|
||||
|
||||
const loader = await this.loadingCtrl.create({
|
||||
message: 'Verifying Product Key',
|
||||
})
|
||||
await loader.present()
|
||||
|
||||
try {
|
||||
await this.apiService.set02XDrive(this.recoveryDrive.logicalname)
|
||||
this.httpService.productKey = this.productKey
|
||||
await this.apiService.verifyProductKey()
|
||||
this.modalController.dismiss({ productKey: this.productKey })
|
||||
} catch (e) {
|
||||
this.httpService.productKey = undefined
|
||||
this.error = 'Invalid Product Key'
|
||||
} finally {
|
||||
loader.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
cancel () {
|
||||
this.modalController.dismiss()
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,16 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { ProductKeyPage } from './product-key.page';
|
||||
import { NgModule } from '@angular/core'
|
||||
import { RouterModule, Routes } from '@angular/router'
|
||||
import { ProductKeyPage } from './product-key.page'
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: ProductKeyPage,
|
||||
}
|
||||
];
|
||||
},
|
||||
]
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule]
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class ProductKeyPageRoutingModule {}
|
||||
export class ProductKeyPageRoutingModule { }
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { IonicModule } from '@ionic/angular';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { ProductKeyPage } from './product-key.page';
|
||||
import { PasswordPageModule } from '../password/password.module';
|
||||
import { ProductKeyPageRoutingModule } from './product-key-routing.module';
|
||||
import { NgModule } from '@angular/core'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { FormsModule } from '@angular/forms'
|
||||
import { ProductKeyPage } from './product-key.page'
|
||||
import { PasswordPageModule } from '../password/password.module'
|
||||
import { ProductKeyPageRoutingModule } from './product-key-routing.module'
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@@ -14,6 +14,6 @@ import { ProductKeyPageRoutingModule } from './product-key-routing.module';
|
||||
ProductKeyPageRoutingModule,
|
||||
PasswordPageModule,
|
||||
],
|
||||
declarations: [ProductKeyPage]
|
||||
declarations: [ProductKeyPage],
|
||||
})
|
||||
export class ProductKeyPageModule {}
|
||||
export class ProductKeyPageModule { }
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Component } from '@angular/core'
|
||||
import { iosTransitionAnimation, LoadingController, NavController } from '@ionic/angular'
|
||||
import { 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'
|
||||
@@ -13,35 +13,32 @@ export class ProductKeyPage {
|
||||
productKey: string
|
||||
error: string
|
||||
|
||||
constructor(
|
||||
constructor (
|
||||
private readonly navCtrl: NavController,
|
||||
private readonly stateService: StateService,
|
||||
private readonly apiService: ApiService,
|
||||
private readonly loadingCtrl: LoadingController,
|
||||
private readonly httpService: HttpService
|
||||
) {}
|
||||
private readonly httpService: HttpService,
|
||||
) { }
|
||||
|
||||
async submit () {
|
||||
if(!this.productKey) return this.error = "Must enter product key"
|
||||
if (!this.productKey) return this.error = 'Must enter product key'
|
||||
|
||||
const loader = await this.loadingCtrl.create({
|
||||
message: 'Verifying Product Key'
|
||||
message: 'Verifying Product Key',
|
||||
})
|
||||
await loader.present()
|
||||
|
||||
try {
|
||||
this.httpService.productKey = this.productKey
|
||||
const state = await this.apiService.verifyProductKey()
|
||||
if(state['is-recovering']) {
|
||||
await this.navCtrl.navigateForward(`/loading`, { animationDirection: 'forward', animation: iosTransitionAnimation })
|
||||
} else if (!!state['tor-address']) {
|
||||
this.stateService.torAddress = state['tor-address']
|
||||
await this.navCtrl.navigateForward(`/success`, { animationDirection: 'forward', animation: iosTransitionAnimation })
|
||||
await this.apiService.verifyProductKey()
|
||||
if (this.stateService.isMigrating) {
|
||||
await this.navCtrl.navigateForward(`/loading`)
|
||||
} else {
|
||||
await this.navCtrl.navigateForward(`/home`, { animationDirection: 'forward', animation: iosTransitionAnimation })
|
||||
await this.navCtrl.navigateForward(`/home`)
|
||||
}
|
||||
} catch (e) {
|
||||
this.error = e.message
|
||||
this.error = 'Invalid Product Key'
|
||||
this.httpService.productKey = undefined
|
||||
} finally {
|
||||
loader.dismiss()
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { RecoverPage } from './recover.page';
|
||||
import { NgModule } from '@angular/core'
|
||||
import { RouterModule, Routes } from '@angular/router'
|
||||
import { RecoverPage } from './recover.page'
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: RecoverPage,
|
||||
}
|
||||
];
|
||||
},
|
||||
]
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule]
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class RecoverPageRoutingModule {}
|
||||
export class RecoverPageRoutingModule { }
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { IonicModule } from '@ionic/angular';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { RecoverPage } from './recover.page';
|
||||
import { PasswordPageModule } from '../password/password.module';
|
||||
import { RecoverPageRoutingModule } from './recover-routing.module';
|
||||
import { PipesModule } from 'src/app/pipes/pipe.module';
|
||||
import { NgModule } from '@angular/core'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { FormsModule } from '@angular/forms'
|
||||
import { RecoverPage } from './recover.page'
|
||||
import { PasswordPageModule } from '../password/password.module'
|
||||
import { ProdKeyModalModule } from '../prod-key-modal/prod-key-modal.module'
|
||||
import { RecoverPageRoutingModule } from './recover-routing.module'
|
||||
import { PipesModule } from 'src/app/pipes/pipe.module'
|
||||
|
||||
|
||||
@NgModule({
|
||||
@@ -15,8 +16,9 @@ import { PipesModule } from 'src/app/pipes/pipe.module';
|
||||
IonicModule,
|
||||
RecoverPageRoutingModule,
|
||||
PasswordPageModule,
|
||||
ProdKeyModalModule,
|
||||
PipesModule,
|
||||
],
|
||||
declarations: [RecoverPage]
|
||||
declarations: [RecoverPage],
|
||||
})
|
||||
export class RecoverPageModule {}
|
||||
export class RecoverPageModule { }
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
<ion-card color="dark">
|
||||
<ion-card-header class="ion-text-center" style="padding-bottom: 8px;">
|
||||
<ion-card-title>{{ loading ? 'Loading Recovery Drives' : 'Select Recovery Drive'}}</ion-card-title>
|
||||
<ion-card-title>Select Recovery Drive</ion-card-title>
|
||||
<ion-card-subtitle>Select the drive containing the Embassy you want to recover.</ion-card-subtitle>
|
||||
</ion-card-header>
|
||||
|
||||
@@ -50,17 +50,17 @@
|
||||
<span *ngIf="drive.vendor && drive.model"> - </span>
|
||||
{{ drive.model }}
|
||||
</h2>
|
||||
<h2> Embassy version: {{drive['embassy_os'].version}}</h2>
|
||||
<h2> Embassy version: {{drive['embassy-os'].version}}</h2>
|
||||
</ion-label>
|
||||
<ion-icon *ngIf="drive['embassy_os'].version.startsWith('0.2') || passwords[drive.logicalname]" color="success" slot="end" name="lock-open-outline"></ion-icon>
|
||||
<ion-icon *ngIf="!drive['embassy_os'].version.startsWith('0.2') && !passwords[drive.logicalname]" color="danger" slot="end" name="lock-closed-outline"></ion-icon>
|
||||
<ion-icon *ngIf="(drive['embassy-os'].version.startsWith('0.2') && stateService.hasProductKey) || passwords[drive.logicalname] || prodKeys[drive.logicalname]" color="success" slot="end" name="lock-open-outline"></ion-icon>
|
||||
<ion-icon *ngIf="(drive['embassy-os'].version.startsWith('0.2') && !stateService.hasProductKey && !prodKeys[drive.logicalname]) || (!drive['embassy-os'].version.startsWith('0.2') && !passwords[drive.logicalname])" color="danger" slot="end" name="lock-closed-outline"></ion-icon>
|
||||
|
||||
</ion-item>
|
||||
</ng-container>
|
||||
<ion-button
|
||||
(click)="selectRecoveryDrive()"
|
||||
color="light"
|
||||
[disabled]="!selectedDrive || (!passwords[selectedDrive.logicalname] && !selectedDrive['embassy_os'].version.startsWith('0.2'))"
|
||||
[disabled]="!selectedDrive || (!passwords[selectedDrive.logicalname] && !selectedDrive['embassy-os'].version.startsWith('0.2'))"
|
||||
class="claim-button"
|
||||
*ngIf="recoveryDrives.length"
|
||||
>
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { Component } from '@angular/core'
|
||||
import { iosTransitionAnimation, ModalController, NavController } from '@ionic/angular'
|
||||
import { ModalController, NavController } from '@ionic/angular'
|
||||
import { ApiService, DiskInfo } 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'
|
||||
import { ProdKeyModal } from '../prod-key-modal/prod-key-modal.page'
|
||||
|
||||
@Component({
|
||||
selector: 'app-recover',
|
||||
@@ -11,18 +12,19 @@ import { PasswordPage } from '../password/password.page'
|
||||
styleUrls: ['recover.page.scss'],
|
||||
})
|
||||
export class RecoverPage {
|
||||
passwords = {}
|
||||
passwords = { }
|
||||
prodKeys = { }
|
||||
recoveryDrives = []
|
||||
selectedDrive: DiskInfo = null
|
||||
loading = true
|
||||
|
||||
constructor(
|
||||
constructor (
|
||||
private readonly apiService: ApiService,
|
||||
private readonly navCtrl: NavController,
|
||||
private readonly modalController: ModalController,
|
||||
private readonly stateService: StateService,
|
||||
private readonly modalController: ModalController,
|
||||
readonly stateService: StateService,
|
||||
private readonly errorToastService: ErrorToastService,
|
||||
) {}
|
||||
) { }
|
||||
|
||||
async ngOnInit () {
|
||||
await this.getDrives()
|
||||
@@ -37,7 +39,14 @@ export class RecoverPage {
|
||||
|
||||
async getDrives () {
|
||||
try {
|
||||
this.recoveryDrives = (await this.apiService.getDrives()).filter(d => !!d['embassy_os'])
|
||||
let drives = (await this.apiService.getDrives()).filter(d => !!d['embassy-os'])
|
||||
|
||||
if (!this.stateService.hasProductKey) {
|
||||
drives = drives.filter(d => d['embassy-os'].version.startsWith('0.2'))
|
||||
}
|
||||
|
||||
this.recoveryDrives = drives
|
||||
|
||||
} catch (e) {
|
||||
this.errorToastService.present(`${e.message}: ${e.data}`)
|
||||
} finally {
|
||||
@@ -45,7 +54,7 @@ export class RecoverPage {
|
||||
}
|
||||
}
|
||||
|
||||
async chooseDrive(drive: DiskInfo) {
|
||||
async chooseDrive (drive: DiskInfo) {
|
||||
|
||||
if (this.selectedDrive?.logicalname === drive.logicalname) {
|
||||
this.selectedDrive = null
|
||||
@@ -54,32 +63,51 @@ export class RecoverPage {
|
||||
this.selectedDrive = drive
|
||||
}
|
||||
|
||||
if (drive['embassy_os'].version.startsWith('0.2') || this.passwords[drive.logicalname]) return
|
||||
if ((drive['embassy-os'].version.startsWith('0.2') && this.stateService.hasProductKey) || this.passwords[drive.logicalname] || this.prodKeys[drive.logicalname]) return
|
||||
|
||||
const modal = await this.modalController.create({
|
||||
component: PasswordPage,
|
||||
componentProps: {
|
||||
recoveryDrive: this.selectedDrive
|
||||
},
|
||||
cssClass: 'alertlike-modal',
|
||||
})
|
||||
modal.onDidDismiss().then(async ret => {
|
||||
if (!ret.data) {
|
||||
this.selectedDrive = null
|
||||
} else if(ret.data.password) {
|
||||
this.passwords[drive.logicalname] = ret.data.password
|
||||
}
|
||||
|
||||
})
|
||||
await modal.present();
|
||||
if (this.stateService.hasProductKey) {
|
||||
const modal = await this.modalController.create({
|
||||
component: PasswordPage,
|
||||
componentProps: {
|
||||
recoveryDrive: this.selectedDrive,
|
||||
},
|
||||
cssClass: 'alertlike-modal',
|
||||
})
|
||||
modal.onDidDismiss().then(async ret => {
|
||||
if (!ret.data) {
|
||||
this.selectedDrive = null
|
||||
} else if (ret.data.password) {
|
||||
this.passwords[drive.logicalname] = ret.data.password
|
||||
}
|
||||
|
||||
})
|
||||
await modal.present()
|
||||
} else {
|
||||
const modal = await this.modalController.create({
|
||||
component: ProdKeyModal,
|
||||
componentProps: {
|
||||
recoveryDrive: this.selectedDrive,
|
||||
},
|
||||
cssClass: 'alertlike-modal',
|
||||
})
|
||||
modal.onDidDismiss().then(async ret => {
|
||||
if (!ret.data) {
|
||||
this.selectedDrive = null
|
||||
} else if (ret.data.productKey) {
|
||||
this.prodKeys[drive.logicalname] = ret.data.productKey
|
||||
}
|
||||
|
||||
})
|
||||
await modal.present()
|
||||
}
|
||||
}
|
||||
|
||||
async selectRecoveryDrive() {
|
||||
async selectRecoveryDrive () {
|
||||
this.stateService.recoveryDrive = this.selectedDrive
|
||||
const pw = this.passwords[this.selectedDrive.logicalname]
|
||||
if(pw) {
|
||||
if (pw) {
|
||||
this.stateService.recoveryPassword = pw
|
||||
}
|
||||
await this.navCtrl.navigateForward(`/embassy`, { animationDirection: 'forward', animation: iosTransitionAnimation })
|
||||
}
|
||||
await this.navCtrl.navigateForward(`/embassy`)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { SuccessPage } from './success.page';
|
||||
import { NgModule } from '@angular/core'
|
||||
import { RouterModule, Routes } from '@angular/router'
|
||||
import { SuccessPage } from './success.page'
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: SuccessPage,
|
||||
}
|
||||
];
|
||||
},
|
||||
]
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule]
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class SuccessPageRoutingModule {}
|
||||
export class SuccessPageRoutingModule { }
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { IonicModule } from '@ionic/angular';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { SuccessPage } from './success.page';
|
||||
import { PasswordPageModule } from '../password/password.module';
|
||||
import { NgModule } from '@angular/core'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { FormsModule } from '@angular/forms'
|
||||
import { SuccessPage } from './success.page'
|
||||
import { PasswordPageModule } from '../password/password.module'
|
||||
|
||||
import { SuccessPageRoutingModule } from './success-routing.module';
|
||||
import { SuccessPageRoutingModule } from './success-routing.module'
|
||||
|
||||
|
||||
@NgModule({
|
||||
@@ -16,6 +16,6 @@ import { SuccessPageRoutingModule } from './success-routing.module';
|
||||
SuccessPageRoutingModule,
|
||||
PasswordPageModule,
|
||||
],
|
||||
declarations: [SuccessPage]
|
||||
declarations: [SuccessPage],
|
||||
})
|
||||
export class SuccessPageModule {}
|
||||
export class SuccessPageModule { }
|
||||
|
||||
@@ -8,9 +8,9 @@ import { StateService } from 'src/app/services/state.service'
|
||||
styleUrls: ['success.page.scss'],
|
||||
})
|
||||
export class SuccessPage {
|
||||
constructor(
|
||||
public stateService: StateService,
|
||||
private toastCtrl: ToastController
|
||||
constructor (
|
||||
private readonly toastCtrl: ToastController,
|
||||
public readonly stateService: StateService,
|
||||
) { }
|
||||
|
||||
window = window
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { ConvertBytesPipe } from './convert-bytes.pipe';
|
||||
import { NgModule } from '@angular/core'
|
||||
import { ConvertBytesPipe } from './convert-bytes.pipe'
|
||||
|
||||
@NgModule({
|
||||
declarations: [ConvertBytesPipe],
|
||||
@@ -7,4 +7,4 @@ imports: [],
|
||||
exports: [ConvertBytesPipe],
|
||||
})
|
||||
|
||||
export class PipesModule {}
|
||||
export class PipesModule { }
|
||||
@@ -1,30 +1,26 @@
|
||||
import { Subject } from 'rxjs'
|
||||
|
||||
export abstract class ApiService {
|
||||
abstract verifyProductKey (): Promise<VerifyProductKeyRes>;
|
||||
abstract getDrives (): Promise<DiskInfo[]>;
|
||||
abstract getDataTransferProgress (): Promise<TransferProgressRes>;
|
||||
abstract verifyRecoveryPassword (logicalname: string, password: string): Promise<boolean>;
|
||||
abstract setupEmbassy (setupInfo: {
|
||||
'embassy-logicalname': string,
|
||||
'embassy-password': string
|
||||
'recovery-logicalname'?: string,
|
||||
'recovery-password'?: string
|
||||
}): Promise<SetupEmbassyRes>
|
||||
// unencrypted
|
||||
abstract getStatus (): Promise<GetStatusRes> // setup.status
|
||||
abstract getDrives (): Promise<DiskInfo[]> // setup.disk.list
|
||||
abstract set02XDrive (logicalname: string): Promise<void> // setup.recovery.v2.set
|
||||
abstract getRecoveryStatus (): Promise<RecoveryStatusRes> // setup.recovery.status
|
||||
|
||||
// encrypted
|
||||
abstract verifyProductKey (): Promise<void> // echo - throws error if invalid
|
||||
abstract verify03XPassword (logicalname: string, password: string): Promise<boolean> // setup.recovery.test-password
|
||||
abstract setupEmbassy (setupInfo: SetupEmbassyReq): Promise<string> // setup.execute
|
||||
}
|
||||
|
||||
export interface VerifyProductKeyRes {
|
||||
"is-recovering": boolean
|
||||
"tor-address": string
|
||||
export interface GetStatusRes {
|
||||
'product-key': boolean
|
||||
migrating: boolean
|
||||
}
|
||||
|
||||
export interface TransferProgressRes {
|
||||
'bytes-transfered': number;
|
||||
'total-bytes': number;
|
||||
}
|
||||
|
||||
export interface SetupEmbassyRes {
|
||||
"tor-address": string
|
||||
export interface SetupEmbassyReq {
|
||||
'embassy-logicalname': string
|
||||
'embassy-password': string
|
||||
'recovery-logicalname'?: string
|
||||
'recovery-password'?: string
|
||||
}
|
||||
|
||||
export interface DiskInfo {
|
||||
@@ -33,7 +29,12 @@ export interface DiskInfo {
|
||||
model: string | null,
|
||||
partitions: PartitionInfo[],
|
||||
capacity: number,
|
||||
embassy_os: EmbassyOsDiskInfo | null,
|
||||
'embassy-os': EmbassyOsDiskInfo | null,
|
||||
}
|
||||
|
||||
export interface RecoveryStatusRes {
|
||||
'bytes-transferred': number
|
||||
'total-bytes': number
|
||||
}
|
||||
|
||||
interface PartitionInfo {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Injectable } from '@angular/core'
|
||||
import { HttpClient, HttpErrorResponse, HttpHeaders, HttpParams } from '@angular/common/http'
|
||||
import { Observable, from, interval, race } from 'rxjs'
|
||||
import { map, take } from 'rxjs/operators'
|
||||
import { Observable } from 'rxjs'
|
||||
import * as aesjs from 'aes-js'
|
||||
import * as pbkdf2 from 'pbkdf2'
|
||||
|
||||
@@ -19,7 +18,7 @@ export class HttpService {
|
||||
this.fullUrl = `${window.location.protocol}//${window.location.hostname}:${port}/rpc/v1`
|
||||
}
|
||||
|
||||
async rpcRequest<T> (body: RPCOptions): Promise<T> {
|
||||
async rpcRequest<T> (body: RPCOptions, encrypted = true): Promise<T> {
|
||||
|
||||
const httpOpts = {
|
||||
method: Method.POST,
|
||||
@@ -27,14 +26,20 @@ export class HttpService {
|
||||
url: this.fullUrl,
|
||||
}
|
||||
|
||||
const res = await this.httpRequest<RPCResponse<T>>(httpOpts)
|
||||
let res: RPCResponse<T>
|
||||
|
||||
if (encrypted) {
|
||||
res = await this.encryptedHttpRequest<RPCResponse<T>>(httpOpts)
|
||||
} else {
|
||||
res = await this.httpRequest<RPCResponse<T>>(httpOpts)
|
||||
}
|
||||
|
||||
if (isRpcError(res)) throw new RpcError(res.error)
|
||||
|
||||
if (isRpcSuccess(res)) return res.result
|
||||
}
|
||||
|
||||
async httpRequest<T> (httpOpts: {
|
||||
async encryptedHttpRequest<T> (httpOpts: {
|
||||
body: RPCOptions;
|
||||
url: string;
|
||||
}): Promise<T> {
|
||||
@@ -53,7 +58,7 @@ export class HttpService {
|
||||
|
||||
headers: {
|
||||
'Content-Encoding': 'aesctr256',
|
||||
'Content-Type': 'application/json'
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
} as any
|
||||
|
||||
@@ -71,6 +76,31 @@ export class HttpService {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async httpRequest<T> (httpOpts: {
|
||||
body: RPCOptions;
|
||||
url: string;
|
||||
}): Promise<T> {
|
||||
const urlIsRelative = httpOpts.url.startsWith('/')
|
||||
const url = urlIsRelative ?
|
||||
this.fullUrl + httpOpts.url :
|
||||
httpOpts.url
|
||||
|
||||
const options = {
|
||||
responseType: 'json',
|
||||
body: httpOpts.body,
|
||||
observe: 'events',
|
||||
reportProgress: false,
|
||||
headers: { 'content-type': 'application/json', accept: 'application/json' },
|
||||
} as any
|
||||
|
||||
const req: Observable<{ body: T }> = this.http.post(url, httpOpts.body, options) as any
|
||||
|
||||
return (req)
|
||||
.toPromise()
|
||||
.then(res => res.body)
|
||||
.catch(e => { throw new HttpError(e) })
|
||||
}
|
||||
}
|
||||
|
||||
function RpcError (e: RPCError['error']): void {
|
||||
@@ -167,13 +197,6 @@ export interface HttpOptions {
|
||||
timeout?: number
|
||||
}
|
||||
|
||||
function withTimeout<U> (req: Observable<U>, timeout: number): Observable<U> {
|
||||
return race(
|
||||
from(req.toPromise()), // this guarantees it only emits on completion, intermediary emissions are suppressed.
|
||||
interval(timeout).pipe(take(1), map(() => { throw new Error('timeout') })),
|
||||
)
|
||||
}
|
||||
|
||||
type AES_CTR = {
|
||||
encryptPbkdf2: (secretKey: string, messageBuffer: Uint8Array) => Promise<Uint8Array>
|
||||
decryptPbkdf2: (secretKey, arr: ArrayBuffer) => Promise<string>
|
||||
@@ -184,10 +207,10 @@ export const AES_CTR: AES_CTR = {
|
||||
const salt = window.crypto.getRandomValues(new Uint8Array(16))
|
||||
const counter = window.crypto.getRandomValues(new Uint8Array(16))
|
||||
|
||||
const key = pbkdf2.pbkdf2Sync(secretKey, salt, 1000, 256 / 8, 'sha256');
|
||||
const key = pbkdf2.pbkdf2Sync(secretKey, salt, 1000, 256 / 8, 'sha256')
|
||||
|
||||
const aesCtr = new aesjs.ModeOfOperation.ctr(key, new aesjs.Counter(counter));
|
||||
const encryptedBytes = aesCtr.encrypt(messageBuffer);
|
||||
const aesCtr = new aesjs.ModeOfOperation.ctr(key, new aesjs.Counter(counter))
|
||||
const encryptedBytes = aesCtr.encrypt(messageBuffer)
|
||||
return new Uint8Array([...counter, ...salt, ...encryptedBytes])
|
||||
},
|
||||
decryptPbkdf2: async (secretKey: string, arr: ArrayBuffer) => {
|
||||
@@ -196,12 +219,12 @@ export const AES_CTR: AES_CTR = {
|
||||
const salt = buff.slice(16, 32)
|
||||
|
||||
const cipher = buff.slice(32)
|
||||
const key = pbkdf2.pbkdf2Sync(secretKey, salt, 1000, 256 / 8, 'sha256');
|
||||
const key = pbkdf2.pbkdf2Sync(secretKey, salt, 1000, 256 / 8, 'sha256')
|
||||
|
||||
const aesCtr = new aesjs.ModeOfOperation.ctr(key, new aesjs.Counter(counter));
|
||||
const decryptedBytes = aesCtr.decrypt(cipher);
|
||||
const aesCtr = new aesjs.ModeOfOperation.ctr(key, new aesjs.Counter(counter))
|
||||
const decryptedBytes = aesCtr.decrypt(cipher)
|
||||
|
||||
return aesjs.utils.utf8.fromBytes(decryptedBytes);
|
||||
return aesjs.utils.utf8.fromBytes(decryptedBytes)
|
||||
},
|
||||
}
|
||||
|
||||
@@ -214,5 +237,5 @@ export function encodeUtf8 (str: string): Uint8Array {
|
||||
}
|
||||
|
||||
export function decodeUtf8 (arr: Uint8Array): string {
|
||||
return new TextDecoder().decode(arr);
|
||||
return new TextDecoder().decode(arr)
|
||||
}
|
||||
@@ -1,53 +1,66 @@
|
||||
import { Injectable } from '@angular/core'
|
||||
import { ApiService, DiskInfo, SetupEmbassyRes, TransferProgressRes, VerifyProductKeyRes } from './api.service'
|
||||
import { ApiService, DiskInfo, GetStatusRes, RecoveryStatusRes, SetupEmbassyReq } from './api.service'
|
||||
import { HttpService } from './http.service'
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class LiveApiService extends ApiService {
|
||||
|
||||
constructor(
|
||||
private readonly http: HttpService
|
||||
constructor (
|
||||
private readonly http: HttpService,
|
||||
) { super() }
|
||||
|
||||
async verifyProductKey () {
|
||||
return this.http.rpcRequest<VerifyProductKeyRes>({
|
||||
method: 'setup.status',
|
||||
params: {}
|
||||
})
|
||||
}
|
||||
// ** UNENCRYPTED **
|
||||
|
||||
async getDataTransferProgress () {
|
||||
return this.http.rpcRequest<TransferProgressRes>({
|
||||
method: 'setup.recovery.status',
|
||||
params: {}
|
||||
})
|
||||
async getStatus () {
|
||||
return this.http.rpcRequest<GetStatusRes>({
|
||||
method: 'setup.status',
|
||||
params: { },
|
||||
}, false)
|
||||
}
|
||||
|
||||
async getDrives () {
|
||||
return this.http.rpcRequest<DiskInfo[]>({
|
||||
method: 'setup.disk.list',
|
||||
params: {}
|
||||
params: { },
|
||||
}, false)
|
||||
}
|
||||
|
||||
async set02XDrive (logicalname) {
|
||||
return this.http.rpcRequest<void>({
|
||||
method: 'setup.recovery.v2.set',
|
||||
params: { logicalname },
|
||||
}, false)
|
||||
}
|
||||
|
||||
async getRecoveryStatus () {
|
||||
return this.http.rpcRequest<RecoveryStatusRes>({
|
||||
method: 'setup.recovery.status',
|
||||
params: { },
|
||||
}, false)
|
||||
}
|
||||
|
||||
// ** ENCRYPTED **
|
||||
|
||||
async verifyProductKey () {
|
||||
return this.http.rpcRequest<void>({
|
||||
method: 'echo',
|
||||
params: { },
|
||||
})
|
||||
}
|
||||
|
||||
async verifyRecoveryPassword (logicalname: string, password: string) {
|
||||
async verify03XPassword (logicalname: string, password: string) {
|
||||
return this.http.rpcRequest<boolean>({
|
||||
method: 'setup.recovery.test-password',
|
||||
params: {logicalname, password}
|
||||
params: { logicalname, password },
|
||||
})
|
||||
}
|
||||
|
||||
async setupEmbassy (setupInfo: {
|
||||
'embassy-logicalname': string,
|
||||
'embassy-password': string
|
||||
'recovery-logicalname'?: string,
|
||||
'recovery-password'?: string
|
||||
}) {
|
||||
return this.http.rpcRequest<SetupEmbassyRes>({
|
||||
async setupEmbassy (setupInfo: SetupEmbassyReq) {
|
||||
return this.http.rpcRequest<string>({
|
||||
method: 'setup.execute',
|
||||
params: setupInfo
|
||||
params: setupInfo as any,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,33 +1,30 @@
|
||||
import { Injectable } from '@angular/core'
|
||||
import { pauseFor } from '../state.service'
|
||||
import { ApiService } from './api.service'
|
||||
import { ApiService, SetupEmbassyReq } from './api.service'
|
||||
|
||||
let tries = 0
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class MockApiService extends ApiService {
|
||||
|
||||
constructor() {
|
||||
constructor () {
|
||||
super()
|
||||
}
|
||||
|
||||
async verifyProductKey () {
|
||||
await pauseFor(2000)
|
||||
return {
|
||||
"is-recovering": false,
|
||||
"tor-address": null
|
||||
}
|
||||
}
|
||||
// ** UNENCRYPTED **
|
||||
|
||||
async getDataTransferProgress () {
|
||||
tries = Math.min(tries + 1, 4)
|
||||
async getStatus () {
|
||||
await pauseFor(1000)
|
||||
return {
|
||||
'bytes-transfered': tries,
|
||||
'total-bytes': 4
|
||||
'product-key': true,
|
||||
migrating: false,
|
||||
}
|
||||
}
|
||||
|
||||
async getDrives () {
|
||||
await pauseFor(1000)
|
||||
return [
|
||||
{
|
||||
vendor: 'Vendor',
|
||||
@@ -38,17 +35,17 @@ export class MockApiService extends ApiService {
|
||||
logicalname: 'sda1',
|
||||
label: 'label 1',
|
||||
capacity: 100000,
|
||||
used: 200.1255312
|
||||
used: 200.1255312,
|
||||
},
|
||||
{
|
||||
logicalname: 'sda2',
|
||||
label: 'label 2',
|
||||
capacity: 50000,
|
||||
used: 200.1255312
|
||||
}
|
||||
used: 200.1255312,
|
||||
},
|
||||
],
|
||||
capacity: 150000,
|
||||
'embassy_os': null
|
||||
'embassy-os': null,
|
||||
},
|
||||
{
|
||||
vendor: 'Vendor',
|
||||
@@ -63,7 +60,7 @@ export class MockApiService extends ApiService {
|
||||
// }
|
||||
],
|
||||
capacity: 1600.01234,
|
||||
'embassy_os': null
|
||||
'embassy-os': null,
|
||||
},
|
||||
{
|
||||
vendor: 'Vendor',
|
||||
@@ -74,64 +71,77 @@ export class MockApiService extends ApiService {
|
||||
logicalname: 'sdc1',
|
||||
label: 'label 1',
|
||||
capacity: null,
|
||||
used: null
|
||||
}
|
||||
used: null,
|
||||
},
|
||||
],
|
||||
capacity: 100000,
|
||||
'embassy_os': {
|
||||
'embassy-os': {
|
||||
version: '0.3.3',
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
vendor: 'Vendor',
|
||||
model: 'Model',
|
||||
logicalname: '/dev/sdd',
|
||||
partitions: [
|
||||
partitions: [
|
||||
{
|
||||
logicalname: 'sdd1',
|
||||
label: null,
|
||||
capacity: 10000,
|
||||
used: null
|
||||
}
|
||||
used: null,
|
||||
},
|
||||
],
|
||||
capacity: 10000,
|
||||
'embassy_os': {
|
||||
'embassy-os': {
|
||||
version: '0.2.7',
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
async set02XDrive () {
|
||||
await pauseFor(1000)
|
||||
return
|
||||
}
|
||||
|
||||
async getRecoveryStatus () {
|
||||
tries = Math.min(tries + 1, 4)
|
||||
return {
|
||||
'bytes-transferred': tries,
|
||||
'total-bytes': 4,
|
||||
}
|
||||
}
|
||||
|
||||
// ** ENCRYPTED **
|
||||
|
||||
async verifyProductKey () {
|
||||
await pauseFor(1000)
|
||||
return
|
||||
}
|
||||
|
||||
async verify03XPassword (logicalname: string, password: string) {
|
||||
await pauseFor(2000)
|
||||
return password.length > 8
|
||||
}
|
||||
|
||||
async setupEmbassy (setupInfo: SetupEmbassyReq) {
|
||||
await pauseFor(3000)
|
||||
return 'asdfasdfasdf.onion'
|
||||
}
|
||||
|
||||
async getRecoveryDrives () {
|
||||
await pauseFor(2000)
|
||||
return [
|
||||
{
|
||||
logicalname: 'Name1',
|
||||
version: '0.3.3',
|
||||
name: 'My Embassy'
|
||||
name: 'My Embassy',
|
||||
},
|
||||
{
|
||||
logicalname: 'Name2',
|
||||
version: '0.2.7',
|
||||
name: 'My Embassy'
|
||||
}
|
||||
name: 'My Embassy',
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
async verifyRecoveryPassword (logicalname: string, password: string) {
|
||||
await pauseFor(2000)
|
||||
return password.length > 8
|
||||
}
|
||||
|
||||
async setupEmbassy (setupInfo: {
|
||||
'embassy-logicalname': string,
|
||||
'embassy-password': string
|
||||
'recovery-logicalname'?: string,
|
||||
'recovery-password'?: string
|
||||
}) {
|
||||
await pauseFor(2000)
|
||||
return { "tor-address": 'asdfasdfasdf.onion' }
|
||||
}
|
||||
}
|
||||
|
||||
let tries = 0
|
||||
|
||||
@@ -1,52 +1,55 @@
|
||||
import { Injectable } from '@angular/core'
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { BehaviorSubject } from 'rxjs'
|
||||
import { ApiService, DiskInfo } from './api/api.service'
|
||||
import { ErrorToastService } from './error-toast.service';
|
||||
import { ErrorToastService } from './error-toast.service'
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class StateService {
|
||||
hasProductKey: boolean
|
||||
isMigrating: boolean
|
||||
|
||||
polling = false
|
||||
|
||||
storageDrive: DiskInfo;
|
||||
storageDrive: DiskInfo
|
||||
embassyPassword: string
|
||||
recoveryDrive: DiskInfo;
|
||||
recoveryDrive: DiskInfo
|
||||
recoveryPassword: string
|
||||
dataTransferProgress: { bytesTransfered: number; totalBytes: number } | null;
|
||||
dataProgress = 0;
|
||||
dataTransferProgress: { bytesTransferred: number; totalBytes: number } | null
|
||||
dataProgress = 0
|
||||
dataProgSubject = new BehaviorSubject(this.dataProgress)
|
||||
|
||||
torAddress: string
|
||||
|
||||
constructor(
|
||||
constructor (
|
||||
private readonly apiService: ApiService,
|
||||
private readonly errorToastService: ErrorToastService
|
||||
) {}
|
||||
private readonly errorToastService: ErrorToastService,
|
||||
) { }
|
||||
|
||||
async pollDataTransferProgress(callback?: () => void) {
|
||||
async pollDataTransferProgress (callback?: () => void) {
|
||||
this.polling = true
|
||||
await pauseFor(1000)
|
||||
|
||||
if (
|
||||
this.dataTransferProgress?.totalBytes &&
|
||||
this.dataTransferProgress.bytesTransfered === this.dataTransferProgress.totalBytes
|
||||
) {return }
|
||||
|
||||
this.dataTransferProgress.bytesTransferred === this.dataTransferProgress.totalBytes
|
||||
) return
|
||||
|
||||
let progress
|
||||
|
||||
let progress
|
||||
try {
|
||||
progress =await this.apiService.getDataTransferProgress()
|
||||
progress = await this.apiService.getRecoveryStatus()
|
||||
} catch (e) {
|
||||
this.errorToastService.present(`${e.message}: ${e.details}`)
|
||||
}
|
||||
if (progress) {
|
||||
this.dataTransferProgress = {
|
||||
bytesTransfered: progress['bytes-transfered'],
|
||||
totalBytes: progress['total-bytes']
|
||||
bytesTransferred: progress['bytes-transferred'],
|
||||
totalBytes: progress['total-bytes'],
|
||||
}
|
||||
if (this.dataTransferProgress.totalBytes) {
|
||||
this.dataProgress = this.dataTransferProgress.bytesTransfered / this.dataTransferProgress.totalBytes
|
||||
this.dataProgress = this.dataTransferProgress.bytesTransferred / this.dataTransferProgress.totalBytes
|
||||
this.dataProgSubject.next(this.dataProgress)
|
||||
}
|
||||
}
|
||||
@@ -58,13 +61,13 @@ export class StateService {
|
||||
'embassy-logicalname': this.storageDrive.logicalname,
|
||||
'embassy-password': this.embassyPassword,
|
||||
'recovery-logicalname': this.recoveryDrive?.logicalname,
|
||||
'recovery-password': this.recoveryPassword
|
||||
'recovery-password': this.recoveryPassword,
|
||||
})
|
||||
return { torAddress: ret['tor-address'] }
|
||||
return { torAddress: ret }
|
||||
}
|
||||
}
|
||||
|
||||
export const pauseFor = (ms: number) => {
|
||||
const promise = new Promise(resolve => setTimeout(resolve, ms))
|
||||
return promise
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
export const environment = {
|
||||
production: true
|
||||
};
|
||||
production: true,
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
// The list of file replacements can be found in `angular.json`.
|
||||
|
||||
export const environment = {
|
||||
production: false
|
||||
};
|
||||
production: false,
|
||||
}
|
||||
|
||||
/*
|
||||
* For easier debugging in development mode, you can import the following file
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { enableProdMode } from '@angular/core';
|
||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
import { enableProdMode } from '@angular/core'
|
||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'
|
||||
|
||||
import { AppModule } from './app/app.module';
|
||||
import { environment } from './environments/environment';
|
||||
import { AppModule } from './app/app.module'
|
||||
import { environment } from './environments/environment'
|
||||
|
||||
if (environment.production) {
|
||||
enableProdMode();
|
||||
enableProdMode()
|
||||
}
|
||||
|
||||
platformBrowserDynamic().bootstrapModule(AppModule)
|
||||
.catch(err => console.log(err));
|
||||
.catch(err => console.log(err))
|
||||
|
||||
@@ -52,12 +52,12 @@
|
||||
*
|
||||
*/
|
||||
|
||||
import './zone-flags';
|
||||
import './zone-flags'
|
||||
|
||||
/***************************************************************************************************
|
||||
* Zone JS is required by default for Angular itself.
|
||||
*/
|
||||
import 'zone.js/dist/zone'; // Included with Angular CLI.
|
||||
import 'zone.js/dist/zone' // Included with Angular CLI.
|
||||
|
||||
|
||||
/***************************************************************************************************
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
/** Ionic CSS Variables **/
|
||||
:root {
|
||||
--ion-font-family: 'Benton Sans';
|
||||
--ion-text-color: var(--ion-color-dark);
|
||||
--ion-text-color-rgb: var(--ion-color-dark-rgb);
|
||||
/** primary **/
|
||||
--ion-color-primary: #428cff;
|
||||
--ion-color-primary-rgb: 66,140,255;
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
* running with certain Web Component callbacks
|
||||
*/
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
(window as any).__Zone_disable_customElements = true;
|
||||
(window as any).__Zone_disable_customElements = true
|
||||
|
||||
47
setup-wizard/tslint.json
Normal file
47
setup-wizard/tslint.json
Normal file
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"rules": {
|
||||
"no-unused-variable": true,
|
||||
"no-unused-expression": true,
|
||||
"space-before-function-paren": true,
|
||||
"semicolon": [
|
||||
true,
|
||||
"never"
|
||||
],
|
||||
"no-trailing-whitespace": true,
|
||||
"indent": [
|
||||
true,
|
||||
"spaces",
|
||||
2
|
||||
],
|
||||
"whitespace": [
|
||||
true,
|
||||
"check-branch",
|
||||
"check-decl",
|
||||
"check-module",
|
||||
"check-operator",
|
||||
"check-separator",
|
||||
"check-rest-spread",
|
||||
"check-type",
|
||||
"check-typecast",
|
||||
"check-type-operator",
|
||||
"check-preblock",
|
||||
"check-postbrace"
|
||||
],
|
||||
"trailing-comma": [
|
||||
true,
|
||||
{
|
||||
"multiline": {
|
||||
"objects": "always",
|
||||
"arrays": "always",
|
||||
"functions": "always",
|
||||
"typeLiterals": "never"
|
||||
},
|
||||
"singleline": "never"
|
||||
}
|
||||
],
|
||||
"quotemark": [
|
||||
true,
|
||||
"single"
|
||||
]
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user