mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-31 04:23:40 +00:00
feat: refactor logs (#2856)
* feat: refactor logs * download root ca, minor transaltions, better interfaces buttons * chore: comments --------- Co-authored-by: Matt Hill <mattnine@protonmail.com>
This commit is contained in:
@@ -12,7 +12,7 @@ export default {
|
|||||||
email: 'Email',
|
email: 'Email',
|
||||||
backup: 'Create Backup',
|
backup: 'Create Backup',
|
||||||
restore: 'Restore Backup',
|
restore: 'Restore Backup',
|
||||||
interfaces: 'User Interface Addresses',
|
interfaces: 'StartOS UI',
|
||||||
acme: 'ACME',
|
acme: 'ACME',
|
||||||
wifi: 'WiFi',
|
wifi: 'WiFi',
|
||||||
sessions: 'Active Sessions',
|
sessions: 'Active Sessions',
|
||||||
@@ -22,16 +22,29 @@ export default {
|
|||||||
general: {
|
general: {
|
||||||
title: 'General Settings',
|
title: 'General Settings',
|
||||||
subtitle: 'Manage your overall setup and preferences',
|
subtitle: 'Manage your overall setup and preferences',
|
||||||
update: 'Software Update',
|
|
||||||
restart: 'Restart to apply',
|
|
||||||
check: 'Check for updates',
|
|
||||||
tab: 'Browser Tab Title',
|
tab: 'Browser Tab Title',
|
||||||
language: 'Language',
|
language: 'Language',
|
||||||
tor: 'Reset Tor',
|
repair: {
|
||||||
daemon: 'Restart the Tor daemon on your server',
|
title: 'Disk Repair',
|
||||||
disk: 'Disk Repair',
|
subtitle: 'Attempt automatic repair',
|
||||||
attempt: 'Attempt automatic repair',
|
button: 'Repair',
|
||||||
repair: 'Repair',
|
},
|
||||||
|
ca: {
|
||||||
|
title: 'Root Certificate Authority',
|
||||||
|
subtitle: `Download your server's Root CA`,
|
||||||
|
button: 'Download',
|
||||||
|
},
|
||||||
|
tor: {
|
||||||
|
title: 'Reset Tor',
|
||||||
|
subtitle: 'Restart the Tor daemon on your server',
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
title: 'Software Update',
|
||||||
|
button: {
|
||||||
|
restart: 'Restart to apply',
|
||||||
|
check: 'Check for updates',
|
||||||
|
},
|
||||||
|
},
|
||||||
sync: {
|
sync: {
|
||||||
title: 'Clock sync failure',
|
title: 'Clock sync failure',
|
||||||
subtitle:
|
subtitle:
|
||||||
|
|||||||
@@ -24,16 +24,29 @@ export default {
|
|||||||
general: {
|
general: {
|
||||||
title: 'Configuración General',
|
title: 'Configuración General',
|
||||||
subtitle: 'Gestiona tu configuración general y preferencias',
|
subtitle: 'Gestiona tu configuración general y preferencias',
|
||||||
update: 'Actualización de Software',
|
|
||||||
restart: 'Reiniciar para aplicar',
|
|
||||||
check: 'Buscar actualizaciones',
|
|
||||||
tab: 'Título de la Pestaña del Navegador',
|
tab: 'Título de la Pestaña del Navegador',
|
||||||
language: 'Idioma',
|
language: 'Idioma',
|
||||||
tor: 'Reiniciar Tor',
|
repair: {
|
||||||
daemon: 'Reiniciar el daemon de Tor en tu servidor',
|
title: 'Reparación de Disco',
|
||||||
disk: 'Reparación de Disco',
|
subtitle: 'Intentar reparación automática',
|
||||||
attempt: 'Intentar reparación automática',
|
button: 'Reparar',
|
||||||
repair: 'Reparar',
|
},
|
||||||
|
ca: {
|
||||||
|
title: 'Autoridad de Certificación Raíz',
|
||||||
|
subtitle: 'Descarga la autoridad certificadora raíz de tu servidor',
|
||||||
|
button: 'Descarga',
|
||||||
|
},
|
||||||
|
tor: {
|
||||||
|
title: 'Reiniciar Tor',
|
||||||
|
subtitle: 'Reiniciar el daemon de Tor en tu servidor',
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
title: 'Actualización de Software',
|
||||||
|
button: {
|
||||||
|
restart: 'Reiniciar para aplicar',
|
||||||
|
check: 'Buscar actualizaciones',
|
||||||
|
},
|
||||||
|
},
|
||||||
sync: {
|
sync: {
|
||||||
title: 'Fallo en la sincronización del reloj',
|
title: 'Fallo en la sincronización del reloj',
|
||||||
subtitle:
|
subtitle:
|
||||||
|
|||||||
@@ -61,6 +61,15 @@ type ClearnetForm = {
|
|||||||
Learn More
|
Learn More
|
||||||
</a>
|
</a>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
<button
|
||||||
|
tuiButton
|
||||||
|
appearance="accent"
|
||||||
|
[iconStart]="isPublic() ? '@tui.globe-lock' : '@tui.globe'"
|
||||||
|
[style.margin-inline-start]="'auto'"
|
||||||
|
(click)="toggle()"
|
||||||
|
>
|
||||||
|
Make {{ isPublic() ? 'private' : 'public' }}
|
||||||
|
</button>
|
||||||
@if (clearnet().length) {
|
@if (clearnet().length) {
|
||||||
<button
|
<button
|
||||||
tuiButton
|
tuiButton
|
||||||
@@ -70,14 +79,6 @@ type ClearnetForm = {
|
|||||||
>
|
>
|
||||||
Add
|
Add
|
||||||
</button>
|
</button>
|
||||||
<button
|
|
||||||
tuiButton
|
|
||||||
appearance="accent"
|
|
||||||
[iconStart]="isPublic() ? '@tui.globe-lock' : '@tui.globe'"
|
|
||||||
(click)="toggle()"
|
|
||||||
>
|
|
||||||
Make {{ isPublic() ? 'private' : 'public' }}
|
|
||||||
</button>
|
|
||||||
}
|
}
|
||||||
</header>
|
</header>
|
||||||
@if (clearnet().length) {
|
@if (clearnet().length) {
|
||||||
@@ -110,8 +111,10 @@ type ClearnetForm = {
|
|||||||
</table>
|
</table>
|
||||||
} @else {
|
} @else {
|
||||||
<app-placeholder icon="@tui.app-window">
|
<app-placeholder icon="@tui.app-window">
|
||||||
No interfaces available
|
No public addresses
|
||||||
<button tuiButton iconStart="@tui.plus" (click)="add()">Add</button>
|
<button tuiButton iconStart="@tui.plus" (click)="add()">
|
||||||
|
Add Domain
|
||||||
|
</button>
|
||||||
</app-placeholder>
|
</app-placeholder>
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
|
|||||||
@@ -54,6 +54,16 @@ type OnionForm = {
|
|||||||
Learn More
|
Learn More
|
||||||
</a>
|
</a>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
@if (tor().length) {
|
||||||
|
<button
|
||||||
|
tuiButton
|
||||||
|
iconStart="@tui.plus"
|
||||||
|
[style.margin-inline-start]="'auto'"
|
||||||
|
(click)="add()"
|
||||||
|
>
|
||||||
|
Add
|
||||||
|
</button>
|
||||||
|
}
|
||||||
</header>
|
</header>
|
||||||
@if (tor().length) {
|
@if (tor().length) {
|
||||||
<table [appTable]="['Protocol', 'URL', '']">
|
<table [appTable]="['Protocol', 'URL', '']">
|
||||||
@@ -85,7 +95,7 @@ type OnionForm = {
|
|||||||
} @else {
|
} @else {
|
||||||
<app-placeholder icon="@tui.app-window">
|
<app-placeholder icon="@tui.app-window">
|
||||||
No Tor addresses available
|
No Tor addresses available
|
||||||
<button tuiButton iconStart="@tui.plus">Add</button>
|
<button tuiButton iconStart="@tui.plus" (click)="add()">Add</button>
|
||||||
</app-placeholder>
|
</app-placeholder>
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
@@ -149,7 +159,7 @@ export class InterfaceTorComponent {
|
|||||||
|
|
||||||
async add() {
|
async add() {
|
||||||
const options: Partial<TuiDialogOptions<FormContext<OnionForm>>> = {
|
const options: Partial<TuiDialogOptions<FormContext<OnionForm>>> = {
|
||||||
label: 'Select Domain/Subdomain',
|
label: 'New Tor Address',
|
||||||
data: {
|
data: {
|
||||||
spec: await configBuilderToSpec(
|
spec: await configBuilderToSpec(
|
||||||
ISB.InputSpec.of({
|
ISB.InputSpec.of({
|
||||||
|
|||||||
@@ -7,6 +7,9 @@
|
|||||||
|
|
||||||
.scrollbar {
|
.scrollbar {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
background: var(--tui-background-neutral-1);
|
||||||
|
border-radius: var(--tui-radius-m);
|
||||||
|
border: 1rem solid transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.loading-dots {
|
.loading-dots {
|
||||||
@@ -27,7 +30,6 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding-top: 1rem;
|
padding-top: 1rem;
|
||||||
border-top: 1px solid var(--tui-background-neutral-1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-status='reconnecting'] {
|
[data-status='reconnecting'] {
|
||||||
|
|||||||
@@ -1,44 +1,87 @@
|
|||||||
import { ChangeDetectionStrategy, Component, inject } from '@angular/core'
|
import { KeyValuePipe } from '@angular/common'
|
||||||
import { FormsModule } from '@angular/forms'
|
import {
|
||||||
import { TuiSelectModule, TuiTextfieldControllerModule } from '@taiga-ui/legacy'
|
ChangeDetectionStrategy,
|
||||||
|
Component,
|
||||||
|
inject,
|
||||||
|
signal,
|
||||||
|
} from '@angular/core'
|
||||||
|
import {
|
||||||
|
TuiAppearance,
|
||||||
|
TuiButton,
|
||||||
|
TuiIcon,
|
||||||
|
TuiLink,
|
||||||
|
TuiTitle,
|
||||||
|
} from '@taiga-ui/core'
|
||||||
|
import { TuiCardMedium } from '@taiga-ui/layout'
|
||||||
import { LogsComponent } from 'src/app/routes/portal/components/logs/logs.component'
|
import { LogsComponent } from 'src/app/routes/portal/components/logs/logs.component'
|
||||||
import { RR } from 'src/app/services/api/api.types'
|
import { RR } from 'src/app/services/api/api.types'
|
||||||
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||||
import { TitleDirective } from 'src/app/services/title.service'
|
import { TitleDirective } from 'src/app/services/title.service'
|
||||||
|
|
||||||
|
interface Log {
|
||||||
|
title: string
|
||||||
|
subtitle: string
|
||||||
|
icon: string
|
||||||
|
follow: (params: RR.FollowServerLogsReq) => Promise<RR.FollowServerLogsRes>
|
||||||
|
fetch: (params: RR.GetServerLogsReq) => Promise<RR.GetServerLogsRes>
|
||||||
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
template: `
|
template: `
|
||||||
<ng-container *title>Logs</ng-container>
|
<ng-container *title>
|
||||||
<tui-select
|
@if (current(); as key) {
|
||||||
tuiTextfieldAppearance="secondary"
|
<button
|
||||||
tuiTextfieldSize="m"
|
tuiIconButton
|
||||||
[style.max-width.rem]="26"
|
iconStart="@tui.arrow-left"
|
||||||
[(ngModel)]="logs"
|
(click)="current.set(null)"
|
||||||
>
|
>
|
||||||
{{ subtitle }}
|
Back
|
||||||
<select tuiSelect [items]="items"></select>
|
</button>
|
||||||
</tui-select>
|
{{ logs[key].title }}
|
||||||
@switch (logs) {
|
} @else {
|
||||||
@case ('OS Logs') {
|
Logs
|
||||||
<logs
|
|
||||||
context="OS Logs"
|
|
||||||
[followLogs]="followOS"
|
|
||||||
[fetchLogs]="fetchOS"
|
|
||||||
></logs>
|
|
||||||
}
|
}
|
||||||
@case ('Kernel Logs') {
|
</ng-container>
|
||||||
<logs
|
@if (current(); as key) {
|
||||||
context="Kernel Logs"
|
<header tuiTitle>
|
||||||
[followLogs]="followKernel"
|
<strong class="title">
|
||||||
[fetchLogs]="fetchKernel"
|
<button
|
||||||
></logs>
|
tuiIconButton
|
||||||
|
appearance="secondary-grayscale"
|
||||||
|
iconStart="@tui.x"
|
||||||
|
size="s"
|
||||||
|
class="close"
|
||||||
|
(click)="current.set(null)"
|
||||||
|
>
|
||||||
|
Close
|
||||||
|
</button>
|
||||||
|
{{ logs[key].title }}
|
||||||
|
</strong>
|
||||||
|
<p tuiSubtitle>{{ logs[key].subtitle }}</p>
|
||||||
|
</header>
|
||||||
|
@for (log of logs | keyvalue; track $index) {
|
||||||
|
@if (log.key === current()) {
|
||||||
|
<logs
|
||||||
|
[context]="log.value.title"
|
||||||
|
[followLogs]="log.value.follow"
|
||||||
|
[fetchLogs]="log.value.fetch"
|
||||||
|
/>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@case ('Tor Logs') {
|
} @else {
|
||||||
<logs
|
@for (log of logs | keyvalue; track $index) {
|
||||||
context="Tor Logs"
|
<button
|
||||||
[followLogs]="followTor"
|
tuiCardMedium
|
||||||
[fetchLogs]="fetchTor"
|
tuiAppearance="neutral"
|
||||||
></logs>
|
(click)="current.set(log.key)"
|
||||||
|
>
|
||||||
|
<tui-icon [icon]="log.value.icon" />
|
||||||
|
<span tuiTitle>
|
||||||
|
<strong>{{ log.value.title }}</strong>
|
||||||
|
<span tuiSubtitle>{{ log.value.subtitle }}</span>
|
||||||
|
</span>
|
||||||
|
<tui-icon icon="@tui.chevron-right" />
|
||||||
|
</button>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
@@ -47,51 +90,114 @@ import { TitleDirective } from 'src/app/services/title.service'
|
|||||||
host: { class: 'g-page' },
|
host: { class: 'g-page' },
|
||||||
styles: [
|
styles: [
|
||||||
`
|
`
|
||||||
tui-select {
|
:host {
|
||||||
margin: 1rem 0;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 1rem;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
logs {
|
logs {
|
||||||
height: calc(100% - 5rem);
|
height: calc(100% - 4rem);
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
border-radius: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
button::before {
|
||||||
|
margin: 0 -0.25rem 0 -0.375rem;
|
||||||
|
--tui-icon-size: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
[tuiCardMedium] {
|
||||||
|
height: 14rem;
|
||||||
|
width: 14rem;
|
||||||
|
cursor: pointer;
|
||||||
|
box-shadow:
|
||||||
|
inset 0 0 0 1px var(--tui-background-neutral-1),
|
||||||
|
var(--tui-shadow-small);
|
||||||
|
|
||||||
|
[tuiSubtitle] {
|
||||||
|
color: var(--tui-text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
tui-icon:last-child {
|
||||||
|
align-self: flex-end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:host-context(tui-root._mobile) {
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: flex-start;
|
||||||
|
|
||||||
|
header {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
logs {
|
||||||
|
height: calc(100% - 2rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
[tuiCardMedium] {
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
FormsModule,
|
|
||||||
TuiSelectModule,
|
|
||||||
TuiTextfieldControllerModule,
|
|
||||||
LogsComponent,
|
LogsComponent,
|
||||||
TitleDirective,
|
TitleDirective,
|
||||||
|
KeyValuePipe,
|
||||||
|
TuiTitle,
|
||||||
|
TuiCardMedium,
|
||||||
|
TuiIcon,
|
||||||
|
TuiAppearance,
|
||||||
|
TuiLink,
|
||||||
|
TuiButton,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export default class SystemLogsComponent {
|
export default class SystemLogsComponent {
|
||||||
private readonly api = inject(ApiService)
|
private readonly api = inject(ApiService)
|
||||||
readonly items = ['OS Logs', 'Kernel Logs', 'Tor Logs']
|
|
||||||
logs = 'OS Logs'
|
|
||||||
|
|
||||||
readonly followOS = async (params: RR.FollowServerLogsReq) =>
|
readonly current = signal<string | null>(null)
|
||||||
this.api.followServerLogs(params)
|
readonly logs: Record<string, Log> = {
|
||||||
readonly fetchOS = async (params: RR.GetServerLogsReq) =>
|
os: {
|
||||||
this.api.getServerLogs(params)
|
title: 'OS Logs',
|
||||||
|
subtitle: 'Raw, unfiltered operating system logs',
|
||||||
readonly followKernel = async (params: RR.FollowServerLogsReq) =>
|
icon: '@tui.square-dashed-bottom-code',
|
||||||
this.api.followKernelLogs(params)
|
follow: params => this.api.followServerLogs(params),
|
||||||
readonly fetchKernel = async (params: RR.GetServerLogsReq) =>
|
fetch: params => this.api.getServerLogs(params),
|
||||||
this.api.getKernelLogs(params)
|
},
|
||||||
|
kernel: {
|
||||||
readonly followTor = async (params: RR.FollowServerLogsReq) =>
|
title: 'Kernel Logs',
|
||||||
this.api.followTorLogs(params)
|
subtitle: 'Diagnostics for drivers and other kernel processes',
|
||||||
readonly fetchTor = async (params: RR.GetServerLogsReq) =>
|
icon: '@tui.square-chevron-right',
|
||||||
this.api.getTorLogs(params)
|
follow: params => this.api.followKernelLogs(params),
|
||||||
|
fetch: params => this.api.getKernelLogs(params),
|
||||||
get subtitle(): string {
|
},
|
||||||
switch (this.logs) {
|
tor: {
|
||||||
case 'OS Logs':
|
title: 'Tor Logs',
|
||||||
return 'Raw, unfiltered operating system logs'
|
subtitle: 'Diagnostic logs for the Tor daemon on StartOS',
|
||||||
case 'Kernel Logs':
|
icon: '@tui.globe',
|
||||||
return 'Diagnostic log stream for device drivers and other kernel processes'
|
follow: params => this.api.followTorLogs(params),
|
||||||
default:
|
fetch: params => this.api.getTorLogs(params),
|
||||||
return 'Diagnostic log stream for the Tor daemon on StartOS'
|
},
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,9 @@ import {
|
|||||||
import { toSignal } from '@angular/core/rxjs-interop'
|
import { toSignal } from '@angular/core/rxjs-interop'
|
||||||
import { RouterLink } from '@angular/router'
|
import { RouterLink } from '@angular/router'
|
||||||
import { getPkgId } from '@start9labs/shared'
|
import { getPkgId } from '@start9labs/shared'
|
||||||
import { TuiButton } from '@taiga-ui/core'
|
import { TuiItem } from '@taiga-ui/cdk'
|
||||||
|
import { TuiButton, TuiLink } from '@taiga-ui/core'
|
||||||
|
import { TuiBreadcrumbs } from '@taiga-ui/kit'
|
||||||
import { PatchDB } from 'patch-db-client'
|
import { PatchDB } from 'patch-db-client'
|
||||||
import { InterfaceComponent } from 'src/app/routes/portal/components/interfaces/interface.component'
|
import { InterfaceComponent } from 'src/app/routes/portal/components/interfaces/interface.component'
|
||||||
import { getAddresses } from 'src/app/routes/portal/components/interfaces/interface.utils'
|
import { getAddresses } from 'src/app/routes/portal/components/interfaces/interface.utils'
|
||||||
@@ -22,6 +24,12 @@ import { TitleDirective } from 'src/app/services/title.service'
|
|||||||
<a routerLink="../.." tuiIconButton iconStart="@tui.arrow-left">Back</a>
|
<a routerLink="../.." tuiIconButton iconStart="@tui.arrow-left">Back</a>
|
||||||
{{ interface()?.name }}
|
{{ interface()?.name }}
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
<tui-breadcrumbs size="l" [style.margin-block-end.rem]="1">
|
||||||
|
<a *tuiItem tuiLink appearance="action-grayscale" routerLink="../..">
|
||||||
|
Dashboard
|
||||||
|
</a>
|
||||||
|
<span *tuiItem class="g-primary">{{ interface()?.name }}</span>
|
||||||
|
</tui-breadcrumbs>
|
||||||
@if (interface(); as serviceInterface) {
|
@if (interface(); as serviceInterface) {
|
||||||
<app-interface
|
<app-interface
|
||||||
[packageId]="pkgId"
|
[packageId]="pkgId"
|
||||||
@@ -29,10 +37,23 @@ import { TitleDirective } from 'src/app/services/title.service'
|
|||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
|
styles: `
|
||||||
|
:host-context(tui-root._mobile) tui-breadcrumbs {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
`,
|
||||||
host: { class: 'g-subpage' },
|
host: { class: 'g-subpage' },
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [InterfaceComponent, RouterLink, TuiButton, TitleDirective],
|
imports: [
|
||||||
|
InterfaceComponent,
|
||||||
|
RouterLink,
|
||||||
|
TuiButton,
|
||||||
|
TitleDirective,
|
||||||
|
TuiBreadcrumbs,
|
||||||
|
TuiItem,
|
||||||
|
TuiLink,
|
||||||
|
],
|
||||||
})
|
})
|
||||||
export default class ServiceInterfaceRoute {
|
export default class ServiceInterfaceRoute {
|
||||||
private readonly config = inject(ConfigService)
|
private readonly config = inject(ConfigService)
|
||||||
|
|||||||
@@ -52,6 +52,9 @@ const ICONS = {
|
|||||||
>
|
>
|
||||||
<tui-icon [icon]="icons[item]" />
|
<tui-icon [icon]="icons[item]" />
|
||||||
<span tuiTitle>{{ item }}</span>
|
<span tuiTitle>{{ item }}</span>
|
||||||
|
@if (item === 'dashboard') {
|
||||||
|
<a routerLink="interface" routerLinkActive="active"></a>
|
||||||
|
}
|
||||||
</a>
|
</a>
|
||||||
}
|
}
|
||||||
</nav>
|
</nav>
|
||||||
@@ -80,7 +83,12 @@ const ICONS = {
|
|||||||
margin: 0 -0.5rem;
|
margin: 0 -0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.active {
|
a a {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.active,
|
||||||
|
a:has(.active) {
|
||||||
color: var(--tui-text-primary);
|
color: var(--tui-text-primary);
|
||||||
|
|
||||||
[tuiTitle] {
|
[tuiTitle] {
|
||||||
@@ -117,7 +125,8 @@ const ICONS = {
|
|||||||
background: var(--tui-background-neutral-1);
|
background: var(--tui-background-neutral-1);
|
||||||
box-shadow: inset 0 -1px var(--tui-background-neutral-1);
|
box-shadow: inset 0 -1px var(--tui-background-neutral-1);
|
||||||
|
|
||||||
&.active {
|
&.active,
|
||||||
|
&:has(.active) {
|
||||||
background: none;
|
background: none;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { AsyncPipe } from '@angular/common'
|
import { AsyncPipe, DOCUMENT } from '@angular/common'
|
||||||
import {
|
import {
|
||||||
ChangeDetectionStrategy,
|
ChangeDetectionStrategy,
|
||||||
Component,
|
Component,
|
||||||
@@ -68,7 +68,7 @@ import { SystemWipeComponent } from './wipe.component'
|
|||||||
<tui-icon icon="@tui.zap" />
|
<tui-icon icon="@tui.zap" />
|
||||||
<span tuiTitle>
|
<span tuiTitle>
|
||||||
<strong>
|
<strong>
|
||||||
{{ 'system.general.update' | i18n }}
|
{{ 'system.general.update.title' | i18n }}
|
||||||
</strong>
|
</strong>
|
||||||
<span tuiSubtitle>{{ server.version }}</span>
|
<span tuiSubtitle>{{ server.version }}</span>
|
||||||
</span>
|
</span>
|
||||||
@@ -79,12 +79,12 @@ import { SystemWipeComponent } from './wipe.component'
|
|||||||
(click)="onUpdate()"
|
(click)="onUpdate()"
|
||||||
>
|
>
|
||||||
@if (server.statusInfo.updated) {
|
@if (server.statusInfo.updated) {
|
||||||
{{ 'system.general.restart' | i18n }}
|
{{ 'system.general.update.button.restart' | i18n }}
|
||||||
} @else {
|
} @else {
|
||||||
@if (eos.showUpdate$ | async) {
|
@if (eos.showUpdate$ | async) {
|
||||||
{{ 'ui.update' | i18n }}
|
{{ 'ui.update' | i18n }}
|
||||||
} @else {
|
} @else {
|
||||||
{{ 'system.general.check' | i18n }}
|
{{ 'system.general.update.button.check' | i18n }}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</button>
|
</button>
|
||||||
@@ -124,14 +124,28 @@ import { SystemWipeComponent } from './wipe.component'
|
|||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div tuiCell tuiAppearance="outline-grayscale">
|
||||||
|
<tui-icon icon="@tui.download" />
|
||||||
|
<span tuiTitle>
|
||||||
|
<strong>
|
||||||
|
{{ 'system.general.ca.title' | i18n }}
|
||||||
|
</strong>
|
||||||
|
<span tuiSubtitle>
|
||||||
|
{{ 'system.general.ca.subtitle' | i18n }}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<button tuiButton (click)="downloadCA()">
|
||||||
|
{{ 'system.general.ca.button' | i18n }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
<div tuiCell tuiAppearance="outline-grayscale">
|
<div tuiCell tuiAppearance="outline-grayscale">
|
||||||
<tui-icon icon="@tui.circle-power" (click)="count = count + 1" />
|
<tui-icon icon="@tui.circle-power" (click)="count = count + 1" />
|
||||||
<span tuiTitle>
|
<span tuiTitle>
|
||||||
<strong>
|
<strong>
|
||||||
{{ 'system.general.tor' | i18n }}
|
{{ 'system.general.tor.title' | i18n }}
|
||||||
</strong>
|
</strong>
|
||||||
<span tuiSubtitle>
|
<span tuiSubtitle>
|
||||||
{{ 'system.general.daemon' | i18n }}
|
{{ 'system.general.tor.subtitle' | i18n }}
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
<button tuiButton appearance="glass" (click)="onReset()">
|
<button tuiButton appearance="glass" (click)="onReset()">
|
||||||
@@ -143,18 +157,20 @@ import { SystemWipeComponent } from './wipe.component'
|
|||||||
<tui-icon icon="@tui.briefcase-medical" />
|
<tui-icon icon="@tui.briefcase-medical" />
|
||||||
<span tuiTitle>
|
<span tuiTitle>
|
||||||
<strong>
|
<strong>
|
||||||
{{ 'system.general.disk' | i18n }}
|
{{ 'system.general.repair.title' | i18n }}
|
||||||
</strong>
|
</strong>
|
||||||
<span tuiSubtitle>
|
<span tuiSubtitle>
|
||||||
{{ 'system.general.attempt' | i18n }}
|
{{ 'system.general.repair.subtitle' | i18n }}
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
<button tuiButton appearance="glass" (click)="onRepair()">
|
<button tuiButton appearance="glass" (click)="onRepair()">
|
||||||
{{ 'system.general.repair' | i18n }}
|
{{ 'system.general.repair.button' | i18n }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
<!-- hidden element for downloading cert -->
|
||||||
|
<a id="download-ca" href="/static/local-root-ca.crt"></a>
|
||||||
`,
|
`,
|
||||||
styles: `
|
styles: `
|
||||||
:host {
|
:host {
|
||||||
@@ -209,6 +225,7 @@ export default class SystemGeneralComponent {
|
|||||||
SystemWipeComponent,
|
SystemWipeComponent,
|
||||||
inject(INJECTOR),
|
inject(INJECTOR),
|
||||||
)
|
)
|
||||||
|
private readonly document = inject(DOCUMENT)
|
||||||
|
|
||||||
wipe = false
|
wipe = false
|
||||||
count = 0
|
count = 0
|
||||||
@@ -268,6 +285,10 @@ export default class SystemGeneralComponent {
|
|||||||
.subscribe(() => this.resetTor(this.wipe))
|
.subscribe(() => this.resetTor(this.wipe))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
downloadCA() {
|
||||||
|
this.document.getElementById('download-ca')?.click()
|
||||||
|
}
|
||||||
|
|
||||||
async onRepair() {
|
async onRepair() {
|
||||||
this.dialogs
|
this.dialogs
|
||||||
.open(TUI_CONFIRM, {
|
.open(TUI_CONFIRM, {
|
||||||
|
|||||||
@@ -14,9 +14,9 @@ import { TitleDirective } from 'src/app/services/title.service'
|
|||||||
|
|
||||||
const iface: T.ServiceInterface = {
|
const iface: T.ServiceInterface = {
|
||||||
id: '',
|
id: '',
|
||||||
name: 'StartOS User Interface',
|
name: 'StartOS UI',
|
||||||
description:
|
description:
|
||||||
'The primary user interface for your StartOS server, accessible from any browser.',
|
'The web user interface for your StartOS server, accessible from any browser.',
|
||||||
type: 'ui' as const,
|
type: 'ui' as const,
|
||||||
masked: false,
|
masked: false,
|
||||||
addressInfo: {
|
addressInfo: {
|
||||||
@@ -33,15 +33,12 @@ const iface: T.ServiceInterface = {
|
|||||||
template: `
|
template: `
|
||||||
<ng-container *title>
|
<ng-container *title>
|
||||||
<a routerLink=".." tuiIconButton iconStart="@tui.arrow-left">Back</a>
|
<a routerLink=".." tuiIconButton iconStart="@tui.arrow-left">Back</a>
|
||||||
Web Addresses
|
StartOS UI
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<header tuiHeader>
|
<header tuiHeader>
|
||||||
<hgroup tuiTitle>
|
<hgroup tuiTitle>
|
||||||
<h3>User Interface Addresses</h3>
|
<h3>{{ iface.name }}</h3>
|
||||||
<p tuiSubtitle>
|
<p tuiSubtitle>{{ iface.description }}</p>
|
||||||
View and manage private and public addresses for accessing your
|
|
||||||
StartOS UI
|
|
||||||
</p>
|
|
||||||
</hgroup>
|
</hgroup>
|
||||||
</header>
|
</header>
|
||||||
@if (ui(); as ui) {
|
@if (ui(); as ui) {
|
||||||
@@ -61,6 +58,7 @@ const iface: T.ServiceInterface = {
|
|||||||
})
|
})
|
||||||
export default class StartOsUiComponent {
|
export default class StartOsUiComponent {
|
||||||
private readonly config = inject(ConfigService)
|
private readonly config = inject(ConfigService)
|
||||||
|
iface = iface
|
||||||
|
|
||||||
readonly ui = toSignal(
|
readonly ui = toSignal(
|
||||||
inject<PatchDB<DataModel>>(PatchDB)
|
inject<PatchDB<DataModel>>(PatchDB)
|
||||||
|
|||||||
@@ -366,6 +366,10 @@ button.g-action {
|
|||||||
color: var(--tui-status-info) !important;
|
color: var(--tui-status-info) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.g-primary {
|
||||||
|
color: var(--tui-text-primary) !important;
|
||||||
|
}
|
||||||
|
|
||||||
.g-secondary {
|
.g-secondary {
|
||||||
color: var(--tui-text-secondary) !important;
|
color: var(--tui-text-secondary) !important;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user