mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-31 04:23:40 +00:00
fix dep error display, show starting if any health check starting, show disabled health check message, remove loader from service list, animated dots, better color (#3025)
* refector addresses to not need gateways array * fix dep error display, show starting if any health check starting, show disabled health check message, remove loader from service list, animated dots, better color * fix: fix action results textfields --------- Co-authored-by: waterplea <alexander@inkin.ru>
This commit is contained in:
@@ -110,6 +110,7 @@ body {
|
|||||||
animation-fill-mode: forwards;
|
animation-fill-mode: forwards;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
width: 1em;
|
width: 1em;
|
||||||
|
margin-left: -.3rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes ellipsis-dot {
|
@keyframes ellipsis-dot {
|
||||||
|
|||||||
@@ -57,7 +57,7 @@
|
|||||||
var(--tui-status-warning) 24%,
|
var(--tui-status-warning) 24%,
|
||||||
transparent
|
transparent
|
||||||
);
|
);
|
||||||
--tui-status-info: rgba(128, 89, 229, 1);
|
--tui-status-info: rgba(53, 96, 240, 1);
|
||||||
--tui-status-info-pale: color-mix(
|
--tui-status-info-pale: color-mix(
|
||||||
in hsl,
|
in hsl,
|
||||||
var(--tui-status-info) 12%,
|
var(--tui-status-info) 12%,
|
||||||
|
|||||||
@@ -166,12 +166,10 @@ export class InterfaceService {
|
|||||||
}, [] as AddressWithInfo[])
|
}, [] as AddressWithInfo[])
|
||||||
|
|
||||||
return {
|
return {
|
||||||
common: bestAddrs.map(a =>
|
common: bestAddrs.map(a => this.toDisplayAddress(a, host.publicDomains)),
|
||||||
this.toDisplayAddress(a, gateways, host.publicDomains),
|
|
||||||
),
|
|
||||||
uncommon: allAddressesWithInfo
|
uncommon: allAddressesWithInfo
|
||||||
.filter(a => !bestAddrs.includes(a))
|
.filter(a => !bestAddrs.includes(a))
|
||||||
.map(a => this.toDisplayAddress(a, gateways, host.publicDomains)),
|
.map(a => this.toDisplayAddress(a, host.publicDomains)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -314,7 +312,6 @@ export class InterfaceService {
|
|||||||
|
|
||||||
private toDisplayAddress(
|
private toDisplayAddress(
|
||||||
{ info, url, gateway }: AddressWithInfo,
|
{ info, url, gateway }: AddressWithInfo,
|
||||||
gateways: GatewayPlus[],
|
|
||||||
publicDomains: Record<string, T.PublicDomainConfig>,
|
publicDomains: Record<string, T.PublicDomainConfig>,
|
||||||
): DisplayAddress {
|
): DisplayAddress {
|
||||||
let access: DisplayAddress['access']
|
let access: DisplayAddress['access']
|
||||||
@@ -360,11 +357,11 @@ export class InterfaceService {
|
|||||||
// ** Not Tor **
|
// ** Not Tor **
|
||||||
} else {
|
} else {
|
||||||
const port = info.hostname.sslPort || info.hostname.port
|
const port = info.hostname.sslPort || info.hostname.port
|
||||||
const gateway = gateways.find(g => g.id === info.gateway.id)!
|
const g = gateway!
|
||||||
gatewayName = gateway.name
|
gatewayName = g.name
|
||||||
|
|
||||||
const gatewayLanIpv4 = gateway.lanIpv4[0]
|
const gatewayLanIpv4 = g.lanIpv4[0]
|
||||||
const isWireguard = gateway.ipInfo.deviceType === 'wireguard'
|
const isWireguard = g.ipInfo.deviceType === 'wireguard'
|
||||||
|
|
||||||
const localIdeal = this.i18n.transform('Ideal for local access')
|
const localIdeal = this.i18n.transform('Ideal for local access')
|
||||||
const lanRequired = this.i18n.transform(
|
const lanRequired = this.i18n.transform(
|
||||||
@@ -405,9 +402,9 @@ export class InterfaceService {
|
|||||||
),
|
),
|
||||||
rootCaRequired,
|
rootCaRequired,
|
||||||
]
|
]
|
||||||
if (!gateway.public) {
|
if (!g.public) {
|
||||||
bullets.push(
|
bullets.push(
|
||||||
`${portForwarding} "${gatewayName}": ${port} -> ${gateway.subnets.find(s => s.isIpv4())?.address}:${port}`,
|
`${portForwarding} "${gatewayName}": ${port} -> ${g.subnets.find(s => s.isIpv4())?.address}:${port}`,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -439,12 +436,12 @@ export class InterfaceService {
|
|||||||
if (info.public) {
|
if (info.public) {
|
||||||
access = 'public'
|
access = 'public'
|
||||||
bullets = [
|
bullets = [
|
||||||
`${dnsFor} ${info.hostname.value} ${resolvesTo} ${gateway.ipInfo.wanIp}`,
|
`${dnsFor} ${info.hostname.value} ${resolvesTo} ${g.ipInfo.wanIp}`,
|
||||||
]
|
]
|
||||||
|
|
||||||
if (!gateway.public) {
|
if (!g.public) {
|
||||||
bullets.push(
|
bullets.push(
|
||||||
`${portForwarding} "${gatewayName}": ${port} -> ${gateway.subnets.find(s => s.isIpv4())?.address}:${port === 443 ? 5443 : port}`,
|
`${portForwarding} "${gatewayName}": ${port} -> ${g.subnets.find(s => s.isIpv4())?.address}:${port === 443 ? 5443 : port}`,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -97,10 +97,8 @@ export class ServiceHealthCheckComponent {
|
|||||||
return `${this.i18n.transform('Success')}: ${this.healthCheck.message || 'health check passing'}`
|
return `${this.i18n.transform('Success')}: ${this.healthCheck.message || 'health check passing'}`
|
||||||
case 'loading':
|
case 'loading':
|
||||||
case 'failure':
|
case 'failure':
|
||||||
return this.healthCheck.message
|
case 'disabled':
|
||||||
// disabled
|
return this.healthCheck.message || this.healthCheck.result
|
||||||
default:
|
|
||||||
return this.healthCheck.result
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,22 +6,26 @@ import {
|
|||||||
} from '@angular/core'
|
} from '@angular/core'
|
||||||
import { i18nKey, i18nPipe } from '@start9labs/shared'
|
import { i18nKey, i18nPipe } from '@start9labs/shared'
|
||||||
import { tuiPure } from '@taiga-ui/cdk'
|
import { tuiPure } from '@taiga-ui/cdk'
|
||||||
import { TuiIcon, TuiLoader } from '@taiga-ui/core'
|
import { TuiIcon } from '@taiga-ui/core'
|
||||||
import { getProgressText } from 'src/app/routes/portal/routes/services/pipes/install-progress.pipe'
|
import { getProgressText } from 'src/app/routes/portal/routes/services/pipes/install-progress.pipe'
|
||||||
import { PackageDataEntry } from 'src/app/services/patch-db/data-model'
|
import { PackageDataEntry } from 'src/app/services/patch-db/data-model'
|
||||||
import { renderPkgStatus } from 'src/app/services/pkg-status-rendering.service'
|
import {
|
||||||
|
PrimaryRendering,
|
||||||
|
renderPkgStatus,
|
||||||
|
} from 'src/app/services/pkg-status-rendering.service'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'td[appStatus]',
|
selector: 'td[appStatus]',
|
||||||
template: `
|
template: `
|
||||||
@if (loading) {
|
@if (!healthy) {
|
||||||
<tui-loader size="s" />
|
<tui-icon icon="@tui.triangle-alert" class="g-warning" />
|
||||||
} @else {
|
}
|
||||||
@if (!healthy) {
|
|
||||||
<tui-icon icon="@tui.triangle-alert" class="g-warning" />
|
<b [style.color]="color">{{ status | i18n }}</b>
|
||||||
}
|
|
||||||
|
@if (showDots) {
|
||||||
|
<span class="loading-dots g-info"></span>
|
||||||
}
|
}
|
||||||
<b [style.color]="color">{{ status | i18n }}{{ dots }}</b>
|
|
||||||
`,
|
`,
|
||||||
styles: `
|
styles: `
|
||||||
:host {
|
:host {
|
||||||
@@ -37,7 +41,7 @@ import { renderPkgStatus } from 'src/app/services/pkg-status-rendering.service'
|
|||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
imports: [TuiIcon, TuiLoader, i18nPipe],
|
imports: [TuiIcon, i18nPipe],
|
||||||
})
|
})
|
||||||
export class StatusComponent {
|
export class StatusComponent {
|
||||||
@Input()
|
@Input()
|
||||||
@@ -58,10 +62,6 @@ export class StatusComponent {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
get loading(): boolean {
|
|
||||||
return this.color === 'var(--tui-status-info)'
|
|
||||||
}
|
|
||||||
|
|
||||||
@tuiPure
|
@tuiPure
|
||||||
getStatus(pkg: PackageDataEntry) {
|
getStatus(pkg: PackageDataEntry) {
|
||||||
return renderPkgStatus(pkg)
|
return renderPkgStatus(pkg)
|
||||||
@@ -72,35 +72,10 @@ export class StatusComponent {
|
|||||||
return `${this.i18n.transform('Installing')}... ${this.i18n.transform(getProgressText(this.pkg.stateInfo.installingInfo.progress.overall))}` as i18nKey
|
return `${this.i18n.transform('Installing')}... ${this.i18n.transform(getProgressText(this.pkg.stateInfo.installingInfo.progress.overall))}` as i18nKey
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (this.getStatus(this.pkg).primary) {
|
return PrimaryRendering[this.getStatus(this.pkg).primary].display
|
||||||
case 'running':
|
|
||||||
return 'Running'
|
|
||||||
case 'stopped':
|
|
||||||
return 'Stopped'
|
|
||||||
case 'taskRequired':
|
|
||||||
return 'Task Required'
|
|
||||||
case 'updating':
|
|
||||||
return 'Updating'
|
|
||||||
case 'stopping':
|
|
||||||
return 'Stopping'
|
|
||||||
case 'starting':
|
|
||||||
return 'Starting'
|
|
||||||
case 'backingUp':
|
|
||||||
return 'Backing Up'
|
|
||||||
case 'restarting':
|
|
||||||
return 'Restarting'
|
|
||||||
case 'removing':
|
|
||||||
return 'Removing'
|
|
||||||
case 'restoring':
|
|
||||||
return 'Restoring'
|
|
||||||
case 'error':
|
|
||||||
return 'Error'
|
|
||||||
default:
|
|
||||||
return 'Unknown'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get dots(): '...' | '' {
|
get showDots() {
|
||||||
switch (this.getStatus(this.pkg).primary) {
|
switch (this.getStatus(this.pkg).primary) {
|
||||||
case 'updating':
|
case 'updating':
|
||||||
case 'stopping':
|
case 'stopping':
|
||||||
@@ -108,9 +83,9 @@ export class StatusComponent {
|
|||||||
case 'backingUp':
|
case 'backingUp':
|
||||||
case 'restarting':
|
case 'restarting':
|
||||||
case 'removing':
|
case 'removing':
|
||||||
return '...'
|
return true
|
||||||
default:
|
default:
|
||||||
return ''
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ import { QrCodeComponent } from 'ng-qrcode'
|
|||||||
tuiTextfield
|
tuiTextfield
|
||||||
[readOnly]="true"
|
[readOnly]="true"
|
||||||
[ngModel]="member.value"
|
[ngModel]="member.value"
|
||||||
[style.border-inline-end-width.rem]="border"
|
|
||||||
[type]="member.masked && masked ? 'password' : 'text'"
|
[type]="member.masked && masked ? 'password' : 'text'"
|
||||||
/>
|
/>
|
||||||
@if (member.masked) {
|
@if (member.masked) {
|
||||||
@@ -129,16 +128,6 @@ export class ActionSuccessMemberComponent {
|
|||||||
|
|
||||||
masked = true
|
masked = true
|
||||||
|
|
||||||
get border(): number {
|
|
||||||
let border = 0
|
|
||||||
|
|
||||||
if (this.member.masked) border += 2
|
|
||||||
if (this.member.copyable) border += 2
|
|
||||||
if (this.member.qr) border += 2
|
|
||||||
|
|
||||||
return border
|
|
||||||
}
|
|
||||||
|
|
||||||
show(template: TemplateRef<any>) {
|
show(template: TemplateRef<any>) {
|
||||||
const masked = this.masked
|
const masked = this.masked
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ import { SingleResult } from './types'
|
|||||||
tuiTextfield
|
tuiTextfield
|
||||||
[readOnly]="true"
|
[readOnly]="true"
|
||||||
[ngModel]="single.value"
|
[ngModel]="single.value"
|
||||||
[style.border-inline-end-width.rem]="border"
|
|
||||||
[type]="single.masked && masked ? 'password' : 'text'"
|
[type]="single.masked && masked ? 'password' : 'text'"
|
||||||
/>
|
/>
|
||||||
@if (single.masked) {
|
@if (single.masked) {
|
||||||
@@ -105,15 +104,6 @@ export class ActionSuccessSingleComponent {
|
|||||||
|
|
||||||
masked = true
|
masked = true
|
||||||
|
|
||||||
get border(): number {
|
|
||||||
let border = 0
|
|
||||||
|
|
||||||
if (this.single.masked) border += 2
|
|
||||||
if (this.single.copyable) border += 2
|
|
||||||
|
|
||||||
return border
|
|
||||||
}
|
|
||||||
|
|
||||||
copy() {
|
copy() {
|
||||||
const el = this.input.nativeElement
|
const el = this.input.nativeElement
|
||||||
|
|
||||||
|
|||||||
@@ -126,13 +126,14 @@ export class DepErrorService {
|
|||||||
const expected = currentDep?.versionRange || ''
|
const expected = currentDep?.versionRange || ''
|
||||||
|
|
||||||
// incorrect version
|
// incorrect version
|
||||||
if (!this.exver.satisfies(depManifest.version, expected)) {
|
if (
|
||||||
if (depManifest.satisfies.some(v => !this.exver.satisfies(v, expected))) {
|
!this.exver.satisfies(depManifest.version, expected) &&
|
||||||
return {
|
!depManifest.satisfies.some(v => this.exver.satisfies(v, expected))
|
||||||
expected,
|
) {
|
||||||
type: 'incorrectVersion',
|
return {
|
||||||
received: depManifest.version,
|
expected,
|
||||||
}
|
type: 'incorrectVersion',
|
||||||
|
received: depManifest.version,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,11 +25,21 @@ export function getInstalledPrimaryStatus({
|
|||||||
tasks,
|
tasks,
|
||||||
status,
|
status,
|
||||||
}: T.PackageDataEntry): PrimaryStatus {
|
}: T.PackageDataEntry): PrimaryStatus {
|
||||||
return Object.values(tasks).some(
|
if (
|
||||||
t => t.active && t.task.severity === 'critical',
|
Object.values(tasks).some(t => t.active && t.task.severity === 'critical')
|
||||||
)
|
) {
|
||||||
? 'taskRequired'
|
return 'taskRequired'
|
||||||
: status.main
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
Object.values(status.main === 'running' && status.health)
|
||||||
|
.filter(h => !!h)
|
||||||
|
.some(h => h.result === 'starting')
|
||||||
|
) {
|
||||||
|
return 'starting'
|
||||||
|
}
|
||||||
|
|
||||||
|
return status.main
|
||||||
}
|
}
|
||||||
|
|
||||||
function getHealthStatus(status: T.MainStatus): T.HealthStatus | null {
|
function getHealthStatus(status: T.MainStatus): T.HealthStatus | null {
|
||||||
@@ -43,14 +53,14 @@ function getHealthStatus(status: T.MainStatus): T.HealthStatus | null {
|
|||||||
return 'failure'
|
return 'failure'
|
||||||
}
|
}
|
||||||
|
|
||||||
if (values.some(h => h.result === 'loading')) {
|
|
||||||
return 'loading'
|
|
||||||
}
|
|
||||||
|
|
||||||
if (values.some(h => h.result === 'starting')) {
|
if (values.some(h => h.result === 'starting')) {
|
||||||
return 'starting'
|
return 'starting'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (values.some(h => h.result === 'loading')) {
|
||||||
|
return 'loading'
|
||||||
|
}
|
||||||
|
|
||||||
return 'success'
|
return 'success'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user