* tell user to restart server after kiosk chnage

* remove unused import

* dont show tor address on server setup

* chore: address comments

* revert mock

* chore: remove uptime block on mobile

* utiliser le futur proche

---------

Co-authored-by: waterplea <alexander@inkin.ru>
Co-authored-by: Aiden McClelland <3732071+dr-bonez@users.noreply.github.com>
This commit is contained in:
Matt Hill
2025-11-19 10:35:07 -07:00
committed by GitHub
parent f26791ba39
commit ad0632892e
14 changed files with 111 additions and 129 deletions

View File

@@ -1,17 +1,17 @@
import {
ChangeDetectionStrategy,
Component,
inject,
Input,
computed,
input,
} from '@angular/core'
import { i18nKey, i18nPipe } from '@start9labs/shared'
import { T } from '@start9labs/start-sdk'
import { i18nPipe } from '@start9labs/shared'
import { TuiLoader } from '@taiga-ui/core'
import { ServiceUptimeComponent } from 'src/app/routes/portal/routes/services/components/uptime.component'
import { getProgressText } from 'src/app/routes/portal/routes/services/pipes/install-progress.pipe'
import { InstallingInfo } from 'src/app/services/patch-db/data-model'
import { PackageDataEntry } from 'src/app/services/patch-db/data-model'
import {
getInstalledPrimaryStatus,
PrimaryRendering,
PrimaryStatus,
} from 'src/app/services/pkg-status-rendering.service'
@Component({
@@ -19,23 +19,27 @@ import {
template: `
<header>{{ 'Status' | i18n }}</header>
<div>
@if (installingInfo) {
@if (info()) {
<h3>
<tui-loader size="s" [inheritColor]="true" />
{{ 'Installing' | i18n }}
<span class="loading-dots"></span>
{{ getText(installingInfo.progress.overall) | i18n }}
{{ info() | i18n }}
</h3>
} @else {
<h3 [class]="class">
{{ text | i18n }}
@if (text === 'Task Required') {
<h3 [class]="class()">
{{ text() || 'Unknown' | i18n }}
@if (text() === 'Task Required') {
<small>{{ 'See below' | i18n }}</small>
}
@if (rendering?.showDots) {
@if (rendering().showDots) {
<span class="loading-dots"></span>
}
@if ($any(pkg().status)?.started; as started) {
<service-uptime [started]="started" />
}
</h3>
}
<ng-content />
@@ -76,6 +80,12 @@ import {
margin: 0 0.25rem -0.125rem 0;
}
service-uptime {
display: none;
width: fit-content;
margin: 0.5rem 0.125rem;
}
:host-context(tui-root._mobile) {
:host {
min-height: 0;
@@ -94,32 +104,33 @@ import {
small {
text-align: left;
}
service-uptime {
display: flex;
}
}
`,
host: { class: 'g-card' },
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [TuiLoader, i18nPipe],
imports: [TuiLoader, i18nPipe, ServiceUptimeComponent],
})
export class ServiceStatusComponent {
@Input({ required: true })
status?: PrimaryStatus
readonly pkg = input.required<PackageDataEntry>()
readonly connected = input(false)
@Input()
installingInfo?: InstallingInfo
protected readonly status = computed((pkg = this.pkg()) =>
pkg?.stateInfo.state === 'installed'
? getInstalledPrimaryStatus(pkg)
: pkg?.stateInfo.state,
)
@Input()
connected = false
protected readonly rendering = computed(() => PrimaryRendering[this.status()])
protected readonly text = computed(
() => this.connected() && this.rendering().display,
)
private readonly i18n = inject(i18nPipe)
get text(): i18nKey {
return this.connected ? this.rendering?.display || 'Unknown' : 'Unknown'
}
get class(): string | null {
if (!this.connected) return null
switch (this.rendering?.color) {
protected readonly class = computed(() => {
switch (this.connected() && this.rendering().color) {
case 'danger':
return 'g-negative'
case 'warning':
@@ -131,13 +142,10 @@ export class ServiceStatusComponent {
default:
return null
}
}
})
get rendering() {
return this.status && PrimaryRendering[this.status]
}
getText(progress: T.Progress): i18nKey {
return getProgressText(progress)
}
protected readonly info = computed(
(progress = this.pkg().stateInfo.installingInfo?.progress.overall) =>
progress ? getProgressText(progress) : '',
)
}

View File

@@ -79,6 +79,10 @@ import { getManifest } from 'src/app/utils/get-package-data'
overflow: hidden;
}
td:not(:last-child) {
padding-inline-end: 1.5rem;
}
td:last-child {
white-space: nowrap;
text-align: right;

View File

@@ -66,7 +66,8 @@ import { distinctUntilChanged } from 'rxjs/operators'
color: var(--tui-text-primary);
}
:host-context(table) {
:host-context(table),
:host-context(service-status) {
padding: 0;
header {

View File

@@ -39,11 +39,7 @@ import { ServiceUptimeComponent } from '../components/uptime.component'
} @else if (installing()) {
<service-install-progress [pkg]="pkg" />
} @else if (installed()) {
<service-status
[connected]="!!connected()"
[installingInfo]="pkg.stateInfo.installingInfo"
[status]="status()"
>
<service-status [connected]="!!connected()" [pkg]="pkg">
@if (connected()) {
<service-controls [pkg]="pkg" [status]="status()" />
}
@@ -51,10 +47,8 @@ import { ServiceUptimeComponent } from '../components/uptime.component'
@if (status() !== 'backingUp') {
<service-health-checks [checks]="health()" />
<service-uptime
class="g-card"
[started]="$any(pkg.status)?.started"
/>
<service-uptime class="g-card" [started]="$any(pkg.status).started" />
<service-interfaces [pkg]="pkg" [disabled]="status() !== 'running'" />
@if (errors() | async; as errors) {
<service-dependencies
@@ -63,7 +57,6 @@ import { ServiceUptimeComponent } from '../components/uptime.component'
[errors]="errors"
/>
}
<service-interfaces [pkg]="pkg" [disabled]="status() !== 'running'" />
<service-tasks
#tasks="elementRef"
@@ -91,7 +84,7 @@ import { ServiceUptimeComponent } from '../components/uptime.component'
</button>
}
} @else if (removing()) {
<service-status [connected]="!!connected()" [status]="status()" />
<service-status [connected]="!!connected()" [pkg]="pkg" />
}
}
`,
@@ -139,6 +132,10 @@ import { ServiceUptimeComponent } from '../components/uptime.component'
> * {
grid-column: span 1;
}
service-uptime {
display: none;
}
}
`,
host: { class: 'g-subpage' },

View File

@@ -4,7 +4,6 @@ import {
Component,
inject,
INJECTOR,
DOCUMENT,
} from '@angular/core'
import { toSignal } from '@angular/core/rxjs-interop'
import { FormsModule } from '@angular/forms'
@@ -151,7 +150,7 @@ import { SystemWipeComponent } from './wipe.component'
</span>
</span>
@if (server.kiosk !== null) {
<button tuiButton appearance="primary" (click)="tryToggleKiosk()">
<button tuiButton appearance="primary" (click)="toggleKiosk()">
{{ server.kiosk ? ('Disable' | i18n) : ('Enable' | i18n) }}
</button>
}
@@ -242,7 +241,6 @@ export default class SystemGeneralComponent {
private readonly patch = inject<PatchDB<DataModel>>(PatchDB)
private readonly api = inject(ApiService)
private readonly isTor = inject(ConfigService).isTor()
private readonly document = inject(DOCUMENT)
private readonly dialog = inject(DialogService)
private readonly i18n = inject(i18nPipe)
private readonly injector = inject(INJECTOR)
@@ -326,28 +324,6 @@ export default class SystemGeneralComponent {
.subscribe(() => this.resetTor(this.wipe))
}
async tryToggleKiosk() {
if (
this.server()?.kiosk &&
['localhost', '127.0.0.1'].includes(this.document.location.hostname)
) {
return this.dialog
.openConfirm({
label: 'Warning',
data: {
content:
'You are currently using a kiosk. Disabling Kiosk Mode will result in the kiosk disconnecting.',
yes: 'Disable',
no: 'Cancel',
},
})
.pipe(filter(Boolean))
.subscribe(async () => this.toggleKiosk())
}
this.toggleKiosk()
}
async onRepair() {
this.dialog
.openConfirm({
@@ -370,7 +346,7 @@ export default class SystemGeneralComponent {
})
}
private async toggleKiosk() {
async toggleKiosk() {
const kiosk = this.server()?.kiosk
const loader = this.loader
@@ -379,6 +355,11 @@ export default class SystemGeneralComponent {
try {
await this.api.toggleKiosk(!kiosk)
this.dialog
.openAlert('This change will take effect after the next boot', {
label: 'Restart to apply',
})
.subscribe()
} catch (e: any) {
this.errorService.handleError(e)
} finally {

View File

@@ -1,16 +1,20 @@
import { ChangeDetectionStrategy, Component, inject } from '@angular/core'
import {
ChangeDetectionStrategy,
Component,
inject,
viewChild,
} from '@angular/core'
import { toSignal } from '@angular/core/rxjs-interop'
import { RouterLink } from '@angular/router'
import { verify } from '@start9labs/argon2'
import {
DialogService,
ErrorService,
i18nKey,
i18nPipe,
LoadingService,
} from '@start9labs/shared'
import { ISB } from '@start9labs/start-sdk'
import { TuiButton, TuiTitle } from '@taiga-ui/core'
import { TuiAlertService, TuiButton, TuiTitle } from '@taiga-ui/core'
import { TuiHeader } from '@taiga-ui/layout'
import { PatchDB } from 'patch-db-client'
import { from } from 'rxjs'
@@ -70,13 +74,14 @@ import { getServerInfo } from 'src/app/utils/get-server-info'
],
})
export default class SystemPasswordComponent {
private readonly dialog = inject(DialogService)
private readonly alerts = inject(TuiAlertService)
private readonly loader = inject(LoadingService)
private readonly errorService = inject(ErrorService)
private readonly patch = inject<PatchDB<DataModel>>(PatchDB)
private readonly api = inject(ApiService)
private readonly i18n = inject(i18nPipe)
readonly form = viewChild(FormComponent)
readonly spec = toSignal(from(configBuilderToSpec(this.passwordSpec())))
readonly buttons = [
{
@@ -119,7 +124,12 @@ export default class SystemPasswordComponent {
try {
await this.api.resetPassword({ oldPassword, newPassword })
this.dialog.openAlert('Password changed').subscribe()
this.form()?.form.reset()
this.alerts
.open(this.i18n.transform('Password changed'), {
appearance: 'positive',
})
.subscribe()
} catch (e: any) {
this.errorService.handleError(e)
} finally {