From 61ee46f289893f920eb584f732286887992ae0f7 Mon Sep 17 00:00:00 2001 From: Drew Ansbacher Date: Sun, 27 Feb 2022 19:07:43 -0700 Subject: [PATCH] no init setup complete fix and minor copy changes return SetupResult on setup.complete --- backend/src/context/setup.rs | 14 +- backend/src/setup.rs | 55 ++- .../src/app/app-routing.module.ts | 30 +- .../setup-wizard/src/app/app.module.ts | 2 - .../src/app/pages/embassy/embassy.page.ts | 4 +- .../src/app/pages/init/init.module.ts | 18 - .../src/app/pages/init/init.page.html | 48 --- .../src/app/pages/init/init.page.scss | 0 .../src/app/pages/init/init.page.ts | 61 --- .../src/app/pages/loading/loading.page.ts | 21 +- .../src/app/pages/recover/recover.page.ts | 2 +- .../success-routing.module.ts} | 6 +- .../src/app/pages/success/success.module.ts | 4 +- .../src/app/pages/success/success.page.html | 386 +++++++++++------- .../src/app/pages/success/success.page.ts | 59 ++- .../src/app/services/api/api.service.ts | 40 +- .../src/app/services/api/live-api.service.ts | 96 +++-- .../src/app/services/api/mock-api.service.ts | 1 + .../src/app/services/state.service.ts | 7 + .../src/app/modals/markdown/markdown.page.ts | 6 +- .../app-show-status.component.ts | 2 +- 21 files changed, 456 insertions(+), 406 deletions(-) delete mode 100644 frontend/projects/setup-wizard/src/app/pages/init/init.module.ts delete mode 100644 frontend/projects/setup-wizard/src/app/pages/init/init.page.html delete mode 100644 frontend/projects/setup-wizard/src/app/pages/init/init.page.scss delete mode 100644 frontend/projects/setup-wizard/src/app/pages/init/init.page.ts rename frontend/projects/setup-wizard/src/app/pages/{init/init-routing.module.ts => success/success-routing.module.ts} (67%) diff --git a/backend/src/context/setup.rs b/backend/src/context/setup.rs index 02f67e19d..41df19d1a 100644 --- a/backend/src/context/setup.rs +++ b/backend/src/context/setup.rs @@ -8,7 +8,7 @@ use patch_db::json_ptr::JsonPointer; use patch_db::PatchDb; use rpc_toolkit::yajrc::RpcError; use rpc_toolkit::Context; -use serde::Deserialize; +use serde::{Deserialize, Serialize}; use sqlx::sqlite::SqliteConnectOptions; use sqlx::SqlitePool; use tokio::fs::File; @@ -25,6 +25,14 @@ use crate::util::io::from_toml_async_reader; use crate::util::AsyncFileExt; use crate::{Error, ResultExt}; +#[derive(Clone, Serialize, Deserialize)] +#[serde(rename_all = "kebab-case")] +pub struct SetupResult { + pub tor_address: String, + pub lan_address: String, + pub root_ca: String, +} + #[derive(Debug, Default, Deserialize)] #[serde(rename_all = "kebab-case")] pub struct SetupContextConfig { @@ -62,7 +70,7 @@ pub struct SetupContextSeed { pub selected_v2_drive: RwLock>, pub cached_product_key: RwLock>>, pub recovery_status: RwLock>>, - pub disk_guid: RwLock>>, + pub setup_result: RwLock, SetupResult)>>, } #[derive(Clone)] @@ -81,7 +89,7 @@ impl SetupContext { selected_v2_drive: RwLock::new(None), cached_product_key: RwLock::new(None), recovery_status: RwLock::new(None), - disk_guid: RwLock::new(None), + setup_result: RwLock::new(None), }))) } #[instrument(skip(self))] diff --git a/backend/src/setup.rs b/backend/src/setup.rs index 1846d3c89..f330664dc 100644 --- a/backend/src/setup.rs +++ b/backend/src/setup.rs @@ -25,6 +25,7 @@ use tracing::instrument; use crate::backup::restore::recover_full_embassy; use crate::backup::target::BackupTargetFS; use crate::context::rpc::RpcContextConfig; +use crate::context::setup::SetupResult; use crate::context::SetupContext; use crate::db::model::RecoveredPackageInfo; use crate::disk::main::DEFAULT_PASSWORD; @@ -112,18 +113,19 @@ pub async fn attach( &*ctx.product_key().await?, ) .await?; - *ctx.disk_guid.write().await = Some(guid.clone()); let secrets = ctx.secret_store().await?; let tor_key = crate::net::tor::os_key(&mut secrets.acquire().await?).await?; let (_, root_ca) = SslManager::init(secrets).await?.export_root_ca().await?; - Ok(SetupResult { + let setup_result = SetupResult { tor_address: format!("http://{}", tor_key.public().get_onion_address()), lan_address: format!( "https://embassy-{}.local", crate::hostname::derive_id(&*ctx.product_key().await?) ), root_ca: String::from_utf8(root_ca.to_pem()?)?, - }) + }; + *ctx.setup_result.write().await = Some((guid, setup_result.clone())); + Ok(setup_result) } #[command(subcommands(v2, recovery_status))] @@ -191,14 +193,6 @@ pub async fn verify_cifs( embassy_os.ok_or_else(|| Error::new(eyre!("No Backup Found"), crate::ErrorKind::NotFound)) } -#[derive(Serialize, Deserialize)] -#[serde(rename_all = "kebab-case")] -pub struct SetupResult { - tor_address: String, - lan_address: String, - root_ca: String, -} - #[command(rpc_only)] pub async fn execute( #[context] ctx: SetupContext, @@ -240,9 +234,9 @@ pub async fn execute( #[instrument(skip(ctx))] #[command(rpc_only)] -pub async fn complete(#[context] ctx: SetupContext) -> Result<(), Error> { - let guid = if let Some(guid) = &*ctx.disk_guid.read().await { - guid.clone() +pub async fn complete(#[context] ctx: SetupContext) -> Result { + let (guid, setup_result) = if let Some((guid, setup_result)) = &*ctx.setup_result.read().await { + (guid.clone(), setup_result.clone()) } else { return Err(Error::new( eyre!("setup.execute has not completed successfully"), @@ -281,7 +275,7 @@ pub async fn complete(#[context] ctx: SetupContext) -> Result<(), Error> { guid_file.write_all(guid.as_bytes()).await?; guid_file.sync_all().await?; ctx.shutdown.send(()).expect("failed to shutdown"); - Ok(()) + Ok(setup_result) } #[instrument(skip(ctx, embassy_password, recovery_password))] @@ -323,10 +317,21 @@ pub async fn execute_inner( &ctx.product_key().await?, ) .await?; + let res = (tor_addr, root_ca.clone()); tokio::spawn(async move { if let Err(e) = recover_fut .and_then(|_| async { - *ctx.disk_guid.write().await = Some(guid); + *ctx.setup_result.write().await = Some(( + guid, + SetupResult { + tor_address: format!("http://{}", tor_addr), + lan_address: format!( + "https://embassy-{}.local", + crate::hostname::derive_id(&ctx.product_key().await?) + ), + root_ca: String::from_utf8(root_ca.to_pem()?)?, + }, + )); if let Some(Ok(recovery_status)) = &mut *ctx.recovery_status.write().await { recovery_status.complete = true; } @@ -342,16 +347,26 @@ pub async fn execute_inner( tracing::info!("Recovery Complete!"); } }); - (tor_addr, root_ca) + res } else { - let res = fresh_setup(&ctx, &embassy_password).await?; + let (tor_addr, root_ca) = fresh_setup(&ctx, &embassy_password).await?; init( &RpcContextConfig::load(ctx.config_path.as_ref()).await?, &ctx.product_key().await?, ) .await?; - *ctx.disk_guid.write().await = Some(guid); - res + *ctx.setup_result.write().await = Some(( + guid, + SetupResult { + tor_address: format!("http://{}", tor_addr), + lan_address: format!( + "https://embassy-{}.local", + crate::hostname::derive_id(&ctx.product_key().await?) + ), + root_ca: String::from_utf8(root_ca.to_pem()?)?, + }, + )); + (tor_addr, root_ca) }; Ok(res) diff --git a/frontend/projects/setup-wizard/src/app/app-routing.module.ts b/frontend/projects/setup-wizard/src/app/app-routing.module.ts index 846a2dd58..9e35ea007 100644 --- a/frontend/projects/setup-wizard/src/app/app-routing.module.ts +++ b/frontend/projects/setup-wizard/src/app/app-routing.module.ts @@ -4,33 +4,41 @@ import { NavGuard, RecoveryNavGuard } from './guards/nav-guard' const routes: Routes = [ { path: '', redirectTo: '/product-key', pathMatch: 'full' }, - { - path: 'init', - loadChildren: () => import('./pages/init/init.module').then( m => m.InitPageModule), - canActivate: [NavGuard], - }, { path: 'product-key', - loadChildren: () => import('./pages/product-key/product-key.module').then( m => m.ProductKeyPageModule), + loadChildren: () => + import('./pages/product-key/product-key.module').then( + m => m.ProductKeyPageModule, + ), }, { path: 'home', - loadChildren: () => import('./pages/home/home.module').then( m => m.HomePageModule), + loadChildren: () => + import('./pages/home/home.module').then(m => m.HomePageModule), canActivate: [NavGuard], }, { path: 'recover', - loadChildren: () => import('./pages/recover/recover.module').then( m => m.RecoverPageModule), + loadChildren: () => + import('./pages/recover/recover.module').then(m => m.RecoverPageModule), canActivate: [RecoveryNavGuard], }, { path: 'embassy', - loadChildren: () => import('./pages/embassy/embassy.module').then( m => m.EmbassyPageModule), + loadChildren: () => + import('./pages/embassy/embassy.module').then(m => m.EmbassyPageModule), canActivate: [NavGuard], }, { path: 'loading', - loadChildren: () => import('./pages/loading/loading.module').then( m => m.LoadingPageModule), + loadChildren: () => + import('./pages/loading/loading.module').then(m => m.LoadingPageModule), + canActivate: [NavGuard], + }, + { + path: 'success', + loadChildren: () => + import('./pages/success/success.module').then(m => m.SuccessPageModule), canActivate: [NavGuard], }, ] @@ -46,4 +54,4 @@ const routes: Routes = [ ], exports: [RouterModule], }) -export class AppRoutingModule { } +export class AppRoutingModule {} diff --git a/frontend/projects/setup-wizard/src/app/app.module.ts b/frontend/projects/setup-wizard/src/app/app.module.ts index fef910d76..96448452e 100644 --- a/frontend/projects/setup-wizard/src/app/app.module.ts +++ b/frontend/projects/setup-wizard/src/app/app.module.ts @@ -15,7 +15,6 @@ import { AppComponent } from './app.component' import { AppRoutingModule } from './app-routing.module' import { GlobalErrorHandler } from './services/global-error-handler.service' import { SuccessPageModule } from './pages/success/success.module' -import { InitPageModule } from './pages/init/init.module' import { HomePageModule } from './pages/home/home.module' import { LoadingPageModule } from './pages/loading/loading.module' import { ProdKeyModalModule } from './modals/prod-key-modal/prod-key-modal.module' @@ -42,7 +41,6 @@ const { useMocks } = require('../../../../config.json') as WorkspaceConfig ProdKeyModalModule, ProductKeyPageModule, RecoverPageModule, - InitPageModule, ], providers: [ { provide: RouteReuseStrategy, useClass: IonicRouteStrategy }, diff --git a/frontend/projects/setup-wizard/src/app/pages/embassy/embassy.page.ts b/frontend/projects/setup-wizard/src/app/pages/embassy/embassy.page.ts index 972c78e7d..46167230f 100644 --- a/frontend/projects/setup-wizard/src/app/pages/embassy/embassy.page.ts +++ b/frontend/projects/setup-wizard/src/app/pages/embassy/embassy.page.ts @@ -129,7 +129,7 @@ export class EmbassyPage { private async setupEmbassy(drive: DiskInfo, password: string): Promise { const loader = await this.loadingCtrl.create({ - message: 'Transferring encrypted data. This could take a while...', + message: 'Initializing data drive. This could take a while...', }) await loader.present() @@ -139,7 +139,7 @@ export class EmbassyPage { if (!!this.stateService.recoverySource) { await this.navCtrl.navigateForward(`/loading`) } else { - await this.navCtrl.navigateForward(`/init`) + await this.navCtrl.navigateForward(`/success`) } } catch (e) { this.errorToastService.present( diff --git a/frontend/projects/setup-wizard/src/app/pages/init/init.module.ts b/frontend/projects/setup-wizard/src/app/pages/init/init.module.ts deleted file mode 100644 index 850f65bcd..000000000 --- a/frontend/projects/setup-wizard/src/app/pages/init/init.module.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { NgModule } from '@angular/core' -import { CommonModule } from '@angular/common' -import { IonicModule } from '@ionic/angular' -import { InitPage } from './init.page' -import { InitPageRoutingModule } from './init-routing.module' -import { SuccessPageModule } from '../success/success.module' - -@NgModule({ - imports: [ - CommonModule, - IonicModule, - InitPageRoutingModule, - SuccessPageModule, - ], - declarations: [InitPage], - exports: [InitPage], -}) -export class InitPageModule { } diff --git a/frontend/projects/setup-wizard/src/app/pages/init/init.page.html b/frontend/projects/setup-wizard/src/app/pages/init/init.page.html deleted file mode 100644 index 10b3db268..000000000 --- a/frontend/projects/setup-wizard/src/app/pages/init/init.page.html +++ /dev/null @@ -1,48 +0,0 @@ - - - - -
- -
- - - - - - Initializing Embassy - Progress: {{ progress }}% - - - - -

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

