mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 02:11:53 +00:00
multiple bugs, better outbound gateway UX
This commit is contained in:
@@ -16,7 +16,7 @@ export const VERSION = new InjectionToken<string>('VERSION')
|
|||||||
host: {
|
host: {
|
||||||
target: '_blank',
|
target: '_blank',
|
||||||
rel: 'noreferrer',
|
rel: 'noreferrer',
|
||||||
'[href]': 'url()',
|
'[attr.href]': 'url()',
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
export class DocsLinkDirective {
|
export class DocsLinkDirective {
|
||||||
|
|||||||
@@ -658,8 +658,6 @@ export default {
|
|||||||
721: 'Gateway für ausgehenden Datenverkehr auswählen',
|
721: 'Gateway für ausgehenden Datenverkehr auswählen',
|
||||||
722: 'Der Typ des Gateways',
|
722: 'Der Typ des Gateways',
|
||||||
723: 'Nur ausgehend',
|
723: 'Nur ausgehend',
|
||||||
724: 'Als Standard für ausgehenden Verkehr festlegen',
|
|
||||||
725: 'Gesamten ausgehenden Datenverkehr über dieses Gateway leiten',
|
|
||||||
726: 'WireGuard-Konfigurationsdatei',
|
726: 'WireGuard-Konfigurationsdatei',
|
||||||
727: 'Eingehend/Ausgehend',
|
727: 'Eingehend/Ausgehend',
|
||||||
728: 'StartTunnel (Eingehend/Ausgehend)',
|
728: 'StartTunnel (Eingehend/Ausgehend)',
|
||||||
@@ -668,7 +666,6 @@ export default {
|
|||||||
731: 'Öffentliche Domain',
|
731: 'Öffentliche Domain',
|
||||||
732: 'Private Domain',
|
732: 'Private Domain',
|
||||||
733: 'Ausblenden',
|
733: 'Ausblenden',
|
||||||
734: 'Standard ausgehend',
|
|
||||||
735: 'Zertifikat',
|
735: 'Zertifikat',
|
||||||
736: 'Selbstsigniert',
|
736: 'Selbstsigniert',
|
||||||
737: 'Portweiterleitung',
|
737: 'Portweiterleitung',
|
||||||
@@ -710,4 +707,7 @@ export default {
|
|||||||
781: 'Lokal',
|
781: 'Lokal',
|
||||||
782: 'Unbekanntes Laufwerk',
|
782: 'Unbekanntes Laufwerk',
|
||||||
783: 'Muss eine gültige E-Mail-Adresse sein',
|
783: 'Muss eine gültige E-Mail-Adresse sein',
|
||||||
|
786: 'Automatisch',
|
||||||
|
787: 'Ausgehender Datenverkehr',
|
||||||
|
788: 'Gateway verwenden',
|
||||||
} satisfies i18n
|
} satisfies i18n
|
||||||
|
|||||||
@@ -658,8 +658,6 @@ export const ENGLISH: Record<string, number> = {
|
|||||||
'Select the gateway for outbound traffic': 721,
|
'Select the gateway for outbound traffic': 721,
|
||||||
'The type of gateway': 722,
|
'The type of gateway': 722,
|
||||||
'Outbound Only': 723,
|
'Outbound Only': 723,
|
||||||
'Set as default outbound': 724,
|
|
||||||
'Route all outbound traffic through this gateway': 725,
|
|
||||||
'WireGuard Config File': 726,
|
'WireGuard Config File': 726,
|
||||||
'Inbound/Outbound': 727,
|
'Inbound/Outbound': 727,
|
||||||
'StartTunnel (Inbound/Outbound)': 728,
|
'StartTunnel (Inbound/Outbound)': 728,
|
||||||
@@ -668,7 +666,6 @@ export const ENGLISH: Record<string, number> = {
|
|||||||
'Public Domain': 731,
|
'Public Domain': 731,
|
||||||
'Private Domain': 732,
|
'Private Domain': 732,
|
||||||
'Hide': 733,
|
'Hide': 733,
|
||||||
'default outbound': 734,
|
|
||||||
'Certificate': 735,
|
'Certificate': 735,
|
||||||
'Self signed': 736,
|
'Self signed': 736,
|
||||||
'Port Forwarding': 737,
|
'Port Forwarding': 737,
|
||||||
@@ -710,4 +707,7 @@ export const ENGLISH: Record<string, number> = {
|
|||||||
'Local': 781, // as in, locally accessible
|
'Local': 781, // as in, locally accessible
|
||||||
'Unknown Drive': 782,
|
'Unknown Drive': 782,
|
||||||
'Must be a valid email address': 783,
|
'Must be a valid email address': 783,
|
||||||
|
'Auto': 786,
|
||||||
|
'Outbound Traffic': 787,
|
||||||
|
'Use gateway': 788,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -658,8 +658,6 @@ export default {
|
|||||||
721: 'Selecciona la puerta de enlace para el tráfico saliente',
|
721: 'Selecciona la puerta de enlace para el tráfico saliente',
|
||||||
722: 'El tipo de puerta de enlace',
|
722: 'El tipo de puerta de enlace',
|
||||||
723: 'Solo saliente',
|
723: 'Solo saliente',
|
||||||
724: 'Establecer como saliente predeterminado',
|
|
||||||
725: 'Enrutar todo el tráfico saliente a través de esta puerta de enlace',
|
|
||||||
726: 'Archivo de configuración WireGuard',
|
726: 'Archivo de configuración WireGuard',
|
||||||
727: 'Entrante/Saliente',
|
727: 'Entrante/Saliente',
|
||||||
728: 'StartTunnel (Entrante/Saliente)',
|
728: 'StartTunnel (Entrante/Saliente)',
|
||||||
@@ -668,7 +666,6 @@ export default {
|
|||||||
731: 'Dominio público',
|
731: 'Dominio público',
|
||||||
732: 'Dominio privado',
|
732: 'Dominio privado',
|
||||||
733: 'Ocultar',
|
733: 'Ocultar',
|
||||||
734: 'saliente predeterminado',
|
|
||||||
735: 'Certificado',
|
735: 'Certificado',
|
||||||
736: 'Autofirmado',
|
736: 'Autofirmado',
|
||||||
737: 'Reenvío de puertos',
|
737: 'Reenvío de puertos',
|
||||||
@@ -710,4 +707,7 @@ export default {
|
|||||||
781: 'Local',
|
781: 'Local',
|
||||||
782: 'Unidad desconocida',
|
782: 'Unidad desconocida',
|
||||||
783: 'Debe ser una dirección de correo electrónico válida',
|
783: 'Debe ser una dirección de correo electrónico válida',
|
||||||
|
786: 'Automático',
|
||||||
|
787: 'Tráfico saliente',
|
||||||
|
788: 'Usar gateway',
|
||||||
} satisfies i18n
|
} satisfies i18n
|
||||||
|
|||||||
@@ -658,8 +658,6 @@ export default {
|
|||||||
721: 'Sélectionnez la passerelle pour le trafic sortant',
|
721: 'Sélectionnez la passerelle pour le trafic sortant',
|
||||||
722: 'Le type de passerelle',
|
722: 'Le type de passerelle',
|
||||||
723: 'Sortant uniquement',
|
723: 'Sortant uniquement',
|
||||||
724: 'Définir comme sortant par défaut',
|
|
||||||
725: 'Acheminer tout le trafic sortant via cette passerelle',
|
|
||||||
726: 'Fichier de configuration WireGuard',
|
726: 'Fichier de configuration WireGuard',
|
||||||
727: 'Entrant/Sortant',
|
727: 'Entrant/Sortant',
|
||||||
728: 'StartTunnel (Entrant/Sortant)',
|
728: 'StartTunnel (Entrant/Sortant)',
|
||||||
@@ -668,7 +666,6 @@ export default {
|
|||||||
731: 'Domaine public',
|
731: 'Domaine public',
|
||||||
732: 'Domaine privé',
|
732: 'Domaine privé',
|
||||||
733: 'Masquer',
|
733: 'Masquer',
|
||||||
734: 'sortant par défaut',
|
|
||||||
735: 'Certificat',
|
735: 'Certificat',
|
||||||
736: 'Auto-signé',
|
736: 'Auto-signé',
|
||||||
737: 'Redirection de ports',
|
737: 'Redirection de ports',
|
||||||
@@ -710,4 +707,7 @@ export default {
|
|||||||
781: 'Local',
|
781: 'Local',
|
||||||
782: 'Lecteur inconnu',
|
782: 'Lecteur inconnu',
|
||||||
783: 'Doit être une adresse e-mail valide',
|
783: 'Doit être une adresse e-mail valide',
|
||||||
|
786: 'Automatique',
|
||||||
|
787: 'Trafic sortant',
|
||||||
|
788: 'Utiliser la passerelle',
|
||||||
} satisfies i18n
|
} satisfies i18n
|
||||||
|
|||||||
@@ -658,8 +658,6 @@ export default {
|
|||||||
721: 'Wybierz bramę dla ruchu wychodzącego',
|
721: 'Wybierz bramę dla ruchu wychodzącego',
|
||||||
722: 'Typ bramy',
|
722: 'Typ bramy',
|
||||||
723: 'Tylko wychodzący',
|
723: 'Tylko wychodzący',
|
||||||
724: 'Ustaw jako domyślne wychodzące',
|
|
||||||
725: 'Kieruj cały ruch wychodzący przez tę bramę',
|
|
||||||
726: 'Plik konfiguracyjny WireGuard',
|
726: 'Plik konfiguracyjny WireGuard',
|
||||||
727: 'Przychodzący/Wychodzący',
|
727: 'Przychodzący/Wychodzący',
|
||||||
728: 'StartTunnel (Przychodzący/Wychodzący)',
|
728: 'StartTunnel (Przychodzący/Wychodzący)',
|
||||||
@@ -668,7 +666,6 @@ export default {
|
|||||||
731: 'Domena publiczna',
|
731: 'Domena publiczna',
|
||||||
732: 'Domena prywatna',
|
732: 'Domena prywatna',
|
||||||
733: 'Ukryj',
|
733: 'Ukryj',
|
||||||
734: 'domyślne wychodzące',
|
|
||||||
735: 'Certyfikat',
|
735: 'Certyfikat',
|
||||||
736: 'Samopodpisany',
|
736: 'Samopodpisany',
|
||||||
737: 'Przekierowanie portów',
|
737: 'Przekierowanie portów',
|
||||||
@@ -710,4 +707,7 @@ export default {
|
|||||||
781: 'Lokalny',
|
781: 'Lokalny',
|
||||||
782: 'Nieznany dysk',
|
782: 'Nieznany dysk',
|
||||||
783: 'Musi być prawidłowy adres e-mail',
|
783: 'Musi być prawidłowy adres e-mail',
|
||||||
|
786: 'Automatycznie',
|
||||||
|
787: 'Ruch wychodzący',
|
||||||
|
788: 'Użyj bramy',
|
||||||
} satisfies i18n
|
} satisfies i18n
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ import { ABOUT } from './about.component'
|
|||||||
}
|
}
|
||||||
<tui-data-list [style.width.rem]="13">
|
<tui-data-list [style.width.rem]="13">
|
||||||
<tui-opt-group>
|
<tui-opt-group>
|
||||||
<button tuiOption iconStart="@tui.info" (click)="about()">
|
<button tuiOption iconStart="@tui.info" new (click)="about()">
|
||||||
{{ 'About this server' | i18n }}
|
{{ 'About this server' | i18n }}
|
||||||
</button>
|
</button>
|
||||||
</tui-opt-group>
|
</tui-opt-group>
|
||||||
@@ -53,13 +53,15 @@ import { ABOUT } from './about.component'
|
|||||||
<a
|
<a
|
||||||
tuiOption
|
tuiOption
|
||||||
docsLink
|
docsLink
|
||||||
iconStart="@tui.book-open"
|
new
|
||||||
path="/start-os/user-manual/index.html"
|
iconStart="@tui.book-open-text"
|
||||||
|
path="/start-os/user-manual"
|
||||||
>
|
>
|
||||||
{{ 'User manual' | i18n }}
|
{{ 'User manual' | i18n }}
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
tuiOption
|
tuiOption
|
||||||
|
new
|
||||||
iconStart="@tui.headphones"
|
iconStart="@tui.headphones"
|
||||||
href="https://start9.com/contact"
|
href="https://start9.com/contact"
|
||||||
>
|
>
|
||||||
@@ -67,6 +69,7 @@ import { ABOUT } from './about.component'
|
|||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
tuiOption
|
tuiOption
|
||||||
|
new
|
||||||
iconStart="@tui.dollar-sign"
|
iconStart="@tui.dollar-sign"
|
||||||
href="https://donate.start9.com"
|
href="https://donate.start9.com"
|
||||||
>
|
>
|
||||||
@@ -76,6 +79,7 @@ import { ABOUT } from './about.component'
|
|||||||
<tui-opt-group label="">
|
<tui-opt-group label="">
|
||||||
<a
|
<a
|
||||||
tuiOption
|
tuiOption
|
||||||
|
new
|
||||||
iconStart="@tui.settings"
|
iconStart="@tui.settings"
|
||||||
routerLink="/system"
|
routerLink="/system"
|
||||||
(click)="open = false"
|
(click)="open = false"
|
||||||
@@ -86,6 +90,7 @@ import { ABOUT } from './about.component'
|
|||||||
<tui-opt-group label="">
|
<tui-opt-group label="">
|
||||||
<button
|
<button
|
||||||
tuiOption
|
tuiOption
|
||||||
|
new
|
||||||
iconStart="@tui.refresh-cw"
|
iconStart="@tui.refresh-cw"
|
||||||
(click)="promptPower('restart')"
|
(click)="promptPower('restart')"
|
||||||
>
|
>
|
||||||
@@ -93,12 +98,13 @@ import { ABOUT } from './about.component'
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
tuiOption
|
tuiOption
|
||||||
|
new
|
||||||
iconStart="@tui.power"
|
iconStart="@tui.power"
|
||||||
(click)="promptPower('shutdown')"
|
(click)="promptPower('shutdown')"
|
||||||
>
|
>
|
||||||
{{ 'Shutdown' | i18n }}
|
{{ 'Shutdown' | i18n }}
|
||||||
</button>
|
</button>
|
||||||
<button tuiOption iconStart="@tui.log-out" (click)="logout()">
|
<button tuiOption new iconStart="@tui.log-out" (click)="logout()">
|
||||||
{{ 'Logout' | i18n }}
|
{{ 'Logout' | i18n }}
|
||||||
</button>
|
</button>
|
||||||
</tui-opt-group>
|
</tui-opt-group>
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import { AuthoritiesTableComponent } from './table.component'
|
|||||||
docsLink
|
docsLink
|
||||||
path="/start-os/user-manual/trust-ca.html"
|
path="/start-os/user-manual/trust-ca.html"
|
||||||
appearance="icon"
|
appearance="icon"
|
||||||
iconStart="@tui.external-link"
|
iconStart="@tui.book-open-text"
|
||||||
>
|
>
|
||||||
{{ 'Documentation' | i18n }}
|
{{ 'Documentation' | i18n }}
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ const ipv6 =
|
|||||||
docsLink
|
docsLink
|
||||||
path="/start-os/user-manual/dns.html"
|
path="/start-os/user-manual/dns.html"
|
||||||
appearance="icon"
|
appearance="icon"
|
||||||
iconStart="@tui.external-link"
|
iconStart="@tui.book-open-text"
|
||||||
>
|
>
|
||||||
{{ 'Documentation' | i18n }}
|
{{ 'Documentation' | i18n }}
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ function detectProviderKey(host: string | undefined): string {
|
|||||||
docsLink
|
docsLink
|
||||||
path="/start-os/user-manual/smtp.html"
|
path="/start-os/user-manual/smtp.html"
|
||||||
appearance="icon"
|
appearance="icon"
|
||||||
iconStart="@tui.external-link"
|
iconStart="@tui.book-open-text"
|
||||||
>
|
>
|
||||||
{{ 'Documentation' | i18n }}
|
{{ 'Documentation' | i18n }}
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
import { CommonModule } from '@angular/common'
|
import { CommonModule } from '@angular/common'
|
||||||
import { ChangeDetectionStrategy, Component, inject } from '@angular/core'
|
import {
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
Component,
|
||||||
|
computed,
|
||||||
|
inject,
|
||||||
|
linkedSignal,
|
||||||
|
} from '@angular/core'
|
||||||
|
import { FormsModule } from '@angular/forms'
|
||||||
import { RouterLink } from '@angular/router'
|
import { RouterLink } from '@angular/router'
|
||||||
import {
|
import {
|
||||||
DocsLinkDirective,
|
DocsLinkDirective,
|
||||||
@@ -7,14 +14,18 @@ import {
|
|||||||
i18nPipe,
|
i18nPipe,
|
||||||
LoadingService,
|
LoadingService,
|
||||||
} from '@start9labs/shared'
|
} from '@start9labs/shared'
|
||||||
import { TuiButton } from '@taiga-ui/core'
|
|
||||||
import { FormComponent } from 'src/app/routes/portal/components/form.component'
|
|
||||||
import { FormDialogService } from 'src/app/services/form-dialog.service'
|
|
||||||
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
|
||||||
import { GatewaysTableComponent } from './table.component'
|
|
||||||
import { configBuilderToSpec } from 'src/app/utils/configBuilderToSpec'
|
|
||||||
import { TitleDirective } from 'src/app/services/title.service'
|
|
||||||
import { ISB } from '@start9labs/start-sdk'
|
import { ISB } from '@start9labs/start-sdk'
|
||||||
|
import { TUI_IS_MOBILE } from '@taiga-ui/cdk'
|
||||||
|
import { TuiButton, TuiTextfield, TuiTitle } from '@taiga-ui/core'
|
||||||
|
import { TuiChevron, TuiDataListWrapper, TuiSelect } from '@taiga-ui/kit'
|
||||||
|
import { TuiHeader } from '@taiga-ui/layout'
|
||||||
|
import { FormComponent } from 'src/app/routes/portal/components/form.component'
|
||||||
|
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||||
|
import { FormDialogService } from 'src/app/services/form-dialog.service'
|
||||||
|
import { GatewayService } from 'src/app/services/gateway.service'
|
||||||
|
import { TitleDirective } from 'src/app/services/title.service'
|
||||||
|
import { configBuilderToSpec } from 'src/app/utils/configBuilderToSpec'
|
||||||
|
import { GatewaysTableComponent } from './table.component'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
template: `
|
template: `
|
||||||
@@ -34,7 +45,7 @@ import { ISB } from '@start9labs/start-sdk'
|
|||||||
docsLink
|
docsLink
|
||||||
path="/start-os/user-manual/gateways.html"
|
path="/start-os/user-manual/gateways.html"
|
||||||
appearance="icon"
|
appearance="icon"
|
||||||
iconStart="@tui.external-link"
|
iconStart="@tui.book-open-text"
|
||||||
>
|
>
|
||||||
{{ 'Documentation' | i18n }}
|
{{ 'Documentation' | i18n }}
|
||||||
</a>
|
</a>
|
||||||
@@ -50,12 +61,99 @@ import { ISB } from '@start9labs/start-sdk'
|
|||||||
</header>
|
</header>
|
||||||
<gateways-table />
|
<gateways-table />
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
@if (outboundOptions(); as options) {
|
||||||
|
<section class="outbound">
|
||||||
|
<header tuiHeader="body-l">
|
||||||
|
<h3 tuiTitle>
|
||||||
|
<b>
|
||||||
|
{{ 'Outbound Traffic' | i18n }}
|
||||||
|
<a
|
||||||
|
tuiIconButton
|
||||||
|
size="xs"
|
||||||
|
docsLink
|
||||||
|
path="/start-os/user-manual/gateways.html"
|
||||||
|
fragment="#outbound-traffic"
|
||||||
|
appearance="icon"
|
||||||
|
iconStart="@tui.book-open-text"
|
||||||
|
>
|
||||||
|
{{ 'Documentation' | i18n }}
|
||||||
|
</a>
|
||||||
|
</b>
|
||||||
|
</h3>
|
||||||
|
</header>
|
||||||
|
<tui-textfield
|
||||||
|
tuiChevron
|
||||||
|
[stringify]="stringifyOutbound"
|
||||||
|
[tuiTextfieldCleaner]="false"
|
||||||
|
>
|
||||||
|
<label tuiLabel>{{ 'Use gateway' | i18n }}</label>
|
||||||
|
@if (mobile) {
|
||||||
|
<select
|
||||||
|
tuiSelect
|
||||||
|
[ngModel]="selectedOutbound()"
|
||||||
|
(ngModelChange)="selectedOutbound.set($event)"
|
||||||
|
[items]="options"
|
||||||
|
></select>
|
||||||
|
} @else {
|
||||||
|
<input
|
||||||
|
tuiSelect
|
||||||
|
[ngModel]="selectedOutbound()"
|
||||||
|
(ngModelChange)="selectedOutbound.set($event)"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
@if (!mobile) {
|
||||||
|
<tui-data-list-wrapper
|
||||||
|
new
|
||||||
|
*tuiTextfieldDropdown
|
||||||
|
[items]="options"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
</tui-textfield>
|
||||||
|
<footer>
|
||||||
|
<button
|
||||||
|
tuiButton
|
||||||
|
[disabled]="
|
||||||
|
selectedOutbound()?.id ===
|
||||||
|
(gatewayService.defaultOutbound() ?? null)
|
||||||
|
"
|
||||||
|
(click)="saveOutbound()"
|
||||||
|
>
|
||||||
|
{{ 'Save' | i18n }}
|
||||||
|
</button>
|
||||||
|
</footer>
|
||||||
|
</section>
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
styles: `
|
||||||
|
.outbound {
|
||||||
|
max-width: 24rem;
|
||||||
|
margin-top: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.outbound header {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.outbound footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
`,
|
`,
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
providers: [GatewayService],
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
|
FormsModule,
|
||||||
RouterLink,
|
RouterLink,
|
||||||
TuiButton,
|
TuiButton,
|
||||||
|
TuiTextfield,
|
||||||
|
TuiTitle,
|
||||||
|
TuiChevron,
|
||||||
|
TuiSelect,
|
||||||
|
TuiDataListWrapper,
|
||||||
|
TuiHeader,
|
||||||
GatewaysTableComponent,
|
GatewaysTableComponent,
|
||||||
TitleDirective,
|
TitleDirective,
|
||||||
i18nPipe,
|
i18nPipe,
|
||||||
@@ -68,6 +166,50 @@ export default class GatewaysComponent {
|
|||||||
private readonly api = inject(ApiService)
|
private readonly api = inject(ApiService)
|
||||||
private readonly formDialog = inject(FormDialogService)
|
private readonly formDialog = inject(FormDialogService)
|
||||||
private readonly i18n = inject(i18nPipe)
|
private readonly i18n = inject(i18nPipe)
|
||||||
|
readonly gatewayService = inject(GatewayService)
|
||||||
|
readonly mobile = inject(TUI_IS_MOBILE)
|
||||||
|
|
||||||
|
private readonly autoOption = {
|
||||||
|
id: null,
|
||||||
|
name: this.i18n.transform('Auto') ?? 'Auto',
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly outboundOptions = computed(() => {
|
||||||
|
const gateways = this.gatewayService.gateways()
|
||||||
|
if (!gateways) return null
|
||||||
|
return [
|
||||||
|
this.autoOption,
|
||||||
|
...gateways.map(g => ({ id: g.id as string | null, name: g.name })),
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
readonly selectedOutbound = linkedSignal(() => {
|
||||||
|
const options = this.outboundOptions()
|
||||||
|
const defaultId = this.gatewayService.defaultOutbound() ?? null
|
||||||
|
if (options) {
|
||||||
|
return options.find(o => o.id === defaultId) ?? options[0]
|
||||||
|
}
|
||||||
|
return this.autoOption
|
||||||
|
})
|
||||||
|
|
||||||
|
readonly stringifyOutbound = (opt: { id: string | null; name: string }) =>
|
||||||
|
opt.name
|
||||||
|
|
||||||
|
async saveOutbound() {
|
||||||
|
const loader = this.loader.open('Saving').subscribe()
|
||||||
|
|
||||||
|
console.log('outbound', this.selectedOutbound())
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.api.setDefaultOutbound({
|
||||||
|
gateway: this.selectedOutbound()?.id ?? null,
|
||||||
|
})
|
||||||
|
} catch (e: any) {
|
||||||
|
this.errorService.handleError(e)
|
||||||
|
} finally {
|
||||||
|
loader.unsubscribe()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async add() {
|
async add() {
|
||||||
const spec = ISB.InputSpec.of({
|
const spec = ISB.InputSpec.of({
|
||||||
@@ -108,13 +250,6 @@ export default class GatewaysComponent {
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
setAsDefaultOutbound: ISB.Value.toggle({
|
|
||||||
name: this.i18n.transform('Set as default outbound'),
|
|
||||||
description: this.i18n.transform(
|
|
||||||
'Route all outbound traffic through this gateway',
|
|
||||||
),
|
|
||||||
default: false,
|
|
||||||
}),
|
|
||||||
})
|
})
|
||||||
|
|
||||||
this.formDialog.open(FormComponent, {
|
this.formDialog.open(FormComponent, {
|
||||||
@@ -135,7 +270,7 @@ export default class GatewaysComponent {
|
|||||||
? input.config.value.file
|
? input.config.value.file
|
||||||
: await (input.config.value.file as any as File).text(),
|
: await (input.config.value.file as any as File).text(),
|
||||||
type: null, // @TODO Aiden why is attr here?
|
type: null, // @TODO Aiden why is attr here?
|
||||||
setAsDefaultOutbound: input.setAsDefaultOutbound,
|
setAsDefaultOutbound: false,
|
||||||
})
|
})
|
||||||
return true
|
return true
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
|
|||||||
@@ -23,9 +23,8 @@ import { filter } from 'rxjs'
|
|||||||
import { FormComponent } from 'src/app/routes/portal/components/form.component'
|
import { FormComponent } from 'src/app/routes/portal/components/form.component'
|
||||||
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||||
import { FormDialogService } from 'src/app/services/form-dialog.service'
|
import { FormDialogService } from 'src/app/services/form-dialog.service'
|
||||||
import { configBuilderToSpec } from 'src/app/utils/configBuilderToSpec'
|
|
||||||
import { GatewayPlus } from 'src/app/services/gateway.service'
|
import { GatewayPlus } from 'src/app/services/gateway.service'
|
||||||
import { TuiBadge } from '@taiga-ui/kit'
|
import { configBuilderToSpec } from 'src/app/utils/configBuilderToSpec'
|
||||||
import { PORT_FORWARDS_MODAL } from './port-forwards.component'
|
import { PORT_FORWARDS_MODAL } from './port-forwards.component'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@@ -45,11 +44,6 @@ import { PORT_FORWARDS_MODAL } from './port-forwards.component'
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
{{ gateway.name }}
|
{{ gateway.name }}
|
||||||
@if (gateway.isDefaultOutbound) {
|
|
||||||
<tui-badge appearance="primary-success">
|
|
||||||
{{ 'default outbound' | i18n }}
|
|
||||||
</tui-badge>
|
|
||||||
}
|
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
@if (gateway.type === 'outbound-only') {
|
@if (gateway.type === 'outbound-only') {
|
||||||
@@ -91,13 +85,6 @@ import { PORT_FORWARDS_MODAL } from './port-forwards.component'
|
|||||||
</button>
|
</button>
|
||||||
</tui-opt-group>
|
</tui-opt-group>
|
||||||
}
|
}
|
||||||
@if (!gateway.isDefaultOutbound) {
|
|
||||||
<tui-opt-group>
|
|
||||||
<button tuiOption new (click)="setDefaultOutbound()">
|
|
||||||
{{ 'Set as default outbound' | i18n }}
|
|
||||||
</button>
|
|
||||||
</tui-opt-group>
|
|
||||||
}
|
|
||||||
@if (gateway.ipInfo.deviceType === 'wireguard') {
|
@if (gateway.ipInfo.deviceType === 'wireguard') {
|
||||||
<tui-opt-group>
|
<tui-opt-group>
|
||||||
<button tuiOption new class="g-negative" (click)="remove()">
|
<button tuiOption new class="g-negative" (click)="remove()">
|
||||||
@@ -116,8 +103,8 @@ import { PORT_FORWARDS_MODAL } from './port-forwards.component'
|
|||||||
margin-right: 0.7rem;
|
margin-right: 0.7rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
tui-badge {
|
td:first-child {
|
||||||
margin-left: 1rem;
|
width: 24rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
td:last-child {
|
td:last-child {
|
||||||
@@ -171,7 +158,6 @@ import { PORT_FORWARDS_MODAL } from './port-forwards.component'
|
|||||||
TuiOptGroup,
|
TuiOptGroup,
|
||||||
TuiTextfield,
|
TuiTextfield,
|
||||||
i18nPipe,
|
i18nPipe,
|
||||||
TuiBadge,
|
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class GatewaysItemComponent {
|
export class GatewaysItemComponent {
|
||||||
@@ -214,18 +200,6 @@ export class GatewaysItemComponent {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async setDefaultOutbound() {
|
|
||||||
const loader = this.loader.open().subscribe()
|
|
||||||
|
|
||||||
try {
|
|
||||||
await this.api.setDefaultOutbound({ gateway: this.gateway().id })
|
|
||||||
} catch (e: any) {
|
|
||||||
this.errorService.handleError(e)
|
|
||||||
} finally {
|
|
||||||
loader.unsubscribe()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async rename() {
|
async rename() {
|
||||||
const { id, name } = this.gateway()
|
const { id, name } = this.gateway()
|
||||||
const renameSpec = ISB.InputSpec.of({
|
const renameSpec = ISB.InputSpec.of({
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ import { GatewayService } from 'src/app/services/gateway.service'
|
|||||||
</table>
|
</table>
|
||||||
`,
|
`,
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
providers: [GatewayService],
|
|
||||||
imports: [TuiSkeleton, i18nPipe, TableComponent, GatewaysItemComponent],
|
imports: [TuiSkeleton, i18nPipe, TableComponent, GatewaysItemComponent],
|
||||||
})
|
})
|
||||||
export class GatewaysTableComponent {
|
export class GatewaysTableComponent {
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ import { SSHTableComponent } from './table.component'
|
|||||||
docsLink
|
docsLink
|
||||||
path="/start-os/user-manual/ssh.html"
|
path="/start-os/user-manual/ssh.html"
|
||||||
appearance="icon"
|
appearance="icon"
|
||||||
iconStart="@tui.external-link"
|
iconStart="@tui.book-open-text"
|
||||||
>
|
>
|
||||||
{{ 'Documentation' | i18n }}
|
{{ 'Documentation' | i18n }}
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ import { wifiSpec } from './wifi.const'
|
|||||||
docsLink
|
docsLink
|
||||||
path="/start-os/user-manual/wifi.html"
|
path="/start-os/user-manual/wifi.html"
|
||||||
appearance="icon"
|
appearance="icon"
|
||||||
iconStart="@tui.external-link"
|
iconStart="@tui.book-open-text"
|
||||||
>
|
>
|
||||||
{{ 'Documentation' | i18n }}
|
{{ 'Documentation' | i18n }}
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ export type GatewayPlus = T.NetworkInterfaceInfo & {
|
|||||||
subnets: utils.IpNet[]
|
subnets: utils.IpNet[]
|
||||||
lanIpv4: string[]
|
lanIpv4: string[]
|
||||||
wanIp?: utils.IpAddress
|
wanIp?: utils.IpAddress
|
||||||
isDefaultOutbound: boolean
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@@ -29,7 +28,6 @@ export class GatewayService {
|
|||||||
this.network$.pipe(
|
this.network$.pipe(
|
||||||
map(network => {
|
map(network => {
|
||||||
const gateways = network.gateways
|
const gateways = network.gateways
|
||||||
const defaultOutbound = network.defaultOutbound
|
|
||||||
return Object.entries(gateways)
|
return Object.entries(gateways)
|
||||||
.filter(([_, val]) => !!val?.ipInfo)
|
.filter(([_, val]) => !!val?.ipInfo)
|
||||||
.filter(
|
.filter(
|
||||||
@@ -49,7 +47,6 @@ export class GatewayService {
|
|||||||
lanIpv4: subnets.filter(s => s.isIpv4()).map(s => s.address),
|
lanIpv4: subnets.filter(s => s.isIpv4()).map(s => s.address),
|
||||||
wanIp:
|
wanIp:
|
||||||
val.ipInfo?.wanIp && utils.IpAddress.parse(val.ipInfo?.wanIp),
|
val.ipInfo?.wanIp && utils.IpAddress.parse(val.ipInfo?.wanIp),
|
||||||
isDefaultOutbound: id === defaultOutbound,
|
|
||||||
} as GatewayPlus
|
} as GatewayPlus
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -161,7 +161,6 @@ export class MarketplaceService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fetchRegistry$(url: string): Observable<StoreDataWithUrl | null> {
|
private fetchRegistry$(url: string): Observable<StoreDataWithUrl | null> {
|
||||||
console.log('FETCHING REGISTRY: ', url)
|
|
||||||
return combineLatest([this.fetchInfo$(url), this.fetchPackages$(url)]).pipe(
|
return combineLatest([this.fetchInfo$(url), this.fetchPackages$(url)]).pipe(
|
||||||
map(([info, packages]) => ({ info, packages, url })),
|
map(([info, packages]) => ({ info, packages, url })),
|
||||||
catchError(e => {
|
catchError(e => {
|
||||||
|
|||||||
@@ -70,6 +70,12 @@ hr {
|
|||||||
min-height: fit-content;
|
min-height: fit-content;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
display: block;
|
||||||
|
height: 1rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.g-aside {
|
.g-aside {
|
||||||
|
|||||||
Reference in New Issue
Block a user