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: {
|
||||
target: '_blank',
|
||||
rel: 'noreferrer',
|
||||
'[href]': 'url()',
|
||||
'[attr.href]': 'url()',
|
||||
},
|
||||
})
|
||||
export class DocsLinkDirective {
|
||||
|
||||
@@ -658,8 +658,6 @@ export default {
|
||||
721: 'Gateway für ausgehenden Datenverkehr auswählen',
|
||||
722: 'Der Typ des Gateways',
|
||||
723: 'Nur ausgehend',
|
||||
724: 'Als Standard für ausgehenden Verkehr festlegen',
|
||||
725: 'Gesamten ausgehenden Datenverkehr über dieses Gateway leiten',
|
||||
726: 'WireGuard-Konfigurationsdatei',
|
||||
727: 'Eingehend/Ausgehend',
|
||||
728: 'StartTunnel (Eingehend/Ausgehend)',
|
||||
@@ -668,7 +666,6 @@ export default {
|
||||
731: 'Öffentliche Domain',
|
||||
732: 'Private Domain',
|
||||
733: 'Ausblenden',
|
||||
734: 'Standard ausgehend',
|
||||
735: 'Zertifikat',
|
||||
736: 'Selbstsigniert',
|
||||
737: 'Portweiterleitung',
|
||||
@@ -710,4 +707,7 @@ export default {
|
||||
781: 'Lokal',
|
||||
782: 'Unbekanntes Laufwerk',
|
||||
783: 'Muss eine gültige E-Mail-Adresse sein',
|
||||
786: 'Automatisch',
|
||||
787: 'Ausgehender Datenverkehr',
|
||||
788: 'Gateway verwenden',
|
||||
} satisfies i18n
|
||||
|
||||
@@ -658,8 +658,6 @@ export const ENGLISH: Record<string, number> = {
|
||||
'Select the gateway for outbound traffic': 721,
|
||||
'The type of gateway': 722,
|
||||
'Outbound Only': 723,
|
||||
'Set as default outbound': 724,
|
||||
'Route all outbound traffic through this gateway': 725,
|
||||
'WireGuard Config File': 726,
|
||||
'Inbound/Outbound': 727,
|
||||
'StartTunnel (Inbound/Outbound)': 728,
|
||||
@@ -668,7 +666,6 @@ export const ENGLISH: Record<string, number> = {
|
||||
'Public Domain': 731,
|
||||
'Private Domain': 732,
|
||||
'Hide': 733,
|
||||
'default outbound': 734,
|
||||
'Certificate': 735,
|
||||
'Self signed': 736,
|
||||
'Port Forwarding': 737,
|
||||
@@ -710,4 +707,7 @@ export const ENGLISH: Record<string, number> = {
|
||||
'Local': 781, // as in, locally accessible
|
||||
'Unknown Drive': 782,
|
||||
'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',
|
||||
722: 'El tipo de puerta de enlace',
|
||||
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',
|
||||
727: 'Entrante/Saliente',
|
||||
728: 'StartTunnel (Entrante/Saliente)',
|
||||
@@ -668,7 +666,6 @@ export default {
|
||||
731: 'Dominio público',
|
||||
732: 'Dominio privado',
|
||||
733: 'Ocultar',
|
||||
734: 'saliente predeterminado',
|
||||
735: 'Certificado',
|
||||
736: 'Autofirmado',
|
||||
737: 'Reenvío de puertos',
|
||||
@@ -710,4 +707,7 @@ export default {
|
||||
781: 'Local',
|
||||
782: 'Unidad desconocida',
|
||||
783: 'Debe ser una dirección de correo electrónico válida',
|
||||
786: 'Automático',
|
||||
787: 'Tráfico saliente',
|
||||
788: 'Usar gateway',
|
||||
} satisfies i18n
|
||||
|
||||
@@ -658,8 +658,6 @@ export default {
|
||||
721: 'Sélectionnez la passerelle pour le trafic sortant',
|
||||
722: 'Le type de passerelle',
|
||||
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',
|
||||
727: 'Entrant/Sortant',
|
||||
728: 'StartTunnel (Entrant/Sortant)',
|
||||
@@ -668,7 +666,6 @@ export default {
|
||||
731: 'Domaine public',
|
||||
732: 'Domaine privé',
|
||||
733: 'Masquer',
|
||||
734: 'sortant par défaut',
|
||||
735: 'Certificat',
|
||||
736: 'Auto-signé',
|
||||
737: 'Redirection de ports',
|
||||
@@ -710,4 +707,7 @@ export default {
|
||||
781: 'Local',
|
||||
782: 'Lecteur inconnu',
|
||||
783: 'Doit être une adresse e-mail valide',
|
||||
786: 'Automatique',
|
||||
787: 'Trafic sortant',
|
||||
788: 'Utiliser la passerelle',
|
||||
} satisfies i18n
|
||||
|
||||
@@ -658,8 +658,6 @@ export default {
|
||||
721: 'Wybierz bramę dla ruchu wychodzącego',
|
||||
722: 'Typ bramy',
|
||||
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',
|
||||
727: 'Przychodzący/Wychodzący',
|
||||
728: 'StartTunnel (Przychodzący/Wychodzący)',
|
||||
@@ -668,7 +666,6 @@ export default {
|
||||
731: 'Domena publiczna',
|
||||
732: 'Domena prywatna',
|
||||
733: 'Ukryj',
|
||||
734: 'domyślne wychodzące',
|
||||
735: 'Certyfikat',
|
||||
736: 'Samopodpisany',
|
||||
737: 'Przekierowanie portów',
|
||||
@@ -710,4 +707,7 @@ export default {
|
||||
781: 'Lokalny',
|
||||
782: 'Nieznany dysk',
|
||||
783: 'Musi być prawidłowy adres e-mail',
|
||||
786: 'Automatycznie',
|
||||
787: 'Ruch wychodzący',
|
||||
788: 'Użyj bramy',
|
||||
} satisfies i18n
|
||||
|
||||
@@ -45,7 +45,7 @@ import { ABOUT } from './about.component'
|
||||
}
|
||||
<tui-data-list [style.width.rem]="13">
|
||||
<tui-opt-group>
|
||||
<button tuiOption iconStart="@tui.info" (click)="about()">
|
||||
<button tuiOption iconStart="@tui.info" new (click)="about()">
|
||||
{{ 'About this server' | i18n }}
|
||||
</button>
|
||||
</tui-opt-group>
|
||||
@@ -53,13 +53,15 @@ import { ABOUT } from './about.component'
|
||||
<a
|
||||
tuiOption
|
||||
docsLink
|
||||
iconStart="@tui.book-open"
|
||||
path="/start-os/user-manual/index.html"
|
||||
new
|
||||
iconStart="@tui.book-open-text"
|
||||
path="/start-os/user-manual"
|
||||
>
|
||||
{{ 'User manual' | i18n }}
|
||||
</a>
|
||||
<a
|
||||
tuiOption
|
||||
new
|
||||
iconStart="@tui.headphones"
|
||||
href="https://start9.com/contact"
|
||||
>
|
||||
@@ -67,6 +69,7 @@ import { ABOUT } from './about.component'
|
||||
</a>
|
||||
<a
|
||||
tuiOption
|
||||
new
|
||||
iconStart="@tui.dollar-sign"
|
||||
href="https://donate.start9.com"
|
||||
>
|
||||
@@ -76,6 +79,7 @@ import { ABOUT } from './about.component'
|
||||
<tui-opt-group label="">
|
||||
<a
|
||||
tuiOption
|
||||
new
|
||||
iconStart="@tui.settings"
|
||||
routerLink="/system"
|
||||
(click)="open = false"
|
||||
@@ -86,6 +90,7 @@ import { ABOUT } from './about.component'
|
||||
<tui-opt-group label="">
|
||||
<button
|
||||
tuiOption
|
||||
new
|
||||
iconStart="@tui.refresh-cw"
|
||||
(click)="promptPower('restart')"
|
||||
>
|
||||
@@ -93,12 +98,13 @@ import { ABOUT } from './about.component'
|
||||
</button>
|
||||
<button
|
||||
tuiOption
|
||||
new
|
||||
iconStart="@tui.power"
|
||||
(click)="promptPower('shutdown')"
|
||||
>
|
||||
{{ 'Shutdown' | i18n }}
|
||||
</button>
|
||||
<button tuiOption iconStart="@tui.log-out" (click)="logout()">
|
||||
<button tuiOption new iconStart="@tui.log-out" (click)="logout()">
|
||||
{{ 'Logout' | i18n }}
|
||||
</button>
|
||||
</tui-opt-group>
|
||||
|
||||
@@ -23,7 +23,7 @@ import { AuthoritiesTableComponent } from './table.component'
|
||||
docsLink
|
||||
path="/start-os/user-manual/trust-ca.html"
|
||||
appearance="icon"
|
||||
iconStart="@tui.external-link"
|
||||
iconStart="@tui.book-open-text"
|
||||
>
|
||||
{{ 'Documentation' | i18n }}
|
||||
</a>
|
||||
|
||||
@@ -49,7 +49,7 @@ const ipv6 =
|
||||
docsLink
|
||||
path="/start-os/user-manual/dns.html"
|
||||
appearance="icon"
|
||||
iconStart="@tui.external-link"
|
||||
iconStart="@tui.book-open-text"
|
||||
>
|
||||
{{ 'Documentation' | i18n }}
|
||||
</a>
|
||||
|
||||
@@ -57,7 +57,7 @@ function detectProviderKey(host: string | undefined): string {
|
||||
docsLink
|
||||
path="/start-os/user-manual/smtp.html"
|
||||
appearance="icon"
|
||||
iconStart="@tui.external-link"
|
||||
iconStart="@tui.book-open-text"
|
||||
>
|
||||
{{ 'Documentation' | i18n }}
|
||||
</a>
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
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 {
|
||||
DocsLinkDirective,
|
||||
@@ -7,14 +14,18 @@ import {
|
||||
i18nPipe,
|
||||
LoadingService,
|
||||
} 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 { 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({
|
||||
template: `
|
||||
@@ -34,7 +45,7 @@ import { ISB } from '@start9labs/start-sdk'
|
||||
docsLink
|
||||
path="/start-os/user-manual/gateways.html"
|
||||
appearance="icon"
|
||||
iconStart="@tui.external-link"
|
||||
iconStart="@tui.book-open-text"
|
||||
>
|
||||
{{ 'Documentation' | i18n }}
|
||||
</a>
|
||||
@@ -50,12 +61,99 @@ import { ISB } from '@start9labs/start-sdk'
|
||||
</header>
|
||||
<gateways-table />
|
||||
</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,
|
||||
providers: [GatewayService],
|
||||
imports: [
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
RouterLink,
|
||||
TuiButton,
|
||||
TuiTextfield,
|
||||
TuiTitle,
|
||||
TuiChevron,
|
||||
TuiSelect,
|
||||
TuiDataListWrapper,
|
||||
TuiHeader,
|
||||
GatewaysTableComponent,
|
||||
TitleDirective,
|
||||
i18nPipe,
|
||||
@@ -68,6 +166,50 @@ export default class GatewaysComponent {
|
||||
private readonly api = inject(ApiService)
|
||||
private readonly formDialog = inject(FormDialogService)
|
||||
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() {
|
||||
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, {
|
||||
@@ -135,7 +270,7 @@ export default class GatewaysComponent {
|
||||
? input.config.value.file
|
||||
: await (input.config.value.file as any as File).text(),
|
||||
type: null, // @TODO Aiden why is attr here?
|
||||
setAsDefaultOutbound: input.setAsDefaultOutbound,
|
||||
setAsDefaultOutbound: false,
|
||||
})
|
||||
return true
|
||||
} catch (e: any) {
|
||||
|
||||
@@ -23,9 +23,8 @@ import { filter } from 'rxjs'
|
||||
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 { configBuilderToSpec } from 'src/app/utils/configBuilderToSpec'
|
||||
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'
|
||||
|
||||
@Component({
|
||||
@@ -45,11 +44,6 @@ import { PORT_FORWARDS_MODAL } from './port-forwards.component'
|
||||
}
|
||||
}
|
||||
{{ gateway.name }}
|
||||
@if (gateway.isDefaultOutbound) {
|
||||
<tui-badge appearance="primary-success">
|
||||
{{ 'default outbound' | i18n }}
|
||||
</tui-badge>
|
||||
}
|
||||
</td>
|
||||
<td>
|
||||
@if (gateway.type === 'outbound-only') {
|
||||
@@ -91,13 +85,6 @@ import { PORT_FORWARDS_MODAL } from './port-forwards.component'
|
||||
</button>
|
||||
</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') {
|
||||
<tui-opt-group>
|
||||
<button tuiOption new class="g-negative" (click)="remove()">
|
||||
@@ -116,8 +103,8 @@ import { PORT_FORWARDS_MODAL } from './port-forwards.component'
|
||||
margin-right: 0.7rem;
|
||||
}
|
||||
|
||||
tui-badge {
|
||||
margin-left: 1rem;
|
||||
td:first-child {
|
||||
width: 24rem;
|
||||
}
|
||||
|
||||
td:last-child {
|
||||
@@ -171,7 +158,6 @@ import { PORT_FORWARDS_MODAL } from './port-forwards.component'
|
||||
TuiOptGroup,
|
||||
TuiTextfield,
|
||||
i18nPipe,
|
||||
TuiBadge,
|
||||
],
|
||||
})
|
||||
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() {
|
||||
const { id, name } = this.gateway()
|
||||
const renameSpec = ISB.InputSpec.of({
|
||||
|
||||
@@ -21,7 +21,6 @@ import { GatewayService } from 'src/app/services/gateway.service'
|
||||
</table>
|
||||
`,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
providers: [GatewayService],
|
||||
imports: [TuiSkeleton, i18nPipe, TableComponent, GatewaysItemComponent],
|
||||
})
|
||||
export class GatewaysTableComponent {
|
||||
|
||||
@@ -41,7 +41,7 @@ import { SSHTableComponent } from './table.component'
|
||||
docsLink
|
||||
path="/start-os/user-manual/ssh.html"
|
||||
appearance="icon"
|
||||
iconStart="@tui.external-link"
|
||||
iconStart="@tui.book-open-text"
|
||||
>
|
||||
{{ 'Documentation' | i18n }}
|
||||
</a>
|
||||
|
||||
@@ -56,7 +56,7 @@ import { wifiSpec } from './wifi.const'
|
||||
docsLink
|
||||
path="/start-os/user-manual/wifi.html"
|
||||
appearance="icon"
|
||||
iconStart="@tui.external-link"
|
||||
iconStart="@tui.book-open-text"
|
||||
>
|
||||
{{ 'Documentation' | i18n }}
|
||||
</a>
|
||||
|
||||
@@ -12,7 +12,6 @@ export type GatewayPlus = T.NetworkInterfaceInfo & {
|
||||
subnets: utils.IpNet[]
|
||||
lanIpv4: string[]
|
||||
wanIp?: utils.IpAddress
|
||||
isDefaultOutbound: boolean
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
@@ -29,7 +28,6 @@ export class GatewayService {
|
||||
this.network$.pipe(
|
||||
map(network => {
|
||||
const gateways = network.gateways
|
||||
const defaultOutbound = network.defaultOutbound
|
||||
return Object.entries(gateways)
|
||||
.filter(([_, val]) => !!val?.ipInfo)
|
||||
.filter(
|
||||
@@ -49,7 +47,6 @@ export class GatewayService {
|
||||
lanIpv4: subnets.filter(s => s.isIpv4()).map(s => s.address),
|
||||
wanIp:
|
||||
val.ipInfo?.wanIp && utils.IpAddress.parse(val.ipInfo?.wanIp),
|
||||
isDefaultOutbound: id === defaultOutbound,
|
||||
} as GatewayPlus
|
||||
})
|
||||
}),
|
||||
|
||||
@@ -161,7 +161,6 @@ export class MarketplaceService {
|
||||
}
|
||||
|
||||
private fetchRegistry$(url: string): Observable<StoreDataWithUrl | null> {
|
||||
console.log('FETCHING REGISTRY: ', url)
|
||||
return combineLatest([this.fetchInfo$(url), this.fetchPackages$(url)]).pipe(
|
||||
map(([info, packages]) => ({ info, packages, url })),
|
||||
catchError(e => {
|
||||
|
||||
@@ -70,6 +70,12 @@ hr {
|
||||
min-height: fit-content;
|
||||
flex: 1;
|
||||
padding: 1rem;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
display: block;
|
||||
height: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.g-aside {
|
||||
|
||||
Reference in New Issue
Block a user