mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-30 20:14:49 +00:00
refactor loaders, better err toasts, rework Embassy tab organization
This commit is contained in:
committed by
Aiden McClelland
parent
2ff9c622ac
commit
eb245aea50
@@ -1,17 +1,15 @@
|
||||
import { Component, ViewChild } from '@angular/core'
|
||||
import { ActivatedRoute } from '@angular/router'
|
||||
import { ApiService } from 'src/app/services/api/embassy/embassy-api.service'
|
||||
import { AlertController, IonContent, ModalController, NavController } from '@ionic/angular'
|
||||
import { LoaderService } from 'src/app/services/loader.service'
|
||||
import { HttpErrorResponse } from '@angular/common/http'
|
||||
import { AlertController, IonContent, LoadingController, ModalController, NavController } from '@ionic/angular'
|
||||
import { PatchDbService } from 'src/app/services/patch-db/patch-db.service'
|
||||
import { Action, Manifest, PackageDataEntry, PackageMainStatus } from 'src/app/services/patch-db/data-model'
|
||||
import { wizardModal } from 'src/app/components/install-wizard/install-wizard.component'
|
||||
import { WizardBaker } from 'src/app/components/install-wizard/prebaked-wizards'
|
||||
import { Subscription } from 'rxjs'
|
||||
import { AppConfigObjectPage } from 'src/app/modals/app-config-object/app-config-object.page'
|
||||
import { ConfigCursor } from 'src/app/pkg-config/config-cursor'
|
||||
import { AppActionInputPage } from 'src/app/modals/app-action-input/app-action-input.page'
|
||||
import { ErrorToastService } from 'src/app/services/error-toast.service'
|
||||
|
||||
@Component({
|
||||
selector: 'app-actions',
|
||||
@@ -29,7 +27,8 @@ export class AppActionsPage {
|
||||
private readonly embassyApi: ApiService,
|
||||
private readonly modalCtrl: ModalController,
|
||||
private readonly alertCtrl: AlertController,
|
||||
private readonly loaderService: LoaderService,
|
||||
private readonly errToast: ErrorToastService,
|
||||
private readonly loadingCtrl: LoadingController,
|
||||
private readonly wizardBaker: WizardBaker,
|
||||
private readonly navCtrl: NavController,
|
||||
public readonly patch: PatchDbService,
|
||||
@@ -120,11 +119,16 @@ export class AppActionsPage {
|
||||
return this.navCtrl.navigateRoot('/services')
|
||||
}
|
||||
|
||||
private async executeAction (pkgId: string, actionId: string) {
|
||||
private async executeAction (pkgId: string, actionId: string): Promise<void> {
|
||||
const loader = await this.loadingCtrl.create({
|
||||
spinner: 'lines',
|
||||
message: 'Executing action...',
|
||||
cssClass: 'loader',
|
||||
})
|
||||
await loader.present()
|
||||
|
||||
try {
|
||||
const res = await this.loaderService.displayDuringP(
|
||||
this.embassyApi.executePackageAction({ id: pkgId, 'action-id': actionId }),
|
||||
)
|
||||
const res = await this.embassyApi.executePackageAction({ id: pkgId, 'action-id': actionId })
|
||||
|
||||
const successAlert = await this.alertCtrl.create({
|
||||
header: 'Execution Complete',
|
||||
@@ -132,23 +136,11 @@ export class AppActionsPage {
|
||||
buttons: ['OK'],
|
||||
cssClass: 'alert-success-message',
|
||||
})
|
||||
return await successAlert.present()
|
||||
await successAlert.present()
|
||||
} catch (e) {
|
||||
if (e instanceof HttpErrorResponse) {
|
||||
this.presentAlertActionFail(e.status, e.message)
|
||||
} else {
|
||||
this.presentAlertActionFail(-1, e.message || JSON.stringify(e))
|
||||
}
|
||||
this.errToast.present(e)
|
||||
} finally {
|
||||
loader.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
private async presentAlertActionFail (code: number, message: string): Promise<void> {
|
||||
const failureAlert = await this.alertCtrl.create({
|
||||
header: 'Execution Failed',
|
||||
message: `Error code ${code}. ${message}`,
|
||||
buttons: ['OK'],
|
||||
cssClass: 'alert-error-message',
|
||||
})
|
||||
return await failureAlert.present()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { Component, ViewChild } from '@angular/core'
|
||||
import { NavController, AlertController, ModalController, IonContent } from '@ionic/angular'
|
||||
import { NavController, AlertController, ModalController, IonContent, LoadingController } from '@ionic/angular'
|
||||
import { ActivatedRoute } from '@angular/router'
|
||||
import { ApiService } from 'src/app/services/api/embassy/embassy-api.service'
|
||||
import { isEmptyObject, Recommendation } from 'src/app/util/misc.util'
|
||||
import { LoaderService } from 'src/app/services/loader.service'
|
||||
import { TrackingModalController } from 'src/app/services/tracking-modal-controller.service'
|
||||
import { from, fromEvent, of, Subscription } from 'rxjs'
|
||||
import { catchError, concatMap, map, take, tap } from 'rxjs/operators'
|
||||
@@ -13,6 +12,7 @@ import { ConfigSpec } from 'src/app/pkg-config/config-types'
|
||||
import { ConfigCursor } from 'src/app/pkg-config/config-cursor'
|
||||
import { PackageDataEntry, PackageState } from 'src/app/services/patch-db/data-model'
|
||||
import { PatchDbService } from 'src/app/services/patch-db/patch-db.service'
|
||||
import { ErrorToastService } from 'src/app/services/error-toast.service'
|
||||
|
||||
@Component({
|
||||
selector: 'app-config',
|
||||
@@ -51,7 +51,8 @@ export class AppConfigPage {
|
||||
private readonly route: ActivatedRoute,
|
||||
private readonly wizardBaker: WizardBaker,
|
||||
private readonly embassyApi: ApiService,
|
||||
private readonly loader: LoaderService,
|
||||
private readonly errToast: ErrorToastService,
|
||||
private readonly loadingCtrl: LoadingController,
|
||||
private readonly alertCtrl: AlertController,
|
||||
private readonly modalController: ModalController,
|
||||
private readonly trackingModalCtrl: TrackingModalController,
|
||||
@@ -85,7 +86,7 @@ export class AppConfigPage {
|
||||
this.patch.watch$('package-data', pkgId)
|
||||
.pipe(
|
||||
tap(pkg => this.pkg = pkg),
|
||||
tap(() => this.loadingText = 'Fetching config spec...'),
|
||||
tap(() => this.loadingText = 'Loading config...'),
|
||||
concatMap(() => this.embassyApi.getPackageConfig({ id: pkgId })),
|
||||
concatMap(({ spec, config }) => {
|
||||
const rec = history.state && history.state.configRecommendation as Recommendation
|
||||
@@ -157,11 +158,14 @@ export class AppConfigPage {
|
||||
}
|
||||
|
||||
async save (pkg: PackageDataEntry) {
|
||||
return this.loader.of({
|
||||
message: `Saving config...`,
|
||||
const loader = await this.loadingCtrl.create({
|
||||
spinner: 'lines',
|
||||
message: `Saving config...`,
|
||||
cssClass: 'loader',
|
||||
}).displayDuringAsync(async () => {
|
||||
})
|
||||
await loader.present()
|
||||
|
||||
try {
|
||||
const breakages = await this.embassyApi.drySetPackageConfig({ id: pkg.manifest.id, config: this.config })
|
||||
|
||||
if (!isEmptyObject(breakages.length)) {
|
||||
@@ -172,17 +176,16 @@ export class AppConfigPage {
|
||||
breakages,
|
||||
}),
|
||||
)
|
||||
if (cancelled) return { skip: true }
|
||||
if (cancelled) return
|
||||
}
|
||||
|
||||
return this.embassyApi.setPackageConfig({ id: pkg.manifest.id, config: this.config })
|
||||
.then(() => ({ skip: false }))
|
||||
})
|
||||
.then(({ skip }) => {
|
||||
if (skip) return
|
||||
await this.embassyApi.setPackageConfig({ id: pkg.manifest.id, config: this.config })
|
||||
this.navCtrl.back()
|
||||
})
|
||||
.catch(e => this.error = { text: e.message })
|
||||
} catch (e) {
|
||||
this.errToast.present(e)
|
||||
} finally {
|
||||
loader.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
handleObjectEdit () {
|
||||
|
||||
@@ -31,8 +31,7 @@ export class AppInstructionsPage {
|
||||
try {
|
||||
this.instructions = await this.embassyApi.getStatic(url)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
this.errToast.present(e.message)
|
||||
this.errToast.present(e)
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
|
||||
@@ -33,8 +33,7 @@ export class AppLogsPage {
|
||||
this.logs = logs.map(l => `${l.timestamp} ${l.log}`).join('\n\n')
|
||||
setTimeout(async () => await this.content.scrollToBottom(100), 200)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
this.errToast.present(e.message)
|
||||
this.errToast.present(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,8 +68,7 @@ export class AppMetricsPage {
|
||||
try {
|
||||
this.metrics = await this.embassyApi.getPkgMetrics({ id: this.pkgId})
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
this.errToast.present(e.message)
|
||||
this.errToast.present(e)
|
||||
this.stopDaemon()
|
||||
} finally {
|
||||
this.loading = false
|
||||
|
||||
@@ -122,8 +122,7 @@ export class AppPropertiesPage {
|
||||
this.properties = await this.embassyApi.getPackageProperties({ id: this.pkgId })
|
||||
this.node = JsonPointer.get(this.properties, this.pointer || '')
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
this.errToast.present(e.message)
|
||||
this.errToast.present(e)
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
|
||||
@@ -32,36 +32,29 @@
|
||||
<ion-text class="ion-text-wrap" color="warning">No partitions available. Insert the storage device containing the backup you intend to restore.</ion-text>
|
||||
</ion-item>
|
||||
|
||||
<ion-grid>
|
||||
<ion-row>
|
||||
<ion-col *ngFor="let disk of disks | keyvalue" sizeSm="12" sizeMd="6">
|
||||
<ion-card>
|
||||
<ion-card-header>
|
||||
<ion-card-title>
|
||||
{{ disk.value.size }}
|
||||
</ion-card-title>
|
||||
<ion-card-subtitle>
|
||||
{{ disk.key }}
|
||||
</ion-card-subtitle>
|
||||
</ion-card-header>
|
||||
<ion-card-content>
|
||||
<ion-item-group>
|
||||
<ion-item button *ngFor="let partition of disk.value.partitions | keyvalue" [disabled]="partition.value['is-mounted']" (click)="presentModal(partition.key)">
|
||||
<ion-icon slot="start" name="save-outline"></ion-icon>
|
||||
<ion-label>
|
||||
<h2>{{ partition.value.label || partition.key }} ({{ partition.value.size || 'unknown size' }})</h2>
|
||||
<p *ngIf="!partition.value['is-mounted']; else unavailable"><ion-text color="success">Available</ion-text></p>
|
||||
<ng-template #unavailable>
|
||||
<p><ion-text color="danger">Unavailable</ion-text></p>
|
||||
</ng-template>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
</ion-item-group>
|
||||
</ion-card-content>
|
||||
</ion-card>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
</ion-grid>
|
||||
<ion-card *ngFor="let disk of disks | keyvalue">
|
||||
<ion-card-header>
|
||||
<ion-card-title>
|
||||
{{ disk.value.size }}
|
||||
</ion-card-title>
|
||||
<ion-card-subtitle>
|
||||
{{ disk.key }}
|
||||
</ion-card-subtitle>
|
||||
</ion-card-header>
|
||||
<ion-card-content>
|
||||
<ion-item-group>
|
||||
<ion-item button *ngFor="let partition of disk.value.partitions | keyvalue" [disabled]="partition.value['is-mounted']" (click)="presentModal(partition.key)">
|
||||
<ion-icon slot="start" name="save-outline"></ion-icon>
|
||||
<ion-label>
|
||||
<h2>{{ partition.value.label || partition.key }} ({{ partition.value.size || 'unknown size' }})</h2>
|
||||
<p *ngIf="!partition.value['is-mounted']; else unavailable"><ion-text color="success">Available</ion-text></p>
|
||||
<ng-template #unavailable>
|
||||
<p><ion-text color="danger">Unavailable</ion-text></p>
|
||||
</ng-template>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
</ion-item-group>
|
||||
</ion-card-content>
|
||||
</ion-card>
|
||||
</ng-template>
|
||||
|
||||
</ion-content>
|
||||
|
||||
@@ -7,6 +7,7 @@ import { ActivatedRoute } from '@angular/router'
|
||||
import { PatchDbService } from 'src/app/services/patch-db/patch-db.service'
|
||||
import { Subscription } from 'rxjs'
|
||||
import { take } from 'rxjs/operators'
|
||||
import { ErrorToastService } from 'src/app/services/error-toast.service'
|
||||
|
||||
@Component({
|
||||
selector: 'app-restore',
|
||||
@@ -18,7 +19,6 @@ export class AppRestorePage {
|
||||
pkgId: string
|
||||
title: string
|
||||
loading = true
|
||||
error: string
|
||||
allPartitionsMounted: boolean
|
||||
|
||||
@ViewChild(IonContent) content: IonContent
|
||||
@@ -29,6 +29,7 @@ export class AppRestorePage {
|
||||
private readonly modalCtrl: ModalController,
|
||||
private readonly embassyApi: ApiService,
|
||||
private readonly loadingCtrl: LoadingController,
|
||||
private readonly errToast: ErrorToastService,
|
||||
public readonly patch: PatchDbService,
|
||||
) { }
|
||||
|
||||
@@ -51,8 +52,7 @@ export class AppRestorePage {
|
||||
this.disks = await this.embassyApi.getDisks({ })
|
||||
this.allPartitionsMounted = Object.values(this.disks).every(d => Object.values(d.partitions).every(p => p['is-mounted']))
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
this.error = e.message
|
||||
this.errToast.present(e)
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
@@ -74,12 +74,10 @@ export class AppRestorePage {
|
||||
this.restore(logicalname, data.password)
|
||||
})
|
||||
|
||||
return await m.present()
|
||||
await m.present()
|
||||
}
|
||||
|
||||
private async restore (logicalname: string, password: string): Promise<void> {
|
||||
this.error = ''
|
||||
|
||||
const loader = await this.loadingCtrl.create({
|
||||
spinner: 'lines',
|
||||
})
|
||||
@@ -92,8 +90,7 @@ export class AppRestorePage {
|
||||
password,
|
||||
})
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
this.error = e.message
|
||||
this.errToast.present(e)
|
||||
} finally {
|
||||
loader.dismiss()
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import { Component, ViewChild } from '@angular/core'
|
||||
import { AlertController, NavController, ModalController, IonContent } from '@ionic/angular'
|
||||
import { AlertController, NavController, ModalController, IonContent, LoadingController } from '@ionic/angular'
|
||||
import { ApiService } from 'src/app/services/api/embassy/embassy-api.service'
|
||||
import { ActivatedRoute, NavigationExtras } from '@angular/router'
|
||||
import { chill, isEmptyObject, Recommendation } from 'src/app/util/misc.util'
|
||||
import { LoaderService } from 'src/app/services/loader.service'
|
||||
import { combineLatest, Observable, of, Subscription } from 'rxjs'
|
||||
import { combineLatest, Subscription } from 'rxjs'
|
||||
import { wizardModal } from 'src/app/components/install-wizard/install-wizard.component'
|
||||
import { WizardBaker } from 'src/app/components/install-wizard/prebaked-wizards'
|
||||
import { ConfigService } from 'src/app/services/config.service'
|
||||
@@ -38,7 +37,7 @@ export class AppShowPage {
|
||||
private readonly route: ActivatedRoute,
|
||||
private readonly navCtrl: NavController,
|
||||
private readonly errToast: ErrorToastService,
|
||||
private readonly loader: LoaderService,
|
||||
private readonly loadingCtrl: LoadingController,
|
||||
private readonly modalCtrl: ModalController,
|
||||
private readonly embassyApi: ApiService,
|
||||
private readonly wizardBaker: WizardBaker,
|
||||
@@ -77,11 +76,14 @@ export class AppShowPage {
|
||||
|
||||
async stop (): Promise<void> {
|
||||
const { id, title, version } = this.pkg.manifest
|
||||
await this.loader.of({
|
||||
const loader = await this.loadingCtrl.create({
|
||||
message: `Stopping...`,
|
||||
spinner: 'lines',
|
||||
cssClass: 'loader',
|
||||
}).displayDuringAsync(async () => {
|
||||
})
|
||||
await loader.present()
|
||||
|
||||
try {
|
||||
const breakages = await this.embassyApi.dryStopPackage({ id })
|
||||
|
||||
console.log('BREAKAGES', breakages)
|
||||
@@ -96,12 +98,14 @@ export class AppShowPage {
|
||||
breakages,
|
||||
}),
|
||||
)
|
||||
|
||||
if (cancelled) return { }
|
||||
if (cancelled) return
|
||||
}
|
||||
|
||||
return this.embassyApi.stopPackage({ id }).then(chill)
|
||||
}).catch(e => this.setError(e))
|
||||
} catch (e) {
|
||||
this.errToast.present(e)
|
||||
} finally {
|
||||
loader.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
async tryStart (): Promise<void> {
|
||||
@@ -206,19 +210,20 @@ export class AppShowPage {
|
||||
}
|
||||
|
||||
private async start (): Promise<void> {
|
||||
this.loader.of({
|
||||
const loader = await this.loadingCtrl.create({
|
||||
message: `Starting...`,
|
||||
spinner: 'lines',
|
||||
cssClass: 'loader',
|
||||
}).displayDuringP(
|
||||
this.embassyApi.startPackage({ id: this.pkgId }),
|
||||
).catch(e => this.setError(e))
|
||||
}
|
||||
})
|
||||
await loader.present()
|
||||
|
||||
private setError (e: Error): Observable<void> {
|
||||
console.error(e)
|
||||
this.errToast.present(e.message)
|
||||
return of()
|
||||
try {
|
||||
await this.embassyApi.startPackage({ id: this.pkgId })
|
||||
} catch (e) {
|
||||
this.errToast.present(e)
|
||||
} finally {
|
||||
loader.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
setButtons (): void {
|
||||
|
||||
@@ -68,8 +68,7 @@ export class MarketplaceListPage {
|
||||
this.data.categories = [this.category, 'updates'].concat(filterdCategories).concat(['all'])
|
||||
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
this.errToast.present(e.message)
|
||||
this.errToast.present(e)
|
||||
} finally {
|
||||
this.pageLoading = false
|
||||
this.pkgsLoading = false
|
||||
@@ -128,8 +127,7 @@ export class MarketplaceListPage {
|
||||
this.pkgs = doInfinite ? this.pkgs.concat(pkgs) : pkgs
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
this.errToast.present(e.message)
|
||||
this.errToast.present(e)
|
||||
} finally {
|
||||
this.pkgsLoading = false
|
||||
}
|
||||
|
||||
@@ -71,8 +71,7 @@ export class MarketplaceShowPage {
|
||||
try {
|
||||
await this.marketplaceService.getPkg(this.pkgId, version)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
this.errToast.present(e.message)
|
||||
this.errToast.present(e)
|
||||
} finally {
|
||||
await pauseFor(100)
|
||||
this.loading = false
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { Component } from '@angular/core'
|
||||
import { ApiService } from 'src/app/services/api/embassy/embassy-api.service'
|
||||
import { LoaderService } from 'src/app/services/loader.service'
|
||||
import { ServerNotification, ServerNotifications } from 'src/app/services/api/api.types'
|
||||
import { AlertController } from '@ionic/angular'
|
||||
import { AlertController, LoadingController } from '@ionic/angular'
|
||||
import { ActivatedRoute } from '@angular/router'
|
||||
import { ErrorToastService } from 'src/app/services/error-toast.service'
|
||||
|
||||
@@ -21,7 +20,7 @@ export class NotificationsPage {
|
||||
|
||||
constructor (
|
||||
private readonly embassyApi: ApiService,
|
||||
private readonly loader: LoaderService,
|
||||
private readonly loadingCtrl: LoadingController,
|
||||
private readonly errToast: ErrorToastService,
|
||||
private readonly alertCtrl: AlertController,
|
||||
private readonly route: ActivatedRoute,
|
||||
@@ -52,26 +51,28 @@ export class NotificationsPage {
|
||||
this.needInfinite = notifications.length >= this.perPage
|
||||
this.page++
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
this.errToast.present(e.message)
|
||||
this.errToast.present(e)
|
||||
} finally {
|
||||
return notifications
|
||||
}
|
||||
}
|
||||
|
||||
async remove (id: string, index: number): Promise<void> {
|
||||
this.loader.of({
|
||||
message: 'Deleting...',
|
||||
const loader = await this.loadingCtrl.create({
|
||||
spinner: 'lines',
|
||||
message: 'Deleting...',
|
||||
cssClass: 'loader',
|
||||
}).displayDuringP(
|
||||
this.embassyApi.deleteNotification({ id }).then(() => {
|
||||
this.notifications.splice(index, 1)
|
||||
}),
|
||||
).catch(e => {
|
||||
console.error(e)
|
||||
this.errToast.present(e.message)
|
||||
})
|
||||
await loader.present()
|
||||
|
||||
try {
|
||||
await this.embassyApi.deleteNotification({ id })
|
||||
this.notifications.splice(index, 1)
|
||||
} catch (e) {
|
||||
this.errToast.present(e)
|
||||
} finally {
|
||||
loader.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
async viewBackupReport (notification: ServerNotification<1>) {
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-buttons slot="start">
|
||||
<pwa-back-button></pwa-back-button>
|
||||
</ion-buttons>
|
||||
<ion-title>Developer Options</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content class="ion-padding-top" *ngIf="patch.data['server-info'] as server">
|
||||
|
||||
<ion-item-group>
|
||||
<ion-item detail="true" button [routerLink]="['ssh-keys']">
|
||||
<ion-label>SSH Keys</ion-label>
|
||||
</ion-item>
|
||||
</ion-item-group>
|
||||
|
||||
</ion-content>
|
||||
@@ -1,20 +0,0 @@
|
||||
import { Component } from '@angular/core'
|
||||
import { ServerConfigService } from 'src/app/services/server-config.service'
|
||||
import { PatchDbService } from 'src/app/services/patch-db/patch-db.service'
|
||||
|
||||
@Component({
|
||||
selector: 'dev-options',
|
||||
templateUrl: './dev-options.page.html',
|
||||
styleUrls: ['./dev-options.page.scss'],
|
||||
})
|
||||
export class DevOptionsPage {
|
||||
|
||||
constructor (
|
||||
private readonly serverConfigService: ServerConfigService,
|
||||
public readonly patch: PatchDbService,
|
||||
) { }
|
||||
|
||||
async presentModalValueEdit (key: string, current?: any): Promise<void> {
|
||||
await this.serverConfigService.presentModalValueEdit(key, current)
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,10 @@
|
||||
<ion-icon slot="start" name="list-outline"></ion-icon>
|
||||
<ion-label>View Instructions</ion-label>
|
||||
</ion-item>
|
||||
<ion-item button (click)="installCert()" [disabled]="lanDisabled">
|
||||
<ion-icon slot="start" name="download-outline"></ion-icon>
|
||||
<ion-label>Download Root Certificate Authority</ion-label>
|
||||
</ion-item>
|
||||
|
||||
<ng-container *ngIf="lanDisabled">
|
||||
<ion-item-divider></ion-item-divider>
|
||||
@@ -37,38 +41,13 @@
|
||||
<ion-item>
|
||||
<ion-label class="ion-text-wrap">
|
||||
<p style="padding-bottom: 6px;">Troubleshooting</p>
|
||||
<h2>If you are having issues connecting to your Embassy over LAN, try refreshing the network by clicking the button below.</h2>
|
||||
<h2>If you are having issues connecting to your Embassy over LAN, try refreshing your LAN services by clicking the button below.</h2>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item button (click)="refreshLAN()" detail="false">
|
||||
<ion-icon slot="start" name="refresh-outline"></ion-icon>
|
||||
<ion-label>Refresh Network</ion-label>
|
||||
<ion-label>Refresh LAN</ion-label>
|
||||
</ion-item>
|
||||
|
||||
<ion-item-divider></ion-item-divider>
|
||||
|
||||
<!-- Certificate and Lan Address -->
|
||||
<ng-container *ngIf="!lanDisabled">
|
||||
<ion-item>
|
||||
<ion-label class="ion-text-wrap">
|
||||
<h2>Root Certificate Authority</h2>
|
||||
<p>Embassy Local CA</p>
|
||||
</ion-label>
|
||||
<ion-button slot="end" fill="clear" (click)="installCert()">
|
||||
<ion-icon slot="icon-only" name="download-outline"></ion-icon>
|
||||
</ion-button>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label class="ion-text-wrap">
|
||||
<h2>LAN Address</h2>
|
||||
<p>{{ lanAddress }}</p>
|
||||
</ion-label>
|
||||
<ion-button slot="end" fill="clear" (click)="copyLAN()">
|
||||
<ion-icon slot="icon-only" name="copy-outline"></ion-icon>
|
||||
</ion-button>
|
||||
</ion-item>
|
||||
</ng-container>
|
||||
</ion-item-group>
|
||||
|
||||
<!-- hidden element for downloading cert -->
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { Component } from '@angular/core'
|
||||
import { isPlatform, ToastController } from '@ionic/angular'
|
||||
import { isPlatform, LoadingController, ToastController } from '@ionic/angular'
|
||||
import { copyToClipboard } from 'src/app/util/web.util'
|
||||
import { ConfigService } from 'src/app/services/config.service'
|
||||
import { LoaderService } from 'src/app/services/loader.service'
|
||||
import { ApiService } from 'src/app/services/api/embassy/embassy-api.service'
|
||||
import { PatchDbService } from 'src/app/services/patch-db/patch-db.service'
|
||||
import { Subscription } from 'rxjs'
|
||||
import { ErrorToastService } from 'src/app/services/error-toast.service'
|
||||
|
||||
@Component({
|
||||
selector: 'lan',
|
||||
@@ -25,7 +25,8 @@ export class LANPage {
|
||||
constructor (
|
||||
private readonly toastCtrl: ToastController,
|
||||
private readonly config: ConfigService,
|
||||
private readonly loader: LoaderService,
|
||||
private readonly loadingCtrl: LoadingController,
|
||||
private readonly errToast: ErrorToastService,
|
||||
private readonly embassyApi: ApiService,
|
||||
private readonly patch: PatchDbService,
|
||||
) { }
|
||||
@@ -49,15 +50,21 @@ export class LANPage {
|
||||
}
|
||||
|
||||
async refreshLAN (): Promise<void> {
|
||||
this.loader.of({
|
||||
message: 'Refreshing Network',
|
||||
const loader = await this.loadingCtrl.create({
|
||||
spinner: 'lines',
|
||||
message: 'Refreshing LAN...',
|
||||
cssClass: 'loader',
|
||||
}).displayDuringAsync( async () => {
|
||||
await this.embassyApi.refreshLan({ })
|
||||
}).catch(e => {
|
||||
console.error(e)
|
||||
})
|
||||
await loader.present()
|
||||
|
||||
try {
|
||||
await this.embassyApi.refreshLan({ })
|
||||
this.presentToastSuccess()
|
||||
} catch (e) {
|
||||
this.errToast.present(e)
|
||||
} finally {
|
||||
loader.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
async copyLAN (): Promise <void> {
|
||||
@@ -74,6 +81,27 @@ export class LANPage {
|
||||
installCert (): void {
|
||||
document.getElementById('install-cert').click()
|
||||
}
|
||||
|
||||
private async presentToastSuccess (): Promise<void> {
|
||||
const toast = await this.toastCtrl.create({
|
||||
header: 'Success',
|
||||
message: `LAN refreshed.`,
|
||||
position: 'bottom',
|
||||
duration: 3000,
|
||||
buttons: [
|
||||
{
|
||||
side: 'start',
|
||||
icon: 'close',
|
||||
handler: () => {
|
||||
return true
|
||||
},
|
||||
},
|
||||
],
|
||||
cssClass: 'success-toast',
|
||||
})
|
||||
|
||||
await toast.present()
|
||||
}
|
||||
}
|
||||
|
||||
enum LanSetupIssue {
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
import { NgModule } from '@angular/core'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { PrivacyPage } from './privacy.page'
|
||||
import { Routes, RouterModule } from '@angular/router'
|
||||
import { SharingModule } from 'src/app/modules/sharing.module'
|
||||
import { PwaBackComponentModule } from 'src/app/components/pwa-back-button/pwa-back.component.module'
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: PrivacyPage,
|
||||
},
|
||||
]
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
IonicModule,
|
||||
SharingModule,
|
||||
RouterModule.forChild(routes),
|
||||
PwaBackComponentModule,
|
||||
],
|
||||
declarations: [
|
||||
PrivacyPage,
|
||||
],
|
||||
})
|
||||
export class PrivacyPageModule { }
|
||||
@@ -1,7 +1,7 @@
|
||||
import { NgModule } from '@angular/core'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { DevOptionsPage } from './dev-options.page'
|
||||
import { SecurityOptionsPage } from './security-options.page'
|
||||
import { Routes, RouterModule } from '@angular/router'
|
||||
import { PwaBackComponentModule } from 'src/app/components/pwa-back-button/pwa-back.component.module'
|
||||
import { ObjectConfigComponentModule } from 'src/app/components/object-config/object-config.component.module'
|
||||
@@ -10,7 +10,7 @@ import { SharingModule } from 'src/app/modules/sharing.module'
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: DevOptionsPage,
|
||||
component: SecurityOptionsPage,
|
||||
},
|
||||
]
|
||||
|
||||
@@ -24,7 +24,7 @@ const routes: Routes = [
|
||||
SharingModule,
|
||||
],
|
||||
declarations: [
|
||||
DevOptionsPage,
|
||||
SecurityOptionsPage,
|
||||
],
|
||||
})
|
||||
export class DevOptionsPageModule { }
|
||||
export class SecurityOptionsPageModule { }
|
||||
@@ -7,28 +7,37 @@
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content class="ion-padding-top">
|
||||
<ion-content class="ion-padding-top" *ngIf="patch.data['server-info'] as server">
|
||||
|
||||
<ion-item-group>
|
||||
<ion-item-divider>Marketplace Settings</ion-item-divider>
|
||||
<ion-item button (click)="presentModalValueEdit('eosMarketplace', patch.data['server-info']['eos-marketplace'])">
|
||||
<ion-label>Use Tor</ion-label>
|
||||
<ion-item-divider>General</ion-item-divider>
|
||||
<ion-item button (click)="presentModalValueEdit('shareStats', patch.data['server-info']['share-stats'])">
|
||||
<ion-label>Share Anonymous Statistics</ion-label>
|
||||
<ion-note slot="end">{{ patch.data['server-info']['share-stats'] }}</ion-note>
|
||||
</ion-item>
|
||||
|
||||
<ion-item-divider>Marketplace</ion-item-divider>
|
||||
<ion-item button (click)="presentModalValueEdit('autoCheckUpdates', patch.data.ui['auto-check-updates'])">
|
||||
<ion-label>Auto Check for Updates</ion-label>
|
||||
<ion-note slot="end">{{ patch.data.ui['auto-check-updates'] }}</ion-note>
|
||||
</ion-item>
|
||||
<ion-item button (click)="presentModalValueEdit('eosMarketplace', patch.data['server-info']['eos-marketplace'] === config.start9Marketplace.tor)">
|
||||
<ion-label>Tor Only Marketplace</ion-label>
|
||||
<ion-note slot="end">{{ patch.data['server-info']['eos-marketplace'] === config.start9Marketplace.tor }}</ion-note>
|
||||
</ion-item>
|
||||
<!-- <ion-item button (click)="presentModalValueEdit('packageMarketplace', patch.data['server-info']['package-marketplace'])">
|
||||
<ion-label>Package Marketplace</ion-label>
|
||||
<ion-note slot="end">{{ patch.data['server-info']['package-marketplace'] }}</ion-note>
|
||||
</ion-item> -->
|
||||
<ion-item button (click)="presentModalValueEdit('autoCheckUpdates', patch.data.ui['auto-check-updates'])">
|
||||
<ion-label>Auto Check for Updates</ion-label>
|
||||
<ion-note slot="end">{{ patch.data.ui['auto-check-updates'] }}</ion-note>
|
||||
</ion-item>
|
||||
|
||||
<!-- <ion-item-divider></ion-item-divider>
|
||||
<ion-item button (click)="presentModalValueEdit('password')">
|
||||
<ion-item-divider>Security</ion-item-divider>
|
||||
<!-- <ion-item button (click)="presentModalValueEdit('password')">
|
||||
<ion-label>Change Password</ion-label>
|
||||
<ion-note slot="end">********</ion-note>
|
||||
</ion-item> -->
|
||||
<ion-item detail="true" button [routerLink]="['ssh-keys']">
|
||||
<ion-label>SSH Keys</ion-label>
|
||||
</ion-item>
|
||||
</ion-item-group>
|
||||
|
||||
</ion-content>
|
||||
</ion-content>
|
||||
@@ -1,16 +1,14 @@
|
||||
import { Component } from '@angular/core'
|
||||
import { ServerConfigService } from 'src/app/services/server-config.service'
|
||||
import { PatchDbService } from 'src/app/services/patch-db/patch-db.service'
|
||||
import { Subscription } from 'rxjs'
|
||||
import { ConfigService } from 'src/app/services/config.service'
|
||||
|
||||
@Component({
|
||||
selector: 'privacy',
|
||||
templateUrl: './privacy.page.html',
|
||||
styleUrls: ['./privacy.page.scss'],
|
||||
selector: 'security-options',
|
||||
templateUrl: './security-options.page.html',
|
||||
styleUrls: ['./security-options.page.scss'],
|
||||
})
|
||||
export class PrivacyPage {
|
||||
subs: Subscription[] = []
|
||||
export class SecurityOptionsPage {
|
||||
|
||||
constructor (
|
||||
private readonly serverConfigService: ServerConfigService,
|
||||
@@ -18,7 +16,7 @@ export class PrivacyPage {
|
||||
public readonly patch: PatchDbService,
|
||||
) { }
|
||||
|
||||
async presentModalValueEdit (key: string, current?: string): Promise<void> {
|
||||
async presentModalValueEdit (key: string, current?: any): Promise<void> {
|
||||
await this.serverConfigService.presentModalValueEdit(key, current)
|
||||
}
|
||||
}
|
||||
@@ -4,11 +4,11 @@ import { Routes, RouterModule } from '@angular/router'
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
loadChildren: () => import('./dev-options/dev-options.module').then(m => m.DevOptionsPageModule),
|
||||
loadChildren: () => import('./security-options/security-options.module').then(m => m.SecurityOptionsPageModule),
|
||||
},
|
||||
{
|
||||
path: 'ssh-keys',
|
||||
loadChildren: () => import('./dev-ssh-keys/dev-ssh-keys.module').then(m => m.DevSSHKeysPageModule),
|
||||
loadChildren: () => import('./ssh-keys/ssh-keys.module').then(m => m.SSHKeysPageModule),
|
||||
},
|
||||
]
|
||||
|
||||
@@ -16,4 +16,4 @@ const routes: Routes = [
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class DeveloperRoutingModule { }
|
||||
export class SecurityRoutingModule { }
|
||||
@@ -2,7 +2,7 @@ import { NgModule } from '@angular/core'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { RouterModule, Routes } from '@angular/router'
|
||||
import { DevSSHKeysPage } from './dev-ssh-keys.page'
|
||||
import { SSHKeysPage } from './ssh-keys.page'
|
||||
import { PwaBackComponentModule } from 'src/app/components/pwa-back-button/pwa-back.component.module'
|
||||
import { SharingModule } from 'src/app/modules/sharing.module'
|
||||
import { TextSpinnerComponentModule } from 'src/app/components/text-spinner/text-spinner.component.module'
|
||||
@@ -10,7 +10,7 @@ import { TextSpinnerComponentModule } from 'src/app/components/text-spinner/text
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: DevSSHKeysPage,
|
||||
component: SSHKeysPage,
|
||||
},
|
||||
]
|
||||
|
||||
@@ -23,6 +23,6 @@ const routes: Routes = [
|
||||
SharingModule,
|
||||
TextSpinnerComponentModule,
|
||||
],
|
||||
declarations: [DevSSHKeysPage],
|
||||
declarations: [SSHKeysPage],
|
||||
})
|
||||
export class DevSSHKeysPageModule { }
|
||||
export class SSHKeysPageModule { }
|
||||
@@ -1,24 +1,23 @@
|
||||
import { Component } from '@angular/core'
|
||||
import { ServerConfigService } from 'src/app/services/server-config.service'
|
||||
import { AlertController } from '@ionic/angular'
|
||||
import { LoaderService } from 'src/app/services/loader.service'
|
||||
import { AlertController, LoadingController } from '@ionic/angular'
|
||||
import { SSHService } from './ssh.service'
|
||||
import { Subscription } from 'rxjs'
|
||||
import { SSHKeys } from 'src/app/services/api/api.types'
|
||||
import { ErrorToastService } from 'src/app/services/error-toast.service'
|
||||
|
||||
@Component({
|
||||
selector: 'dev-ssh-keys',
|
||||
templateUrl: 'dev-ssh-keys.page.html',
|
||||
styleUrls: ['dev-ssh-keys.page.scss'],
|
||||
selector: 'ssh-keys',
|
||||
templateUrl: 'ssh-keys.page.html',
|
||||
styleUrls: ['ssh-keys.page.scss'],
|
||||
})
|
||||
export class DevSSHKeysPage {
|
||||
export class SSHKeysPage {
|
||||
loading = true
|
||||
sshKeys: SSHKeys
|
||||
subs: Subscription[] = []
|
||||
|
||||
constructor (
|
||||
private readonly loader: LoaderService,
|
||||
private readonly loadingCtrl: LoadingController,
|
||||
private readonly errToast: ErrorToastService,
|
||||
private readonly serverConfigService: ServerConfigService,
|
||||
private readonly alertCtrl: AlertController,
|
||||
@@ -69,16 +68,20 @@ export class DevSSHKeysPage {
|
||||
}
|
||||
|
||||
async delete (hash: string): Promise<void> {
|
||||
this.loader.of({
|
||||
message: 'Deleting...',
|
||||
const loader = await this.loadingCtrl.create({
|
||||
spinner: 'lines',
|
||||
message: 'Deleting...',
|
||||
cssClass: 'loader',
|
||||
}).displayDuringAsync(async () => {
|
||||
await this.sshService.delete(hash)
|
||||
}).catch(e => {
|
||||
console.error(e)
|
||||
this.errToast.present(e.message)
|
||||
})
|
||||
await loader.present()
|
||||
|
||||
try {
|
||||
await this.sshService.delete(hash)
|
||||
} catch (e) {
|
||||
this.errToast.present(e)
|
||||
} finally {
|
||||
loader.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
asIsOrder (a: any, b: any) {
|
||||
@@ -33,36 +33,30 @@
|
||||
<ion-text *ngIf="type === 'restore'" class="ion-text-wrap" color="warning">No partitions available. Insert the storage device containing the backup you wish to restore.</ion-text>
|
||||
</ion-item>
|
||||
|
||||
<ion-grid>
|
||||
<ion-row>
|
||||
<ion-col *ngFor="let disk of disks | keyvalue" sizeSm="12" sizeMd="6">
|
||||
<ion-card>
|
||||
<ion-card-header>
|
||||
<ion-card-title>
|
||||
{{ disk.value.size }}
|
||||
</ion-card-title>
|
||||
<ion-card-subtitle>
|
||||
{{ disk.key }}
|
||||
</ion-card-subtitle>
|
||||
</ion-card-header>
|
||||
<ion-card-content>
|
||||
<ion-item-group>
|
||||
<ion-item button *ngFor="let partition of disk.value.partitions | keyvalue" [disabled]="partition.value['is-mounted']" (click)="presentModal(partition.key)">
|
||||
<ion-icon slot="start" name="save-outline"></ion-icon>
|
||||
<ion-label>
|
||||
<h2>{{ partition.value.label || partition.key }} ({{ partition.value.size || 'unknown size' }})</h2>
|
||||
<p *ngIf="!partition.value['is-mounted']; else unavailable"><ion-text color="success">Available</ion-text></p>
|
||||
<ng-template #unavailable>
|
||||
<p><ion-text color="danger">Unavailable</ion-text></p>
|
||||
</ng-template>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
</ion-item-group>
|
||||
</ion-card-content>
|
||||
</ion-card>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
</ion-grid>
|
||||
<ion-card *ngFor="let disk of disks | keyvalue">
|
||||
<ion-card-header>
|
||||
<ion-card-title>
|
||||
{{ disk.value.size }}
|
||||
</ion-card-title>
|
||||
<ion-card-subtitle>
|
||||
{{ disk.key }}
|
||||
</ion-card-subtitle>
|
||||
</ion-card-header>
|
||||
<ion-card-content>
|
||||
<ion-item-group>
|
||||
<ion-item button *ngFor="let partition of disk.value.partitions | keyvalue" [disabled]="partition.value['is-mounted']" (click)="presentModal(partition.key)">
|
||||
<ion-icon slot="start" name="save-outline"></ion-icon>
|
||||
<ion-label>
|
||||
<h2>{{ partition.value.label || partition.key }} ({{ partition.value.size || 'unknown size' }})</h2>
|
||||
<p *ngIf="!partition.value['is-mounted']; else unavailable"><ion-text color="success">Available</ion-text></p>
|
||||
<ng-template #unavailable>
|
||||
<p><ion-text color="danger">Unavailable</ion-text></p>
|
||||
</ng-template>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
</ion-item-group>
|
||||
</ion-card-content>
|
||||
</ion-card>
|
||||
</ng-template>
|
||||
|
||||
</ion-content>
|
||||
|
||||
@@ -3,6 +3,7 @@ import { LoadingController, ModalController } from '@ionic/angular'
|
||||
import { ApiService } from 'src/app/services/api/embassy/embassy-api.service'
|
||||
import { BackupConfirmationComponent } from 'src/app/modals/backup-confirmation/backup-confirmation.component'
|
||||
import { DiskInfo } from 'src/app/services/api/api.types'
|
||||
import { ErrorToastService } from 'src/app/services/error-toast.service'
|
||||
|
||||
@Component({
|
||||
selector: 'server-backup',
|
||||
@@ -12,12 +13,12 @@ import { DiskInfo } from 'src/app/services/api/api.types'
|
||||
export class ServerBackupPage {
|
||||
disks: DiskInfo
|
||||
loading = true
|
||||
error: string
|
||||
allPartitionsMounted: boolean
|
||||
|
||||
constructor (
|
||||
private readonly modalCtrl: ModalController,
|
||||
private readonly embassyApi: ApiService,
|
||||
private readonly errToast: ErrorToastService,
|
||||
private readonly loadingCtrl: LoadingController,
|
||||
) { }
|
||||
|
||||
@@ -35,8 +36,7 @@ export class ServerBackupPage {
|
||||
this.disks = await this.embassyApi.getDisks({ })
|
||||
this.allPartitionsMounted = Object.values(this.disks).every(d => Object.values(d.partitions).every(p => p['is-mounted']))
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
this.error = e.message
|
||||
this.errToast.present(e)
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
@@ -62,18 +62,17 @@ export class ServerBackupPage {
|
||||
}
|
||||
|
||||
private async create (logicalname: string, password: string): Promise<void> {
|
||||
this.error = ''
|
||||
|
||||
const loader = await this.loadingCtrl.create({
|
||||
spinner: 'lines',
|
||||
message: 'Starting backup...',
|
||||
cssClass: 'loader',
|
||||
})
|
||||
await loader.present()
|
||||
|
||||
try {
|
||||
await this.embassyApi.createBackup({ logicalname, password })
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
this.error = e.message
|
||||
this.errToast.present(e)
|
||||
} finally {
|
||||
loader.dismiss()
|
||||
}
|
||||
|
||||
@@ -30,8 +30,7 @@ export class ServerLogsPage {
|
||||
this.logs = logs.map(l => `${l.timestamp} ${l.log}`).join('\n\n')
|
||||
setTimeout(async () => await this.content.scrollToBottom(100), 200)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
this.errToast.present(e.message)
|
||||
this.errToast.present(e)
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
|
||||
@@ -43,8 +43,7 @@ export class ServerMetricsPage {
|
||||
try {
|
||||
this.metrics = await this.embassyApi.getServerMetrics({ })
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
this.errToast.present(e.message)
|
||||
this.errToast.present(e)
|
||||
this.stopDaemon()
|
||||
} finally {
|
||||
this.loading = false
|
||||
|
||||
@@ -22,10 +22,6 @@ const routes: Routes = [
|
||||
path: 'logs',
|
||||
loadChildren: () => import('./server-logs/server-logs.module').then(m => m.ServerLogsPageModule),
|
||||
},
|
||||
{
|
||||
path: 'privacy',
|
||||
loadChildren: () => import('./privacy/privacy.module').then(m => m.PrivacyPageModule),
|
||||
},
|
||||
{
|
||||
path: 'wifi',
|
||||
loadChildren: () => import('./wifi/wifi.module').then(m => m.WifiListPageModule),
|
||||
@@ -35,8 +31,8 @@ const routes: Routes = [
|
||||
loadChildren: () => import('./lan/lan.module').then(m => m.LANPageModule),
|
||||
},
|
||||
{
|
||||
path: 'developer',
|
||||
loadChildren: () => import('./developer-routes/developer-routing.module').then( m => m.DeveloperRoutingModule),
|
||||
path: 'security',
|
||||
loadChildren: () => import('./security-routes/security-routing.module').then( m => m.SecurityRoutingModule),
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
@@ -9,15 +9,13 @@
|
||||
|
||||
<ion-content>
|
||||
|
||||
<ion-grid>
|
||||
<ion-row>
|
||||
<ion-col *ngFor="let cat of settings | keyvalue : asIsOrder" sizeXs="12" sizeMd="6">
|
||||
<ion-item-divider>{{ cat.key }}</ion-item-divider>
|
||||
<ion-item style="cursor: pointer;" button *ngFor="let button of cat.value" (click)="button.action()">
|
||||
<ion-icon slot="start" [name]="button.icon"></ion-icon>
|
||||
<ion-label>{{ button.title }}</ion-label>
|
||||
</ion-item>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
</ion-grid>
|
||||
<ion-item-group>
|
||||
<div *ngFor="let cat of settings | keyvalue : asIsOrder">
|
||||
<ion-item-divider>{{ cat.key }}</ion-item-divider>
|
||||
<ion-item style="cursor: pointer;" button *ngFor="let button of cat.value" (click)="button.action()">
|
||||
<ion-icon slot="start" [name]="button.icon"></ion-icon>
|
||||
<ion-label>{{ button.title }}</ion-label>
|
||||
</ion-item>
|
||||
</div>
|
||||
</ion-item-group>
|
||||
</ion-content>
|
||||
@@ -1,9 +1,8 @@
|
||||
import { Component } from '@angular/core'
|
||||
import { LoadingOptions } from '@ionic/core'
|
||||
import { AlertController, NavController } from '@ionic/angular'
|
||||
import { AlertController, LoadingController, NavController } from '@ionic/angular'
|
||||
import { ApiService } from 'src/app/services/api/embassy/embassy-api.service'
|
||||
import { LoaderService } from 'src/app/services/loader.service'
|
||||
import { ActivatedRoute } from '@angular/router'
|
||||
import { ErrorToastService } from 'src/app/services/error-toast.service'
|
||||
|
||||
@Component({
|
||||
selector: 'server-show',
|
||||
@@ -15,7 +14,8 @@ export class ServerShowPage {
|
||||
|
||||
constructor (
|
||||
private readonly alertCtrl: AlertController,
|
||||
private readonly loader: LoaderService,
|
||||
private readonly loadingCtrl: LoadingController,
|
||||
private readonly errToast: ErrorToastService,
|
||||
private readonly embassyApi: ApiService,
|
||||
private readonly navCtrl: NavController,
|
||||
private readonly route: ActivatedRoute,
|
||||
@@ -70,21 +70,37 @@ export class ServerShowPage {
|
||||
}
|
||||
|
||||
private async restart () {
|
||||
this.loader
|
||||
.of(LoadingSpinner(`Restarting...`))
|
||||
.displayDuringAsync( async () => {
|
||||
await this.embassyApi.restartServer({ })
|
||||
})
|
||||
.catch(console.error)
|
||||
const loader = await this.loadingCtrl.create({
|
||||
spinner: 'lines',
|
||||
message: 'Restarting...',
|
||||
cssClass: 'loader',
|
||||
})
|
||||
await loader.present()
|
||||
|
||||
try {
|
||||
await this.embassyApi.restartServer({ })
|
||||
} catch (e) {
|
||||
this.errToast.present(e)
|
||||
} finally {
|
||||
loader.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
private async shutdown () {
|
||||
this.loader
|
||||
.of(LoadingSpinner(`Shutting down...`))
|
||||
.displayDuringAsync( async () => {
|
||||
await this.embassyApi.shutdownServer({ })
|
||||
})
|
||||
.catch(console.error)
|
||||
const loader = await this.loadingCtrl.create({
|
||||
spinner: 'lines',
|
||||
message: 'Shutting down...',
|
||||
cssClass: 'loader',
|
||||
})
|
||||
await loader.present()
|
||||
|
||||
try {
|
||||
await this.embassyApi.shutdownServer({ })
|
||||
} catch (e) {
|
||||
this.errToast.present(e)
|
||||
} finally {
|
||||
loader.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
private setButtons (): void {
|
||||
@@ -93,7 +109,7 @@ export class ServerShowPage {
|
||||
{
|
||||
title: 'Privacy and Security',
|
||||
icon: 'shield-checkmark-outline',
|
||||
action: () => this.navCtrl.navigateForward(['privacy'], { relativeTo: this.route }),
|
||||
action: () => this.navCtrl.navigateForward(['security'], { relativeTo: this.route }),
|
||||
},
|
||||
{
|
||||
title: 'LAN',
|
||||
@@ -105,11 +121,6 @@ export class ServerShowPage {
|
||||
icon: 'wifi',
|
||||
action: () => this.navCtrl.navigateForward(['wifi'], { relativeTo: this.route }),
|
||||
},
|
||||
{
|
||||
title: 'Developer Options',
|
||||
icon: 'terminal-outline',
|
||||
action: () => this.navCtrl.navigateForward(['developer'], { relativeTo: this.route }),
|
||||
},
|
||||
],
|
||||
'Insights': [
|
||||
{
|
||||
@@ -155,15 +166,6 @@ export class ServerShowPage {
|
||||
}
|
||||
}
|
||||
|
||||
const LoadingSpinner: (m?: string) => LoadingOptions = (m) => {
|
||||
const toMergeIn = m ? { message: m } : { }
|
||||
return {
|
||||
spinner: 'lines',
|
||||
cssClass: 'loader',
|
||||
...toMergeIn,
|
||||
} as LoadingOptions
|
||||
}
|
||||
|
||||
interface ServerSettings {
|
||||
[key: string]: {
|
||||
title: string
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { Component } from '@angular/core'
|
||||
import { NavController } from '@ionic/angular'
|
||||
import { LoadingController, NavController } from '@ionic/angular'
|
||||
import { ApiService } from 'src/app/services/api/embassy/embassy-api.service'
|
||||
import { WifiService } from '../wifi.service'
|
||||
import { LoaderService } from 'src/app/services/loader.service'
|
||||
import { ErrorToastService } from 'src/app/services/error-toast.service'
|
||||
|
||||
@Component({
|
||||
@@ -20,16 +19,19 @@ export class WifiAddPage {
|
||||
private readonly navCtrl: NavController,
|
||||
private readonly errToast: ErrorToastService,
|
||||
private readonly embassyApi: ApiService,
|
||||
private readonly loader: LoaderService,
|
||||
private readonly loadingCtrl: LoadingController,
|
||||
private readonly wifiService: WifiService,
|
||||
) { }
|
||||
|
||||
async save (): Promise<void> {
|
||||
this.loader.of({
|
||||
message: 'Saving...',
|
||||
const loader = await this.loadingCtrl.create({
|
||||
spinner: 'lines',
|
||||
message: 'Saving...',
|
||||
cssClass: 'loader',
|
||||
}).displayDuringAsync(async () => {
|
||||
})
|
||||
await loader.present()
|
||||
|
||||
try {
|
||||
await this.embassyApi.addWifi({
|
||||
ssid: this.ssid,
|
||||
password: this.password,
|
||||
@@ -38,18 +40,22 @@ export class WifiAddPage {
|
||||
connect: false,
|
||||
})
|
||||
this.navCtrl.back()
|
||||
}).catch(e => {
|
||||
console.error(e)
|
||||
this.errToast.present(e.message)
|
||||
})
|
||||
} catch (e) {
|
||||
this.errToast.present(e)
|
||||
} finally {
|
||||
loader.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
async saveAndConnect (): Promise<void> {
|
||||
this.loader.of({
|
||||
message: 'Connecting. This could take while...',
|
||||
const loader = await this.loadingCtrl.create({
|
||||
spinner: 'lines',
|
||||
message: 'Connecting. This could take while...',
|
||||
cssClass: 'loader',
|
||||
}).displayDuringAsync(async () => {
|
||||
})
|
||||
await loader.present()
|
||||
|
||||
try {
|
||||
await this.embassyApi.addWifi({
|
||||
ssid: this.ssid,
|
||||
password: this.password,
|
||||
@@ -58,13 +64,14 @@ export class WifiAddPage {
|
||||
connect: true,
|
||||
})
|
||||
const success = this.wifiService.confirmWifi(this.ssid)
|
||||
if (success) {
|
||||
this.navCtrl.back()
|
||||
}
|
||||
}).catch (e => {
|
||||
console.error(e)
|
||||
this.errToast.present(e.message)
|
||||
})
|
||||
if (success) {
|
||||
this.navCtrl.back()
|
||||
}
|
||||
} catch (e) {
|
||||
this.errToast.present(e)
|
||||
} finally {
|
||||
loader.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
asIsOrder (a: any, b: any) {
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<ion-item>
|
||||
<ion-label class="ion-text-wrap">
|
||||
<p style="padding-bottom: 6px;">About</p>
|
||||
<h2>Embassy will automatically connect to available networks, allowing you to remove the Ethernet cable.</h2>
|
||||
<h2>Embassy will automatically connect to saved WiFi networks when they are available, allowing you to remove the Ethernet cable.</h2>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { Component } from '@angular/core'
|
||||
import { ActionSheetController } from '@ionic/angular'
|
||||
import { ActionSheetController, LoadingController } from '@ionic/angular'
|
||||
import { ApiService } from 'src/app/services/api/embassy/embassy-api.service'
|
||||
import { ActionSheetButton } from '@ionic/core'
|
||||
import { WifiService } from './wifi.service'
|
||||
import { LoaderService } from 'src/app/services/loader.service'
|
||||
import { WiFiInfo } from 'src/app/services/patch-db/data-model'
|
||||
import { PatchDbService } from 'src/app/services/patch-db/patch-db.service'
|
||||
import { Subscription } from 'rxjs'
|
||||
@@ -19,7 +18,7 @@ export class WifiListPage {
|
||||
|
||||
constructor (
|
||||
private readonly embassyApi: ApiService,
|
||||
private readonly loader: LoaderService,
|
||||
private readonly loadingCtrl: LoadingController,
|
||||
private readonly errToast: ErrorToastService,
|
||||
private readonly actionCtrl: ActionSheetController,
|
||||
private readonly wifiService: WifiService,
|
||||
@@ -57,29 +56,37 @@ export class WifiListPage {
|
||||
|
||||
// Let's add country code here
|
||||
async connect (ssid: string): Promise<void> {
|
||||
this.loader.of({
|
||||
message: 'Connecting. This could take while...',
|
||||
const loader = await this.loadingCtrl.create({
|
||||
spinner: 'lines',
|
||||
message: 'Connecting. This could take while...',
|
||||
cssClass: 'loader',
|
||||
}).displayDuringAsync(async () => {
|
||||
})
|
||||
await loader.present()
|
||||
|
||||
try {
|
||||
await this.embassyApi.connectWifi({ ssid })
|
||||
this.wifiService.confirmWifi(ssid)
|
||||
}).catch(e => {
|
||||
console.error(e)
|
||||
this.errToast.present(e.message)
|
||||
})
|
||||
} catch (e) {
|
||||
this.errToast.present(e)
|
||||
} finally {
|
||||
loader.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
async delete (ssid: string): Promise<void> {
|
||||
this.loader.of({
|
||||
message: 'Deleting...',
|
||||
const loader = await this.loadingCtrl.create({
|
||||
spinner: 'lines',
|
||||
message: 'Deleting...',
|
||||
cssClass: 'loader',
|
||||
}).displayDuringAsync(async () => {
|
||||
await this.embassyApi.deleteWifi({ ssid })
|
||||
}).catch(e => {
|
||||
console.error(e)
|
||||
this.errToast.present(e.message)
|
||||
})
|
||||
await loader.present()
|
||||
|
||||
try {
|
||||
await this.embassyApi.deleteWifi({ ssid })
|
||||
} catch (e) {
|
||||
this.errToast.present(e)
|
||||
} finally {
|
||||
loader.dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user