mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-31 04:23:40 +00:00
no init
setup complete fix and minor copy changes return SetupResult on setup.complete
This commit is contained in:
committed by
Aiden McClelland
parent
df16943502
commit
61ee46f289
@@ -8,7 +8,7 @@ use patch_db::json_ptr::JsonPointer;
|
|||||||
use patch_db::PatchDb;
|
use patch_db::PatchDb;
|
||||||
use rpc_toolkit::yajrc::RpcError;
|
use rpc_toolkit::yajrc::RpcError;
|
||||||
use rpc_toolkit::Context;
|
use rpc_toolkit::Context;
|
||||||
use serde::Deserialize;
|
use serde::{Deserialize, Serialize};
|
||||||
use sqlx::sqlite::SqliteConnectOptions;
|
use sqlx::sqlite::SqliteConnectOptions;
|
||||||
use sqlx::SqlitePool;
|
use sqlx::SqlitePool;
|
||||||
use tokio::fs::File;
|
use tokio::fs::File;
|
||||||
@@ -25,6 +25,14 @@ use crate::util::io::from_toml_async_reader;
|
|||||||
use crate::util::AsyncFileExt;
|
use crate::util::AsyncFileExt;
|
||||||
use crate::{Error, ResultExt};
|
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)]
|
#[derive(Debug, Default, Deserialize)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
pub struct SetupContextConfig {
|
pub struct SetupContextConfig {
|
||||||
@@ -62,7 +70,7 @@ pub struct SetupContextSeed {
|
|||||||
pub selected_v2_drive: RwLock<Option<PathBuf>>,
|
pub selected_v2_drive: RwLock<Option<PathBuf>>,
|
||||||
pub cached_product_key: RwLock<Option<Arc<String>>>,
|
pub cached_product_key: RwLock<Option<Arc<String>>>,
|
||||||
pub recovery_status: RwLock<Option<Result<RecoveryStatus, RpcError>>>,
|
pub recovery_status: RwLock<Option<Result<RecoveryStatus, RpcError>>>,
|
||||||
pub disk_guid: RwLock<Option<Arc<String>>>,
|
pub setup_result: RwLock<Option<(Arc<String>, SetupResult)>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@@ -81,7 +89,7 @@ impl SetupContext {
|
|||||||
selected_v2_drive: RwLock::new(None),
|
selected_v2_drive: RwLock::new(None),
|
||||||
cached_product_key: RwLock::new(None),
|
cached_product_key: RwLock::new(None),
|
||||||
recovery_status: RwLock::new(None),
|
recovery_status: RwLock::new(None),
|
||||||
disk_guid: RwLock::new(None),
|
setup_result: RwLock::new(None),
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
#[instrument(skip(self))]
|
#[instrument(skip(self))]
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ use tracing::instrument;
|
|||||||
use crate::backup::restore::recover_full_embassy;
|
use crate::backup::restore::recover_full_embassy;
|
||||||
use crate::backup::target::BackupTargetFS;
|
use crate::backup::target::BackupTargetFS;
|
||||||
use crate::context::rpc::RpcContextConfig;
|
use crate::context::rpc::RpcContextConfig;
|
||||||
|
use crate::context::setup::SetupResult;
|
||||||
use crate::context::SetupContext;
|
use crate::context::SetupContext;
|
||||||
use crate::db::model::RecoveredPackageInfo;
|
use crate::db::model::RecoveredPackageInfo;
|
||||||
use crate::disk::main::DEFAULT_PASSWORD;
|
use crate::disk::main::DEFAULT_PASSWORD;
|
||||||
@@ -112,18 +113,19 @@ pub async fn attach(
|
|||||||
&*ctx.product_key().await?,
|
&*ctx.product_key().await?,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
*ctx.disk_guid.write().await = Some(guid.clone());
|
|
||||||
let secrets = ctx.secret_store().await?;
|
let secrets = ctx.secret_store().await?;
|
||||||
let tor_key = crate::net::tor::os_key(&mut secrets.acquire().await?).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?;
|
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()),
|
tor_address: format!("http://{}", tor_key.public().get_onion_address()),
|
||||||
lan_address: format!(
|
lan_address: format!(
|
||||||
"https://embassy-{}.local",
|
"https://embassy-{}.local",
|
||||||
crate::hostname::derive_id(&*ctx.product_key().await?)
|
crate::hostname::derive_id(&*ctx.product_key().await?)
|
||||||
),
|
),
|
||||||
root_ca: String::from_utf8(root_ca.to_pem()?)?,
|
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))]
|
#[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))
|
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)]
|
#[command(rpc_only)]
|
||||||
pub async fn execute(
|
pub async fn execute(
|
||||||
#[context] ctx: SetupContext,
|
#[context] ctx: SetupContext,
|
||||||
@@ -240,9 +234,9 @@ pub async fn execute(
|
|||||||
|
|
||||||
#[instrument(skip(ctx))]
|
#[instrument(skip(ctx))]
|
||||||
#[command(rpc_only)]
|
#[command(rpc_only)]
|
||||||
pub async fn complete(#[context] ctx: SetupContext) -> Result<(), Error> {
|
pub async fn complete(#[context] ctx: SetupContext) -> Result<SetupResult, Error> {
|
||||||
let guid = if let Some(guid) = &*ctx.disk_guid.read().await {
|
let (guid, setup_result) = if let Some((guid, setup_result)) = &*ctx.setup_result.read().await {
|
||||||
guid.clone()
|
(guid.clone(), setup_result.clone())
|
||||||
} else {
|
} else {
|
||||||
return Err(Error::new(
|
return Err(Error::new(
|
||||||
eyre!("setup.execute has not completed successfully"),
|
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.write_all(guid.as_bytes()).await?;
|
||||||
guid_file.sync_all().await?;
|
guid_file.sync_all().await?;
|
||||||
ctx.shutdown.send(()).expect("failed to shutdown");
|
ctx.shutdown.send(()).expect("failed to shutdown");
|
||||||
Ok(())
|
Ok(setup_result)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(skip(ctx, embassy_password, recovery_password))]
|
#[instrument(skip(ctx, embassy_password, recovery_password))]
|
||||||
@@ -323,10 +317,21 @@ pub async fn execute_inner(
|
|||||||
&ctx.product_key().await?,
|
&ctx.product_key().await?,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
let res = (tor_addr, root_ca.clone());
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
if let Err(e) = recover_fut
|
if let Err(e) = recover_fut
|
||||||
.and_then(|_| async {
|
.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 {
|
if let Some(Ok(recovery_status)) = &mut *ctx.recovery_status.write().await {
|
||||||
recovery_status.complete = true;
|
recovery_status.complete = true;
|
||||||
}
|
}
|
||||||
@@ -342,16 +347,26 @@ pub async fn execute_inner(
|
|||||||
tracing::info!("Recovery Complete!");
|
tracing::info!("Recovery Complete!");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
(tor_addr, root_ca)
|
res
|
||||||
} else {
|
} else {
|
||||||
let res = fresh_setup(&ctx, &embassy_password).await?;
|
let (tor_addr, root_ca) = fresh_setup(&ctx, &embassy_password).await?;
|
||||||
init(
|
init(
|
||||||
&RpcContextConfig::load(ctx.config_path.as_ref()).await?,
|
&RpcContextConfig::load(ctx.config_path.as_ref()).await?,
|
||||||
&ctx.product_key().await?,
|
&ctx.product_key().await?,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
*ctx.disk_guid.write().await = Some(guid);
|
*ctx.setup_result.write().await = Some((
|
||||||
res
|
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)
|
Ok(res)
|
||||||
|
|||||||
@@ -4,33 +4,41 @@ import { NavGuard, RecoveryNavGuard } from './guards/nav-guard'
|
|||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{ path: '', redirectTo: '/product-key', pathMatch: 'full' },
|
{ path: '', redirectTo: '/product-key', pathMatch: 'full' },
|
||||||
{
|
|
||||||
path: 'init',
|
|
||||||
loadChildren: () => import('./pages/init/init.module').then( m => m.InitPageModule),
|
|
||||||
canActivate: [NavGuard],
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: 'product-key',
|
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',
|
path: 'home',
|
||||||
loadChildren: () => import('./pages/home/home.module').then( m => m.HomePageModule),
|
loadChildren: () =>
|
||||||
|
import('./pages/home/home.module').then(m => m.HomePageModule),
|
||||||
canActivate: [NavGuard],
|
canActivate: [NavGuard],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'recover',
|
path: 'recover',
|
||||||
loadChildren: () => import('./pages/recover/recover.module').then( m => m.RecoverPageModule),
|
loadChildren: () =>
|
||||||
|
import('./pages/recover/recover.module').then(m => m.RecoverPageModule),
|
||||||
canActivate: [RecoveryNavGuard],
|
canActivate: [RecoveryNavGuard],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'embassy',
|
path: 'embassy',
|
||||||
loadChildren: () => import('./pages/embassy/embassy.module').then( m => m.EmbassyPageModule),
|
loadChildren: () =>
|
||||||
|
import('./pages/embassy/embassy.module').then(m => m.EmbassyPageModule),
|
||||||
canActivate: [NavGuard],
|
canActivate: [NavGuard],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'loading',
|
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],
|
canActivate: [NavGuard],
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
@@ -46,4 +54,4 @@ const routes: Routes = [
|
|||||||
],
|
],
|
||||||
exports: [RouterModule],
|
exports: [RouterModule],
|
||||||
})
|
})
|
||||||
export class AppRoutingModule { }
|
export class AppRoutingModule {}
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import { AppComponent } from './app.component'
|
|||||||
import { AppRoutingModule } from './app-routing.module'
|
import { AppRoutingModule } from './app-routing.module'
|
||||||
import { GlobalErrorHandler } from './services/global-error-handler.service'
|
import { GlobalErrorHandler } from './services/global-error-handler.service'
|
||||||
import { SuccessPageModule } from './pages/success/success.module'
|
import { SuccessPageModule } from './pages/success/success.module'
|
||||||
import { InitPageModule } from './pages/init/init.module'
|
|
||||||
import { HomePageModule } from './pages/home/home.module'
|
import { HomePageModule } from './pages/home/home.module'
|
||||||
import { LoadingPageModule } from './pages/loading/loading.module'
|
import { LoadingPageModule } from './pages/loading/loading.module'
|
||||||
import { ProdKeyModalModule } from './modals/prod-key-modal/prod-key-modal.module'
|
import { ProdKeyModalModule } from './modals/prod-key-modal/prod-key-modal.module'
|
||||||
@@ -42,7 +41,6 @@ const { useMocks } = require('../../../../config.json') as WorkspaceConfig
|
|||||||
ProdKeyModalModule,
|
ProdKeyModalModule,
|
||||||
ProductKeyPageModule,
|
ProductKeyPageModule,
|
||||||
RecoverPageModule,
|
RecoverPageModule,
|
||||||
InitPageModule,
|
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy },
|
{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy },
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ export class EmbassyPage {
|
|||||||
|
|
||||||
private async setupEmbassy(drive: DiskInfo, password: string): Promise<void> {
|
private async setupEmbassy(drive: DiskInfo, password: string): Promise<void> {
|
||||||
const loader = await this.loadingCtrl.create({
|
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()
|
await loader.present()
|
||||||
@@ -139,7 +139,7 @@ export class EmbassyPage {
|
|||||||
if (!!this.stateService.recoverySource) {
|
if (!!this.stateService.recoverySource) {
|
||||||
await this.navCtrl.navigateForward(`/loading`)
|
await this.navCtrl.navigateForward(`/loading`)
|
||||||
} else {
|
} else {
|
||||||
await this.navCtrl.navigateForward(`/init`)
|
await this.navCtrl.navigateForward(`/success`)
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.errorToastService.present(
|
this.errorToastService.present(
|
||||||
|
|||||||
@@ -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 { }
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
<ion-content>
|
|
||||||
<ion-grid>
|
|
||||||
<ion-row>
|
|
||||||
<ion-col class="ion-text-center">
|
|
||||||
<div style="padding-bottom: 32px">
|
|
||||||
<img src="assets/img/logo.png" style="max-width: 240px" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<success
|
|
||||||
[hidden]="!stateService.embassyLoaded"
|
|
||||||
(onDownload)="download()"
|
|
||||||
></success>
|
|
||||||
|
|
||||||
<ion-card [hidden]="stateService.embassyLoaded" color="dark">
|
|
||||||
<ion-card-header>
|
|
||||||
<ion-card-title style="font-size: 40px"
|
|
||||||
>Initializing Embassy</ion-card-title
|
|
||||||
>
|
|
||||||
<ion-card-subtitle>Progress: {{ progress }}%</ion-card-subtitle>
|
|
||||||
</ion-card-header>
|
|
||||||
|
|
||||||
<ion-card-content class="ion-margin">
|
|
||||||
<ion-progress-bar
|
|
||||||
color="primary"
|
|
||||||
style="
|
|
||||||
max-width: 700px;
|
|
||||||
margin: auto;
|
|
||||||
padding-bottom: 20px;
|
|
||||||
margin-bottom: 40px;
|
|
||||||
"
|
|
||||||
[value]="progress / 100"
|
|
||||||
></ion-progress-bar>
|
|
||||||
<p class="ion-text-start">
|
|
||||||
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.
|
|
||||||
</p>
|
|
||||||
<p class="ion-text-start" style="color: var(--ion-color-danger)">
|
|
||||||
<b>DO NOT:</b> Close or refresh the browser window during
|
|
||||||
intialization process.
|
|
||||||
</p>
|
|
||||||
</ion-card-content>
|
|
||||||
</ion-card>
|
|
||||||
</ion-col>
|
|
||||||
</ion-row>
|
|
||||||
</ion-grid>
|
|
||||||
</ion-content>
|
|
||||||
@@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -8,19 +8,20 @@ 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.dataCompletionSubject.subscribe(async complete => {
|
const progSub = this.stateService.dataCompletionSubject.subscribe(
|
||||||
if (complete) {
|
async complete => {
|
||||||
progSub.unsubscribe()
|
if (complete) {
|
||||||
await this.navCtrl.navigateForward(`/init`)
|
progSub.unsubscribe()
|
||||||
}
|
await this.navCtrl.navigateForward(`/success`)
|
||||||
})
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -204,7 +204,7 @@ export class RecoverPage {
|
|||||||
await loader.present()
|
await loader.present()
|
||||||
try {
|
try {
|
||||||
await this.stateService.importDrive(guid)
|
await this.stateService.importDrive(guid)
|
||||||
await this.navCtrl.navigateForward(`/init`)
|
await this.navCtrl.navigateForward(`/success`)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.errorToastService.present(e.message)
|
this.errorToastService.present(e.message)
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { NgModule } from '@angular/core'
|
import { NgModule } from '@angular/core'
|
||||||
import { RouterModule, Routes } from '@angular/router'
|
import { RouterModule, Routes } from '@angular/router'
|
||||||
import { InitPage } from './init.page'
|
import { SuccessPage } from './success.page'
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
component: InitPage,
|
component: SuccessPage,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -13,4 +13,4 @@ const routes: Routes = [
|
|||||||
imports: [RouterModule.forChild(routes)],
|
imports: [RouterModule.forChild(routes)],
|
||||||
exports: [RouterModule],
|
exports: [RouterModule],
|
||||||
})
|
})
|
||||||
export class InitPageRoutingModule { }
|
export class SuccessPageRoutingModule {}
|
||||||
@@ -4,6 +4,7 @@ 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 '../../modals/password/password.module'
|
import { PasswordPageModule } from '../../modals/password/password.module'
|
||||||
|
import { SuccessPageRoutingModule } from './success-routing.module'
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
@@ -11,8 +12,9 @@ import { PasswordPageModule } from '../../modals/password/password.module'
|
|||||||
FormsModule,
|
FormsModule,
|
||||||
IonicModule,
|
IonicModule,
|
||||||
PasswordPageModule,
|
PasswordPageModule,
|
||||||
|
SuccessPageRoutingModule,
|
||||||
],
|
],
|
||||||
declarations: [SuccessPage],
|
declarations: [SuccessPage],
|
||||||
exports: [SuccessPage],
|
exports: [SuccessPage],
|
||||||
})
|
})
|
||||||
export class SuccessPageModule { }
|
export class SuccessPageModule {}
|
||||||
|
|||||||
@@ -1,167 +1,243 @@
|
|||||||
|
<ion-content>
|
||||||
|
<ion-grid>
|
||||||
|
<ion-row>
|
||||||
|
<ion-col>
|
||||||
|
<ion-card color="dark">
|
||||||
|
<ion-card-header class="ion-text-center" color="success">
|
||||||
|
<ion-icon
|
||||||
|
style="font-size: 80px"
|
||||||
|
name="checkmark-circle-outline"
|
||||||
|
></ion-icon>
|
||||||
|
<ion-card-title>Setup Complete!</ion-card-title>
|
||||||
|
</ion-card-header>
|
||||||
|
<ion-card-content>
|
||||||
|
<br />
|
||||||
|
<ng-template
|
||||||
|
[ngIf]="stateService.recoverySource && stateService.recoverySource.type === 'disk'"
|
||||||
|
>
|
||||||
|
<h2>You can now safely unplug your backup drive.</h2>
|
||||||
|
</ng-template>
|
||||||
|
<!-- Tor Instructions -->
|
||||||
|
<div (click)="toggleTor()" class="toggle-label">
|
||||||
|
<h2>Tor Instructions:</h2>
|
||||||
|
<ion-icon
|
||||||
|
name="chevron-down-outline"
|
||||||
|
[ngStyle]="{
|
||||||
|
'transform': torOpen ? 'rotate(-90deg)' : 'rotate(0deg)',
|
||||||
|
'transition': 'transform 0.4s ease-out'
|
||||||
|
}"
|
||||||
|
></ion-icon>
|
||||||
|
</div>
|
||||||
|
|
||||||
<ion-card color="dark">
|
<div
|
||||||
<ion-card-header class="ion-text-center" color="success">
|
[ngStyle]="{
|
||||||
<ion-icon style="font-size: 80px;" name="checkmark-circle-outline"></ion-icon>
|
'overflow' : 'hidden',
|
||||||
<ion-card-title>Setup Complete!</ion-card-title>
|
'max-height': torOpen ? '500px' : '0px',
|
||||||
</ion-card-header>
|
'transition': 'max-height 0.4s ease-out'
|
||||||
<ion-card-content>
|
}"
|
||||||
<br />
|
>
|
||||||
<ng-template [ngIf]="stateService.recoverySource && stateService.recoverySource.type === 'disk'">
|
<div class="ion-padding ion-text-start">
|
||||||
<h2>You can now safely unplug your backup drive.</h2>
|
<p>
|
||||||
</ng-template>
|
To use your Embassy over Tor, visit its unique Tor address
|
||||||
<!-- Tor Instructions -->
|
from any Tor-enabled browser. For a list of recommended
|
||||||
<div (click)="toggleTor()" class="toggle-label">
|
browsers, click
|
||||||
<h2>Tor Instructions:</h2>
|
<a
|
||||||
<ion-icon
|
href="https://start9.com/latest/user-manual/connecting/connecting-tor"
|
||||||
name="chevron-down-outline"
|
target="_blank"
|
||||||
[ngStyle]="{
|
rel="noreferrer"
|
||||||
'transform': torOpen ? 'rotate(-90deg)' : 'rotate(0deg)',
|
><b>here</b></a
|
||||||
'transition': 'transform 0.4s ease-out'
|
>.
|
||||||
}"
|
</p>
|
||||||
></ion-icon>
|
<br />
|
||||||
</div>
|
<p>Tor Address</p>
|
||||||
|
<ion-item lines="none" color="dark">
|
||||||
|
<ion-label class="ion-text-wrap">
|
||||||
|
<code
|
||||||
|
><ion-text color="light"
|
||||||
|
>{{ stateService.torAddress }}</ion-text
|
||||||
|
></code
|
||||||
|
>
|
||||||
|
</ion-label>
|
||||||
|
<ion-button
|
||||||
|
color="light"
|
||||||
|
fill="clear"
|
||||||
|
(click)="copy(stateService.torAddress)"
|
||||||
|
>
|
||||||
|
<ion-icon slot="icon-only" name="copy-outline"></ion-icon>
|
||||||
|
</ion-button>
|
||||||
|
</ion-item>
|
||||||
|
</div>
|
||||||
|
<div style="padding-bottom: 24px; border-bottom: solid 1px"></div>
|
||||||
|
<br />
|
||||||
|
</div>
|
||||||
|
|
||||||
<div
|
<!-- LAN Instructions -->
|
||||||
[ngStyle]="{
|
<div (click)="toggleLan()" class="toggle-label">
|
||||||
'overflow' : 'hidden',
|
<h2>LAN Instructions (Slightly Advanced):</h2>
|
||||||
'max-height': torOpen ? '500px' : '0px',
|
<ion-icon
|
||||||
'transition': 'max-height 0.4s ease-out'
|
name="chevron-down-outline"
|
||||||
}"
|
[ngStyle]="{
|
||||||
>
|
'transform': lanOpen ? 'rotate(-90deg)' : 'rotate(0deg)',
|
||||||
<div class="ion-padding ion-text-start">
|
'transition': 'transform 0.4s ease-out'
|
||||||
<p>
|
}"
|
||||||
To use your Embassy over Tor, visit its unique Tor address from any Tor-enabled browser.
|
></ion-icon>
|
||||||
For a list of recommended browsers, click <a href="https://start9.com/latest/user-manual/connecting" target="_blank" rel="noreferrer"><b>here</b></a>.
|
</div>
|
||||||
</p>
|
|
||||||
<br />
|
|
||||||
<p>Tor Address</p>
|
|
||||||
<ion-item lines="none" color="dark">
|
|
||||||
<ion-label class="ion-text-wrap">
|
|
||||||
<code><ion-text color="light">{{ stateService.torAddress }}</ion-text></code>
|
|
||||||
</ion-label>
|
|
||||||
<ion-button color="light" fill="clear" (click)="copy(stateService.torAddress)">
|
|
||||||
<ion-icon slot="icon-only" name="copy-outline"></ion-icon>
|
|
||||||
</ion-button>
|
|
||||||
</ion-item>
|
|
||||||
</div>
|
|
||||||
<div style="padding-bottom: 24px; border-bottom: solid 1px;"></div>
|
|
||||||
<br />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- LAN Instructions -->
|
<div
|
||||||
<div (click)="toggleLan()" class="toggle-label">
|
[ngStyle]="{
|
||||||
<h2>LAN Instructions (Slightly Advanced):</h2>
|
'overflow' : 'hidden',
|
||||||
<ion-icon
|
'max-height': lanOpen ? '500px' : '0px',
|
||||||
name="chevron-down-outline"
|
'transition': 'max-height 0.4s ease-out'
|
||||||
[ngStyle]="{
|
}"
|
||||||
'transform': lanOpen ? 'rotate(-90deg)' : 'rotate(0deg)',
|
>
|
||||||
'transition': 'transform 0.4s ease-out'
|
<div class="ion-padding ion-text-start">
|
||||||
}"
|
<p>To use your Embassy locally, you must:</p>
|
||||||
></ion-icon>
|
<ol>
|
||||||
</div>
|
<li>
|
||||||
|
Currently be connected to the same Local Area Network (LAN)
|
||||||
|
as your Embassy.
|
||||||
|
</li>
|
||||||
|
<li>Download your Embassy's Root Certificate Authority.</li>
|
||||||
|
<li>
|
||||||
|
Trust your Embassy's Root CA on <i>both</i> your
|
||||||
|
computer/phone and in your browser settings.
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
<p>
|
||||||
|
For step-by-step instructions, click
|
||||||
|
<a
|
||||||
|
href="https://start9.com/latest/user-manual/connecting/connecting-lan"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
><b>here</b></a
|
||||||
|
>.
|
||||||
|
</p>
|
||||||
|
|
||||||
<div
|
<p>
|
||||||
[ngStyle]="{
|
<b
|
||||||
'overflow' : 'hidden',
|
>Please note, once setup is complete, the embassy.local
|
||||||
'max-height': lanOpen ? '500px' : '0px',
|
address will no longer connect to your Embassy.</b
|
||||||
'transition': 'max-height 0.4s ease-out'
|
>
|
||||||
}"
|
</p>
|
||||||
>
|
|
||||||
<div class="ion-padding ion-text-start">
|
|
||||||
<p>To use your Embassy locally, you must:</p>
|
|
||||||
<ol>
|
|
||||||
<li>Currently be connected to the same Local Area Network (LAN) as your Embassy.</li>
|
|
||||||
<li>Download your Embassy's Root Certificate Authority.</li>
|
|
||||||
<li>Trust your Embassy's Root CA on <i>both</i> your computer/phone and in your browser settings.</li>
|
|
||||||
</ol>
|
|
||||||
<p>
|
|
||||||
For step-by-step instructions, click
|
|
||||||
<a href="https://start9.com/latest/user-manual/connecting/connecting-lan" target="_blank" rel="noreferrer"><b>here</b></a>.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
<ion-button
|
||||||
<b>Please note, once setup is complete, the embassy.local address will no longer connect to your Embassy.</b>
|
style="margin-top: 24px; margin-bottom: 24px"
|
||||||
</p>
|
color="light"
|
||||||
|
(click)="installCert()"
|
||||||
|
>
|
||||||
|
Download Root CA
|
||||||
|
<ion-icon slot="end" name="download-outline"></ion-icon>
|
||||||
|
</ion-button>
|
||||||
|
|
||||||
<ion-button style="margin-top: 24px; margin-bottom: 24px;" color="light" (click)="installCert()">
|
<p>LAN Address</p>
|
||||||
Download Root CA
|
<ion-item lines="none" color="dark">
|
||||||
<ion-icon slot="end" name="download-outline"></ion-icon>
|
<ion-label class="ion-text-wrap">
|
||||||
</ion-button>
|
<code
|
||||||
|
><ion-text color="light"
|
||||||
|
>{{ stateService.lanAddress }}</ion-text
|
||||||
|
></code
|
||||||
|
>
|
||||||
|
</ion-label>
|
||||||
|
<ion-button
|
||||||
|
color="light"
|
||||||
|
fill="clear"
|
||||||
|
(click)="copy(stateService.lanAddress)"
|
||||||
|
>
|
||||||
|
<ion-icon slot="icon-only" name="copy-outline"></ion-icon>
|
||||||
|
</ion-button>
|
||||||
|
</ion-item>
|
||||||
|
</div>
|
||||||
|
<div style="padding-bottom: 24px; border-bottom: solid 1px"></div>
|
||||||
|
<br />
|
||||||
|
</div>
|
||||||
|
<div class="ion-text-center ion-padding-top">
|
||||||
|
<ion-button
|
||||||
|
color="light"
|
||||||
|
fill="clear"
|
||||||
|
color="primary"
|
||||||
|
strong
|
||||||
|
(click)="download()"
|
||||||
|
>
|
||||||
|
Download this page
|
||||||
|
<ion-icon slot="end" name="download-outline"></ion-icon>
|
||||||
|
</ion-button>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
</ion-card-content>
|
||||||
|
</ion-card>
|
||||||
|
|
||||||
<p>LAN Address</p>
|
<!-- cert elem -->
|
||||||
<ion-item lines="none" color="dark">
|
<a hidden id="install-cert" download="embassy.crt"></a>
|
||||||
<ion-label class="ion-text-wrap">
|
|
||||||
<code><ion-text color="light">{{ stateService.lanAddress }}</ion-text></code>
|
|
||||||
</ion-label>
|
|
||||||
<ion-button color="light" fill="clear" (click)="copy(stateService.lanAddress)">
|
|
||||||
<ion-icon slot="icon-only" name="copy-outline"></ion-icon>
|
|
||||||
</ion-button>
|
|
||||||
</ion-item>
|
|
||||||
</div>
|
|
||||||
<div style="padding-bottom: 24px; border-bottom: solid 1px;"></div>
|
|
||||||
<br />
|
|
||||||
</div>
|
|
||||||
<div class="ion-text-center ion-padding-top">
|
|
||||||
<ion-button color="light" fill="clear" color="primary" strong (click)="download()">
|
|
||||||
Download this page
|
|
||||||
<ion-icon slot="end" name="download-outline"></ion-icon>
|
|
||||||
</ion-button>
|
|
||||||
</div>
|
|
||||||
<br />
|
|
||||||
</ion-card-content>
|
|
||||||
</ion-card>
|
|
||||||
|
|
||||||
|
<!-- download elem -->
|
||||||
|
<div hidden id="downloadable">
|
||||||
|
<div style="padding: 0 24px; font-family: Courier">
|
||||||
|
<h1>Embassy Info</h1>
|
||||||
|
|
||||||
<!-- cert elem -->
|
<section style="padding: 16px; border: solid 1px">
|
||||||
<a hidden id="install-cert" download="embassy.crt"></a>
|
<h2>Tor Info</h2>
|
||||||
|
<p>
|
||||||
|
To use your Embassy over Tor, visit its unique Tor address from
|
||||||
|
any Tor-enabled browser.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
For a list of recommended browsers, click
|
||||||
|
<a
|
||||||
|
href="https://start9.com/latest/user-manual/connecting/connecting-tor"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
><b>here</b></a
|
||||||
|
>.
|
||||||
|
</p>
|
||||||
|
<p><b>Tor Address: </b><code id="tor-addr"></code></p>
|
||||||
|
</section>
|
||||||
|
|
||||||
<!-- download elem -->
|
<section style="padding: 16px; border: solid 1px; border-top: none">
|
||||||
<div hidden id="downloadable">
|
<h2>LAN Info</h2>
|
||||||
<div style="padding: 0 24px; font-family: Courier;">
|
<p>To use your Embassy locally, you must:</p>
|
||||||
<h1>Embassy Info</h1>
|
<ol>
|
||||||
|
<li>
|
||||||
|
Currently be connected to the same Local Area Network (LAN) as
|
||||||
|
your Embassy.
|
||||||
|
</li>
|
||||||
|
<li>Download your Embassy's Root Certificate Authority.</li>
|
||||||
|
<li>
|
||||||
|
Trust your Embassy's Root CA on <i>both</i> your
|
||||||
|
computer/phone and in your browser settings.
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
<p>
|
||||||
|
For step-by-step instructions, click
|
||||||
|
<a
|
||||||
|
href="https://start9.com/latest/user-manual/connecting/connecting-lan"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
><b>here</b></a
|
||||||
|
>.
|
||||||
|
</p>
|
||||||
|
|
||||||
<section style="padding: 16px; border: solid 1px;">
|
<div style="margin: 42px 0">
|
||||||
<h2>Tor Info</h2>
|
<a
|
||||||
<p>
|
id="cert"
|
||||||
To use your Embassy over Tor, visit its unique Tor address from any Tor-enabled browser.
|
download="embassy.crt"
|
||||||
</p>
|
style="
|
||||||
<p>
|
background: #25272b;
|
||||||
For a list of recommended browsers, click <a href="https://start9.com/latest/user-manual/connecting" target="_blank" rel="noreferrer"><b>here</b></a>.
|
padding: 10px;
|
||||||
</p>
|
text-decoration: none;
|
||||||
<p><b>Tor Address: </b><code id="tor-addr"></code></p>
|
text-align: center;
|
||||||
</section>
|
border-radius: 4px;
|
||||||
|
color: white;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
Download Root CA
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
<section style="padding: 16px; border: solid 1px; border-top: none;">
|
<p><b>LAN Address: </b><code id="lan-addr"></code></p>
|
||||||
<h2>LAN Info</h2>
|
</section>
|
||||||
<p>To use your Embassy locally, you must:</p>
|
</div>
|
||||||
<ol>
|
</div>
|
||||||
<li>Currently be connected to the same Local Area Network (LAN) as your Embassy.</li>
|
</ion-col>
|
||||||
<li>Download your Embassy's Root Certificate Authority.</li>
|
</ion-row>
|
||||||
<li>Trust your Embassy's Root CA on <i>both</i> your computer/phone and in your browser settings.</li>
|
</ion-grid>
|
||||||
</ol>
|
</ion-content>
|
||||||
<p>
|
|
||||||
For step-by-step instructions, click
|
|
||||||
<a href="https://start9.com/latest/user-manual/connecting/connecting-lan" target="_blank" rel="noreferrer"><b>here</b></a>.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div style="margin: 42px 0;">
|
|
||||||
<a
|
|
||||||
id="cert"
|
|
||||||
download="embassy.crt"
|
|
||||||
style="
|
|
||||||
background: #25272b;
|
|
||||||
padding: 10px;
|
|
||||||
text-decoration: none;
|
|
||||||
text-align: center;
|
|
||||||
border-radius: 4px;
|
|
||||||
color: white;
|
|
||||||
"
|
|
||||||
>
|
|
||||||
Download Root CA
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p><b>LAN Address: </b><code id="lan-addr"></code></p>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Component, EventEmitter, Output } from '@angular/core'
|
import { Component, EventEmitter, Output } from '@angular/core'
|
||||||
import { ToastController } from '@ionic/angular'
|
import { ToastController } from '@ionic/angular'
|
||||||
|
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'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@@ -12,16 +13,29 @@ export class SuccessPage {
|
|||||||
torOpen = true
|
torOpen = true
|
||||||
lanOpen = false
|
lanOpen = false
|
||||||
|
|
||||||
constructor (
|
constructor(
|
||||||
private readonly toastCtrl: ToastController,
|
private readonly toastCtrl: ToastController,
|
||||||
|
private readonly errCtrl: ErrorToastService,
|
||||||
public readonly stateService: StateService,
|
public readonly stateService: StateService,
|
||||||
) { }
|
) {}
|
||||||
|
|
||||||
ngAfterViewInit () {
|
async ngAfterViewInit() {
|
||||||
document.getElementById('install-cert').setAttribute('href', 'data:application/x-x509-ca-cert;base64,' + encodeURIComponent(this.stateService.cert))
|
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<void> {
|
async copy(address: string): Promise<void> {
|
||||||
const success = await this.copyToClipboard(address)
|
const success = await this.copyToClipboard(address)
|
||||||
const message = success ? 'copied to clipboard!' : 'failed to copy'
|
const message = success ? 'copied to clipboard!' : 'failed to copy'
|
||||||
|
|
||||||
@@ -33,23 +47,45 @@ export class SuccessPage {
|
|||||||
await toast.present()
|
await toast.present()
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleTor () {
|
toggleTor() {
|
||||||
this.torOpen = !this.torOpen
|
this.torOpen = !this.torOpen
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleLan () {
|
toggleLan() {
|
||||||
this.lanOpen = !this.lanOpen
|
this.lanOpen = !this.lanOpen
|
||||||
}
|
}
|
||||||
|
|
||||||
installCert () {
|
installCert() {
|
||||||
document.getElementById('install-cert').click()
|
document.getElementById('install-cert').click()
|
||||||
}
|
}
|
||||||
|
|
||||||
download () {
|
download() {
|
||||||
this.onDownload.emit()
|
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<boolean> {
|
private async copyToClipboard(str: string): Promise<boolean> {
|
||||||
const el = document.createElement('textarea')
|
const el = document.createElement('textarea')
|
||||||
el.value = str
|
el.value = str
|
||||||
el.setAttribute('readonly', '')
|
el.setAttribute('readonly', '')
|
||||||
@@ -62,4 +98,3 @@ export class SuccessPage {
|
|||||||
return copy
|
return copy
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
export abstract class ApiService {
|
export abstract class ApiService {
|
||||||
// unencrypted
|
// unencrypted
|
||||||
abstract getStatus (): Promise<GetStatusRes> // setup.status
|
abstract getStatus(): Promise<GetStatusRes> // setup.status
|
||||||
abstract getDrives (): Promise<DiskListResponse> // setup.disk.list
|
abstract getDrives(): Promise<DiskListResponse> // setup.disk.list
|
||||||
abstract set02XDrive (logicalname: string): Promise<void> // setup.recovery.v2.set
|
abstract set02XDrive(logicalname: string): Promise<void> // setup.recovery.v2.set
|
||||||
abstract getRecoveryStatus (): Promise<RecoveryStatusRes> // setup.recovery.status
|
abstract getRecoveryStatus(): Promise<RecoveryStatusRes> // setup.recovery.status
|
||||||
|
|
||||||
// encrypted
|
// encrypted
|
||||||
abstract verifyCifs (cifs: CifsRecoverySource): Promise<EmbassyOSRecoveryInfo> // setup.cifs.verify
|
abstract verifyCifs(cifs: CifsRecoverySource): Promise<EmbassyOSRecoveryInfo> // setup.cifs.verify
|
||||||
abstract verifyProductKey (): Promise<void> // echo - throws error if invalid
|
abstract verifyProductKey(): Promise<void> // echo - throws error if invalid
|
||||||
abstract importDrive (guid: string): Promise<SetupEmbassyRes> // setup.execute
|
abstract importDrive(guid: string): Promise<SetupEmbassyRes> // setup.execute
|
||||||
abstract setupEmbassy (setupInfo: SetupEmbassyReq): Promise<SetupEmbassyRes> // setup.execute
|
abstract setupEmbassy(setupInfo: SetupEmbassyReq): Promise<SetupEmbassyRes> // setup.execute
|
||||||
abstract setupComplete (): Promise<void> // setup.complete
|
abstract setupComplete(): Promise<SetupEmbassyRes> // setup.complete
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GetStatusRes {
|
export interface GetStatusRes {
|
||||||
@@ -75,12 +75,12 @@ export interface CifsRecoverySource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface DiskInfo {
|
export interface DiskInfo {
|
||||||
logicalname: string,
|
logicalname: string
|
||||||
vendor: string | null,
|
vendor: string | null
|
||||||
model: string | null,
|
model: string | null
|
||||||
partitions: PartitionInfo[],
|
partitions: PartitionInfo[]
|
||||||
capacity: number,
|
capacity: number
|
||||||
guid: string | null, // cant back up if guid exists
|
guid: string | null // cant back up if guid exists
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RecoveryStatusRes {
|
export interface RecoveryStatusRes {
|
||||||
@@ -90,9 +90,9 @@ export interface RecoveryStatusRes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface PartitionInfo {
|
export interface PartitionInfo {
|
||||||
logicalname: string,
|
logicalname: string
|
||||||
label: string | null,
|
label: string | null
|
||||||
capacity: number,
|
capacity: number
|
||||||
used: number | null,
|
used: number | null
|
||||||
'embassy-os': EmbassyOSRecoveryInfo | null,
|
'embassy-os': EmbassyOSRecoveryInfo | null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,49 +1,71 @@
|
|||||||
import { Injectable } from '@angular/core'
|
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'
|
import { HttpService } from './http.service'
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
})
|
})
|
||||||
export class LiveApiService extends ApiService {
|
export class LiveApiService extends ApiService {
|
||||||
|
constructor(private readonly http: HttpService) {
|
||||||
constructor (
|
super()
|
||||||
private readonly http: HttpService,
|
}
|
||||||
) { super() }
|
|
||||||
|
|
||||||
// ** UNENCRYPTED **
|
// ** UNENCRYPTED **
|
||||||
|
|
||||||
async getStatus () {
|
async getStatus() {
|
||||||
return this.http.rpcRequest<GetStatusRes>({
|
return this.http.rpcRequest<GetStatusRes>(
|
||||||
method: 'setup.status',
|
{
|
||||||
params: { },
|
method: 'setup.status',
|
||||||
}, false)
|
params: {},
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async getDrives () {
|
async getDrives() {
|
||||||
return this.http.rpcRequest<DiskListResponse>({
|
return this.http.rpcRequest<DiskListResponse>(
|
||||||
method: 'setup.disk.list',
|
{
|
||||||
params: { },
|
method: 'setup.disk.list',
|
||||||
}, false)
|
params: {},
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async set02XDrive (logicalname) {
|
async set02XDrive(logicalname) {
|
||||||
return this.http.rpcRequest<void>({
|
return this.http.rpcRequest<void>(
|
||||||
method: 'setup.recovery.v2.set',
|
{
|
||||||
params: { logicalname },
|
method: 'setup.recovery.v2.set',
|
||||||
}, false)
|
params: { logicalname },
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async getRecoveryStatus () {
|
async getRecoveryStatus() {
|
||||||
return this.http.rpcRequest<RecoveryStatusRes>({
|
return this.http.rpcRequest<RecoveryStatusRes>(
|
||||||
method: 'setup.recovery.status',
|
{
|
||||||
params: { },
|
method: 'setup.recovery.status',
|
||||||
}, false)
|
params: {},
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ** ENCRYPTED **
|
// ** ENCRYPTED **
|
||||||
|
|
||||||
async verifyCifs (source: CifsRecoverySource) {
|
async verifyCifs(source: CifsRecoverySource) {
|
||||||
source.path = source.path.replace('/\\/g', '/')
|
source.path = source.path.replace('/\\/g', '/')
|
||||||
return this.http.rpcRequest<EmbassyOSRecoveryInfo>({
|
return this.http.rpcRequest<EmbassyOSRecoveryInfo>({
|
||||||
method: 'setup.cifs.verify',
|
method: 'setup.cifs.verify',
|
||||||
@@ -51,14 +73,14 @@ export class LiveApiService extends ApiService {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async verifyProductKey () {
|
async verifyProductKey() {
|
||||||
return this.http.rpcRequest<void>({
|
return this.http.rpcRequest<void>({
|
||||||
method: 'echo',
|
method: 'echo',
|
||||||
params: { 'message': 'hello' },
|
params: { message: 'hello' },
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async importDrive (guid: string) {
|
async importDrive(guid: string) {
|
||||||
const res = await this.http.rpcRequest<SetupEmbassyRes>({
|
const res = await this.http.rpcRequest<SetupEmbassyRes>({
|
||||||
method: 'setup.attach',
|
method: 'setup.attach',
|
||||||
params: { guid },
|
params: { guid },
|
||||||
@@ -70,9 +92,11 @@ export class LiveApiService extends ApiService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async setupEmbassy (setupInfo: SetupEmbassyReq) {
|
async setupEmbassy(setupInfo: SetupEmbassyReq) {
|
||||||
if (isCifsSource(setupInfo['recovery-source'])) {
|
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<SetupEmbassyRes>({
|
const res = await this.http.rpcRequest<SetupEmbassyRes>({
|
||||||
@@ -86,14 +110,16 @@ export class LiveApiService extends ApiService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async setupComplete () {
|
async setupComplete() {
|
||||||
await this.http.rpcRequest<SetupEmbassyRes>({
|
return this.http.rpcRequest<SetupEmbassyRes>({
|
||||||
method: 'setup.complete',
|
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
|
return !!(source as CifsRecoverySource)?.hostname
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -96,6 +96,7 @@ export class MockApiService extends ApiService {
|
|||||||
|
|
||||||
async setupComplete() {
|
async setupComplete() {
|
||||||
await pauseFor(1000)
|
await pauseFor(1000)
|
||||||
|
return setupRes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -91,4 +91,11 @@ export class StateService {
|
|||||||
this.lanAddress = ret['lan-address']
|
this.lanAddress = ret['lan-address']
|
||||||
this.cert = ret['root-ca']
|
this.cert = ret['root-ca']
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async completeEmbassy(): Promise<void> {
|
||||||
|
const ret = await this.apiService.setupComplete()
|
||||||
|
this.torAddress = ret['tor-address']
|
||||||
|
this.lanAddress = ret['lan-address']
|
||||||
|
this.cert = ret['root-ca']
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,9 +26,6 @@ export class MarkdownPage {
|
|||||||
if (!this.content) {
|
if (!this.content) {
|
||||||
this.content = await this.embassyApi.getStatic(this.contentUrl)
|
this.content = await this.embassyApi.getStatic(this.contentUrl)
|
||||||
}
|
}
|
||||||
} catch (e) {
|
|
||||||
this.loadingError = getErrorMessage(e)
|
|
||||||
} finally {
|
|
||||||
this.loading = false
|
this.loading = false
|
||||||
await pauseFor(50)
|
await pauseFor(50)
|
||||||
const links = document.links
|
const links = document.links
|
||||||
@@ -39,6 +36,9 @@ export class MarkdownPage {
|
|||||||
links[i].className += ' externalLink'
|
links[i].className += ' externalLink'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch (e) {
|
||||||
|
this.loadingError = getErrorMessage(e)
|
||||||
|
this.loading = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ export class AppShowStatusComponent {
|
|||||||
const { id, title, version } = this.pkg.manifest
|
const { id, title, version } = this.pkg.manifest
|
||||||
const hasDependents = !!Object.keys(
|
const hasDependents = !!Object.keys(
|
||||||
this.pkg.installed['current-dependents'],
|
this.pkg.installed['current-dependents'],
|
||||||
).filter(depId => depId !== this.pkg.manifest.id).length
|
).filter(depId => depId !== id).length
|
||||||
|
|
||||||
if (!hasDependents) {
|
if (!hasDependents) {
|
||||||
const loader = await this.loadingCtrl.create({
|
const loader = await this.loadingCtrl.create({
|
||||||
|
|||||||
Reference in New Issue
Block a user