mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-04-04 14:29:45 +00:00
Feature/tor logs (#3077)
* add tor logs, rework services page, other small things * feat: sortable service table and mobile view --------- Co-authored-by: waterplea <alexander@inkin.ru>
This commit is contained in:
@@ -88,6 +88,8 @@ export default {
|
|||||||
88: 'Aktionen',
|
88: 'Aktionen',
|
||||||
89: 'nicht empfohlen',
|
89: 'nicht empfohlen',
|
||||||
90: 'Root-CA ist vertrauenswürdig!',
|
90: 'Root-CA ist vertrauenswürdig!',
|
||||||
|
91: 'Installierte Dienste',
|
||||||
|
92: 'Diagnosen für den Tor-Daemon auf diesem Server',
|
||||||
96: 'Öffentliche Domain hinzufügen',
|
96: 'Öffentliche Domain hinzufügen',
|
||||||
97: 'Wird entfernt',
|
97: 'Wird entfernt',
|
||||||
100: 'Nicht gespeicherte Änderungen',
|
100: 'Nicht gespeicherte Änderungen',
|
||||||
|
|||||||
@@ -87,6 +87,8 @@ export const ENGLISH = {
|
|||||||
'Actions': 88, // as in, actions available to the user
|
'Actions': 88, // as in, actions available to the user
|
||||||
'not recommended': 89,
|
'not recommended': 89,
|
||||||
'Root CA Trusted!': 90,
|
'Root CA Trusted!': 90,
|
||||||
|
'Installed services': 91, // as in, software services installed on this computer
|
||||||
|
'Diagnostics for the Tor daemon on this server': 92,
|
||||||
'Add public domain': 96,
|
'Add public domain': 96,
|
||||||
'Removing': 97,
|
'Removing': 97,
|
||||||
'Unsaved changes': 100,
|
'Unsaved changes': 100,
|
||||||
|
|||||||
@@ -88,6 +88,8 @@ export default {
|
|||||||
88: 'Acciones',
|
88: 'Acciones',
|
||||||
89: 'no recomendado',
|
89: 'no recomendado',
|
||||||
90: '¡CA raíz confiable!',
|
90: '¡CA raíz confiable!',
|
||||||
|
91: 'Servicios instalados',
|
||||||
|
92: 'Diagnósticos para el demonio Tor en este servidor',
|
||||||
96: 'Agregar dominio público',
|
96: 'Agregar dominio público',
|
||||||
97: 'Eliminando',
|
97: 'Eliminando',
|
||||||
100: 'Cambios no guardados',
|
100: 'Cambios no guardados',
|
||||||
|
|||||||
@@ -88,6 +88,8 @@ export default {
|
|||||||
88: 'Actions',
|
88: 'Actions',
|
||||||
89: 'non recommandé',
|
89: 'non recommandé',
|
||||||
90: 'Certificat racine approuvé !',
|
90: 'Certificat racine approuvé !',
|
||||||
|
91: 'Services installés',
|
||||||
|
92: 'Diagnostics pour le service Tor sur ce serveur',
|
||||||
96: 'Ajouter un domaine public',
|
96: 'Ajouter un domaine public',
|
||||||
97: 'Suppression',
|
97: 'Suppression',
|
||||||
100: 'Modifications non enregistrées',
|
100: 'Modifications non enregistrées',
|
||||||
|
|||||||
@@ -88,6 +88,8 @@ export default {
|
|||||||
88: 'Akcje',
|
88: 'Akcje',
|
||||||
89: 'niezalecane',
|
89: 'niezalecane',
|
||||||
90: 'Główny certyfikat CA zaufany!',
|
90: 'Główny certyfikat CA zaufany!',
|
||||||
|
91: 'Zainstalowane usługi',
|
||||||
|
92: 'Diagnostyka demona Tor na tym serwerze',
|
||||||
96: 'Dodaj domenę publiczną',
|
96: 'Dodaj domenę publiczną',
|
||||||
97: 'Usuwanie',
|
97: 'Usuwanie',
|
||||||
100: 'Niezapisane zmiany',
|
100: 'Niezapisane zmiany',
|
||||||
|
|||||||
@@ -3,7 +3,12 @@ import { TuiIcon } from '@taiga-ui/core'
|
|||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-placeholder',
|
selector: 'app-placeholder',
|
||||||
template: '<tui-icon [icon]="icon()" /><ng-content/>',
|
template: `
|
||||||
|
@if (icon(); as icon) {
|
||||||
|
<tui-icon [icon]="icon" />
|
||||||
|
}
|
||||||
|
<ng-content />
|
||||||
|
`,
|
||||||
styles: `
|
styles: `
|
||||||
:host {
|
:host {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -26,5 +31,5 @@ import { TuiIcon } from '@taiga-ui/core'
|
|||||||
imports: [TuiIcon],
|
imports: [TuiIcon],
|
||||||
})
|
})
|
||||||
export class PlaceholderComponent {
|
export class PlaceholderComponent {
|
||||||
readonly icon = input.required<string>()
|
readonly icon = input<string>()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
import { ChangeDetectionStrategy, Component, input } from '@angular/core'
|
import { ChangeDetectionStrategy, Component, input } from '@angular/core'
|
||||||
import { i18nKey, i18nPipe } from '@start9labs/shared'
|
import { i18nKey, i18nPipe } from '@start9labs/shared'
|
||||||
|
import {
|
||||||
|
TuiComparator,
|
||||||
|
TuiTable,
|
||||||
|
TuiTableDirective,
|
||||||
|
} from '@taiga-ui/addon-table'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'table[appTable]',
|
selector: 'table[appTable]',
|
||||||
@@ -8,7 +13,13 @@ import { i18nKey, i18nPipe } from '@start9labs/shared'
|
|||||||
<tr>
|
<tr>
|
||||||
<ng-content select="th" />
|
<ng-content select="th" />
|
||||||
@for (header of appTable(); track $index) {
|
@for (header of appTable(); track $index) {
|
||||||
<th>{{ header | i18n }}</th>
|
<th
|
||||||
|
tuiTh
|
||||||
|
[requiredSort]="true"
|
||||||
|
[sorter]="appTableSorters()[$index] || null"
|
||||||
|
>
|
||||||
|
{{ header | i18n }}
|
||||||
|
</th>
|
||||||
}
|
}
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@@ -22,9 +33,16 @@ import { i18nKey, i18nPipe } from '@start9labs/shared'
|
|||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
host: { class: 'g-table' },
|
host: { class: 'g-table' },
|
||||||
|
hostDirectives: [
|
||||||
|
{
|
||||||
|
directive: TuiTableDirective,
|
||||||
|
inputs: ['sorter'],
|
||||||
|
},
|
||||||
|
],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
imports: [i18nPipe],
|
imports: [i18nPipe, TuiTable],
|
||||||
})
|
})
|
||||||
export class TableComponent {
|
export class TableComponent {
|
||||||
readonly appTable = input.required<ReadonlyArray<i18nKey | null>>()
|
readonly appTable = input.required<ReadonlyArray<i18nKey | null>>()
|
||||||
|
readonly appTableSorters = input<ReadonlyArray<TuiComparator<any> | null>>([])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,10 @@ export const ROUTES: Routes = [
|
|||||||
path: 'os',
|
path: 'os',
|
||||||
loadComponent: () => import('./routes/os.component'),
|
loadComponent: () => import('./routes/os.component'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'tor',
|
||||||
|
loadComponent: () => import('./routes/tor.component'),
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
export default ROUTES
|
export default ROUTES
|
||||||
|
|||||||
@@ -79,6 +79,12 @@ export default class SystemLogsComponent {
|
|||||||
subtitle: 'Raw, unfiltered operating system logs',
|
subtitle: 'Raw, unfiltered operating system logs',
|
||||||
icon: '@tui.square-dashed-bottom-code',
|
icon: '@tui.square-dashed-bottom-code',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
link: 'tor',
|
||||||
|
title: 'Tor Logs',
|
||||||
|
subtitle: 'Diagnostics for the Tor daemon on this server',
|
||||||
|
icon: '@tui.target',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
link: 'kernel',
|
link: 'kernel',
|
||||||
title: 'Kernel Logs',
|
title: 'Kernel Logs',
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import { ChangeDetectionStrategy, Component, inject } from '@angular/core'
|
||||||
|
import { i18nPipe } from '@start9labs/shared'
|
||||||
|
import { LogsComponent } from 'src/app/routes/portal/components/logs/logs.component'
|
||||||
|
import { LogsHeaderComponent } from 'src/app/routes/portal/routes/logs/components/header.component'
|
||||||
|
import { RR } from 'src/app/services/api/api.types'
|
||||||
|
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
template: `
|
||||||
|
<logs-header [title]="'Tor Logs' | i18n">
|
||||||
|
{{ 'Diagnostics for the Tor daemon on this server' | i18n }}
|
||||||
|
</logs-header>
|
||||||
|
<logs context="tor" [followLogs]="follow" [fetchLogs]="fetch" />
|
||||||
|
`,
|
||||||
|
styles: `
|
||||||
|
:host {
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
imports: [LogsComponent, LogsHeaderComponent, i18nPipe],
|
||||||
|
host: { class: 'g-page' },
|
||||||
|
})
|
||||||
|
export default class SystemTorComponent {
|
||||||
|
private readonly api = inject(ApiService)
|
||||||
|
|
||||||
|
protected readonly follow = (params: RR.FollowServerLogsReq) =>
|
||||||
|
this.api.followTorLogs(params)
|
||||||
|
|
||||||
|
protected readonly fetch = (params: RR.GetServerLogsReq) =>
|
||||||
|
this.api.getTorLogs(params)
|
||||||
|
}
|
||||||
@@ -167,6 +167,9 @@ export default class MarketplaceComponent {
|
|||||||
takeUntilDestroyed(),
|
takeUntilDestroyed(),
|
||||||
tap(params => {
|
tap(params => {
|
||||||
const registry = params.get('registry')
|
const registry = params.get('registry')
|
||||||
|
|
||||||
|
this.categoryService.setQuery(params.get('search') || '')
|
||||||
|
|
||||||
if (!registry) {
|
if (!registry) {
|
||||||
this.router.navigate([], {
|
this.router.navigate([], {
|
||||||
queryParams: {
|
queryParams: {
|
||||||
|
|||||||
@@ -49,16 +49,6 @@ import { NotificationsTableComponent } from './table.component'
|
|||||||
:host {
|
:host {
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
:host-context(tui-root._mobile) {
|
|
||||||
header {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
section {
|
|
||||||
padding-block: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`,
|
`,
|
||||||
host: { class: 'g-page' },
|
host: { class: 'g-page' },
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import { ServerNotification } from 'src/app/services/api/api.types'
|
|||||||
import { TableComponent } from 'src/app/routes/portal/components/table.component'
|
import { TableComponent } from 'src/app/routes/portal/components/table.component'
|
||||||
import { NotificationItemComponent } from './item.component'
|
import { NotificationItemComponent } from './item.component'
|
||||||
import { i18nPipe } from '@start9labs/shared'
|
import { i18nPipe } from '@start9labs/shared'
|
||||||
|
import { PlaceholderComponent } from '../../components/placeholder.component'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: '[notifications]',
|
selector: '[notifications]',
|
||||||
@@ -48,9 +49,9 @@ import { i18nPipe } from '@start9labs/shared'
|
|||||||
</tr>
|
</tr>
|
||||||
} @empty {
|
} @empty {
|
||||||
@if (notifications()) {
|
@if (notifications()) {
|
||||||
<tr>
|
<app-placeholder icon="@tui.bell">
|
||||||
<td colspan="4">{{ 'No notifications' | i18n }}</td>
|
{{ 'No notifications' | i18n }}
|
||||||
</tr>
|
</app-placeholder>
|
||||||
} @else {
|
} @else {
|
||||||
@for (i of ['', '']; track $index) {
|
@for (i of ['', '']; track $index) {
|
||||||
<tr>
|
<tr>
|
||||||
@@ -71,10 +72,6 @@ import { i18nPipe } from '@start9labs/shared'
|
|||||||
transform: translateY(-50%);
|
transform: translateY(-50%);
|
||||||
}
|
}
|
||||||
|
|
||||||
td:only-child {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
:host-context(tui-root._mobile) {
|
:host-context(tui-root._mobile) {
|
||||||
input {
|
input {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@@ -97,6 +94,7 @@ import { i18nPipe } from '@start9labs/shared'
|
|||||||
TuiSkeleton,
|
TuiSkeleton,
|
||||||
i18nPipe,
|
i18nPipe,
|
||||||
TableComponent,
|
TableComponent,
|
||||||
|
PlaceholderComponent,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class NotificationsTableComponent<T extends ServerNotification<number>>
|
export class NotificationsTableComponent<T extends ServerNotification<number>>
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ import { ToManifestPipe } from '../../../pipes/to-manifest'
|
|||||||
@let services = this.services();
|
@let services = this.services();
|
||||||
|
|
||||||
@for (d of pkg().currentDependencies | keyvalue; track $index) {
|
@for (d of pkg().currentDependencies | keyvalue; track $index) {
|
||||||
<!-- @TODO Alex Marketplace should use "search" query param to prefill search bar -->
|
|
||||||
<a
|
<a
|
||||||
tuiCell
|
tuiCell
|
||||||
[routerLink]="services[d.key] ? ['..', d.key] : ['/marketplace']"
|
[routerLink]="services[d.key] ? ['..', d.key] : ['/marketplace']"
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ import { getManifest } from 'src/app/utils/get-package-data'
|
|||||||
|
|
||||||
td:last-child {
|
td:last-child {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
text-align: right;
|
justify-content: end;
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,80 +0,0 @@
|
|||||||
// import { AsyncPipe } from '@angular/common'
|
|
||||||
// import {
|
|
||||||
// ChangeDetectionStrategy,
|
|
||||||
// Component,
|
|
||||||
// computed,
|
|
||||||
// inject,
|
|
||||||
// input,
|
|
||||||
// } from '@angular/core'
|
|
||||||
// import { i18nPipe } from '@start9labs/shared'
|
|
||||||
// import { TuiButton, tuiButtonOptionsProvider } from '@taiga-ui/core'
|
|
||||||
// import { map } from 'rxjs'
|
|
||||||
// import { ControlsService } from 'src/app/services/controls.service'
|
|
||||||
// import { DepErrorService } from 'src/app/services/dep-error.service'
|
|
||||||
// import { PackageDataEntry } from 'src/app/services/patch-db/data-model'
|
|
||||||
// import { renderPkgStatus } from 'src/app/services/pkg-status-rendering.service'
|
|
||||||
// import { getManifest } from 'src/app/utils/get-package-data'
|
|
||||||
// import { UILaunchComponent } from './ui-launch.component'
|
|
||||||
|
|
||||||
// const RUNNING = ['running', 'starting', 'restarting']
|
|
||||||
|
|
||||||
// @Component({
|
|
||||||
// selector: 'fieldset[appControls]',
|
|
||||||
// template: `
|
|
||||||
// <app-ui-launch [pkg]="pkg()" />
|
|
||||||
// @if (running()) {
|
|
||||||
// <button
|
|
||||||
// tuiIconButton
|
|
||||||
// iconStart="@tui.square"
|
|
||||||
// (click)="controls.stop(manifest())"
|
|
||||||
// >
|
|
||||||
// {{ 'Stop' | i18n }}
|
|
||||||
// </button>
|
|
||||||
// } @else {
|
|
||||||
// @let unmet = hasUnmet() | async;
|
|
||||||
// <button
|
|
||||||
// tuiIconButton
|
|
||||||
// iconStart="@tui.play"
|
|
||||||
// [disabled]="status().primary !== 'stopped'"
|
|
||||||
// (click)="controls.start(manifest(), !!unmet)"
|
|
||||||
// >
|
|
||||||
// {{ 'Start' | i18n }}
|
|
||||||
// </button>
|
|
||||||
// }
|
|
||||||
// `,
|
|
||||||
// changeDetection: ChangeDetectionStrategy.OnPush,
|
|
||||||
// imports: [TuiButton, UILaunchComponent, AsyncPipe, i18nPipe],
|
|
||||||
// providers: [tuiButtonOptionsProvider({ size: 's', appearance: 'none' })],
|
|
||||||
// styles: `
|
|
||||||
// :host {
|
|
||||||
// padding: 0;
|
|
||||||
// border: none;
|
|
||||||
// cursor: default;
|
|
||||||
// text-align: right;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// :host-context(tui-root._mobile) {
|
|
||||||
// button {
|
|
||||||
// display: none;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// `,
|
|
||||||
// })
|
|
||||||
// export class ControlsComponent {
|
|
||||||
// private readonly errors = inject(DepErrorService)
|
|
||||||
|
|
||||||
// readonly controls = inject(ControlsService)
|
|
||||||
// readonly pkg = input.required<PackageDataEntry>()
|
|
||||||
// readonly status = computed(() => renderPkgStatus(this.pkg()))
|
|
||||||
// readonly running = computed(() => RUNNING.includes(this.status().primary))
|
|
||||||
// readonly manifest = computed(() => getManifest(this.pkg()))
|
|
||||||
// readonly hasUnmet = computed(() =>
|
|
||||||
// this.errors.getPkgDepErrors$(this.manifest().id).pipe(
|
|
||||||
// map(errors =>
|
|
||||||
// Object.keys(this.pkg().currentDependencies)
|
|
||||||
// .map(id => errors?.[id])
|
|
||||||
// .some(Boolean),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
@@ -1,160 +1,61 @@
|
|||||||
import { ChangeDetectionStrategy, Component, inject } from '@angular/core'
|
import {
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
Component,
|
||||||
|
inject,
|
||||||
|
viewChild,
|
||||||
|
} from '@angular/core'
|
||||||
import { toSignal } from '@angular/core/rxjs-interop'
|
import { toSignal } from '@angular/core/rxjs-interop'
|
||||||
import { RouterLink } from '@angular/router'
|
|
||||||
import { i18nPipe } from '@start9labs/shared'
|
import { i18nPipe } from '@start9labs/shared'
|
||||||
import { TuiComparator, TuiTable } from '@taiga-ui/addon-table'
|
|
||||||
import { TuiButton, TuiLoader } from '@taiga-ui/core'
|
|
||||||
import { PatchDB } from 'patch-db-client'
|
import { PatchDB } from 'patch-db-client'
|
||||||
import { map, shareReplay } from 'rxjs'
|
import { map, shareReplay } from 'rxjs'
|
||||||
import { ToManifestPipe } from 'src/app/routes/portal/pipes/to-manifest'
|
import { DataModel } from 'src/app/services/patch-db/data-model'
|
||||||
import { DepErrorService } from 'src/app/services/dep-error.service'
|
|
||||||
import {
|
|
||||||
DataModel,
|
|
||||||
PackageDataEntry,
|
|
||||||
} from 'src/app/services/patch-db/data-model'
|
|
||||||
import { getInstalledPrimaryStatus } from 'src/app/services/pkg-status-rendering.service'
|
|
||||||
import { TitleDirective } from 'src/app/services/title.service'
|
import { TitleDirective } from 'src/app/services/title.service'
|
||||||
import { getManifest } from 'src/app/utils/get-package-data'
|
import { ServicesTableComponent } from './table.component'
|
||||||
|
|
||||||
import { ServiceComponent } from './service.component'
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
template: `
|
template: `
|
||||||
<ng-container *title>{{ 'Services' | i18n }}</ng-container>
|
<ng-container *title>{{ 'Installed services' | i18n }}</ng-container>
|
||||||
@if (!services()) {
|
|
||||||
<tui-loader [style.height.%]="100" [textContent]="'Loading' | i18n" />
|
<section class="g-card">
|
||||||
} @else {
|
<header>
|
||||||
@if (services()?.length) {
|
{{ 'Installed services' | i18n }}
|
||||||
<table tuiTable class="g-table" [(sorter)]="sorter">
|
</header>
|
||||||
<thead>
|
|
||||||
<tr>
|
<div #table [services]="services()"></div>
|
||||||
<th [style.width.rem]="3"></th>
|
</section>
|
||||||
<th tuiTh [requiredSort]="true" [sorter]="name">
|
|
||||||
{{ 'Name' | i18n }}
|
|
||||||
</th>
|
|
||||||
<th tuiTh [requiredSort]="true" [sorter]="status">
|
|
||||||
{{ 'Status' | i18n }}
|
|
||||||
</th>
|
|
||||||
<th tuiTh>{{ 'Version' | i18n }}</th>
|
|
||||||
<th
|
|
||||||
tuiTh
|
|
||||||
[requiredSort]="true"
|
|
||||||
[sorter]="uptime"
|
|
||||||
[style.width.rem]="10"
|
|
||||||
>
|
|
||||||
{{ 'Uptime' | i18n }}
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
@for (pkg of services() | tuiTableSort; track $index) {
|
|
||||||
<tr
|
|
||||||
appService
|
|
||||||
[pkg]="pkg"
|
|
||||||
[depErrors]="errors()?.[(pkg | toManifest).id]"
|
|
||||||
></tr>
|
|
||||||
}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
} @else {
|
|
||||||
<section>
|
|
||||||
<div>
|
|
||||||
{{ 'Welcome to' | i18n }}
|
|
||||||
<span>StartOS</span>
|
|
||||||
</div>
|
|
||||||
<p>
|
|
||||||
{{
|
|
||||||
'To get started, visit the Marketplace and download your first service'
|
|
||||||
| i18n
|
|
||||||
}}
|
|
||||||
</p>
|
|
||||||
<a tuiButton routerLink="../marketplace">
|
|
||||||
{{ 'View Marketplace' | i18n }}
|
|
||||||
</a>
|
|
||||||
</section>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`,
|
`,
|
||||||
styles: `
|
styles: `
|
||||||
:host {
|
:host {
|
||||||
position: relative;
|
padding: 1rem;
|
||||||
font-size: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
table {
|
|
||||||
max-width: 60rem;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
:host-context(tui-root._mobile) {
|
:host-context(tui-root._mobile) {
|
||||||
padding: 0;
|
header {
|
||||||
}
|
display: none;
|
||||||
|
|
||||||
section {
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
div {
|
|
||||||
font-size: min(12vw, 4rem);
|
|
||||||
line-height: normal;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
p {
|
.g-card {
|
||||||
font-size: 1.5rem;
|
padding: 0;
|
||||||
line-height: 1.25em;
|
margin-top: -0.75rem;
|
||||||
padding: 0 1rem;
|
background: none;
|
||||||
}
|
box-shadow: none;
|
||||||
|
|
||||||
span {
|
|
||||||
color: #ff4961;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
margin-block-start: 1rem;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
host: { class: 'g-page' },
|
host: { class: 'g-page' },
|
||||||
imports: [
|
imports: [TitleDirective, i18nPipe, ServicesTableComponent],
|
||||||
ServiceComponent,
|
|
||||||
ToManifestPipe,
|
|
||||||
TuiTable,
|
|
||||||
TitleDirective,
|
|
||||||
i18nPipe,
|
|
||||||
TuiLoader,
|
|
||||||
TuiButton,
|
|
||||||
RouterLink,
|
|
||||||
],
|
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export default class DashboardComponent {
|
export default class DashboardComponent {
|
||||||
readonly errors = toSignal(inject(DepErrorService).depErrors$)
|
|
||||||
readonly services = toSignal(
|
readonly services = toSignal(
|
||||||
inject<PatchDB<DataModel>>(PatchDB)
|
inject<PatchDB<DataModel>>(PatchDB)
|
||||||
.watch$('packageData')
|
.watch$('packageData')
|
||||||
.pipe(
|
.pipe(
|
||||||
map(pkgs => Object.values(pkgs).sort(byName)),
|
map(pkgs => Object.values(pkgs)),
|
||||||
shareReplay(1),
|
shareReplay(1),
|
||||||
),
|
),
|
||||||
|
{ initialValue: null },
|
||||||
)
|
)
|
||||||
|
|
||||||
readonly name: TuiComparator<PackageDataEntry> = byName
|
protected _ = viewChild<ServicesTableComponent<any>>('table')
|
||||||
|
|
||||||
readonly status: TuiComparator<PackageDataEntry> = (a, b) =>
|
|
||||||
getInstalledPrimaryStatus(b) > getInstalledPrimaryStatus(a) ? -1 : 1
|
|
||||||
|
|
||||||
readonly uptime: TuiComparator<any> = (a, b) =>
|
|
||||||
a.status.started || '' > a.status.started || '' ? -1 : 1
|
|
||||||
|
|
||||||
sorter = this.name
|
|
||||||
}
|
|
||||||
|
|
||||||
function byName(a: PackageDataEntry, b: PackageDataEntry) {
|
|
||||||
return getManifest(b).title.toLowerCase() > getManifest(a).title.toLowerCase()
|
|
||||||
? -1
|
|
||||||
: 1
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,18 +18,15 @@ import { StatusComponent } from './status.component'
|
|||||||
@Component({
|
@Component({
|
||||||
selector: 'tr[appService]',
|
selector: 'tr[appService]',
|
||||||
template: `
|
template: `
|
||||||
<td [style.grid-area]="'1 / 1 / 4'">
|
<td [style.width.rem]="3" [style.grid-area]="'1 / 1 / 4'">
|
||||||
<img alt="logo" [src]="pkg.icon" />
|
<img alt="logo" [src]="pkg.icon" />
|
||||||
</td>
|
</td>
|
||||||
<td class="title">
|
<td class="title">
|
||||||
<a [routerLink]="routerLink">{{ manifest.title }}</a>
|
<a [routerLink]="routerLink">{{ manifest.title }}</a>
|
||||||
</td>
|
</td>
|
||||||
<td
|
<td [style.grid-area]="'3 / 2'">
|
||||||
appStatus
|
<app-status [pkg]="pkg" [hasDepErrors]="hasError(depErrors)" />
|
||||||
[pkg]="pkg"
|
</td>
|
||||||
[hasDepErrors]="hasError(depErrors)"
|
|
||||||
[style.grid-area]="'3 / 2'"
|
|
||||||
></td>
|
|
||||||
<td class="version">{{ manifest.version }}</td>
|
<td class="version">{{ manifest.version }}</td>
|
||||||
<td class="uptime">
|
<td class="uptime">
|
||||||
@if (pkg.statusInfo.started; as started) {
|
@if (pkg.statusInfo.started; as started) {
|
||||||
@@ -45,7 +42,6 @@ import { StatusComponent } from './status.component'
|
|||||||
|
|
||||||
:host {
|
:host {
|
||||||
@include taiga.transition(background);
|
@include taiga.transition(background);
|
||||||
clip-path: inset(0 round 0.5rem);
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
@@ -76,10 +72,14 @@ import { StatusComponent } from './status.component'
|
|||||||
:host-context(tui-root._mobile) {
|
:host-context(tui-root._mobile) {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template: 1.25rem 1.5rem 1.5rem/4rem 1fr;
|
grid-template: 1.25rem 2rem 1.5rem/4rem 1fr;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
|
||||||
img {
|
img {
|
||||||
height: 3rem;
|
height: 3rem;
|
||||||
width: 3rem;
|
width: 3rem;
|
||||||
|
|||||||
@@ -15,12 +15,12 @@ import {
|
|||||||
} from 'src/app/services/pkg-status-rendering.service'
|
} from 'src/app/services/pkg-status-rendering.service'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'td[appStatus]',
|
selector: 'app-status',
|
||||||
template: `
|
template: `
|
||||||
@if (error()) {
|
@if (error()) {
|
||||||
<tui-icon icon="@tui.triangle-alert" class="g-warning" />
|
<tui-icon icon="@tui.triangle-alert" class="g-warning" />
|
||||||
} @else if (loading()) {
|
} @else if (loading()) {
|
||||||
<tui-loader size="m" />
|
<tui-loader size="s" />
|
||||||
}
|
}
|
||||||
|
|
||||||
<b [style.color]="color()">{{ statusText() | i18n }}</b>
|
<b [style.color]="color()">{{ statusText() | i18n }}</b>
|
||||||
@@ -33,13 +33,14 @@ import {
|
|||||||
:host {
|
:host {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.5rem;
|
gap: 0.25rem;
|
||||||
height: 3rem;
|
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
:host-context(tui-root._mobile) {
|
:host-context(tui-root._mobile) {
|
||||||
height: auto;
|
tui-icon {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
|||||||
@@ -0,0 +1,111 @@
|
|||||||
|
import {
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
Component,
|
||||||
|
inject,
|
||||||
|
input,
|
||||||
|
} from '@angular/core'
|
||||||
|
import { FormsModule } from '@angular/forms'
|
||||||
|
import { TableComponent } from 'src/app/routes/portal/components/table.component'
|
||||||
|
import { T } from '@start9labs/start-sdk'
|
||||||
|
import {
|
||||||
|
PackageDataEntry,
|
||||||
|
StateInfo,
|
||||||
|
} from 'src/app/services/patch-db/data-model'
|
||||||
|
import { ServiceComponent } from './service.component'
|
||||||
|
import { TuiComparator, TuiTable } from '@taiga-ui/addon-table'
|
||||||
|
import { getInstalledPrimaryStatus } from 'src/app/services/pkg-status-rendering.service'
|
||||||
|
import { getManifest } from 'src/app/utils/get-package-data'
|
||||||
|
import { ToManifestPipe } from '../../../pipes/to-manifest'
|
||||||
|
import { toSignal } from '@angular/core/rxjs-interop'
|
||||||
|
import { DepErrorService } from 'src/app/services/dep-error.service'
|
||||||
|
import { i18nPipe } from '@start9labs/shared'
|
||||||
|
import { TuiSkeleton } from '@taiga-ui/kit'
|
||||||
|
import { PlaceholderComponent } from '../../../components/placeholder.component'
|
||||||
|
import { TuiButton } from '@taiga-ui/core'
|
||||||
|
import { RouterLink } from '@angular/router'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: '[services]',
|
||||||
|
template: `
|
||||||
|
@if (services()?.length === 0) {
|
||||||
|
<app-placeholder>
|
||||||
|
<h1 [style.margin-bottom]="0">
|
||||||
|
{{ 'Welcome to' | i18n }}
|
||||||
|
<span>StartOS!</span>
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
{{
|
||||||
|
'To get started, visit the Marketplace and download your first service'
|
||||||
|
| i18n
|
||||||
|
}}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<a
|
||||||
|
style="margin: 1.5rem 0;"
|
||||||
|
tuiButton
|
||||||
|
size="m"
|
||||||
|
iconStart="@tui.shopping-cart"
|
||||||
|
routerLink="../marketplace"
|
||||||
|
>
|
||||||
|
{{ 'View Marketplace' | i18n }}
|
||||||
|
</a>
|
||||||
|
</app-placeholder>
|
||||||
|
} @else {
|
||||||
|
<table
|
||||||
|
[sorter]="name"
|
||||||
|
[appTable]="[null, 'Name', 'Status', 'Version', 'Uptime']"
|
||||||
|
[appTableSorters]="[null, name, status]"
|
||||||
|
>
|
||||||
|
@for (service of services() | tuiTableSort; track service) {
|
||||||
|
<tr
|
||||||
|
appService
|
||||||
|
[pkg]="service"
|
||||||
|
[depErrors]="errors()?.[(service | toManifest).id]"
|
||||||
|
></tr>
|
||||||
|
} @empty {
|
||||||
|
@for (_ of ['', '']; track $index) {
|
||||||
|
<tr>
|
||||||
|
<td colspan="5">
|
||||||
|
<div [tuiSkeleton]="true">{{ 'Loading' | i18n }}</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</table>
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
imports: [
|
||||||
|
FormsModule,
|
||||||
|
TableComponent,
|
||||||
|
ServiceComponent,
|
||||||
|
ToManifestPipe,
|
||||||
|
i18nPipe,
|
||||||
|
TuiSkeleton,
|
||||||
|
PlaceholderComponent,
|
||||||
|
TuiButton,
|
||||||
|
RouterLink,
|
||||||
|
TuiTable,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class ServicesTableComponent<
|
||||||
|
T extends T.PackageDataEntry & {
|
||||||
|
stateInfo: StateInfo
|
||||||
|
},
|
||||||
|
> {
|
||||||
|
readonly errors = toSignal(inject(DepErrorService).depErrors$)
|
||||||
|
|
||||||
|
readonly services = input.required<readonly T[] | null>()
|
||||||
|
|
||||||
|
readonly name: TuiComparator<PackageDataEntry> = byName
|
||||||
|
|
||||||
|
readonly status: TuiComparator<PackageDataEntry> = (a, b) =>
|
||||||
|
getInstalledPrimaryStatus(b) > getInstalledPrimaryStatus(a) ? -1 : 1
|
||||||
|
}
|
||||||
|
|
||||||
|
function byName(a: PackageDataEntry, b: PackageDataEntry) {
|
||||||
|
return getManifest(b).title.toLowerCase() > getManifest(a).title.toLowerCase()
|
||||||
|
? -1
|
||||||
|
: 1
|
||||||
|
}
|
||||||
@@ -1,98 +0,0 @@
|
|||||||
// import {
|
|
||||||
// ChangeDetectionStrategy,
|
|
||||||
// Component,
|
|
||||||
// inject,
|
|
||||||
// Input,
|
|
||||||
// DOCUMENT,
|
|
||||||
// } from '@angular/core'
|
|
||||||
// import { i18nPipe } from '@start9labs/shared'
|
|
||||||
// import { T } from '@start9labs/start-sdk'
|
|
||||||
// import { tuiPure } from '@taiga-ui/cdk'
|
|
||||||
// import { TuiDataList, TuiDropdown, TuiButton } from '@taiga-ui/core'
|
|
||||||
// import { PackageDataEntry } from 'src/app/services/patch-db/data-model'
|
|
||||||
// import { InterfaceService } from '../../../components/interfaces/interface.service'
|
|
||||||
// import { getInstalledPrimaryStatus } from 'src/app/services/pkg-status-rendering.service'
|
|
||||||
|
|
||||||
// @Component({
|
|
||||||
// selector: 'app-ui-launch',
|
|
||||||
// template: `
|
|
||||||
// @if (interfaces.length > 1) {
|
|
||||||
// <button
|
|
||||||
// tuiIconButton
|
|
||||||
// iconStart="@tui.external-link"
|
|
||||||
// tuiDropdownOpen
|
|
||||||
// [disabled]="!isRunning"
|
|
||||||
// [tuiDropdown]="content"
|
|
||||||
// >
|
|
||||||
// {{ 'Open' | i18n }}
|
|
||||||
// </button>
|
|
||||||
// <ng-template #content>
|
|
||||||
// <tui-data-list>
|
|
||||||
// @for (interface of interfaces; track $index) {
|
|
||||||
// <a
|
|
||||||
// tuiOption
|
|
||||||
// target="_blank"
|
|
||||||
// rel="noreferrer"
|
|
||||||
// [attr.href]="getHref(interface)"
|
|
||||||
// >
|
|
||||||
// {{ interface.name }}
|
|
||||||
// </a>
|
|
||||||
// }
|
|
||||||
// </tui-data-list>
|
|
||||||
// </ng-template>
|
|
||||||
// } @else if (interfaces[0]) {
|
|
||||||
// <button
|
|
||||||
// tuiIconButton
|
|
||||||
// iconStart="@tui.external-link"
|
|
||||||
// [disabled]="!isRunning"
|
|
||||||
// (click)="openUI(interfaces[0])"
|
|
||||||
// >
|
|
||||||
// {{ interfaces[0].name }}
|
|
||||||
// </button>
|
|
||||||
// }
|
|
||||||
// `,
|
|
||||||
// styles: `
|
|
||||||
// :host-context(tui-root._mobile) *::before {
|
|
||||||
// font-size: 1.5rem !important;
|
|
||||||
// }
|
|
||||||
// `,
|
|
||||||
// changeDetection: ChangeDetectionStrategy.OnPush,
|
|
||||||
// imports: [TuiButton, TuiDropdown, TuiDataList, i18nPipe],
|
|
||||||
// })
|
|
||||||
// export class UILaunchComponent {
|
|
||||||
// private readonly interfaceService = inject(InterfaceService)
|
|
||||||
// private readonly document = inject(DOCUMENT)
|
|
||||||
|
|
||||||
// @Input()
|
|
||||||
// pkg!: PackageDataEntry
|
|
||||||
|
|
||||||
// get interfaces(): readonly T.ServiceInterface[] {
|
|
||||||
// return this.getInterfaces(this.pkg)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// get isRunning(): boolean {
|
|
||||||
// return getInstalledPrimaryStatus(this.pkg) === 'running'
|
|
||||||
// }
|
|
||||||
|
|
||||||
// @tuiPure
|
|
||||||
// getInterfaces(pkg?: PackageDataEntry): T.ServiceInterface[] {
|
|
||||||
// return pkg
|
|
||||||
// ? Object.values(pkg.serviceInterfaces).filter(
|
|
||||||
// i =>
|
|
||||||
// i.type === 'ui' &&
|
|
||||||
// (i.addressInfo.scheme === 'http' ||
|
|
||||||
// i.addressInfo.sslScheme === 'https'),
|
|
||||||
// )
|
|
||||||
// : []
|
|
||||||
// }
|
|
||||||
|
|
||||||
// getHref(ui: T.ServiceInterface): string {
|
|
||||||
// const host = this.pkg.hosts[ui.addressInfo.hostId]
|
|
||||||
// if (!host) return ''
|
|
||||||
// return this.interfaceService.launchableAddress(ui, host)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// openUI(ui: T.ServiceInterface) {
|
|
||||||
// this.document.defaultView?.open(this.getHref(ui), '_blank', 'noreferrer')
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
@@ -73,14 +73,14 @@ export namespace RR {
|
|||||||
uptime: number // seconds
|
uptime: number // seconds
|
||||||
}
|
}
|
||||||
|
|
||||||
export type GetServerLogsReq = FetchLogsReq // server.logs & server.kernel-logs
|
export type GetServerLogsReq = FetchLogsReq // server.logs & server.kernel-logs & net.tor.logs
|
||||||
export type GetServerLogsRes = FetchLogsRes
|
export type GetServerLogsRes = FetchLogsRes
|
||||||
|
|
||||||
export type FollowServerLogsReq = {
|
export type FollowServerLogsReq = {
|
||||||
limit?: number // (optional) default is 50. Ignored if cursor provided
|
limit?: number // (optional) default is 50. Ignored if cursor provided
|
||||||
boot?: number | string | null // (optional) number is offset (0: current, -1 prev, +1 first), string is a specific boot id, null is all. Default is undefined
|
boot?: number | string | null // (optional) number is offset (0: current, -1 prev, +1 first), string is a specific boot id, null is all. Default is undefined
|
||||||
cursor?: string // the last known log. Websocket will return all logs since this log
|
cursor?: string // the last known log. Websocket will return all logs since this log
|
||||||
} // server.logs.follow & server.kernel-logs.follow
|
} // server.logs.follow & server.kernel-logs.follow & net.tor.follow-logs
|
||||||
export type FollowServerLogsRes = {
|
export type FollowServerLogsRes = {
|
||||||
startCursor: string
|
startCursor: string
|
||||||
guid: string
|
guid: string
|
||||||
|
|||||||
@@ -86,6 +86,8 @@ export abstract class ApiService {
|
|||||||
params: RR.GetServerLogsReq,
|
params: RR.GetServerLogsReq,
|
||||||
): Promise<RR.GetServerLogsRes>
|
): Promise<RR.GetServerLogsRes>
|
||||||
|
|
||||||
|
abstract getTorLogs(params: RR.GetServerLogsReq): Promise<RR.GetServerLogsRes>
|
||||||
|
|
||||||
abstract getKernelLogs(
|
abstract getKernelLogs(
|
||||||
params: RR.GetServerLogsReq,
|
params: RR.GetServerLogsReq,
|
||||||
): Promise<RR.GetServerLogsRes>
|
): Promise<RR.GetServerLogsRes>
|
||||||
@@ -94,6 +96,10 @@ export abstract class ApiService {
|
|||||||
params: RR.FollowServerLogsReq,
|
params: RR.FollowServerLogsReq,
|
||||||
): Promise<RR.FollowServerLogsRes>
|
): Promise<RR.FollowServerLogsRes>
|
||||||
|
|
||||||
|
abstract followTorLogs(
|
||||||
|
params: RR.FollowServerLogsReq,
|
||||||
|
): Promise<RR.FollowServerLogsRes>
|
||||||
|
|
||||||
abstract followKernelLogs(
|
abstract followKernelLogs(
|
||||||
params: RR.FollowServerLogsReq,
|
params: RR.FollowServerLogsReq,
|
||||||
): Promise<RR.FollowServerLogsRes>
|
): Promise<RR.FollowServerLogsRes>
|
||||||
|
|||||||
@@ -206,6 +206,10 @@ export class LiveApiService extends ApiService {
|
|||||||
return this.rpcRequest({ method: 'server.logs', params })
|
return this.rpcRequest({ method: 'server.logs', params })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getTorLogs(params: RR.GetServerLogsReq): Promise<RR.GetServerLogsRes> {
|
||||||
|
return this.rpcRequest({ method: 'net.tor.logs', params })
|
||||||
|
}
|
||||||
|
|
||||||
async getKernelLogs(
|
async getKernelLogs(
|
||||||
params: RR.GetServerLogsReq,
|
params: RR.GetServerLogsReq,
|
||||||
): Promise<RR.GetServerLogsRes> {
|
): Promise<RR.GetServerLogsRes> {
|
||||||
@@ -218,6 +222,12 @@ export class LiveApiService extends ApiService {
|
|||||||
return this.rpcRequest({ method: 'server.logs.follow', params })
|
return this.rpcRequest({ method: 'server.logs.follow', params })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async followTorLogs(
|
||||||
|
params: RR.FollowServerLogsReq,
|
||||||
|
): Promise<RR.FollowServerLogsRes> {
|
||||||
|
return this.rpcRequest({ method: 'net.tor.logs.follow', params })
|
||||||
|
}
|
||||||
|
|
||||||
async followKernelLogs(
|
async followKernelLogs(
|
||||||
params: RR.FollowServerLogsReq,
|
params: RR.FollowServerLogsReq,
|
||||||
): Promise<RR.FollowServerLogsRes> {
|
): Promise<RR.FollowServerLogsRes> {
|
||||||
|
|||||||
@@ -290,6 +290,17 @@ export class MockApiService extends ApiService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getTorLogs(params: RR.GetServerLogsReq): Promise<RR.GetServerLogsRes> {
|
||||||
|
await pauseFor(2000)
|
||||||
|
const entries = this.randomLogs(params.limit)
|
||||||
|
|
||||||
|
return {
|
||||||
|
entries,
|
||||||
|
startCursor: 'start-cursor',
|
||||||
|
endCursor: 'end-cursor',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async getKernelLogs(
|
async getKernelLogs(
|
||||||
params: RR.GetServerLogsReq,
|
params: RR.GetServerLogsReq,
|
||||||
): Promise<RR.GetServerLogsRes> {
|
): Promise<RR.GetServerLogsRes> {
|
||||||
@@ -313,6 +324,16 @@ export class MockApiService extends ApiService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async followTorLogs(
|
||||||
|
params: RR.FollowServerLogsReq,
|
||||||
|
): Promise<RR.FollowServerLogsRes> {
|
||||||
|
await pauseFor(2000)
|
||||||
|
return {
|
||||||
|
startCursor: 'start-cursor',
|
||||||
|
guid: 'logs-guid',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async followKernelLogs(
|
async followKernelLogs(
|
||||||
params: RR.FollowServerLogsReq,
|
params: RR.FollowServerLogsReq,
|
||||||
): Promise<RR.FollowServerLogsRes> {
|
): Promise<RR.FollowServerLogsRes> {
|
||||||
|
|||||||
@@ -222,6 +222,167 @@ export const mockPatchData: DataModel = {
|
|||||||
kiosk: true,
|
kiosk: true,
|
||||||
},
|
},
|
||||||
packageData: {
|
packageData: {
|
||||||
|
lnd: {
|
||||||
|
stateInfo: {
|
||||||
|
state: 'installed',
|
||||||
|
manifest: {
|
||||||
|
...Mock.MockManifestLnd,
|
||||||
|
version: '0.11.0:0.0.1',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
s9pk: '/media/startos/data/package-data/archive/installed/asdfasdf.s9pk',
|
||||||
|
icon: '/assets/img/service-icons/lnd.png',
|
||||||
|
lastBackup: null,
|
||||||
|
statusInfo: {
|
||||||
|
desired: { main: 'stopped' },
|
||||||
|
error: null,
|
||||||
|
health: {},
|
||||||
|
started: null,
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
config: {
|
||||||
|
name: 'Config',
|
||||||
|
description: 'LND needs configuration before starting',
|
||||||
|
warning: null,
|
||||||
|
visibility: 'enabled',
|
||||||
|
allowedStatuses: 'any',
|
||||||
|
hasInput: true,
|
||||||
|
group: null,
|
||||||
|
},
|
||||||
|
connect: {
|
||||||
|
name: 'Connect',
|
||||||
|
description: 'View LND connection details',
|
||||||
|
warning: null,
|
||||||
|
visibility: 'enabled',
|
||||||
|
allowedStatuses: 'any',
|
||||||
|
hasInput: true,
|
||||||
|
group: 'Connecting',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
serviceInterfaces: {
|
||||||
|
grpc: {
|
||||||
|
id: 'grpc',
|
||||||
|
masked: false,
|
||||||
|
name: 'GRPC',
|
||||||
|
description:
|
||||||
|
'Used by dependent services and client wallets for connecting to your node',
|
||||||
|
type: 'api',
|
||||||
|
addressInfo: {
|
||||||
|
username: null,
|
||||||
|
hostId: 'qrstuv',
|
||||||
|
internalPort: 10009,
|
||||||
|
scheme: null,
|
||||||
|
sslScheme: 'grpc',
|
||||||
|
suffix: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
lndconnect: {
|
||||||
|
id: 'lndconnect',
|
||||||
|
masked: true,
|
||||||
|
name: 'LND Connect',
|
||||||
|
description:
|
||||||
|
'Used by client wallets adhering to LND Connect protocol to connect to your node',
|
||||||
|
type: 'api',
|
||||||
|
addressInfo: {
|
||||||
|
username: null,
|
||||||
|
hostId: 'qrstuv',
|
||||||
|
internalPort: 10009,
|
||||||
|
scheme: null,
|
||||||
|
sslScheme: 'lndconnect',
|
||||||
|
suffix: 'cert=askjdfbjadnaskjnd&macaroon=ksjbdfnhjasbndjksand',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
p2p: {
|
||||||
|
id: 'p2p',
|
||||||
|
masked: false,
|
||||||
|
name: 'P2P',
|
||||||
|
description:
|
||||||
|
'Used for connecting to other nodes on the Bitcoin network',
|
||||||
|
type: 'p2p',
|
||||||
|
addressInfo: {
|
||||||
|
username: null,
|
||||||
|
hostId: 'rstuvw',
|
||||||
|
internalPort: 8333,
|
||||||
|
scheme: 'bitcoin',
|
||||||
|
sslScheme: null,
|
||||||
|
suffix: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
currentDependencies: {
|
||||||
|
bitcoind: {
|
||||||
|
title: Mock.BitcoinDep.title,
|
||||||
|
icon: Mock.BitcoinDep.icon,
|
||||||
|
kind: 'running',
|
||||||
|
versionRange: '>=26.0.0',
|
||||||
|
healthChecks: [],
|
||||||
|
},
|
||||||
|
'btc-rpc-proxy': {
|
||||||
|
title: Mock.ProxyDep.title,
|
||||||
|
icon: Mock.ProxyDep.icon,
|
||||||
|
kind: 'running',
|
||||||
|
versionRange: '>2.0.0',
|
||||||
|
healthChecks: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
hosts: {},
|
||||||
|
storeExposedDependents: [],
|
||||||
|
registry: 'https://registry.start9.com/',
|
||||||
|
developerKey: 'developer-key',
|
||||||
|
tasks: {
|
||||||
|
config: {
|
||||||
|
active: true,
|
||||||
|
task: {
|
||||||
|
packageId: 'lnd',
|
||||||
|
actionId: 'config',
|
||||||
|
severity: 'critical',
|
||||||
|
reason: 'LND needs configuration before starting',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
connect: {
|
||||||
|
active: true,
|
||||||
|
task: {
|
||||||
|
packageId: 'lnd',
|
||||||
|
actionId: 'connect',
|
||||||
|
severity: 'important',
|
||||||
|
reason: 'View LND connection details',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'bitcoind/config': {
|
||||||
|
active: true,
|
||||||
|
task: {
|
||||||
|
packageId: 'bitcoind',
|
||||||
|
actionId: 'config',
|
||||||
|
severity: 'critical',
|
||||||
|
reason: 'LND likes BTC a certain way',
|
||||||
|
input: {
|
||||||
|
kind: 'partial',
|
||||||
|
value: {
|
||||||
|
color: '#ffffff',
|
||||||
|
testnet: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'bitcoind/rpc': {
|
||||||
|
active: true,
|
||||||
|
task: {
|
||||||
|
packageId: 'bitcoind',
|
||||||
|
actionId: 'rpc',
|
||||||
|
severity: 'important',
|
||||||
|
reason: `LND want's its own RPC credentials`,
|
||||||
|
input: {
|
||||||
|
kind: 'partial',
|
||||||
|
value: {
|
||||||
|
rpcsettings: {
|
||||||
|
rpcuser: 'lnd',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
bitcoind: {
|
bitcoind: {
|
||||||
stateInfo: {
|
stateInfo: {
|
||||||
state: 'installed',
|
state: 'installed',
|
||||||
@@ -511,166 +672,5 @@ export const mockPatchData: DataModel = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
lnd: {
|
|
||||||
stateInfo: {
|
|
||||||
state: 'installed',
|
|
||||||
manifest: {
|
|
||||||
...Mock.MockManifestLnd,
|
|
||||||
version: '0.11.0:0.0.1',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
s9pk: '/media/startos/data/package-data/archive/installed/asdfasdf.s9pk',
|
|
||||||
icon: '/assets/img/service-icons/lnd.png',
|
|
||||||
lastBackup: null,
|
|
||||||
statusInfo: {
|
|
||||||
desired: { main: 'stopped' },
|
|
||||||
error: null,
|
|
||||||
health: {},
|
|
||||||
started: null,
|
|
||||||
},
|
|
||||||
actions: {
|
|
||||||
config: {
|
|
||||||
name: 'Config',
|
|
||||||
description: 'LND needs configuration before starting',
|
|
||||||
warning: null,
|
|
||||||
visibility: 'enabled',
|
|
||||||
allowedStatuses: 'any',
|
|
||||||
hasInput: true,
|
|
||||||
group: null,
|
|
||||||
},
|
|
||||||
connect: {
|
|
||||||
name: 'Connect',
|
|
||||||
description: 'View LND connection details',
|
|
||||||
warning: null,
|
|
||||||
visibility: 'enabled',
|
|
||||||
allowedStatuses: 'any',
|
|
||||||
hasInput: true,
|
|
||||||
group: 'Connecting',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
serviceInterfaces: {
|
|
||||||
grpc: {
|
|
||||||
id: 'grpc',
|
|
||||||
masked: false,
|
|
||||||
name: 'GRPC',
|
|
||||||
description:
|
|
||||||
'Used by dependent services and client wallets for connecting to your node',
|
|
||||||
type: 'api',
|
|
||||||
addressInfo: {
|
|
||||||
username: null,
|
|
||||||
hostId: 'qrstuv',
|
|
||||||
internalPort: 10009,
|
|
||||||
scheme: null,
|
|
||||||
sslScheme: 'grpc',
|
|
||||||
suffix: '',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
lndconnect: {
|
|
||||||
id: 'lndconnect',
|
|
||||||
masked: true,
|
|
||||||
name: 'LND Connect',
|
|
||||||
description:
|
|
||||||
'Used by client wallets adhering to LND Connect protocol to connect to your node',
|
|
||||||
type: 'api',
|
|
||||||
addressInfo: {
|
|
||||||
username: null,
|
|
||||||
hostId: 'qrstuv',
|
|
||||||
internalPort: 10009,
|
|
||||||
scheme: null,
|
|
||||||
sslScheme: 'lndconnect',
|
|
||||||
suffix: 'cert=askjdfbjadnaskjnd&macaroon=ksjbdfnhjasbndjksand',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
p2p: {
|
|
||||||
id: 'p2p',
|
|
||||||
masked: false,
|
|
||||||
name: 'P2P',
|
|
||||||
description:
|
|
||||||
'Used for connecting to other nodes on the Bitcoin network',
|
|
||||||
type: 'p2p',
|
|
||||||
addressInfo: {
|
|
||||||
username: null,
|
|
||||||
hostId: 'rstuvw',
|
|
||||||
internalPort: 8333,
|
|
||||||
scheme: 'bitcoin',
|
|
||||||
sslScheme: null,
|
|
||||||
suffix: '',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
currentDependencies: {
|
|
||||||
bitcoind: {
|
|
||||||
title: Mock.BitcoinDep.title,
|
|
||||||
icon: Mock.BitcoinDep.icon,
|
|
||||||
kind: 'running',
|
|
||||||
versionRange: '>=26.0.0',
|
|
||||||
healthChecks: [],
|
|
||||||
},
|
|
||||||
'btc-rpc-proxy': {
|
|
||||||
title: Mock.ProxyDep.title,
|
|
||||||
icon: Mock.ProxyDep.icon,
|
|
||||||
kind: 'running',
|
|
||||||
versionRange: '>2.0.0',
|
|
||||||
healthChecks: [],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
hosts: {},
|
|
||||||
storeExposedDependents: [],
|
|
||||||
registry: 'https://registry.start9.com/',
|
|
||||||
developerKey: 'developer-key',
|
|
||||||
tasks: {
|
|
||||||
config: {
|
|
||||||
active: true,
|
|
||||||
task: {
|
|
||||||
packageId: 'lnd',
|
|
||||||
actionId: 'config',
|
|
||||||
severity: 'critical',
|
|
||||||
reason: 'LND needs configuration before starting',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
connect: {
|
|
||||||
active: true,
|
|
||||||
task: {
|
|
||||||
packageId: 'lnd',
|
|
||||||
actionId: 'connect',
|
|
||||||
severity: 'important',
|
|
||||||
reason: 'View LND connection details',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'bitcoind/config': {
|
|
||||||
active: true,
|
|
||||||
task: {
|
|
||||||
packageId: 'bitcoind',
|
|
||||||
actionId: 'config',
|
|
||||||
severity: 'critical',
|
|
||||||
reason: 'LND likes BTC a certain way',
|
|
||||||
input: {
|
|
||||||
kind: 'partial',
|
|
||||||
value: {
|
|
||||||
color: '#ffffff',
|
|
||||||
testnet: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'bitcoind/rpc': {
|
|
||||||
active: true,
|
|
||||||
task: {
|
|
||||||
packageId: 'bitcoind',
|
|
||||||
actionId: 'rpc',
|
|
||||||
severity: 'important',
|
|
||||||
reason: `LND want's its own RPC credentials`,
|
|
||||||
input: {
|
|
||||||
kind: 'partial',
|
|
||||||
value: {
|
|
||||||
rpcsettings: {
|
|
||||||
rpcuser: 'lnd',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user