mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-31 04:23:40 +00:00
Drew cleanup (#380)
* accordion works * cleanup * styling * more styling * App show change (#387) * show page change * no marketplace * app show changes * update marketplace list * icon * top left icon * toolbar * right size * out of toolbar * no service details * fix skeleton text and server metrics page * stuck * add session management * complete sessions feature * app show page * remove unnecessary icons * add cli to list of possible sessions * Modal global (#383) * modal checkpoint * global modal * black looks good now * black looks good now * not smaller Co-authored-by: Drew Ansbacher <drew.ansbacher@spiredigital.com> Co-authored-by: Drew Ansbacher <drew.ansbacher@spiredigital.com> Co-authored-by: Drew Ansbacher <drew.ansbacher@gmail.com> Co-authored-by: Drew Ansbacher <drew.ansbacher@spiredigital.com> Co-authored-by: Matt Hill <matthewonthemoon@gmail.com> Co-authored-by: Matt Hill <MattDHill@users.noreply.github.com>
This commit is contained in:
committed by
Aiden McClelland
parent
4c294566d7
commit
a43ff976a2
@@ -38,6 +38,9 @@
|
||||
<ion-item detail="true" button [routerLink]="['ssh-keys']">
|
||||
<ion-label>SSH Keys</ion-label>
|
||||
</ion-item>
|
||||
<ion-item detail="true" button [routerLink]="['sessions']">
|
||||
<ion-label>Active Sessions</ion-label>
|
||||
</ion-item>
|
||||
</ion-item-group>
|
||||
|
||||
</ion-content>
|
||||
@@ -6,6 +6,10 @@ const routes: Routes = [
|
||||
path: '',
|
||||
loadChildren: () => import('./security-options/security-options.module').then(m => m.SecurityOptionsPageModule),
|
||||
},
|
||||
{
|
||||
path: 'sessions',
|
||||
loadChildren: () => import('./sessions/sessions.module').then(m => m.SessionsPageModule),
|
||||
},
|
||||
{
|
||||
path: 'ssh-keys',
|
||||
loadChildren: () => import('./ssh-keys/ssh-keys.module').then(m => m.SSHKeysPageModule),
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import { NgModule } from '@angular/core'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { RouterModule, Routes } from '@angular/router'
|
||||
import { SessionsPage } from './sessions.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'
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: SessionsPage,
|
||||
},
|
||||
]
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
IonicModule,
|
||||
RouterModule.forChild(routes),
|
||||
PwaBackComponentModule,
|
||||
SharingModule,
|
||||
TextSpinnerComponentModule,
|
||||
],
|
||||
declarations: [SessionsPage],
|
||||
})
|
||||
export class SessionsPageModule { }
|
||||
@@ -0,0 +1,41 @@
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-buttons slot="start">
|
||||
<pwa-back-button></pwa-back-button>
|
||||
</ion-buttons>
|
||||
<ion-title>Active Sessions</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content class="ion-padding-top">
|
||||
<text-spinner *ngIf="loading" text="Loading Sessions"></text-spinner>
|
||||
|
||||
<ion-item-group *ngIf="!loading">
|
||||
|
||||
<ion-item-divider>Current Session</ion-item-divider>
|
||||
<ion-item *ngIf="sessionInfo.sessions[sessionInfo.current] as current">
|
||||
<ion-icon slot="start" [name]="getPlatformIcon(current.metadata.platforms)"></ion-icon>
|
||||
<ion-label class="ion-text-wrap">
|
||||
<h1>{{ getPlatformName(current.metadata.platforms) }}</h1>
|
||||
<h2>Last Active: {{ current['last-active'] | date : 'medium' }}</h2>
|
||||
<p>{{ current['user-agent'] }}</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
|
||||
<ion-item-divider>Other Sessions</ion-item-divider>
|
||||
<div *ngFor="let session of sessionInfo.sessions | keyvalue : asIsOrder">
|
||||
<ion-item *ngIf="session.key !== sessionInfo.current">
|
||||
<ion-icon slot="start" [name]="getPlatformIcon(session.value.metadata.platforms)"></ion-icon>
|
||||
<ion-label class="ion-text-wrap">
|
||||
<h1>{{ getPlatformName(session.value.metadata.platforms) }}</h1>
|
||||
<h2>Last Active: {{ session.value['last-active'] | date : 'medium' }}</h2>
|
||||
<p>{{ session.value['user-agent'] }}</p>
|
||||
</ion-label>
|
||||
<ion-button slot="end" fill="clear" (click)="presentAlertKill(session.key)">
|
||||
<ion-icon slot="icon-only" name="close-outline"></ion-icon>
|
||||
</ion-button>
|
||||
</ion-item>
|
||||
</div>
|
||||
</ion-item-group>
|
||||
|
||||
</ion-content>
|
||||
@@ -0,0 +1,99 @@
|
||||
import { Component } from '@angular/core'
|
||||
import { AlertController, getPlatforms, LoadingController } from '@ionic/angular'
|
||||
import { ErrorToastService } from 'src/app/services/error-toast.service'
|
||||
import { ApiService } from 'src/app/services/api/embassy/embassy-api.service'
|
||||
import { PlatformType, RR, SessionMetadata } from 'src/app/services/api/api.types'
|
||||
|
||||
@Component({
|
||||
selector: 'sessions',
|
||||
templateUrl: 'sessions.page.html',
|
||||
styleUrls: ['sessions.page.scss'],
|
||||
})
|
||||
export class SessionsPage {
|
||||
loading = true
|
||||
sessionInfo: RR.GetSessionsRes
|
||||
|
||||
constructor (
|
||||
private readonly loadingCtrl: LoadingController,
|
||||
private readonly errToast: ErrorToastService,
|
||||
private readonly alertCtrl: AlertController,
|
||||
private readonly embassyApi: ApiService,
|
||||
) { }
|
||||
|
||||
async ngOnInit () {
|
||||
getPlatforms()
|
||||
this.sessionInfo = await this.embassyApi.getSessions({ })
|
||||
this.loading = false
|
||||
}
|
||||
|
||||
async presentAlertKill (hash: string) {
|
||||
const alert = await this.alertCtrl.create({
|
||||
backdropDismiss: false,
|
||||
header: 'Caution',
|
||||
message: `Are you sure you want to kill this session?`,
|
||||
buttons: [
|
||||
{
|
||||
text: 'Cancel',
|
||||
role: 'cancel',
|
||||
},
|
||||
{
|
||||
text: 'Kill',
|
||||
handler: () => {
|
||||
this.kill(hash)
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
await alert.present()
|
||||
}
|
||||
|
||||
async kill (hash: string): Promise<void> {
|
||||
const loader = await this.loadingCtrl.create({
|
||||
spinner: 'lines',
|
||||
message: 'Killing session...',
|
||||
cssClass: 'loader',
|
||||
})
|
||||
await loader.present()
|
||||
|
||||
try {
|
||||
await this.embassyApi.killSessions({ hashes: [hash] })
|
||||
delete this.sessionInfo.sessions[hash]
|
||||
} catch (e) {
|
||||
this.errToast.present(e)
|
||||
} finally {
|
||||
loader.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
getPlatformIcon (platforms: PlatformType[]): string {
|
||||
if (platforms.includes('cli')) {
|
||||
return 'terminal-outline'
|
||||
} else if (platforms.includes('desktop')) {
|
||||
return 'desktop-outline'
|
||||
} else {
|
||||
return 'phone-portrait-outline'
|
||||
}
|
||||
}
|
||||
|
||||
getPlatformName (platforms: PlatformType[]): string {
|
||||
if (platforms.includes('cli')) {
|
||||
return 'CLI'
|
||||
} else if (platforms.includes('desktop')) {
|
||||
return 'Desktop/Laptop'
|
||||
} else if (platforms.includes('android')) {
|
||||
return 'Android Device'
|
||||
} else if (platforms.includes('iphone')) {
|
||||
return 'iPhone'
|
||||
} else if (platforms.includes('ipad')) {
|
||||
return 'iPad'
|
||||
} else if (platforms.includes('ios')) {
|
||||
return 'iOS Device'
|
||||
} else {
|
||||
return 'Unknown Device'
|
||||
}
|
||||
}
|
||||
|
||||
asIsOrder (a: any, b: any) {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,7 @@
|
||||
{{ ssh.value.alg }} {{ ssh.key }} {{ ssh.value.hostname }}
|
||||
</ion-label>
|
||||
<ion-button slot="end" fill="clear" (click)="presentAlertDelete(ssh.key)">
|
||||
<ion-icon slot="icon-only" name="close-outline" color="medium"></ion-icon>
|
||||
<ion-icon slot="icon-only" name="close-outline"></ion-icon>
|
||||
</ion-button>
|
||||
</ion-item>
|
||||
</ion-item-group>
|
||||
|
||||
@@ -57,7 +57,6 @@ export class SSHKeysPage {
|
||||
},
|
||||
{
|
||||
text: 'Delete',
|
||||
cssClass: 'alert-danger',
|
||||
handler: () => {
|
||||
this.delete(hash)
|
||||
},
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content class="ion-padding" color="light">
|
||||
<ion-content class="ion-padding">
|
||||
<text-spinner *ngIf="loading; else loaded" text="Loading Logs"></text-spinner>
|
||||
|
||||
<ng-template #loaded>
|
||||
|
||||
@@ -7,16 +7,14 @@
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content>
|
||||
<ion-content class="ion-padding">
|
||||
<skeleton-list *ngIf="loading; else loaded" groups="3"></skeleton-list>
|
||||
|
||||
<ng-template #loaded>
|
||||
<ion-item-group *ngFor="let metricGroup of metrics | keyvalue : asIsOrder">
|
||||
<ion-item-divider class="divider">{{ metricGroup.key }}</ion-item-divider>
|
||||
<ion-item-divider>{{ metricGroup.key }}</ion-item-divider>
|
||||
<ion-item *ngFor="let metric of metricGroup.value | keyvalue : asIsOrder">
|
||||
<ion-label>
|
||||
<ion-text color="medium">{{ metric.key }}</ion-text>
|
||||
</ion-label>
|
||||
<ion-label>{{ metric.key }}</ion-label>
|
||||
<ion-note *ngIf="metric.value" slot="end" class="metric-note">
|
||||
<ion-text style="color: white;">{{ metric.value.value }} {{ metric.value.unit }}</ion-text>
|
||||
</ion-note>
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
.metric-note {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,12 +7,11 @@
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content>
|
||||
|
||||
<ion-content class="ion-padding">
|
||||
<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-item-divider><ion-text color="dark">{{ cat.key }}</ion-text></ion-item-divider>
|
||||
<ion-item [detail]="button.detail" 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>
|
||||
|
||||
@@ -37,7 +37,6 @@ export class ServerShowPage {
|
||||
},
|
||||
{
|
||||
text: 'Restart',
|
||||
cssClass: 'alert-danger',
|
||||
handler: () => {
|
||||
this.restart()
|
||||
},
|
||||
@@ -59,7 +58,6 @@ export class ServerShowPage {
|
||||
},
|
||||
{
|
||||
text: 'Shutdown',
|
||||
cssClass: 'alert-danger',
|
||||
handler: () => {
|
||||
this.shutdown()
|
||||
},
|
||||
@@ -110,16 +108,19 @@ export class ServerShowPage {
|
||||
title: 'Privacy and Security',
|
||||
icon: 'shield-checkmark-outline',
|
||||
action: () => this.navCtrl.navigateForward(['security'], { relativeTo: this.route }),
|
||||
detail: true,
|
||||
},
|
||||
{
|
||||
title: 'LAN',
|
||||
icon: 'home-outline',
|
||||
action: () => this.navCtrl.navigateForward(['lan'], { relativeTo: this.route }),
|
||||
detail: true,
|
||||
},
|
||||
{
|
||||
title: 'WiFi',
|
||||
icon: 'wifi',
|
||||
action: () => this.navCtrl.navigateForward(['wifi'], { relativeTo: this.route }),
|
||||
detail: true,
|
||||
},
|
||||
],
|
||||
'Insights': [
|
||||
@@ -127,16 +128,19 @@ export class ServerShowPage {
|
||||
title: 'About',
|
||||
icon: 'information-circle-outline',
|
||||
action: () => this.navCtrl.navigateForward(['specs'], { relativeTo: this.route }),
|
||||
detail: true,
|
||||
},
|
||||
{
|
||||
title: 'Monitor',
|
||||
icon: 'pulse',
|
||||
action: () => this.navCtrl.navigateForward(['metrics'], { relativeTo: this.route }),
|
||||
detail: true,
|
||||
},
|
||||
{
|
||||
title: 'Logs',
|
||||
icon: 'newspaper-outline',
|
||||
action: () => this.navCtrl.navigateForward(['logs'], { relativeTo: this.route }),
|
||||
detail: true,
|
||||
},
|
||||
],
|
||||
'Backups': [
|
||||
@@ -144,6 +148,7 @@ export class ServerShowPage {
|
||||
title: 'Create Backup',
|
||||
icon: 'save-outline',
|
||||
action: () => this.navCtrl.navigateForward(['backup'], { relativeTo: this.route }),
|
||||
detail: true,
|
||||
},
|
||||
],
|
||||
'Power': [
|
||||
@@ -151,11 +156,13 @@ export class ServerShowPage {
|
||||
title: 'Restart',
|
||||
icon: 'reload-outline',
|
||||
action: () => this.presentAlertRestart(),
|
||||
detail: false,
|
||||
},
|
||||
{
|
||||
title: 'Shutdown',
|
||||
icon: 'power',
|
||||
action: () => this.presentAlertShutdown(),
|
||||
detail: false,
|
||||
},
|
||||
],
|
||||
}
|
||||
@@ -171,5 +178,6 @@ interface ServerSettings {
|
||||
title: string
|
||||
icon: string
|
||||
action: Function
|
||||
detail: boolean
|
||||
}[]
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<ion-item-group *ngIf="patch.data['server-info'] as server">
|
||||
<ion-item>
|
||||
<ion-label>
|
||||
<h2>Version</h2>
|
||||
<h2>EmbassyOS Version</h2>
|
||||
<p>{{ server.version | displayEmver }}</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
|
||||
@@ -29,7 +29,6 @@ export class WifiListPage {
|
||||
const buttons: ActionSheetButton[] = [
|
||||
{
|
||||
text: 'Forget',
|
||||
cssClass: 'alert-danger',
|
||||
handler: () => {
|
||||
this.delete(ssid)
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user