mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-31 04:23:40 +00:00
outbound gateway support (#3120)
* Multiple (#3111) * fix alerts i18n, fix status display, better, remove usb media, hide shutdown for install complete * trigger chnage detection for localize pipe and round out implementing localize pipe for consistency even though not needed * Fix PackageInfoShort to handle LocaleString on releaseNotes (#3112) * Fix PackageInfoShort to handle LocaleString on releaseNotes * fix: filter by target_version in get_matching_models and pass otherVersions from install * chore: add exver documentation for ai agents * frontend plus some be types --------- Co-authored-by: Aiden McClelland <3732071+dr-bonez@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
import { Component } from '@angular/core'
|
||||
import { i18nPipe } from '@start9labs/shared'
|
||||
import { TuiButton, TuiDialogContext } from '@taiga-ui/core'
|
||||
import { injectContext } from '@taiga-ui/polymorpheus'
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
imports: [TuiButton, i18nPipe],
|
||||
template: `
|
||||
<div class="animation-container">
|
||||
<div class="port">
|
||||
<div class="port-inner"></div>
|
||||
</div>
|
||||
<div class="usb-stick">
|
||||
<div class="usb-connector"></div>
|
||||
<div class="usb-body"></div>
|
||||
</div>
|
||||
</div>
|
||||
<p>
|
||||
{{
|
||||
'Remove USB stick or other installation media from your server' | i18n
|
||||
}}
|
||||
</p>
|
||||
<footer>
|
||||
<button tuiButton (click)="context.completeWith(true)">
|
||||
{{ 'Done' | i18n }}
|
||||
</button>
|
||||
</footer>
|
||||
`,
|
||||
styles: `
|
||||
:host {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.animation-container {
|
||||
position: relative;
|
||||
width: 160px;
|
||||
height: 69px;
|
||||
}
|
||||
|
||||
.port {
|
||||
position: absolute;
|
||||
left: 20px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 28px;
|
||||
height: 18px;
|
||||
background: var(--tui-background-neutral-1);
|
||||
border: 2px solid var(--tui-border-normal);
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.port-inner {
|
||||
position: absolute;
|
||||
top: 3px;
|
||||
left: 3px;
|
||||
right: 3px;
|
||||
bottom: 3px;
|
||||
background: var(--tui-background-neutral-2);
|
||||
border-radius: 1px;
|
||||
}
|
||||
|
||||
.usb-stick {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
animation: slide-out 2s ease-in-out 0.5s infinite;
|
||||
left: 32px;
|
||||
}
|
||||
|
||||
.usb-connector {
|
||||
width: 20px;
|
||||
height: 12px;
|
||||
background: var(--tui-text-secondary);
|
||||
border-radius: 1px;
|
||||
}
|
||||
|
||||
.usb-body {
|
||||
width: 40px;
|
||||
height: 20px;
|
||||
background: var(--tui-status-info);
|
||||
border-radius: 2px 4px 4px 2px;
|
||||
}
|
||||
|
||||
@keyframes slide-out {
|
||||
0% {
|
||||
left: 32px;
|
||||
opacity: 0;
|
||||
}
|
||||
5% {
|
||||
left: 32px;
|
||||
opacity: 1;
|
||||
}
|
||||
80% {
|
||||
left: 130px;
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
left: 130px;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0 0 2rem;
|
||||
}
|
||||
|
||||
footer {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
`,
|
||||
})
|
||||
export class RemoveMediaDialog {
|
||||
protected readonly context = injectContext<TuiDialogContext<boolean>>()
|
||||
}
|
||||
@@ -1,4 +1,9 @@
|
||||
import { ChangeDetectorRef, Component, inject } from '@angular/core'
|
||||
import {
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
HostListener,
|
||||
inject,
|
||||
} from '@angular/core'
|
||||
import { Router } from '@angular/router'
|
||||
import { FormsModule } from '@angular/forms'
|
||||
import {
|
||||
@@ -21,13 +26,14 @@ import {
|
||||
import { TuiDataListWrapper, TuiSelect, TuiTooltip } from '@taiga-ui/kit'
|
||||
import { TuiCardLarge, TuiHeader } from '@taiga-ui/layout'
|
||||
import { PolymorpheusComponent } from '@taiga-ui/polymorpheus'
|
||||
import { filter } from 'rxjs'
|
||||
import { filter, Subscription } from 'rxjs'
|
||||
import { ApiService } from '../services/api.service'
|
||||
import { StateService } from '../services/state.service'
|
||||
import { PreserveOverwriteDialog } from '../components/preserve-overwrite.dialog'
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
@if (!shuttingDown) {
|
||||
<section tuiCardLarge="compact">
|
||||
<header tuiHeader>
|
||||
<h2 tuiTitle>{{ 'Select Drives' | i18n }}</h2>
|
||||
@@ -132,6 +138,7 @@ import { PreserveOverwriteDialog } from '../components/preserve-overwrite.dialog
|
||||
}
|
||||
</footer>
|
||||
</section>
|
||||
}
|
||||
`,
|
||||
styles: `
|
||||
.no-drives {
|
||||
@@ -176,6 +183,14 @@ export default class DrivesPage {
|
||||
|
||||
protected readonly mobile = inject(TUI_IS_MOBILE)
|
||||
|
||||
@HostListener('document:keydown', ['$event'])
|
||||
onKeydown(event: KeyboardEvent) {
|
||||
if (event.ctrlKey && event.shiftKey && event.key === 'X') {
|
||||
event.preventDefault()
|
||||
this.shutdownServer()
|
||||
}
|
||||
}
|
||||
|
||||
readonly osDriveTooltip = this.i18n.transform(
|
||||
'The drive where the StartOS operating system will be installed.',
|
||||
)
|
||||
@@ -185,6 +200,8 @@ export default class DrivesPage {
|
||||
|
||||
drives: DiskInfo[] = []
|
||||
loading = true
|
||||
shuttingDown = false
|
||||
private dialogSub?: Subscription
|
||||
selectedOsDrive: DiskInfo | null = null
|
||||
selectedDataDrive: DiskInfo | null = null
|
||||
preserveData: boolean | null = null
|
||||
@@ -339,22 +356,18 @@ export default class DrivesPage {
|
||||
loader.unsubscribe()
|
||||
|
||||
// Show success dialog
|
||||
this.dialogs
|
||||
.openConfirm({
|
||||
this.dialogSub = this.dialogs
|
||||
.openAlert('StartOS has been installed successfully.', {
|
||||
label: 'Installation Complete!',
|
||||
size: 's',
|
||||
data: {
|
||||
content: 'StartOS has been installed successfully.',
|
||||
yes: 'Continue to Setup',
|
||||
no: 'Shutdown',
|
||||
},
|
||||
dismissible: false,
|
||||
closeable: true,
|
||||
data: { button: this.i18n.transform('Continue to Setup') },
|
||||
})
|
||||
.subscribe(continueSetup => {
|
||||
if (continueSetup) {
|
||||
.subscribe({
|
||||
complete: () => {
|
||||
this.navigateToNextStep(result.attach)
|
||||
} else {
|
||||
this.shutdownServer()
|
||||
}
|
||||
},
|
||||
})
|
||||
} catch (e: any) {
|
||||
loader.unsubscribe()
|
||||
@@ -372,10 +385,12 @@ export default class DrivesPage {
|
||||
}
|
||||
|
||||
private async shutdownServer() {
|
||||
this.dialogSub?.unsubscribe()
|
||||
const loader = this.loader.open('Beginning shutdown').subscribe()
|
||||
|
||||
try {
|
||||
await this.api.shutdown()
|
||||
this.shuttingDown = true
|
||||
} catch (e: any) {
|
||||
this.errorService.handleError(e)
|
||||
} finally {
|
||||
|
||||
@@ -6,7 +6,12 @@ import {
|
||||
ViewChild,
|
||||
DOCUMENT,
|
||||
} from '@angular/core'
|
||||
import { DownloadHTMLService, ErrorService, i18nPipe } from '@start9labs/shared'
|
||||
import {
|
||||
DialogService,
|
||||
DownloadHTMLService,
|
||||
ErrorService,
|
||||
i18nPipe,
|
||||
} from '@start9labs/shared'
|
||||
import { TuiIcon, TuiLoader, TuiTitle } from '@taiga-ui/core'
|
||||
import { TuiAvatar } from '@taiga-ui/kit'
|
||||
import { TuiCardLarge, TuiCell, TuiHeader } from '@taiga-ui/layout'
|
||||
@@ -14,7 +19,9 @@ import { ApiService } from '../services/api.service'
|
||||
import { StateService } from '../services/state.service'
|
||||
import { DocumentationComponent } from '../components/documentation.component'
|
||||
import { MatrixComponent } from '../components/matrix.component'
|
||||
import { RemoveMediaDialog } from '../components/remove-media.dialog'
|
||||
import { SetupCompleteRes } from '../types'
|
||||
import { PolymorpheusComponent } from '@taiga-ui/polymorpheus'
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
@@ -29,12 +36,8 @@ import { SetupCompleteRes } from '../types'
|
||||
@if (!stateService.kiosk) {
|
||||
<span tuiSubtitle>
|
||||
{{
|
||||
stateService.setupType === 'restore'
|
||||
? ('You can unplug your backup drive' | i18n)
|
||||
: stateService.setupType === 'transfer'
|
||||
? ('You can unplug your transfer drive' | i18n)
|
||||
: ('http://start.local was for setup only. It will no longer work.'
|
||||
| i18n)
|
||||
'http://start.local was for setup only. It will no longer work.'
|
||||
| i18n
|
||||
}}
|
||||
</span>
|
||||
}
|
||||
@@ -69,14 +72,15 @@ import { SetupCompleteRes } from '../types'
|
||||
tuiCell="l"
|
||||
[class.disabled]="!stateService.kiosk && !downloaded"
|
||||
[disabled]="(!stateService.kiosk && !downloaded) || usbRemoved"
|
||||
(click)="usbRemoved = true"
|
||||
(click)="removeMedia()"
|
||||
>
|
||||
<tui-avatar appearance="secondary" src="@tui.usb" />
|
||||
<div tuiTitle>
|
||||
{{ 'USB Removed' | i18n }}
|
||||
{{ 'Remove Installation Media' | i18n }}
|
||||
<div tuiSubtitle>
|
||||
{{
|
||||
'Remove the USB installation media from your server' | i18n
|
||||
'Remove USB stick or other installation media from your server'
|
||||
| i18n
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
@@ -184,6 +188,7 @@ export default class SuccessPage implements AfterViewInit {
|
||||
private readonly errorService = inject(ErrorService)
|
||||
private readonly api = inject(ApiService)
|
||||
private readonly downloadHtml = inject(DownloadHTMLService)
|
||||
private readonly dialogs = inject(DialogService)
|
||||
private readonly i18n = inject(i18nPipe)
|
||||
|
||||
readonly stateService = inject(StateService)
|
||||
@@ -225,6 +230,21 @@ export default class SuccessPage implements AfterViewInit {
|
||||
})
|
||||
}
|
||||
|
||||
removeMedia() {
|
||||
this.dialogs
|
||||
.openComponent<boolean>(
|
||||
new PolymorpheusComponent(RemoveMediaDialog),
|
||||
{
|
||||
size: 's',
|
||||
dismissible: false,
|
||||
closeable: false,
|
||||
},
|
||||
)
|
||||
.subscribe(() => {
|
||||
this.usbRemoved = true
|
||||
})
|
||||
}
|
||||
|
||||
exitKiosk() {
|
||||
this.api.exit()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user