mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-30 20:14:49 +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,
|
model : string | null,
|
||||||
partitions : PartitionInfo[],
|
partitions : PartitionInfo[],
|
||||||
capacity : number,
|
capacity : number,
|
||||||
embassy_os : EmbassyOsDiskInfo | null,
|
embassy-os : EmbassyOsDiskInfo | null,
|
||||||
}[]
|
}[]
|
||||||
|
|
||||||
setup.recovery.status
|
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": {
|
"devDependencies": {
|
||||||
"@angular-devkit/build-angular": "^12.2.1",
|
"@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/cli": "^12.2.1",
|
||||||
"@angular/compiler": "^12.2.1",
|
"@angular/compiler": "^12.2.1",
|
||||||
"@angular/compiler-cli": "^12.2.1",
|
"@angular/compiler-cli": "^12.2.1",
|
||||||
"@angular/language-service": "^12.2.1",
|
"@angular/language-service": "^12.2.1",
|
||||||
"@ionic/angular-toolkit": "^4.0.0",
|
"@ionic/angular-toolkit": "^4.0.0",
|
||||||
"@types/node": "^16.9.1",
|
"@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",
|
"ts-node": "^10.2.0",
|
||||||
|
"tslint": "^6.1.3",
|
||||||
"typescript": "4.3.5"
|
"typescript": "4.3.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,19 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core'
|
||||||
import { PreloadAllModules, RouterModule, Routes } from '@angular/router';
|
import { PreloadAllModules, RouterModule, Routes } from '@angular/router'
|
||||||
|
|
||||||
const routes: Routes = [
|
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',
|
path: 'embassy',
|
||||||
loadChildren: () => import('./pages/embassy/embassy.module').then( m => m.EmbassyPageModule),
|
loadChildren: () => import('./pages/embassy/embassy.module').then( m => m.EmbassyPageModule),
|
||||||
@@ -9,23 +22,11 @@ const routes: Routes = [
|
|||||||
path: 'loading',
|
path: 'loading',
|
||||||
loadChildren: () => import('./pages/loading/loading.module').then( m => m.LoadingPageModule),
|
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',
|
path: 'success',
|
||||||
loadChildren: () => import('./pages/success/success.module').then( m => m.SuccessPageModule),
|
loadChildren: () => import('./pages/success/success.module').then( m => m.SuccessPageModule),
|
||||||
},
|
},
|
||||||
];
|
]
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
@@ -33,8 +34,8 @@ const routes: Routes = [
|
|||||||
scrollPositionRestoration: 'enabled',
|
scrollPositionRestoration: 'enabled',
|
||||||
preloadingStrategy: PreloadAllModules,
|
preloadingStrategy: PreloadAllModules,
|
||||||
useHash: true,
|
useHash: true,
|
||||||
})
|
}),
|
||||||
],
|
],
|
||||||
exports: [RouterModule]
|
exports: [RouterModule],
|
||||||
})
|
})
|
||||||
export class AppRoutingModule { }
|
export class AppRoutingModule { }
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
<ion-app>
|
<ion-app>
|
||||||
<ion-content class="has-header">
|
<ion-router-outlet></ion-router-outlet>
|
||||||
<ion-router-outlet></ion-router-outlet>
|
|
||||||
</ion-content>
|
|
||||||
</ion-app>
|
</ion-app>
|
||||||
|
|||||||
@@ -1,18 +1,36 @@
|
|||||||
import { Component, OnInit } from '@angular/core'
|
import { Component } from '@angular/core'
|
||||||
import { NavController } from '@ionic/angular'
|
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({
|
@Component({
|
||||||
selector: 'app-root',
|
selector: 'app-root',
|
||||||
templateUrl: 'app.component.html',
|
templateUrl: 'app.component.html',
|
||||||
styleUrls: ['app.component.scss'],
|
styleUrls: ['app.component.scss'],
|
||||||
})
|
})
|
||||||
export class AppComponent implements OnInit {
|
export class AppComponent {
|
||||||
|
constructor (
|
||||||
constructor(
|
private readonly apiService: ApiService,
|
||||||
|
private readonly errorToastService: ErrorToastService,
|
||||||
private readonly navCtrl: NavController,
|
private readonly navCtrl: NavController,
|
||||||
) {}
|
private readonly stateService: StateService,
|
||||||
|
) { }
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit () {
|
||||||
await this.navCtrl.navigateForward(`/product-key`)
|
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: [],
|
entryComponents: [],
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
IonicModule.forRoot(),
|
IonicModule.forRoot({
|
||||||
|
navAnimation: iosTransitionAnimation,
|
||||||
|
}),
|
||||||
AppRoutingModule,
|
AppRoutingModule,
|
||||||
HttpClientModule,
|
HttpClientModule,
|
||||||
IonicModule.forRoot({
|
|
||||||
navAnimation: iosTransitionAnimation,
|
|
||||||
}),
|
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy },
|
{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy },
|
||||||
{
|
{
|
||||||
provide: ApiService ,
|
provide: ApiService ,
|
||||||
useFactory: (http: HttpService) => {
|
useFactory: (http: HttpService) => {
|
||||||
if (useMocks) {
|
if (useMocks) {
|
||||||
@@ -34,9 +33,9 @@ const useMocks = require('../../config.json').useMocks as boolean
|
|||||||
return new LiveApiService(http)
|
return new LiveApiService(http)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
deps: [HttpService]
|
deps: [HttpService],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
bootstrap: [AppComponent],
|
bootstrap: [AppComponent],
|
||||||
})
|
})
|
||||||
export class AppModule {}
|
export class AppModule { }
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core'
|
||||||
import { RouterModule, Routes } from '@angular/router';
|
import { RouterModule, Routes } from '@angular/router'
|
||||||
import { EmbassyPage } from './embassy.page';
|
import { EmbassyPage } from './embassy.page'
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
component: EmbassyPage,
|
component: EmbassyPage,
|
||||||
}
|
},
|
||||||
];
|
]
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [RouterModule.forChild(routes)],
|
imports: [RouterModule.forChild(routes)],
|
||||||
exports: [RouterModule]
|
exports: [RouterModule],
|
||||||
})
|
})
|
||||||
export class EmbassyPageRoutingModule {}
|
export class EmbassyPageRoutingModule { }
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core'
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common'
|
||||||
import { IonicModule } from '@ionic/angular';
|
import { IonicModule } from '@ionic/angular'
|
||||||
import { FormsModule } from '@angular/forms';
|
import { FormsModule } from '@angular/forms'
|
||||||
import { EmbassyPage } from './embassy.page';
|
import { EmbassyPage } from './embassy.page'
|
||||||
import { PasswordPageModule } from '../password/password.module';
|
import { PasswordPageModule } from '../password/password.module'
|
||||||
import { EmbassyPageRoutingModule } from './embassy-routing.module';
|
import { EmbassyPageRoutingModule } from './embassy-routing.module'
|
||||||
import { PipesModule } from 'src/app/pipes/pipe.module'
|
import { PipesModule } from 'src/app/pipes/pipe.module'
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
@@ -16,6 +16,6 @@ import { PipesModule } from 'src/app/pipes/pipe.module'
|
|||||||
PasswordPageModule,
|
PasswordPageModule,
|
||||||
PipesModule,
|
PipesModule,
|
||||||
],
|
],
|
||||||
declarations: [EmbassyPage]
|
declarations: [EmbassyPage],
|
||||||
})
|
})
|
||||||
export class EmbassyPageModule {}
|
export class EmbassyPageModule { }
|
||||||
|
|||||||
@@ -9,8 +9,8 @@
|
|||||||
|
|
||||||
<ion-card color="dark">
|
<ion-card color="dark">
|
||||||
<ion-card-header class="ion-text-center" style="padding-bottom: 8px;">
|
<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-title>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-subtitle>Select the drive where your Embassy data will be stored.</ion-card-subtitle>
|
||||||
</ion-card-header>
|
</ion-card-header>
|
||||||
|
|
||||||
<ion-card-content class="ion-margin">
|
<ion-card-content class="ion-margin">
|
||||||
@@ -31,7 +31,6 @@
|
|||||||
<ion-label class="ion-text-wrap">
|
<ion-label class="ion-text-wrap">
|
||||||
<ion-skeleton-text style="width: 80%; margin: 13px 0;" animated></ion-skeleton-text>
|
<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: 60%; margin: 10px 0;" animated></ion-skeleton-text>
|
||||||
<ion-skeleton-text style="width: 30%; margin: 8px 0;" animated></ion-skeleton-text>
|
|
||||||
</ion-label>
|
</ion-label>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Component } from '@angular/core'
|
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 { ApiService, DiskInfo } 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'
|
||||||
@@ -15,7 +15,7 @@ export class EmbassyPage {
|
|||||||
selectedDrive: DiskInfo = null
|
selectedDrive: DiskInfo = null
|
||||||
loading = true
|
loading = true
|
||||||
|
|
||||||
constructor(
|
constructor (
|
||||||
private readonly apiService: ApiService,
|
private readonly apiService: ApiService,
|
||||||
private readonly navCtrl: NavController,
|
private readonly navCtrl: NavController,
|
||||||
private readonly modalController: ModalController,
|
private readonly modalController: ModalController,
|
||||||
@@ -61,9 +61,9 @@ export class EmbassyPage {
|
|||||||
text: 'Continue',
|
text: 'Continue',
|
||||||
handler: () => {
|
handler: () => {
|
||||||
this.presentModalPassword(drive)
|
this.presentModalPassword(drive)
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
await alert.present()
|
await alert.present()
|
||||||
} else {
|
} else {
|
||||||
@@ -75,21 +75,21 @@ export class EmbassyPage {
|
|||||||
const modal = await this.modalController.create({
|
const modal = await this.modalController.create({
|
||||||
component: PasswordPage,
|
component: PasswordPage,
|
||||||
componentProps: {
|
componentProps: {
|
||||||
storageDrive: drive
|
storageDrive: drive,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
modal.onDidDismiss().then(async ret => {
|
modal.onDidDismiss().then(async ret => {
|
||||||
if (!ret.data || !ret.data.password) return
|
if (!ret.data || !ret.data.password) return
|
||||||
|
|
||||||
const loader = await this.loadingCtrl.create({
|
const loader = await this.loadingCtrl.create({
|
||||||
message: 'Setting up your Embassy!'
|
message: 'Setting up your Embassy!',
|
||||||
})
|
})
|
||||||
|
|
||||||
await loader.present()
|
await loader.present()
|
||||||
|
|
||||||
this.stateService.storageDrive = drive
|
this.stateService.storageDrive = drive
|
||||||
this.stateService.embassyPassword = ret.data.password
|
this.stateService.embassyPassword = ret.data.password
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.stateService.torAddress = (await this.stateService.setupEmbassy()).torAddress
|
this.stateService.torAddress = (await this.stateService.setupEmbassy()).torAddress
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -98,10 +98,10 @@ export class EmbassyPage {
|
|||||||
console.error(e.details)
|
console.error(e.details)
|
||||||
} finally {
|
} finally {
|
||||||
loader.dismiss()
|
loader.dismiss()
|
||||||
if(!!this.stateService.recoveryDrive) {
|
if (!!this.stateService.recoveryDrive) {
|
||||||
await this.navCtrl.navigateForward(`/loading`, { animationDirection: 'forward', animation: iosTransitionAnimation })
|
await this.navCtrl.navigateForward(`/loading`, { animationDirection: 'forward' })
|
||||||
} else {
|
} 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 { NgModule } from '@angular/core'
|
||||||
import { RouterModule, Routes } from '@angular/router';
|
import { RouterModule, Routes } from '@angular/router'
|
||||||
import { HomePage } from './home.page';
|
import { HomePage } from './home.page'
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
component: HomePage,
|
component: HomePage,
|
||||||
}
|
},
|
||||||
];
|
]
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [RouterModule.forChild(routes)],
|
imports: [RouterModule.forChild(routes)],
|
||||||
exports: [RouterModule]
|
exports: [RouterModule],
|
||||||
})
|
})
|
||||||
export class HomePageRoutingModule {}
|
export class HomePageRoutingModule { }
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core'
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common'
|
||||||
import { IonicModule } from '@ionic/angular';
|
import { IonicModule } from '@ionic/angular'
|
||||||
import { FormsModule } from '@angular/forms';
|
import { FormsModule } from '@angular/forms'
|
||||||
import { HomePage } from './home.page';
|
import { HomePage } from './home.page'
|
||||||
import { PasswordPageModule } from '../password/password.module';
|
import { PasswordPageModule } from '../password/password.module'
|
||||||
|
|
||||||
import { HomePageRoutingModule } from './home-routing.module';
|
import { HomePageRoutingModule } from './home-routing.module'
|
||||||
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
@@ -16,6 +16,6 @@ import { HomePageRoutingModule } from './home-routing.module';
|
|||||||
HomePageRoutingModule,
|
HomePageRoutingModule,
|
||||||
PasswordPageModule,
|
PasswordPageModule,
|
||||||
],
|
],
|
||||||
declarations: [HomePage]
|
declarations: [HomePage],
|
||||||
})
|
})
|
||||||
export class HomePageModule {}
|
export class HomePageModule { }
|
||||||
|
|||||||
@@ -11,11 +11,10 @@
|
|||||||
<ion-card-content class="ion-margin">
|
<ion-card-content class="ion-margin">
|
||||||
<!-- fresh -->
|
<!-- fresh -->
|
||||||
<ion-card
|
<ion-card
|
||||||
(click)="embassyNav()"
|
routerLink="/embassy"
|
||||||
button="true"
|
button="true"
|
||||||
color="light"
|
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-header>
|
||||||
<ion-card-title style="font-size: 40px;">Start Fresh</ion-card-title>
|
<ion-card-title style="font-size: 40px;">Start Fresh</ion-card-title>
|
||||||
@@ -25,11 +24,10 @@
|
|||||||
<!-- recover -->
|
<!-- recover -->
|
||||||
</ion-card>
|
</ion-card>
|
||||||
<ion-card
|
<ion-card
|
||||||
(click)="recoverNav()"
|
routerLink="/recover"
|
||||||
button="true"
|
button="true"
|
||||||
color="light"
|
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-header>
|
||||||
<ion-card-title style="font-size: 40px;">Recover</ion-card-title>
|
<ion-card-title style="font-size: 40px;">Recover</ion-card-title>
|
||||||
|
|||||||
@@ -1,22 +1,9 @@
|
|||||||
import { Component } from '@angular/core'
|
import { Component } from '@angular/core'
|
||||||
import { iosTransitionAnimation, NavController } from '@ionic/angular'
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-home',
|
selector: 'app-home',
|
||||||
templateUrl: 'home.page.html',
|
templateUrl: 'home.page.html',
|
||||||
styleUrls: ['home.page.scss'],
|
styleUrls: ['home.page.scss'],
|
||||||
})
|
})
|
||||||
export class HomePage {
|
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 })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core'
|
||||||
import { RouterModule, Routes } from '@angular/router';
|
import { RouterModule, Routes } from '@angular/router'
|
||||||
import { LoadingPage } from './loading.page';
|
import { LoadingPage } from './loading.page'
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
component: LoadingPage,
|
component: LoadingPage,
|
||||||
}
|
},
|
||||||
];
|
]
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [RouterModule.forChild(routes)],
|
imports: [RouterModule.forChild(routes)],
|
||||||
exports: [RouterModule]
|
exports: [RouterModule],
|
||||||
})
|
})
|
||||||
export class LoadingPageRoutingModule {}
|
export class LoadingPageRoutingModule { }
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core'
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common'
|
||||||
import { IonicModule } from '@ionic/angular';
|
import { IonicModule } from '@ionic/angular'
|
||||||
import { FormsModule } from '@angular/forms';
|
import { FormsModule } from '@angular/forms'
|
||||||
import { LoadingPage } from './loading.page';
|
import { LoadingPage } from './loading.page'
|
||||||
import { PasswordPageModule } from '../password/password.module';
|
import { PasswordPageModule } from '../password/password.module'
|
||||||
import { LoadingPageRoutingModule } from './loading-routing.module';
|
import { LoadingPageRoutingModule } from './loading-routing.module'
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
@@ -14,6 +14,6 @@ import { LoadingPageRoutingModule } from './loading-routing.module';
|
|||||||
LoadingPageRoutingModule,
|
LoadingPageRoutingModule,
|
||||||
PasswordPageModule,
|
PasswordPageModule,
|
||||||
],
|
],
|
||||||
declarations: [LoadingPage]
|
declarations: [LoadingPage],
|
||||||
})
|
})
|
||||||
export class LoadingPageModule {}
|
export class LoadingPageModule { }
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Component } from '@angular/core'
|
import { Component } from '@angular/core'
|
||||||
import { iosTransitionAnimation, NavController } from '@ionic/angular'
|
import { NavController } from '@ionic/angular'
|
||||||
import { StateService } from 'src/app/services/state.service'
|
import { StateService } from 'src/app/services/state.service'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@@ -8,20 +8,19 @@ import { StateService } from 'src/app/services/state.service'
|
|||||||
styleUrls: ['loading.page.scss'],
|
styleUrls: ['loading.page.scss'],
|
||||||
})
|
})
|
||||||
export class LoadingPage {
|
export class LoadingPage {
|
||||||
constructor(
|
constructor (
|
||||||
public stateService: StateService,
|
public stateService: StateService,
|
||||||
private navCtrl: NavController
|
private navCtrl: NavController,
|
||||||
) {}
|
) { }
|
||||||
|
|
||||||
ngOnInit () {
|
ngOnInit () {
|
||||||
this.stateService.pollDataTransferProgress()
|
this.stateService.pollDataTransferProgress()
|
||||||
const progSub = this.stateService.dataProgSubject.subscribe(async progress => {
|
const progSub = this.stateService.dataProgSubject.subscribe(async progress => {
|
||||||
if(progress === 1) {
|
if (progress === 1) {
|
||||||
progSub.unsubscribe()
|
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 { NgModule } from '@angular/core'
|
||||||
import { RouterModule, Routes } from '@angular/router';
|
import { RouterModule, Routes } from '@angular/router'
|
||||||
import { PasswordPage } from './password.page';
|
import { PasswordPage } from './password.page'
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
component: PasswordPage,
|
component: PasswordPage,
|
||||||
}
|
},
|
||||||
];
|
]
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [RouterModule.forChild(routes)],
|
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,
|
CommonModule,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
IonicModule,
|
IonicModule,
|
||||||
PasswordPageRoutingModule
|
PasswordPageRoutingModule,
|
||||||
],
|
],
|
||||||
declarations: [PasswordPage]
|
declarations: [PasswordPage],
|
||||||
})
|
})
|
||||||
export class PasswordPageModule {}
|
export class PasswordPageModule { }
|
||||||
|
|||||||
@@ -30,7 +30,6 @@
|
|||||||
[ngModelOptions]="{'standalone': true}"
|
[ngModelOptions]="{'standalone': true}"
|
||||||
[type]="!unmasked1 ? 'password' : 'text'"
|
[type]="!unmasked1 ? 'password' : 'text'"
|
||||||
placeholder="Enter Password"
|
placeholder="Enter Password"
|
||||||
debounce="500"
|
|
||||||
(ionChange)="validate()"
|
(ionChange)="validate()"
|
||||||
maxlength="64"
|
maxlength="64"
|
||||||
></ion-input>
|
></ion-input>
|
||||||
@@ -49,7 +48,6 @@
|
|||||||
[(ngModel)]="passwordVer"
|
[(ngModel)]="passwordVer"
|
||||||
[ngModelOptions]="{'standalone': true}"
|
[ngModelOptions]="{'standalone': true}"
|
||||||
[type]="!unmasked2 ? 'password' : 'text'"
|
[type]="!unmasked2 ? 'password' : 'text'"
|
||||||
debounce="500"
|
|
||||||
(ionChange)="checkVer()"
|
(ionChange)="checkVer()"
|
||||||
maxlength="64"
|
maxlength="64"
|
||||||
placeholder="Retype Password"
|
placeholder="Retype Password"
|
||||||
|
|||||||
@@ -21,13 +21,13 @@ export class PasswordPage {
|
|||||||
|
|
||||||
hasData: boolean
|
hasData: boolean
|
||||||
|
|
||||||
constructor(
|
constructor (
|
||||||
private modalController: ModalController,
|
private modalController: ModalController,
|
||||||
private apiService: ApiService,
|
private apiService: ApiService,
|
||||||
private loadingCtrl: LoadingController,
|
private loadingCtrl: LoadingController,
|
||||||
) {}
|
) { }
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit () {
|
||||||
if (this.storageDrive && this.storageDrive.partitions.find(p => p.used)) {
|
if (this.storageDrive && this.storageDrive.partitions.find(p => p.used)) {
|
||||||
this.hasData = true
|
this.hasData = true
|
||||||
}
|
}
|
||||||
@@ -36,16 +36,16 @@ export class PasswordPage {
|
|||||||
async verifyPw () {
|
async verifyPw () {
|
||||||
if (!this.recoveryDrive) this.pwError = 'No recovery drive' // unreachable
|
if (!this.recoveryDrive) this.pwError = 'No recovery drive' // unreachable
|
||||||
const loader = await this.loadingCtrl.create({
|
const loader = await this.loadingCtrl.create({
|
||||||
message: 'Verifying Password'
|
message: 'Verifying Password',
|
||||||
})
|
})
|
||||||
await loader.present()
|
await loader.present()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const isCorrectPassword = await this.apiService.verifyRecoveryPassword(this.recoveryDrive.logicalname, this.password)
|
const isCorrectPassword = await this.apiService.verify03XPassword(this.recoveryDrive.logicalname, this.password)
|
||||||
if(isCorrectPassword) {
|
if (isCorrectPassword) {
|
||||||
this.modalController.dismiss({ password: this.password })
|
this.modalController.dismiss({ password: this.password })
|
||||||
} else {
|
} else {
|
||||||
this.pwError = "Incorrect password provided"
|
this.pwError = 'Incorrect password provided'
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.pwError = 'Error connecting to Embassy'
|
this.pwError = 'Error connecting to Embassy'
|
||||||
@@ -57,29 +57,29 @@ export class PasswordPage {
|
|||||||
async submitPw () {
|
async submitPw () {
|
||||||
this.validate()
|
this.validate()
|
||||||
if (this.password !== this.passwordVer) {
|
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 })
|
this.modalController.dismiss({ password: this.password })
|
||||||
}
|
}
|
||||||
|
|
||||||
validate () {
|
validate () {
|
||||||
if(!!this.recoveryDrive) return this.pwError = ''
|
if (!!this.recoveryDrive) return this.pwError = ''
|
||||||
|
|
||||||
if (this.passwordVer) {
|
if (this.passwordVer) {
|
||||||
this.checkVer()
|
this.checkVer()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.password.length < 12) {
|
if (this.password.length < 12) {
|
||||||
this.pwError="*password must be 12 characters or greater"
|
this.pwError = '*password must be 12 characters or greater'
|
||||||
} else {
|
} else {
|
||||||
this.pwError = ''
|
this.pwError = ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
checkVer () {
|
checkVer () {
|
||||||
this.verError = this.password !== this.passwordVer ? "*passwords do not match" : ''
|
this.verError = this.password !== this.passwordVer ? '*passwords do not match' : ''
|
||||||
}
|
}
|
||||||
|
|
||||||
cancel () {
|
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 { NgModule } from '@angular/core'
|
||||||
import { RouterModule, Routes } from '@angular/router';
|
import { RouterModule, Routes } from '@angular/router'
|
||||||
import { ProductKeyPage } from './product-key.page';
|
import { ProductKeyPage } from './product-key.page'
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
component: ProductKeyPage,
|
component: ProductKeyPage,
|
||||||
}
|
},
|
||||||
];
|
]
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [RouterModule.forChild(routes)],
|
imports: [RouterModule.forChild(routes)],
|
||||||
exports: [RouterModule]
|
exports: [RouterModule],
|
||||||
})
|
})
|
||||||
export class ProductKeyPageRoutingModule {}
|
export class ProductKeyPageRoutingModule { }
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core'
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common'
|
||||||
import { IonicModule } from '@ionic/angular';
|
import { IonicModule } from '@ionic/angular'
|
||||||
import { FormsModule } from '@angular/forms';
|
import { FormsModule } from '@angular/forms'
|
||||||
import { ProductKeyPage } from './product-key.page';
|
import { ProductKeyPage } from './product-key.page'
|
||||||
import { PasswordPageModule } from '../password/password.module';
|
import { PasswordPageModule } from '../password/password.module'
|
||||||
import { ProductKeyPageRoutingModule } from './product-key-routing.module';
|
import { ProductKeyPageRoutingModule } from './product-key-routing.module'
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
@@ -14,6 +14,6 @@ import { ProductKeyPageRoutingModule } from './product-key-routing.module';
|
|||||||
ProductKeyPageRoutingModule,
|
ProductKeyPageRoutingModule,
|
||||||
PasswordPageModule,
|
PasswordPageModule,
|
||||||
],
|
],
|
||||||
declarations: [ProductKeyPage]
|
declarations: [ProductKeyPage],
|
||||||
})
|
})
|
||||||
export class ProductKeyPageModule {}
|
export class ProductKeyPageModule { }
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Component } from '@angular/core'
|
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 { ApiService } from 'src/app/services/api/api.service'
|
||||||
import { HttpService } from 'src/app/services/api/http.service'
|
import { HttpService } from 'src/app/services/api/http.service'
|
||||||
import { StateService } from 'src/app/services/state.service'
|
import { StateService } from 'src/app/services/state.service'
|
||||||
@@ -13,35 +13,32 @@ export class ProductKeyPage {
|
|||||||
productKey: string
|
productKey: string
|
||||||
error: string
|
error: string
|
||||||
|
|
||||||
constructor(
|
constructor (
|
||||||
private readonly navCtrl: NavController,
|
private readonly navCtrl: NavController,
|
||||||
private readonly stateService: StateService,
|
private readonly stateService: StateService,
|
||||||
private readonly apiService: ApiService,
|
private readonly apiService: ApiService,
|
||||||
private readonly loadingCtrl: LoadingController,
|
private readonly loadingCtrl: LoadingController,
|
||||||
private readonly httpService: HttpService
|
private readonly httpService: HttpService,
|
||||||
) {}
|
) { }
|
||||||
|
|
||||||
async submit () {
|
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({
|
const loader = await this.loadingCtrl.create({
|
||||||
message: 'Verifying Product Key'
|
message: 'Verifying Product Key',
|
||||||
})
|
})
|
||||||
await loader.present()
|
await loader.present()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.httpService.productKey = this.productKey
|
this.httpService.productKey = this.productKey
|
||||||
const state = await this.apiService.verifyProductKey()
|
await this.apiService.verifyProductKey()
|
||||||
if(state['is-recovering']) {
|
if (this.stateService.isMigrating) {
|
||||||
await this.navCtrl.navigateForward(`/loading`, { animationDirection: 'forward', animation: iosTransitionAnimation })
|
await this.navCtrl.navigateForward(`/loading`)
|
||||||
} else if (!!state['tor-address']) {
|
|
||||||
this.stateService.torAddress = state['tor-address']
|
|
||||||
await this.navCtrl.navigateForward(`/success`, { animationDirection: 'forward', animation: iosTransitionAnimation })
|
|
||||||
} else {
|
} else {
|
||||||
await this.navCtrl.navigateForward(`/home`, { animationDirection: 'forward', animation: iosTransitionAnimation })
|
await this.navCtrl.navigateForward(`/home`)
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.error = e.message
|
this.error = 'Invalid Product Key'
|
||||||
this.httpService.productKey = undefined
|
this.httpService.productKey = undefined
|
||||||
} finally {
|
} finally {
|
||||||
loader.dismiss()
|
loader.dismiss()
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core'
|
||||||
import { RouterModule, Routes } from '@angular/router';
|
import { RouterModule, Routes } from '@angular/router'
|
||||||
import { RecoverPage } from './recover.page';
|
import { RecoverPage } from './recover.page'
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
component: RecoverPage,
|
component: RecoverPage,
|
||||||
}
|
},
|
||||||
];
|
]
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [RouterModule.forChild(routes)],
|
imports: [RouterModule.forChild(routes)],
|
||||||
exports: [RouterModule]
|
exports: [RouterModule],
|
||||||
})
|
})
|
||||||
export class RecoverPageRoutingModule {}
|
export class RecoverPageRoutingModule { }
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core'
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common'
|
||||||
import { IonicModule } from '@ionic/angular';
|
import { IonicModule } from '@ionic/angular'
|
||||||
import { FormsModule } from '@angular/forms';
|
import { FormsModule } from '@angular/forms'
|
||||||
import { RecoverPage } from './recover.page';
|
import { RecoverPage } from './recover.page'
|
||||||
import { PasswordPageModule } from '../password/password.module';
|
import { PasswordPageModule } from '../password/password.module'
|
||||||
import { RecoverPageRoutingModule } from './recover-routing.module';
|
import { ProdKeyModalModule } from '../prod-key-modal/prod-key-modal.module'
|
||||||
import { PipesModule } from 'src/app/pipes/pipe.module';
|
import { RecoverPageRoutingModule } from './recover-routing.module'
|
||||||
|
import { PipesModule } from 'src/app/pipes/pipe.module'
|
||||||
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
@@ -15,8 +16,9 @@ import { PipesModule } from 'src/app/pipes/pipe.module';
|
|||||||
IonicModule,
|
IonicModule,
|
||||||
RecoverPageRoutingModule,
|
RecoverPageRoutingModule,
|
||||||
PasswordPageModule,
|
PasswordPageModule,
|
||||||
|
ProdKeyModalModule,
|
||||||
PipesModule,
|
PipesModule,
|
||||||
],
|
],
|
||||||
declarations: [RecoverPage]
|
declarations: [RecoverPage],
|
||||||
})
|
})
|
||||||
export class RecoverPageModule {}
|
export class RecoverPageModule { }
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
<ion-card color="dark">
|
<ion-card color="dark">
|
||||||
<ion-card-header class="ion-text-center" style="padding-bottom: 8px;">
|
<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-subtitle>Select the drive containing the Embassy you want to recover.</ion-card-subtitle>
|
||||||
</ion-card-header>
|
</ion-card-header>
|
||||||
|
|
||||||
@@ -50,17 +50,17 @@
|
|||||||
<span *ngIf="drive.vendor && drive.model"> - </span>
|
<span *ngIf="drive.vendor && drive.model"> - </span>
|
||||||
{{ drive.model }}
|
{{ drive.model }}
|
||||||
</h2>
|
</h2>
|
||||||
<h2> Embassy version: {{drive['embassy_os'].version}}</h2>
|
<h2> Embassy version: {{drive['embassy-os'].version}}</h2>
|
||||||
</ion-label>
|
</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') && 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') && !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 && !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>
|
</ion-item>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ion-button
|
<ion-button
|
||||||
(click)="selectRecoveryDrive()"
|
(click)="selectRecoveryDrive()"
|
||||||
color="light"
|
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"
|
class="claim-button"
|
||||||
*ngIf="recoveryDrives.length"
|
*ngIf="recoveryDrives.length"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import { Component } from '@angular/core'
|
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 { ApiService, DiskInfo } 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 { PasswordPage } from '../password/password.page'
|
import { PasswordPage } from '../password/password.page'
|
||||||
|
import { ProdKeyModal } from '../prod-key-modal/prod-key-modal.page'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-recover',
|
selector: 'app-recover',
|
||||||
@@ -11,18 +12,19 @@ import { PasswordPage } from '../password/password.page'
|
|||||||
styleUrls: ['recover.page.scss'],
|
styleUrls: ['recover.page.scss'],
|
||||||
})
|
})
|
||||||
export class RecoverPage {
|
export class RecoverPage {
|
||||||
passwords = {}
|
passwords = { }
|
||||||
|
prodKeys = { }
|
||||||
recoveryDrives = []
|
recoveryDrives = []
|
||||||
selectedDrive: DiskInfo = null
|
selectedDrive: DiskInfo = null
|
||||||
loading = true
|
loading = true
|
||||||
|
|
||||||
constructor(
|
constructor (
|
||||||
private readonly apiService: ApiService,
|
private readonly apiService: ApiService,
|
||||||
private readonly navCtrl: NavController,
|
private readonly navCtrl: NavController,
|
||||||
private readonly modalController: ModalController,
|
private readonly modalController: ModalController,
|
||||||
private readonly stateService: StateService,
|
readonly stateService: StateService,
|
||||||
private readonly errorToastService: ErrorToastService,
|
private readonly errorToastService: ErrorToastService,
|
||||||
) {}
|
) { }
|
||||||
|
|
||||||
async ngOnInit () {
|
async ngOnInit () {
|
||||||
await this.getDrives()
|
await this.getDrives()
|
||||||
@@ -37,7 +39,14 @@ export class RecoverPage {
|
|||||||
|
|
||||||
async getDrives () {
|
async getDrives () {
|
||||||
try {
|
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) {
|
} catch (e) {
|
||||||
this.errorToastService.present(`${e.message}: ${e.data}`)
|
this.errorToastService.present(`${e.message}: ${e.data}`)
|
||||||
} finally {
|
} finally {
|
||||||
@@ -45,7 +54,7 @@ export class RecoverPage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async chooseDrive(drive: DiskInfo) {
|
async chooseDrive (drive: DiskInfo) {
|
||||||
|
|
||||||
if (this.selectedDrive?.logicalname === drive.logicalname) {
|
if (this.selectedDrive?.logicalname === drive.logicalname) {
|
||||||
this.selectedDrive = null
|
this.selectedDrive = null
|
||||||
@@ -54,32 +63,51 @@ export class RecoverPage {
|
|||||||
this.selectedDrive = drive
|
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({
|
if (this.stateService.hasProductKey) {
|
||||||
component: PasswordPage,
|
const modal = await this.modalController.create({
|
||||||
componentProps: {
|
component: PasswordPage,
|
||||||
recoveryDrive: this.selectedDrive
|
componentProps: {
|
||||||
},
|
recoveryDrive: this.selectedDrive,
|
||||||
cssClass: 'alertlike-modal',
|
},
|
||||||
})
|
cssClass: 'alertlike-modal',
|
||||||
modal.onDidDismiss().then(async ret => {
|
})
|
||||||
if (!ret.data) {
|
modal.onDidDismiss().then(async ret => {
|
||||||
this.selectedDrive = null
|
if (!ret.data) {
|
||||||
} else if(ret.data.password) {
|
this.selectedDrive = null
|
||||||
this.passwords[drive.logicalname] = ret.data.password
|
} else if (ret.data.password) {
|
||||||
}
|
this.passwords[drive.logicalname] = ret.data.password
|
||||||
|
}
|
||||||
})
|
|
||||||
await modal.present();
|
})
|
||||||
|
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
|
this.stateService.recoveryDrive = this.selectedDrive
|
||||||
const pw = this.passwords[this.selectedDrive.logicalname]
|
const pw = this.passwords[this.selectedDrive.logicalname]
|
||||||
if(pw) {
|
if (pw) {
|
||||||
this.stateService.recoveryPassword = 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 { NgModule } from '@angular/core'
|
||||||
import { RouterModule, Routes } from '@angular/router';
|
import { RouterModule, Routes } from '@angular/router'
|
||||||
import { SuccessPage } from './success.page';
|
import { SuccessPage } from './success.page'
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
component: SuccessPage,
|
component: SuccessPage,
|
||||||
}
|
},
|
||||||
];
|
]
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [RouterModule.forChild(routes)],
|
imports: [RouterModule.forChild(routes)],
|
||||||
exports: [RouterModule]
|
exports: [RouterModule],
|
||||||
})
|
})
|
||||||
export class SuccessPageRoutingModule {}
|
export class SuccessPageRoutingModule { }
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core'
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common'
|
||||||
import { IonicModule } from '@ionic/angular';
|
import { IonicModule } from '@ionic/angular'
|
||||||
import { FormsModule } from '@angular/forms';
|
import { FormsModule } from '@angular/forms'
|
||||||
import { SuccessPage } from './success.page';
|
import { SuccessPage } from './success.page'
|
||||||
import { PasswordPageModule } from '../password/password.module';
|
import { PasswordPageModule } from '../password/password.module'
|
||||||
|
|
||||||
import { SuccessPageRoutingModule } from './success-routing.module';
|
import { SuccessPageRoutingModule } from './success-routing.module'
|
||||||
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
@@ -16,6 +16,6 @@ import { SuccessPageRoutingModule } from './success-routing.module';
|
|||||||
SuccessPageRoutingModule,
|
SuccessPageRoutingModule,
|
||||||
PasswordPageModule,
|
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'],
|
styleUrls: ['success.page.scss'],
|
||||||
})
|
})
|
||||||
export class SuccessPage {
|
export class SuccessPage {
|
||||||
constructor(
|
constructor (
|
||||||
public stateService: StateService,
|
private readonly toastCtrl: ToastController,
|
||||||
private toastCtrl: ToastController
|
public readonly stateService: StateService,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
window = window
|
window = window
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core'
|
||||||
import { ConvertBytesPipe } from './convert-bytes.pipe';
|
import { ConvertBytesPipe } from './convert-bytes.pipe'
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [ConvertBytesPipe],
|
declarations: [ConvertBytesPipe],
|
||||||
@@ -7,4 +7,4 @@ imports: [],
|
|||||||
exports: [ConvertBytesPipe],
|
exports: [ConvertBytesPipe],
|
||||||
})
|
})
|
||||||
|
|
||||||
export class PipesModule {}
|
export class PipesModule { }
|
||||||
@@ -1,30 +1,26 @@
|
|||||||
import { Subject } from 'rxjs'
|
|
||||||
|
|
||||||
export abstract class ApiService {
|
export abstract class ApiService {
|
||||||
abstract verifyProductKey (): Promise<VerifyProductKeyRes>;
|
// unencrypted
|
||||||
abstract getDrives (): Promise<DiskInfo[]>;
|
abstract getStatus (): Promise<GetStatusRes> // setup.status
|
||||||
abstract getDataTransferProgress (): Promise<TransferProgressRes>;
|
abstract getDrives (): Promise<DiskInfo[]> // setup.disk.list
|
||||||
abstract verifyRecoveryPassword (logicalname: string, password: string): Promise<boolean>;
|
abstract set02XDrive (logicalname: string): Promise<void> // setup.recovery.v2.set
|
||||||
abstract setupEmbassy (setupInfo: {
|
abstract getRecoveryStatus (): Promise<RecoveryStatusRes> // setup.recovery.status
|
||||||
'embassy-logicalname': string,
|
|
||||||
'embassy-password': string
|
// encrypted
|
||||||
'recovery-logicalname'?: string,
|
abstract verifyProductKey (): Promise<void> // echo - throws error if invalid
|
||||||
'recovery-password'?: string
|
abstract verify03XPassword (logicalname: string, password: string): Promise<boolean> // setup.recovery.test-password
|
||||||
}): Promise<SetupEmbassyRes>
|
abstract setupEmbassy (setupInfo: SetupEmbassyReq): Promise<string> // setup.execute
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface VerifyProductKeyRes {
|
export interface GetStatusRes {
|
||||||
"is-recovering": boolean
|
'product-key': boolean
|
||||||
"tor-address": string
|
migrating: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TransferProgressRes {
|
export interface SetupEmbassyReq {
|
||||||
'bytes-transfered': number;
|
'embassy-logicalname': string
|
||||||
'total-bytes': number;
|
'embassy-password': string
|
||||||
}
|
'recovery-logicalname'?: string
|
||||||
|
'recovery-password'?: string
|
||||||
export interface SetupEmbassyRes {
|
|
||||||
"tor-address": string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DiskInfo {
|
export interface DiskInfo {
|
||||||
@@ -33,7 +29,12 @@ export interface DiskInfo {
|
|||||||
model: string | null,
|
model: string | null,
|
||||||
partitions: PartitionInfo[],
|
partitions: PartitionInfo[],
|
||||||
capacity: number,
|
capacity: number,
|
||||||
embassy_os: EmbassyOsDiskInfo | null,
|
'embassy-os': EmbassyOsDiskInfo | null,
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RecoveryStatusRes {
|
||||||
|
'bytes-transferred': number
|
||||||
|
'total-bytes': number
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PartitionInfo {
|
interface PartitionInfo {
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { Injectable } from '@angular/core'
|
import { Injectable } from '@angular/core'
|
||||||
import { HttpClient, HttpErrorResponse, HttpHeaders, HttpParams } from '@angular/common/http'
|
import { HttpClient, HttpErrorResponse, HttpHeaders, HttpParams } from '@angular/common/http'
|
||||||
import { Observable, from, interval, race } from 'rxjs'
|
import { Observable } from 'rxjs'
|
||||||
import { map, take } from 'rxjs/operators'
|
|
||||||
import * as aesjs from 'aes-js'
|
import * as aesjs from 'aes-js'
|
||||||
import * as pbkdf2 from 'pbkdf2'
|
import * as pbkdf2 from 'pbkdf2'
|
||||||
|
|
||||||
@@ -19,7 +18,7 @@ export class HttpService {
|
|||||||
this.fullUrl = `${window.location.protocol}//${window.location.hostname}:${port}/rpc/v1`
|
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 = {
|
const httpOpts = {
|
||||||
method: Method.POST,
|
method: Method.POST,
|
||||||
@@ -27,14 +26,20 @@ export class HttpService {
|
|||||||
url: this.fullUrl,
|
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 (isRpcError(res)) throw new RpcError(res.error)
|
||||||
|
|
||||||
if (isRpcSuccess(res)) return res.result
|
if (isRpcSuccess(res)) return res.result
|
||||||
}
|
}
|
||||||
|
|
||||||
async httpRequest<T> (httpOpts: {
|
async encryptedHttpRequest<T> (httpOpts: {
|
||||||
body: RPCOptions;
|
body: RPCOptions;
|
||||||
url: string;
|
url: string;
|
||||||
}): Promise<T> {
|
}): Promise<T> {
|
||||||
@@ -53,7 +58,7 @@ export class HttpService {
|
|||||||
|
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Encoding': 'aesctr256',
|
'Content-Encoding': 'aesctr256',
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
} as any
|
} 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 {
|
function RpcError (e: RPCError['error']): void {
|
||||||
@@ -167,13 +197,6 @@ export interface HttpOptions {
|
|||||||
timeout?: number
|
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 = {
|
type AES_CTR = {
|
||||||
encryptPbkdf2: (secretKey: string, messageBuffer: Uint8Array) => Promise<Uint8Array>
|
encryptPbkdf2: (secretKey: string, messageBuffer: Uint8Array) => Promise<Uint8Array>
|
||||||
decryptPbkdf2: (secretKey, arr: ArrayBuffer) => Promise<string>
|
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 salt = window.crypto.getRandomValues(new Uint8Array(16))
|
||||||
const counter = 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 aesCtr = new aesjs.ModeOfOperation.ctr(key, new aesjs.Counter(counter))
|
||||||
const encryptedBytes = aesCtr.encrypt(messageBuffer);
|
const encryptedBytes = aesCtr.encrypt(messageBuffer)
|
||||||
return new Uint8Array([...counter, ...salt, ...encryptedBytes])
|
return new Uint8Array([...counter, ...salt, ...encryptedBytes])
|
||||||
},
|
},
|
||||||
decryptPbkdf2: async (secretKey: string, arr: ArrayBuffer) => {
|
decryptPbkdf2: async (secretKey: string, arr: ArrayBuffer) => {
|
||||||
@@ -196,12 +219,12 @@ export const AES_CTR: AES_CTR = {
|
|||||||
const salt = buff.slice(16, 32)
|
const salt = buff.slice(16, 32)
|
||||||
|
|
||||||
const cipher = buff.slice(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 aesCtr = new aesjs.ModeOfOperation.ctr(key, new aesjs.Counter(counter))
|
||||||
const decryptedBytes = aesCtr.decrypt(cipher);
|
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 {
|
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 { 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'
|
import { HttpService } from './http.service'
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root',
|
||||||
})
|
})
|
||||||
export class LiveApiService extends ApiService {
|
export class LiveApiService extends ApiService {
|
||||||
|
|
||||||
constructor(
|
constructor (
|
||||||
private readonly http: HttpService
|
private readonly http: HttpService,
|
||||||
) { super() }
|
) { super() }
|
||||||
|
|
||||||
async verifyProductKey () {
|
// ** UNENCRYPTED **
|
||||||
return this.http.rpcRequest<VerifyProductKeyRes>({
|
|
||||||
method: 'setup.status',
|
|
||||||
params: {}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async getDataTransferProgress () {
|
async getStatus () {
|
||||||
return this.http.rpcRequest<TransferProgressRes>({
|
return this.http.rpcRequest<GetStatusRes>({
|
||||||
method: 'setup.recovery.status',
|
method: 'setup.status',
|
||||||
params: {}
|
params: { },
|
||||||
})
|
}, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
async getDrives () {
|
async getDrives () {
|
||||||
return this.http.rpcRequest<DiskInfo[]>({
|
return this.http.rpcRequest<DiskInfo[]>({
|
||||||
method: 'setup.disk.list',
|
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>({
|
return this.http.rpcRequest<boolean>({
|
||||||
method: 'setup.recovery.test-password',
|
method: 'setup.recovery.test-password',
|
||||||
params: {logicalname, password}
|
params: { logicalname, password },
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async setupEmbassy (setupInfo: {
|
async setupEmbassy (setupInfo: SetupEmbassyReq) {
|
||||||
'embassy-logicalname': string,
|
return this.http.rpcRequest<string>({
|
||||||
'embassy-password': string
|
|
||||||
'recovery-logicalname'?: string,
|
|
||||||
'recovery-password'?: string
|
|
||||||
}) {
|
|
||||||
return this.http.rpcRequest<SetupEmbassyRes>({
|
|
||||||
method: 'setup.execute',
|
method: 'setup.execute',
|
||||||
params: setupInfo
|
params: setupInfo as any,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,33 +1,30 @@
|
|||||||
import { Injectable } from '@angular/core'
|
import { Injectable } from '@angular/core'
|
||||||
import { pauseFor } from '../state.service'
|
import { pauseFor } from '../state.service'
|
||||||
import { ApiService } from './api.service'
|
import { ApiService, SetupEmbassyReq } from './api.service'
|
||||||
|
|
||||||
|
let tries = 0
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root',
|
||||||
})
|
})
|
||||||
export class MockApiService extends ApiService {
|
export class MockApiService extends ApiService {
|
||||||
|
|
||||||
constructor() {
|
constructor () {
|
||||||
super()
|
super()
|
||||||
}
|
}
|
||||||
|
|
||||||
async verifyProductKey () {
|
// ** UNENCRYPTED **
|
||||||
await pauseFor(2000)
|
|
||||||
return {
|
|
||||||
"is-recovering": false,
|
|
||||||
"tor-address": null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async getDataTransferProgress () {
|
async getStatus () {
|
||||||
tries = Math.min(tries + 1, 4)
|
await pauseFor(1000)
|
||||||
return {
|
return {
|
||||||
'bytes-transfered': tries,
|
'product-key': true,
|
||||||
'total-bytes': 4
|
migrating: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async getDrives () {
|
async getDrives () {
|
||||||
|
await pauseFor(1000)
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
vendor: 'Vendor',
|
vendor: 'Vendor',
|
||||||
@@ -38,17 +35,17 @@ export class MockApiService extends ApiService {
|
|||||||
logicalname: 'sda1',
|
logicalname: 'sda1',
|
||||||
label: 'label 1',
|
label: 'label 1',
|
||||||
capacity: 100000,
|
capacity: 100000,
|
||||||
used: 200.1255312
|
used: 200.1255312,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
logicalname: 'sda2',
|
logicalname: 'sda2',
|
||||||
label: 'label 2',
|
label: 'label 2',
|
||||||
capacity: 50000,
|
capacity: 50000,
|
||||||
used: 200.1255312
|
used: 200.1255312,
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
capacity: 150000,
|
capacity: 150000,
|
||||||
'embassy_os': null
|
'embassy-os': null,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
vendor: 'Vendor',
|
vendor: 'Vendor',
|
||||||
@@ -63,7 +60,7 @@ export class MockApiService extends ApiService {
|
|||||||
// }
|
// }
|
||||||
],
|
],
|
||||||
capacity: 1600.01234,
|
capacity: 1600.01234,
|
||||||
'embassy_os': null
|
'embassy-os': null,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
vendor: 'Vendor',
|
vendor: 'Vendor',
|
||||||
@@ -74,64 +71,77 @@ export class MockApiService extends ApiService {
|
|||||||
logicalname: 'sdc1',
|
logicalname: 'sdc1',
|
||||||
label: 'label 1',
|
label: 'label 1',
|
||||||
capacity: null,
|
capacity: null,
|
||||||
used: null
|
used: null,
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
capacity: 100000,
|
capacity: 100000,
|
||||||
'embassy_os': {
|
'embassy-os': {
|
||||||
version: '0.3.3',
|
version: '0.3.3',
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
vendor: 'Vendor',
|
vendor: 'Vendor',
|
||||||
model: 'Model',
|
model: 'Model',
|
||||||
logicalname: '/dev/sdd',
|
logicalname: '/dev/sdd',
|
||||||
partitions: [
|
partitions: [
|
||||||
{
|
{
|
||||||
logicalname: 'sdd1',
|
logicalname: 'sdd1',
|
||||||
label: null,
|
label: null,
|
||||||
capacity: 10000,
|
capacity: 10000,
|
||||||
used: null
|
used: null,
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
capacity: 10000,
|
capacity: 10000,
|
||||||
'embassy_os': {
|
'embassy-os': {
|
||||||
version: '0.2.7',
|
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 () {
|
async getRecoveryDrives () {
|
||||||
await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
logicalname: 'Name1',
|
logicalname: 'Name1',
|
||||||
version: '0.3.3',
|
version: '0.3.3',
|
||||||
name: 'My Embassy'
|
name: 'My Embassy',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
logicalname: 'Name2',
|
logicalname: 'Name2',
|
||||||
version: '0.2.7',
|
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 { Injectable } from '@angular/core'
|
||||||
import { BehaviorSubject } from 'rxjs';
|
import { BehaviorSubject } from 'rxjs'
|
||||||
import { ApiService, DiskInfo } from './api/api.service'
|
import { ApiService, DiskInfo } from './api/api.service'
|
||||||
import { ErrorToastService } from './error-toast.service';
|
import { ErrorToastService } from './error-toast.service'
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root',
|
||||||
})
|
})
|
||||||
export class StateService {
|
export class StateService {
|
||||||
|
hasProductKey: boolean
|
||||||
|
isMigrating: boolean
|
||||||
|
|
||||||
polling = false
|
polling = false
|
||||||
|
|
||||||
storageDrive: DiskInfo;
|
storageDrive: DiskInfo
|
||||||
embassyPassword: string
|
embassyPassword: string
|
||||||
recoveryDrive: DiskInfo;
|
recoveryDrive: DiskInfo
|
||||||
recoveryPassword: string
|
recoveryPassword: string
|
||||||
dataTransferProgress: { bytesTransfered: number; totalBytes: number } | null;
|
dataTransferProgress: { bytesTransferred: number; totalBytes: number } | null
|
||||||
dataProgress = 0;
|
dataProgress = 0
|
||||||
dataProgSubject = new BehaviorSubject(this.dataProgress)
|
dataProgSubject = new BehaviorSubject(this.dataProgress)
|
||||||
|
|
||||||
torAddress: string
|
torAddress: string
|
||||||
|
|
||||||
constructor(
|
constructor (
|
||||||
private readonly apiService: ApiService,
|
private readonly apiService: ApiService,
|
||||||
private readonly errorToastService: ErrorToastService
|
private readonly errorToastService: ErrorToastService,
|
||||||
) {}
|
) { }
|
||||||
|
|
||||||
async pollDataTransferProgress(callback?: () => void) {
|
async pollDataTransferProgress (callback?: () => void) {
|
||||||
this.polling = true
|
this.polling = true
|
||||||
await pauseFor(1000)
|
await pauseFor(1000)
|
||||||
|
|
||||||
if (
|
if (
|
||||||
this.dataTransferProgress?.totalBytes &&
|
this.dataTransferProgress?.totalBytes &&
|
||||||
this.dataTransferProgress.bytesTransfered === this.dataTransferProgress.totalBytes
|
this.dataTransferProgress.bytesTransferred === this.dataTransferProgress.totalBytes
|
||||||
) {return }
|
) return
|
||||||
|
|
||||||
|
|
||||||
let progress
|
|
||||||
|
let progress
|
||||||
try {
|
try {
|
||||||
progress =await this.apiService.getDataTransferProgress()
|
progress = await this.apiService.getRecoveryStatus()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.errorToastService.present(`${e.message}: ${e.details}`)
|
this.errorToastService.present(`${e.message}: ${e.details}`)
|
||||||
}
|
}
|
||||||
if (progress) {
|
if (progress) {
|
||||||
this.dataTransferProgress = {
|
this.dataTransferProgress = {
|
||||||
bytesTransfered: progress['bytes-transfered'],
|
bytesTransferred: progress['bytes-transferred'],
|
||||||
totalBytes: progress['total-bytes']
|
totalBytes: progress['total-bytes'],
|
||||||
}
|
}
|
||||||
if (this.dataTransferProgress.totalBytes) {
|
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)
|
this.dataProgSubject.next(this.dataProgress)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -58,13 +61,13 @@ export class StateService {
|
|||||||
'embassy-logicalname': this.storageDrive.logicalname,
|
'embassy-logicalname': this.storageDrive.logicalname,
|
||||||
'embassy-password': this.embassyPassword,
|
'embassy-password': this.embassyPassword,
|
||||||
'recovery-logicalname': this.recoveryDrive?.logicalname,
|
'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) => {
|
export const pauseFor = (ms: number) => {
|
||||||
const promise = new Promise(resolve => setTimeout(resolve, ms))
|
const promise = new Promise(resolve => setTimeout(resolve, ms))
|
||||||
return promise
|
return promise
|
||||||
};
|
}
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
export const environment = {
|
export const environment = {
|
||||||
production: true
|
production: true,
|
||||||
};
|
}
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
// The list of file replacements can be found in `angular.json`.
|
// The list of file replacements can be found in `angular.json`.
|
||||||
|
|
||||||
export const environment = {
|
export const environment = {
|
||||||
production: false
|
production: false,
|
||||||
};
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* For easier debugging in development mode, you can import the following file
|
* For easier debugging in development mode, you can import the following file
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { enableProdMode } from '@angular/core';
|
import { enableProdMode } from '@angular/core'
|
||||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'
|
||||||
|
|
||||||
import { AppModule } from './app/app.module';
|
import { AppModule } from './app/app.module'
|
||||||
import { environment } from './environments/environment';
|
import { environment } from './environments/environment'
|
||||||
|
|
||||||
if (environment.production) {
|
if (environment.production) {
|
||||||
enableProdMode();
|
enableProdMode()
|
||||||
}
|
}
|
||||||
|
|
||||||
platformBrowserDynamic().bootstrapModule(AppModule)
|
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.
|
* 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 **/
|
/** Ionic CSS Variables **/
|
||||||
:root {
|
:root {
|
||||||
--ion-font-family: 'Benton Sans';
|
--ion-font-family: 'Benton Sans';
|
||||||
|
--ion-text-color: var(--ion-color-dark);
|
||||||
|
--ion-text-color-rgb: var(--ion-color-dark-rgb);
|
||||||
/** primary **/
|
/** primary **/
|
||||||
--ion-color-primary: #428cff;
|
--ion-color-primary: #428cff;
|
||||||
--ion-color-primary-rgb: 66,140,255;
|
--ion-color-primary-rgb: 66,140,255;
|
||||||
|
|||||||
@@ -3,4 +3,4 @@
|
|||||||
* running with certain Web Component callbacks
|
* running with certain Web Component callbacks
|
||||||
*/
|
*/
|
||||||
// eslint-disable-next-line no-underscore-dangle
|
// 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