mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-30 12:11:56 +00:00
[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:
@@ -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'
|
||||
|
||||
|
||||
@@ -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],
|
||||
})
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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'
|
||||
|
||||
|
||||
@@ -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 {}
|
||||
|
||||
@@ -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],
|
||||
})
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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],
|
||||
})
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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',
|
||||
|
||||
Reference in New Issue
Block a user