[Feat] follow logs (#1714)

* tail logs

* add cli

* add FE

* abstract http to shared

* batch new logs

* file download for logs

* fix modal error when no config

Co-authored-by: Chris Guida <chrisguida@users.noreply.github.com>
Co-authored-by: Aiden McClelland <me@drbonez.dev>
Co-authored-by: Matt Hill <matthewonthemoon@gmail.com>
Co-authored-by: BluJ <mogulslayer@gmail.com>
This commit is contained in:
Chris Guida
2022-08-03 13:06:25 -05:00
committed by GitHub
parent c44eb3a2c3
commit 2f8d825970
70 changed files with 2202 additions and 1795 deletions

View File

@@ -1,14 +1,13 @@
import { Component, Input } from '@angular/core'
import { ActivatedRoute } from '@angular/router'
import { ModalController, ToastController } from '@ionic/angular'
import { getPkgId } from '@start9labs/shared'
import { getPkgId, copyToClipboard } from '@start9labs/shared'
import { getUiInterfaceKey } from 'src/app/services/config.service'
import {
InstalledPackageDataEntry,
InterfaceDef,
} from 'src/app/services/patch-db/data-model'
import { PatchDbService } from 'src/app/services/patch-db/patch-db.service'
import { copyToClipboard } from 'src/app/util/web.util'
import { QRComponent } from 'src/app/components/qr/qr.component'
import { getPackage } from '../../../util/get-package-data'

View File

@@ -3,8 +3,7 @@ import { CommonModule } from '@angular/common'
import { Routes, RouterModule } from '@angular/router'
import { IonicModule } from '@ionic/angular'
import { AppLogsPage } from './app-logs.page'
import { SharedPipesModule } from '@start9labs/shared'
import { LogsPageModule } from 'src/app/components/logs/logs.module'
import { LogsComponentModule } from 'src/app/components/logs/logs.component.module'
const routes: Routes = [
{
@@ -18,8 +17,7 @@ const routes: Routes = [
CommonModule,
IonicModule,
RouterModule.forChild(routes),
SharedPipesModule,
LogsPageModule,
LogsComponentModule,
],
declarations: [AppLogsPage],
})

View File

@@ -1,15 +1,7 @@
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button [defaultHref]="'/services/' + pkgId"></ion-back-button>
</ion-buttons>
<ion-title>Logs</ion-title>
<ion-button slot="end" fill="clear" size="small" (click)="copy()">
<ion-icon slot="icon-only" name="copy-outline"></ion-icon>
</ion-button>
</ion-toolbar>
</ion-header>
<div style="height: 100%">
<logs [fetchLogs]="fetchFetchLogs()"></logs>
</div>
<logs
[fetchLogs]="fetchLogs()"
[followLogs]="followLogs()"
[defaultBack]="'/services/' + pkgId"
title="Service Logs"
class="ion-page"
></logs>

View File

@@ -1,9 +1,8 @@
import { Component } from '@angular/core'
import { ActivatedRoute } from '@angular/router'
import { getPkgId } from '@start9labs/shared'
import { ToastController } from '@ionic/angular'
import { ApiService } from 'src/app/services/api/embassy-api.service'
import { copyToClipboard, strip } from 'src/app/util/web.util'
import { RR } from 'src/app/services/api/api.types'
@Component({
selector: 'app-logs',
@@ -16,39 +15,23 @@ export class AppLogsPage {
constructor(
private readonly route: ActivatedRoute,
private readonly embassyApi: ApiService,
private readonly toastCtrl: ToastController,
) {}
fetchFetchLogs() {
return async (params: {
before_flag?: boolean
limit?: number
cursor?: string
}) => {
return this.embassyApi.getPackageLogs({
followLogs() {
return async (params: RR.FollowServerLogsReq) => {
return this.embassyApi.followPackageLogs({
id: this.pkgId,
before_flag: params.before_flag,
cursor: params.cursor,
limit: params.limit,
...params,
})
}
}
async copy(): Promise<void> {
const logs = document
.getElementById('template')
?.cloneNode(true) as HTMLElement
const formatted = '```' + strip(logs.innerHTML) + '```'
const success = await copyToClipboard(formatted)
const message = success
? 'Copied to clipboard!'
: 'Failed to copy to clipboard.'
const toast = await this.toastCtrl.create({
header: message,
position: 'bottom',
duration: 1000,
})
await toast.present()
fetchLogs() {
return async (params: RR.GetServerLogsReq) => {
return this.embassyApi.getPackageLogs({
id: this.pkgId,
...params,
})
}
}
}

View File

@@ -1,7 +1,6 @@
import { Component, ViewChild } from '@angular/core'
import { ActivatedRoute } from '@angular/router'
import { ApiService } from 'src/app/services/api/embassy-api.service'
import { copyToClipboard } from 'src/app/util/web.util'
import {
AlertController,
IonBackButtonDelegate,
@@ -13,7 +12,12 @@ import { PackageProperties } from 'src/app/util/properties.util'
import { QRComponent } from 'src/app/components/qr/qr.component'
import { PatchDbService } from 'src/app/services/patch-db/patch-db.service'
import { PackageMainStatus } from 'src/app/services/patch-db/data-model'
import { DestroyService, ErrorToastService, getPkgId } from '@start9labs/shared'
import {
DestroyService,
ErrorToastService,
getPkgId,
copyToClipboard,
} from '@start9labs/shared'
import { getValueByPointer } from 'fast-json-patch'
import { map, takeUntil } from 'rxjs/operators'

View File

@@ -9,31 +9,46 @@ const routes: Routes = [
},
{
path: 'list',
loadChildren: () => import('./app-list/app-list.module').then(m => m.AppListPageModule),
loadChildren: () =>
import('./app-list/app-list.module').then(m => m.AppListPageModule),
},
{
path: ':pkgId',
loadChildren: () => import('./app-show/app-show.module').then(m => m.AppShowPageModule),
loadChildren: () =>
import('./app-show/app-show.module').then(m => m.AppShowPageModule),
},
{
path: ':pkgId/actions',
loadChildren: () => import('./app-actions/app-actions.module').then(m => m.AppActionsPageModule),
loadChildren: () =>
import('./app-actions/app-actions.module').then(
m => m.AppActionsPageModule,
),
},
{
path: ':pkgId/interfaces',
loadChildren: () => import('./app-interfaces/app-interfaces.module').then(m => m.AppInterfacesPageModule),
loadChildren: () =>
import('./app-interfaces/app-interfaces.module').then(
m => m.AppInterfacesPageModule,
),
},
{
path: ':pkgId/logs',
loadChildren: () => import('./app-logs/app-logs.module').then(m => m.AppLogsPageModule),
loadChildren: () =>
import('./app-logs/app-logs.module').then(m => m.AppLogsPageModule),
},
{
path: ':pkgId/metrics',
loadChildren: () => import('./app-metrics/app-metrics.module').then(m => m.AppMetricsPageModule),
loadChildren: () =>
import('./app-metrics/app-metrics.module').then(
m => m.AppMetricsPageModule,
),
},
{
path: ':pkgId/properties',
loadChildren: () => import('./app-properties/app-properties.module').then(m => m.AppPropertiesPageModule),
loadChildren: () =>
import('./app-properties/app-properties.module').then(
m => m.AppPropertiesPageModule,
),
},
]
@@ -41,4 +56,4 @@ const routes: Routes = [
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class AppsRoutingModule { }
export class AppsRoutingModule {}

View File

@@ -3,8 +3,7 @@ import { CommonModule } from '@angular/common'
import { Routes, RouterModule } from '@angular/router'
import { IonicModule } from '@ionic/angular'
import { KernelLogsPage } from './kernel-logs.page'
import { SharedPipesModule } from '@start9labs/shared'
import { LogsPageModule } from 'src/app/components/logs/logs.module'
import { LogsComponentModule } from 'src/app/components/logs/logs.component.module'
const routes: Routes = [
{
@@ -18,8 +17,7 @@ const routes: Routes = [
CommonModule,
IonicModule,
RouterModule.forChild(routes),
SharedPipesModule,
LogsPageModule,
LogsComponentModule,
],
declarations: [KernelLogsPage],
})

View File

@@ -1,17 +1,7 @@
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button defaultHref="embassy"></ion-back-button>
</ion-buttons>
<ion-title>Kernel Logs</ion-title>
<ion-buttons slot="end">
<ion-button (click)="copy()">
<ion-icon name="copy-outline"></ion-icon>
</ion-button>
</ion-buttons>
</ion-toolbar>
</ion-header>
<div style="height: 100%">
<logs [fetchLogs]="fetchFetchLogs()"></logs>
</div>
<logs
[fetchLogs]="fetchLogs()"
[followLogs]="followLogs()"
defaultBack="embassy"
title="Kernel Logs"
class="ion-page"
></logs>

View File

@@ -1,7 +1,6 @@
import { Component } from '@angular/core'
import { ToastController } from '@ionic/angular'
import { RR } from 'src/app/services/api/api.types'
import { ApiService } from 'src/app/services/api/embassy-api.service'
import { copyToClipboard, strip } from 'src/app/util/web.util'
@Component({
selector: 'kernel-logs',
@@ -9,40 +8,17 @@ import { copyToClipboard, strip } from 'src/app/util/web.util'
styleUrls: ['./kernel-logs.page.scss'],
})
export class KernelLogsPage {
constructor(
private readonly embassyApi: ApiService,
private readonly toastCtrl: ToastController,
) {}
constructor(private readonly embassyApi: ApiService) {}
fetchFetchLogs() {
return async (params: {
before_flag?: boolean
limit?: number
cursor?: string
}) => {
return this.embassyApi.getKernelLogs({
before_flag: params.before_flag,
cursor: params.cursor,
limit: params.limit,
})
followLogs() {
return async (params: RR.FollowServerLogsReq) => {
return this.embassyApi.followKernelLogs(params)
}
}
async copy(): Promise<void> {
const logs = document
.getElementById('template')
?.cloneNode(true) as HTMLElement
const formatted = '```' + strip(logs.innerHTML) + '```'
const success = await copyToClipboard(formatted)
const message = success
? 'Copied to clipboard!'
: 'Failed to copy to clipboard.'
const toast = await this.toastCtrl.create({
header: message,
position: 'bottom',
duration: 1000,
})
await toast.present()
fetchLogs() {
return async (params: RR.GetServerLogsReq) => {
return this.embassyApi.getKernelLogs(params)
}
}
}

View File

@@ -3,8 +3,7 @@ import { CommonModule } from '@angular/common'
import { Routes, RouterModule } from '@angular/router'
import { IonicModule } from '@ionic/angular'
import { ServerLogsPage } from './server-logs.page'
import { SharedPipesModule } from '@start9labs/shared'
import { LogsPageModule } from 'src/app/components/logs/logs.module'
import { LogsComponentModule } from 'src/app/components/logs/logs.component.module'
const routes: Routes = [
{
@@ -18,8 +17,7 @@ const routes: Routes = [
CommonModule,
IonicModule,
RouterModule.forChild(routes),
SharedPipesModule,
LogsPageModule,
LogsComponentModule,
],
declarations: [ServerLogsPage],
})

View File

@@ -1,17 +1,7 @@
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button defaultHref="embassy"></ion-back-button>
</ion-buttons>
<ion-title>OS Logs</ion-title>
<ion-buttons slot="end">
<ion-button (click)="copy()">
<ion-icon name="copy-outline"></ion-icon>
</ion-button>
</ion-buttons>
</ion-toolbar>
</ion-header>
<div style="height: 100%">
<logs [fetchLogs]="fetchFetchLogs()"></logs>
</div>
<logs
[fetchLogs]="fetchLogs()"
[followLogs]="followLogs()"
defaultBack="embassy"
title="OS Logs"
class="ion-page"
></logs>

View File

@@ -1,7 +1,6 @@
import { Component } from '@angular/core'
import { ToastController } from '@ionic/angular'
import { RR } from 'src/app/services/api/api.types'
import { ApiService } from 'src/app/services/api/embassy-api.service'
import { copyToClipboard, strip } from 'src/app/util/web.util'
@Component({
selector: 'server-logs',
@@ -9,40 +8,17 @@ import { copyToClipboard, strip } from 'src/app/util/web.util'
styleUrls: ['./server-logs.page.scss'],
})
export class ServerLogsPage {
constructor(
private readonly embassyApi: ApiService,
private readonly toastCtrl: ToastController,
) {}
constructor(private readonly embassyApi: ApiService) {}
fetchFetchLogs() {
return async (params: {
before_flag?: boolean
limit?: number
cursor?: string
}) => {
return this.embassyApi.getServerLogs({
before_flag: params.before_flag,
cursor: params.cursor,
limit: params.limit,
})
followLogs() {
return async (params: RR.FollowServerLogsReq) => {
return this.embassyApi.followServerLogs(params)
}
}
async copy(): Promise<void> {
const logs = document
.getElementById('template')
?.cloneNode(true) as HTMLElement
const formatted = '```' + strip(logs.innerHTML) + '```'
const success = await copyToClipboard(formatted)
const message = success
? 'Copied to clipboard!'
: 'Failed to copy to clipboard.'
const toast = await this.toastCtrl.create({
header: message,
position: 'bottom',
duration: 1000,
})
await toast.present()
fetchLogs() {
return async (params: RR.GetServerLogsReq) => {
return this.embassyApi.getServerLogs(params)
}
}
}

View File

@@ -1,8 +1,8 @@
import { Component } from '@angular/core'
import { ToastController } from '@ionic/angular'
import { copyToClipboard } from 'src/app/util/web.util'
import { PatchDbService } from 'src/app/services/patch-db/patch-db.service'
import { ConfigService } from 'src/app/services/config.service'
import { copyToClipboard } from '@start9labs/shared'
@Component({
selector: 'server-specs',