-

- DO NOT: Close or refresh the browser window during - intialization process. -

-
-
-
-
-
-
diff --git a/frontend/projects/setup-wizard/src/app/pages/init/init.page.scss b/frontend/projects/setup-wizard/src/app/pages/init/init.page.scss deleted file mode 100644 index e69de29bb..000000000 diff --git a/frontend/projects/setup-wizard/src/app/pages/init/init.page.ts b/frontend/projects/setup-wizard/src/app/pages/init/init.page.ts deleted file mode 100644 index 1a250a650..000000000 --- a/frontend/projects/setup-wizard/src/app/pages/init/init.page.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { Component } from '@angular/core' -import { interval, Subscription } from 'rxjs' -import { finalize, take, tap } from 'rxjs/operators' -import { ApiService } from 'src/app/services/api/api.service' -import { StateService } from 'src/app/services/state.service' - -@Component({ - selector: 'app-init', - templateUrl: 'init.page.html', - styleUrls: ['init.page.scss'], -}) -export class InitPage { - progress = 0 - sub: Subscription - - constructor ( - private readonly apiService: ApiService, - public readonly stateService: StateService, - ) { } - - ngOnInit () { - // call setup.complete to tear down embassy.local and spin up embassy-[id].local - this.apiService.setupComplete() - - this.sub = interval(130) - .pipe( - take(101), - tap(num => { - this.progress = num - }), - finalize(() => { - setTimeout(() => { - this.stateService.embassyLoaded = true - this.download() - }, 500) - }), - ).subscribe() - } - - ngOnDestroy () { - if (this.sub) this.sub.unsubscribe() - } - - download () { - document.getElementById('tor-addr').innerHTML = this.stateService.torAddress - document.getElementById('lan-addr').innerHTML = this.stateService.lanAddress - document.getElementById('cert').setAttribute('href', 'data:application/x-x509-ca-cert;base64,' + encodeURIComponent(this.stateService.cert)) - let html = document.getElementById('downloadable').innerHTML - const filename = 'embassy-info.html' - - const elem = document.createElement('a') - elem.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(html)) - elem.setAttribute('download', filename) - elem.style.display = 'none' - - document.body.appendChild(elem) - elem.click() - document.body.removeChild(elem) - } -} - diff --git a/frontend/projects/setup-wizard/src/app/pages/loading/loading.page.ts b/frontend/projects/setup-wizard/src/app/pages/loading/loading.page.ts index 9803c4405..658b9a55f 100644 --- a/frontend/projects/setup-wizard/src/app/pages/loading/loading.page.ts +++ b/frontend/projects/setup-wizard/src/app/pages/loading/loading.page.ts @@ -8,19 +8,20 @@ import { StateService } from 'src/app/services/state.service' styleUrls: ['loading.page.scss'], }) export class LoadingPage { - constructor ( + constructor( public stateService: StateService, private navCtrl: NavController, - ) { } + ) {} - ngOnInit () { + ngOnInit() { this.stateService.pollDataTransferProgress() - const progSub = this.stateService.dataCompletionSubject.subscribe(async complete => { - if (complete) { - progSub.unsubscribe() - await this.navCtrl.navigateForward(`/init`) - } - }) + const progSub = this.stateService.dataCompletionSubject.subscribe( + async complete => { + if (complete) { + progSub.unsubscribe() + await this.navCtrl.navigateForward(`/success`) + } + }, + ) } } - diff --git a/frontend/projects/setup-wizard/src/app/pages/recover/recover.page.ts b/frontend/projects/setup-wizard/src/app/pages/recover/recover.page.ts index b60e9d86e..3f0ca1e98 100644 --- a/frontend/projects/setup-wizard/src/app/pages/recover/recover.page.ts +++ b/frontend/projects/setup-wizard/src/app/pages/recover/recover.page.ts @@ -204,7 +204,7 @@ export class RecoverPage { await loader.present() try { await this.stateService.importDrive(guid) - await this.navCtrl.navigateForward(`/init`) + await this.navCtrl.navigateForward(`/success`) } catch (e) { this.errorToastService.present(e.message) } finally { diff --git a/frontend/projects/setup-wizard/src/app/pages/init/init-routing.module.ts b/frontend/projects/setup-wizard/src/app/pages/success/success-routing.module.ts similarity index 67% rename from frontend/projects/setup-wizard/src/app/pages/init/init-routing.module.ts rename to frontend/projects/setup-wizard/src/app/pages/success/success-routing.module.ts index 65c6adb97..33c6de9be 100644 --- a/frontend/projects/setup-wizard/src/app/pages/init/init-routing.module.ts +++ b/frontend/projects/setup-wizard/src/app/pages/success/success-routing.module.ts @@ -1,11 +1,11 @@ import { NgModule } from '@angular/core' import { RouterModule, Routes } from '@angular/router' -import { InitPage } from './init.page' +import { SuccessPage } from './success.page' const routes: Routes = [ { path: '', - component: InitPage, + component: SuccessPage, }, ] @@ -13,4 +13,4 @@ const routes: Routes = [ imports: [RouterModule.forChild(routes)], exports: [RouterModule], }) -export class InitPageRoutingModule { } +export class SuccessPageRoutingModule {} diff --git a/frontend/projects/setup-wizard/src/app/pages/success/success.module.ts b/frontend/projects/setup-wizard/src/app/pages/success/success.module.ts index a00510e8d..3562c9e16 100644 --- a/frontend/projects/setup-wizard/src/app/pages/success/success.module.ts +++ b/frontend/projects/setup-wizard/src/app/pages/success/success.module.ts @@ -4,6 +4,7 @@ import { IonicModule } from '@ionic/angular' import { FormsModule } from '@angular/forms' import { SuccessPage } from './success.page' import { PasswordPageModule } from '../../modals/password/password.module' +import { SuccessPageRoutingModule } from './success-routing.module' @NgModule({ imports: [ @@ -11,8 +12,9 @@ import { PasswordPageModule } from '../../modals/password/password.module' FormsModule, IonicModule, PasswordPageModule, + SuccessPageRoutingModule, ], declarations: [SuccessPage], exports: [SuccessPage], }) -export class SuccessPageModule { } +export class SuccessPageModule {} diff --git a/frontend/projects/setup-wizard/src/app/pages/success/success.page.html b/frontend/projects/setup-wizard/src/app/pages/success/success.page.html index c4a22126d..18ed4b6cf 100644 --- a/frontend/projects/setup-wizard/src/app/pages/success/success.page.html +++ b/frontend/projects/setup-wizard/src/app/pages/success/success.page.html @@ -1,167 +1,243 @@ + + + + + + + + Setup Complete! + + +
+ +

You can now safely unplug your backup drive.

+
+ +
+

Tor Instructions:

+ +
- - - - Setup Complete! - - -
- -

You can now safely unplug your backup drive.

-
- -
-

Tor Instructions:

- -
+
+
+

+ To use your Embassy over Tor, visit its unique Tor address + from any Tor-enabled browser. For a list of recommended + browsers, click + here. +

+
+

Tor Address

+ + + {{ stateService.torAddress }} + + + + + +
+
+
+
-
-
-

- To use your Embassy over Tor, visit its unique Tor address from any Tor-enabled browser. - For a list of recommended browsers, click here. -

-
-

Tor Address

- - - {{ stateService.torAddress }} - - - - - -
-
-
-
+ +
+

LAN Instructions (Slightly Advanced):

+ +
- -
-

LAN Instructions (Slightly Advanced):

- -
+
+
+

To use your Embassy locally, you must:

+
    +
  1. + Currently be connected to the same Local Area Network (LAN) + as your Embassy. +
  2. +
  3. Download your Embassy's Root Certificate Authority.
  4. +
  5. + Trust your Embassy's Root CA on both your + computer/phone and in your browser settings. +
  6. +
+

+ For step-by-step instructions, click + here. +

-
-
-

To use your Embassy locally, you must:

-
    -
  1. Currently be connected to the same Local Area Network (LAN) as your Embassy.
  2. -
  3. Download your Embassy's Root Certificate Authority.
  4. -
  5. Trust your Embassy's Root CA on both your computer/phone and in your browser settings.
  6. -
-

- For step-by-step instructions, click - here. -

+

+ Please note, once setup is complete, the embassy.local + address will no longer connect to your Embassy. +

-

- Please note, once setup is complete, the embassy.local address will no longer connect to your Embassy. -

+ + Download Root CA + + - - Download Root CA - - +

LAN Address

+ + + {{ stateService.lanAddress }} + + + + + +
+
+
+
+
+ + Download this page + + +
+
+ + -

LAN Address

- - - {{ stateService.lanAddress }} - - - - - -
-
-
-
-
- - Download this page - - -
-
-
-
+ + + + +
+
+
+
diff --git a/frontend/projects/setup-wizard/src/app/pages/success/success.page.ts b/frontend/projects/setup-wizard/src/app/pages/success/success.page.ts index 3951b908e..5f3c596e5 100644 --- a/frontend/projects/setup-wizard/src/app/pages/success/success.page.ts +++ b/frontend/projects/setup-wizard/src/app/pages/success/success.page.ts @@ -1,5 +1,6 @@ import { Component, EventEmitter, Output } from '@angular/core' import { ToastController } from '@ionic/angular' +import { ErrorToastService } from 'src/app/services/error-toast.service' import { StateService } from 'src/app/services/state.service' @Component({ @@ -12,16 +13,29 @@ export class SuccessPage { torOpen = true lanOpen = false - constructor ( + constructor( private readonly toastCtrl: ToastController, + private readonly errCtrl: ErrorToastService, public readonly stateService: StateService, - ) { } + ) {} - ngAfterViewInit () { - document.getElementById('install-cert').setAttribute('href', 'data:application/x-x509-ca-cert;base64,' + encodeURIComponent(this.stateService.cert)) + async ngAfterViewInit() { + try { + await this.stateService.completeEmbassy() + document + .getElementById('install-cert') + .setAttribute( + 'href', + 'data:application/x-x509-ca-cert;base64,' + + encodeURIComponent(this.stateService.cert), + ) + this.download() + } catch (e) { + await this.errCtrl.present(e) + } } - async copy (address: string): Promise { + async copy(address: string): Promise { const success = await this.copyToClipboard(address) const message = success ? 'copied to clipboard!' : 'failed to copy' @@ -33,23 +47,45 @@ export class SuccessPage { await toast.present() } - toggleTor () { + toggleTor() { this.torOpen = !this.torOpen } - toggleLan () { + toggleLan() { this.lanOpen = !this.lanOpen } - installCert () { + installCert() { document.getElementById('install-cert').click() } - download () { - this.onDownload.emit() + download() { + document.getElementById('tor-addr').innerHTML = this.stateService.torAddress + document.getElementById('lan-addr').innerHTML = this.stateService.lanAddress + document + .getElementById('cert') + .setAttribute( + 'href', + 'data:application/x-x509-ca-cert;base64,' + + encodeURIComponent(this.stateService.cert), + ) + let html = document.getElementById('downloadable').innerHTML + const filename = 'embassy-info.html' + + const elem = document.createElement('a') + elem.setAttribute( + 'href', + 'data:text/plain;charset=utf-8,' + encodeURIComponent(html), + ) + elem.setAttribute('download', filename) + elem.style.display = 'none' + + document.body.appendChild(elem) + elem.click() + document.body.removeChild(elem) } - private async copyToClipboard (str: string): Promise { + private async copyToClipboard(str: string): Promise { const el = document.createElement('textarea') el.value = str el.setAttribute('readonly', '') @@ -62,4 +98,3 @@ export class SuccessPage { return copy } } - diff --git a/frontend/projects/setup-wizard/src/app/services/api/api.service.ts b/frontend/projects/setup-wizard/src/app/services/api/api.service.ts index bd1e3f8ff..abda4fced 100644 --- a/frontend/projects/setup-wizard/src/app/services/api/api.service.ts +++ b/frontend/projects/setup-wizard/src/app/services/api/api.service.ts @@ -1,16 +1,16 @@ export abstract class ApiService { // unencrypted - abstract getStatus (): Promise // setup.status - abstract getDrives (): Promise // setup.disk.list - abstract set02XDrive (logicalname: string): Promise // setup.recovery.v2.set - abstract getRecoveryStatus (): Promise // setup.recovery.status + abstract getStatus(): Promise // setup.status + abstract getDrives(): Promise // setup.disk.list + abstract set02XDrive(logicalname: string): Promise // setup.recovery.v2.set + abstract getRecoveryStatus(): Promise // setup.recovery.status // encrypted - abstract verifyCifs (cifs: CifsRecoverySource): Promise // setup.cifs.verify - abstract verifyProductKey (): Promise // echo - throws error if invalid - abstract importDrive (guid: string): Promise // setup.execute - abstract setupEmbassy (setupInfo: SetupEmbassyReq): Promise // setup.execute - abstract setupComplete (): Promise // setup.complete + abstract verifyCifs(cifs: CifsRecoverySource): Promise // setup.cifs.verify + abstract verifyProductKey(): Promise // echo - throws error if invalid + abstract importDrive(guid: string): Promise // setup.execute + abstract setupEmbassy(setupInfo: SetupEmbassyReq): Promise // setup.execute + abstract setupComplete(): Promise // setup.complete } export interface GetStatusRes { @@ -75,12 +75,12 @@ export interface CifsRecoverySource { } export interface DiskInfo { - logicalname: string, - vendor: string | null, - model: string | null, - partitions: PartitionInfo[], - capacity: number, - guid: string | null, // cant back up if guid exists + logicalname: string + vendor: string | null + model: string | null + partitions: PartitionInfo[] + capacity: number + guid: string | null // cant back up if guid exists } export interface RecoveryStatusRes { @@ -90,9 +90,9 @@ export interface RecoveryStatusRes { } export interface PartitionInfo { - logicalname: string, - label: string | null, - capacity: number, - used: number | null, - 'embassy-os': EmbassyOSRecoveryInfo | null, + logicalname: string + label: string | null + capacity: number + used: number | null + 'embassy-os': EmbassyOSRecoveryInfo | null } diff --git a/frontend/projects/setup-wizard/src/app/services/api/live-api.service.ts b/frontend/projects/setup-wizard/src/app/services/api/live-api.service.ts index 015c9c5a5..580a02414 100644 --- a/frontend/projects/setup-wizard/src/app/services/api/live-api.service.ts +++ b/frontend/projects/setup-wizard/src/app/services/api/live-api.service.ts @@ -1,49 +1,71 @@ import { Injectable } from '@angular/core' -import { ApiService, CifsRecoverySource, DiskInfo, DiskListResponse, DiskRecoverySource, EmbassyOSRecoveryInfo, GetStatusRes, RecoveryStatusRes, SetupEmbassyReq, SetupEmbassyRes } from './api.service' +import { + ApiService, + CifsRecoverySource, + DiskInfo, + DiskListResponse, + DiskRecoverySource, + EmbassyOSRecoveryInfo, + GetStatusRes, + RecoveryStatusRes, + SetupEmbassyReq, + SetupEmbassyRes, +} from './api.service' import { HttpService } from './http.service' @Injectable({ providedIn: 'root', }) export class LiveApiService extends ApiService { - - constructor ( - private readonly http: HttpService, - ) { super() } + constructor(private readonly http: HttpService) { + super() + } // ** UNENCRYPTED ** - async getStatus () { - return this.http.rpcRequest({ - method: 'setup.status', - params: { }, - }, false) + async getStatus() { + return this.http.rpcRequest( + { + method: 'setup.status', + params: {}, + }, + false, + ) } - async getDrives () { - return this.http.rpcRequest({ - method: 'setup.disk.list', - params: { }, - }, false) + async getDrives() { + return this.http.rpcRequest( + { + method: 'setup.disk.list', + params: {}, + }, + false, + ) } - async set02XDrive (logicalname) { - return this.http.rpcRequest({ - method: 'setup.recovery.v2.set', - params: { logicalname }, - }, false) + async set02XDrive(logicalname) { + return this.http.rpcRequest( + { + method: 'setup.recovery.v2.set', + params: { logicalname }, + }, + false, + ) } - async getRecoveryStatus () { - return this.http.rpcRequest({ - method: 'setup.recovery.status', - params: { }, - }, false) + async getRecoveryStatus() { + return this.http.rpcRequest( + { + method: 'setup.recovery.status', + params: {}, + }, + false, + ) } // ** ENCRYPTED ** - async verifyCifs (source: CifsRecoverySource) { + async verifyCifs(source: CifsRecoverySource) { source.path = source.path.replace('/\\/g', '/') return this.http.rpcRequest({ method: 'setup.cifs.verify', @@ -51,14 +73,14 @@ export class LiveApiService extends ApiService { }) } - async verifyProductKey () { + async verifyProductKey() { return this.http.rpcRequest({ method: 'echo', - params: { 'message': 'hello' }, + params: { message: 'hello' }, }) } - async importDrive (guid: string) { + async importDrive(guid: string) { const res = await this.http.rpcRequest({ method: 'setup.attach', params: { guid }, @@ -70,9 +92,11 @@ export class LiveApiService extends ApiService { } } - async setupEmbassy (setupInfo: SetupEmbassyReq) { + async setupEmbassy(setupInfo: SetupEmbassyReq) { if (isCifsSource(setupInfo['recovery-source'])) { - setupInfo['recovery-source'].path = setupInfo['recovery-source'].path.replace('/\\/g', '/') + setupInfo['recovery-source'].path = setupInfo[ + 'recovery-source' + ].path.replace('/\\/g', '/') } const res = await this.http.rpcRequest({ @@ -86,14 +110,16 @@ export class LiveApiService extends ApiService { } } - async setupComplete () { - await this.http.rpcRequest({ + async setupComplete() { + return this.http.rpcRequest({ method: 'setup.complete', - params: { }, + params: {}, }) } } -function isCifsSource (source: CifsRecoverySource | DiskRecoverySource | undefined): source is CifsRecoverySource { +function isCifsSource( + source: CifsRecoverySource | DiskRecoverySource | undefined, +): source is CifsRecoverySource { return !!(source as CifsRecoverySource)?.hostname } diff --git a/frontend/projects/setup-wizard/src/app/services/api/mock-api.service.ts b/frontend/projects/setup-wizard/src/app/services/api/mock-api.service.ts index 6ed817d87..4df65203b 100644 --- a/frontend/projects/setup-wizard/src/app/services/api/mock-api.service.ts +++ b/frontend/projects/setup-wizard/src/app/services/api/mock-api.service.ts @@ -96,6 +96,7 @@ export class MockApiService extends ApiService { async setupComplete() { await pauseFor(1000) + return setupRes } } diff --git a/frontend/projects/setup-wizard/src/app/services/state.service.ts b/frontend/projects/setup-wizard/src/app/services/state.service.ts index df59e5edd..d79dd18ad 100644 --- a/frontend/projects/setup-wizard/src/app/services/state.service.ts +++ b/frontend/projects/setup-wizard/src/app/services/state.service.ts @@ -91,4 +91,11 @@ export class StateService { this.lanAddress = ret['lan-address'] this.cert = ret['root-ca'] } + + async completeEmbassy(): Promise { + const ret = await this.apiService.setupComplete() + this.torAddress = ret['tor-address'] + this.lanAddress = ret['lan-address'] + this.cert = ret['root-ca'] + } } diff --git a/frontend/projects/ui/src/app/modals/markdown/markdown.page.ts b/frontend/projects/ui/src/app/modals/markdown/markdown.page.ts index 5f5ca48af..e185272dd 100644 --- a/frontend/projects/ui/src/app/modals/markdown/markdown.page.ts +++ b/frontend/projects/ui/src/app/modals/markdown/markdown.page.ts @@ -26,9 +26,6 @@ export class MarkdownPage { if (!this.content) { this.content = await this.embassyApi.getStatic(this.contentUrl) } - } catch (e) { - this.loadingError = getErrorMessage(e) - } finally { this.loading = false await pauseFor(50) const links = document.links @@ -39,6 +36,9 @@ export class MarkdownPage { links[i].className += ' externalLink' } } + } catch (e) { + this.loadingError = getErrorMessage(e) + this.loading = false } } diff --git a/frontend/projects/ui/src/app/pages/apps-routes/app-show/components/app-show-status/app-show-status.component.ts b/frontend/projects/ui/src/app/pages/apps-routes/app-show/components/app-show-status/app-show-status.component.ts index f2f0ad6d4..fea8943c5 100644 --- a/frontend/projects/ui/src/app/pages/apps-routes/app-show/components/app-show-status/app-show-status.component.ts +++ b/frontend/projects/ui/src/app/pages/apps-routes/app-show/components/app-show-status/app-show-status.component.ts @@ -110,7 +110,7 @@ export class AppShowStatusComponent { const { id, title, version } = this.pkg.manifest const hasDependents = !!Object.keys( this.pkg.installed['current-dependents'], - ).filter(depId => depId !== this.pkg.manifest.id).length + ).filter(depId => depId !== id).length if (!hasDependents) { const loader = await this.loadingCtrl.create({