diff --git a/web/projects/shared/src/i18n/dictionaries/de.ts b/web/projects/shared/src/i18n/dictionaries/de.ts
index 620f3439a..d96a12dea 100644
--- a/web/projects/shared/src/i18n/dictionaries/de.ts
+++ b/web/projects/shared/src/i18n/dictionaries/de.ts
@@ -90,20 +90,15 @@ export default {
88: 'Aktionen',
89: 'nicht empfohlen',
90: 'Root-CA ist vertrauenswürdig!',
- 93: 'Öffentlich machen',
- 94: 'Privat machen',
- 95: 'Keine öffentlichen Adressen',
96: 'Domain hinzufügen',
97: 'Wird entfernt',
- 98: 'Wird öffentlich gemacht',
- 99: 'Wird privat gemacht',
100: 'Nicht gespeicherte Änderungen',
101: 'Sie haben nicht gespeicherte Änderungen. Möchten Sie die Seite wirklich verlassen?',
102: 'Verlassen',
103: 'Sind Sie sicher?',
104: 'Domain auswählen',
108: 'Öffentlich',
- 109: 'Privat',
+ 109: 'privat',
111: 'Keine Onion-Domains',
112: 'Neue Onion-Domain',
113: 'Privater Schlüssel (optional)',
@@ -282,7 +277,7 @@ export default {
292: 'Upload wird gestartet',
293: 'Erneut versuchen',
294: '.s9pk-Paketdatei hochladen',
- 295: 'Warnung: Der Upload über Tor ist langsam. Wechseln Sie für bessere Leistung ins lokale Netzwerk.',
+ 295: 'Warnung: Der Upload über Tor ist langsam.',
296: 'Hochladen',
297: 'Version 1 s9pk erkannt. Dieses Format ist veraltet. Falls nötig, kann ein V1 s9pk über start-cli installiert werden.',
298: 'Ungültige Paketdatei',
@@ -522,7 +517,7 @@ export default {
544: 'Domain bearbeiten',
545: 'Keine Domains',
546: 'Anbieter',
- 547: 'DNS verwalten',
+ 547: '',
548: '',
549: '',
550: '',
diff --git a/web/projects/shared/src/i18n/dictionaries/en.ts b/web/projects/shared/src/i18n/dictionaries/en.ts
index 36eacaa8f..53960a540 100644
--- a/web/projects/shared/src/i18n/dictionaries/en.ts
+++ b/web/projects/shared/src/i18n/dictionaries/en.ts
@@ -89,20 +89,15 @@ export const ENGLISH = {
'Actions': 88, // as in, actions available to the user
'not recommended': 89,
'Root CA Trusted!': 90,
- 'Make public': 93,
- 'Make private': 94,
- 'No public addresses': 95,
'Add domain': 96,
'Removing': 97,
- 'Making public': 98,
- 'Making private': 99,
'Unsaved changes': 100,
'You have unsaved changes. Are you sure you want to leave?': 101,
'Leave': 102,
'Are you sure?': 103,
'Select domain': 104,
- 'Public': 108,
- 'Private': 109,
+ 'public': 108,
+ 'private': 109,
'No Tor domains': 111,
'New Tor domain': 112,
'Private Key (optional)': 113,
@@ -281,7 +276,7 @@ export const ENGLISH = {
'Starting upload': 292,
'Try again': 293,
'Upload .s9pk package file': 294,
- 'Warning: package upload will be slow over Tor. Switch to local for a better experience.': 295,
+ 'Warning: package upload will be slow over Tor.': 295,
'Upload': 296,
'Version 1 s9pk detected. This package format is deprecated. You can sideload a V1 s9pk via start-cli if necessary.': 297,
'Invalid package file': 298,
@@ -521,7 +516,7 @@ export const ENGLISH = {
'Edit domain': 544,
'No domains': 545,
'Provider': 546,
- 'Manage DNS': 547,
+ 'View DNS': 547,
'Clearnet Domains': 548,
'No clearnet domains': 549,
'Addresses': 550,
diff --git a/web/projects/shared/src/i18n/dictionaries/es.ts b/web/projects/shared/src/i18n/dictionaries/es.ts
index 6c74f568d..8e1b7bd31 100644
--- a/web/projects/shared/src/i18n/dictionaries/es.ts
+++ b/web/projects/shared/src/i18n/dictionaries/es.ts
@@ -90,20 +90,15 @@ export default {
88: 'Acciones',
89: 'no recomendado',
90: '¡CA raíz confiable!',
- 93: 'Hacer público',
- 94: 'Hacer privado',
- 95: 'Sin direcciones públicas',
96: 'Agregar dominio',
97: 'Eliminando',
- 98: 'Haciendo público',
- 99: 'Haciendo privado',
100: 'Cambios no guardados',
101: 'Tienes cambios no guardados. ¿Estás seguro de que deseas salir?',
102: 'Salir',
103: '¿Estás seguro?',
104: 'Seleccionar dominio',
- 108: 'Público',
- 109: 'Privado',
+ 108: 'público',
+ 109: 'privado',
111: 'Sin dominios onion',
112: 'Nueva dominio onion',
113: 'Clave privada (opcional)',
@@ -282,7 +277,7 @@ export default {
292: 'Iniciando carga',
293: 'Intentar de nuevo',
294: 'Subir archivo de paquete .s9pk',
- 295: 'Advertencia: la carga del paquete será lenta a través de Tor. Cambia a conexión local para una mejor experiencia.',
+ 295: 'Advertencia: la carga del paquete será lenta a través de Tor.',
296: 'Subir',
297: 'Se detectó un paquete s9pk de versión 1. Este formato está obsoleto. Puedes instalarlo manualmente con start-cli si es necesario.',
298: 'Archivo de paquete inválido',
@@ -522,7 +517,7 @@ export default {
544: 'Editar dominio',
545: 'Sin dominios',
546: 'Proveedor',
- 547: 'Administrar DNS',
+ 547: '',
548: '',
549: '',
550: '',
diff --git a/web/projects/shared/src/i18n/dictionaries/fr.ts b/web/projects/shared/src/i18n/dictionaries/fr.ts
index 9f7c6a32a..f54ef86ad 100644
--- a/web/projects/shared/src/i18n/dictionaries/fr.ts
+++ b/web/projects/shared/src/i18n/dictionaries/fr.ts
@@ -90,20 +90,15 @@ export default {
88: 'Actions',
89: 'non recommandé',
90: 'Certificat racine approuvé !',
- 93: 'Rendre public',
- 94: 'Rendre privé',
- 95: 'Aucune adresse publique',
96: 'Ajouter un domaine',
97: 'Suppression',
- 98: 'Mise en public',
- 99: 'Mise en privé',
100: 'Modifications non enregistrées',
101: 'Vous avez des modifications non enregistrées. Voulez-vous vraiment quitter ?',
102: 'Quitter',
103: 'Êtes-vous sûr ?',
104: 'Sélectionner un domaine',
- 108: 'Public',
- 109: 'Privé',
+ 108: 'public',
+ 109: 'privé',
111: 'Aucune domaine onion',
112: 'Nouvelle domaine onion',
113: 'Clé privée (optionnel)',
@@ -282,7 +277,7 @@ export default {
292: 'Début du téléversement',
293: 'Réessayer',
294: 'Téléverser un fichier .s9pk',
- 295: 'Attention : le téléversement du paquet sera lent via Tor. Passez en local pour une meilleure expérience.',
+ 295: 'Attention : le téléversement du paquet sera lent via Tor.',
296: 'Téléverser',
297: 'Version 1 de s9pk détectée. Ce format de paquet est obsolète. Vous pouvez installer manuellement un s9pk V1 via start-cli si nécessaire.',
298: 'Fichier paquet invalide',
@@ -522,7 +517,7 @@ export default {
544: 'Modifier le domaine',
545: 'Aucun domaine',
546: 'Fournisseur',
- 547: 'Gérer le DNS',
+ 547: '',
548: '',
549: '',
550: '',
diff --git a/web/projects/shared/src/i18n/dictionaries/pl.ts b/web/projects/shared/src/i18n/dictionaries/pl.ts
index 148fd6dc7..a9928d1b4 100644
--- a/web/projects/shared/src/i18n/dictionaries/pl.ts
+++ b/web/projects/shared/src/i18n/dictionaries/pl.ts
@@ -90,20 +90,15 @@ export default {
88: 'Akcje',
89: 'niezalecane',
90: 'Główny certyfikat CA zaufany!',
- 93: 'Upublicznij',
- 94: 'Ukryj',
- 95: 'Brak publicznych adresów',
96: 'Dodaj domenę',
97: 'Usuwanie',
- 98: 'Upublicznianie',
- 99: 'Ukrywanie',
100: 'Niezapisane zmiany',
101: 'Masz niezapisane zmiany. Czy na pewno chcesz opuścić tę stronę?',
102: 'Opuść',
103: 'Czy jesteś pewien?',
104: 'Wybierz domenę',
- 108: 'Publiczny',
- 109: 'Prywatny',
+ 108: 'publiczny',
+ 109: 'prywatny',
111: 'Brak domeny onion',
112: 'Nowy domenę onion',
113: 'Klucz prywatny (opcjonalnie)',
@@ -282,7 +277,7 @@ export default {
292: 'Rozpoczynanie przesyłania',
293: 'Spróbuj ponownie',
294: 'Prześlij plik pakietu .s9pk',
- 295: 'Uwaga: przesyłanie pakietu przez Tor będzie powolne. Przełącz się na sieć lokalną, aby uzyskać lepszą wydajność.',
+ 295: 'Uwaga: przesyłanie pakietu przez Tor będzie powolne.',
296: 'Prześlij',
297: 'Wykryto pakiet s9pk w wersji 1. Ten format pakietu jest przestarzały. Możesz zainstalować pakiet s9pk V1 przez start-cli, jeśli to konieczne.',
298: 'Nieprawidłowy plik pakietu',
@@ -522,7 +517,7 @@ export default {
544: 'Edytuj domenę',
545: 'Brak domen',
546: 'Dostawca',
- 547: 'Zarządzaj DNS',
+ 547: '',
548: '',
549: '',
550: '',
diff --git a/web/projects/ui/src/app/routes/portal/components/interfaces/acme.pipe.ts b/web/projects/ui/src/app/routes/portal/components/interfaces/acme.pipe.ts
deleted file mode 100644
index 420ef1da8..000000000
--- a/web/projects/ui/src/app/routes/portal/components/interfaces/acme.pipe.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { Pipe, PipeTransform } from '@angular/core'
-import { toAuthorityName } from 'src/app/utils/acme'
-
-@Pipe({
- name: 'authorityName',
-})
-export class AuthorityNamePipe implements PipeTransform {
- transform(value: string | null = null): string {
- return toAuthorityName(value)
- }
-}
diff --git a/web/projects/ui/src/app/routes/portal/components/interfaces/addresses/actions.component.ts b/web/projects/ui/src/app/routes/portal/components/interfaces/addresses/actions.component.ts
index a838fd273..7b2079b4b 100644
--- a/web/projects/ui/src/app/routes/portal/components/interfaces/addresses/actions.component.ts
+++ b/web/projects/ui/src/app/routes/portal/components/interfaces/addresses/actions.component.ts
@@ -16,9 +16,7 @@ import {
} from '@taiga-ui/core'
import { PolymorpheusComponent } from '@taiga-ui/polymorpheus'
import { QRModal } from 'src/app/routes/portal/modals/qr.component'
-
import { InterfaceComponent } from '../interface.component'
-import { InterfaceService } from '../interface.service'
@Component({
selector: 'td[actions]',
diff --git a/web/projects/ui/src/app/routes/portal/components/interfaces/addresses/addresses.component.ts b/web/projects/ui/src/app/routes/portal/components/interfaces/addresses/addresses.component.ts
index 1f75d811a..a4841dea7 100644
--- a/web/projects/ui/src/app/routes/portal/components/interfaces/addresses/addresses.component.ts
+++ b/web/projects/ui/src/app/routes/portal/components/interfaces/addresses/addresses.component.ts
@@ -12,7 +12,7 @@ import { InterfaceAddressItemComponent } from './item.component'
selector: 'section[addresses]',
template: `
-
+
@for (address of addresses()?.common; track $index) {
} @empty {
@@ -27,7 +27,7 @@ import { InterfaceAddressItemComponent } from './item.component'
} @else {
@for (_ of [0, 1]; track $index) {
-
+
{{ 'Loading' | i18n }}
diff --git a/web/projects/ui/src/app/routes/portal/components/interfaces/addresses/item.component.ts b/web/projects/ui/src/app/routes/portal/components/interfaces/addresses/item.component.ts
index 94d413d25..ccd363eaa 100644
--- a/web/projects/ui/src/app/routes/portal/components/interfaces/addresses/item.component.ts
+++ b/web/projects/ui/src/app/routes/portal/components/interfaces/addresses/item.component.ts
@@ -8,6 +8,7 @@ import { DialogService, i18nKey, i18nPipe } from '@start9labs/shared'
import { TuiButton } from '@taiga-ui/core'
import { DisplayAddress } from '../interface.service'
import { AddressActionsComponent } from './actions.component'
+import { TuiBadge } from '@taiga-ui/kit'
@Component({
selector: 'tr[address]',
@@ -17,13 +18,26 @@ import { AddressActionsComponent } from './actions.component'
{{ 'Address details' | i18n }}
{{ address.type }}
+
+ @if (address.access === 'public') {
+
+ {{ 'public' | i18n }}
+
+ } @else if (address.access === 'private') {
+
+ {{ 'private' | i18n }}
+
+ } @else {
+ -
+ }
+
{{ address.gatewayName || '-' }}
@@ -54,7 +68,7 @@ import { AddressActionsComponent } from './actions.component'
}
}
`,
- imports: [i18nPipe, AddressActionsComponent, TuiButton],
+ imports: [i18nPipe, AddressActionsComponent, TuiButton, TuiBadge],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class InterfaceAddressItemComponent {
diff --git a/web/projects/ui/src/app/routes/portal/components/interfaces/clearnet-domains.component.ts b/web/projects/ui/src/app/routes/portal/components/interfaces/clearnet-domains.component.ts
deleted file mode 100644
index 8fd8cc818..000000000
--- a/web/projects/ui/src/app/routes/portal/components/interfaces/clearnet-domains.component.ts
+++ /dev/null
@@ -1,83 +0,0 @@
-import { ChangeDetectionStrategy, Component, input } from '@angular/core'
-import { DocsLinkDirective, i18nPipe } from '@start9labs/shared'
-import { TuiButton } from '@taiga-ui/core'
-import { TuiSkeleton } from '@taiga-ui/kit'
-import { PlaceholderComponent } from 'src/app/routes/portal/components/placeholder.component'
-import { TableComponent } from 'src/app/routes/portal/components/table.component'
-
-import { DomainComponent } from './domain.component'
-import { ClearnetDomain } from './interface.service'
-
-@Component({
- selector: 'section[clearnetDomains]',
- template: `
-
-
- @for (domain of clearnetDomains(); track $index) {
-
- } @empty {
- @if (clearnetDomains()) {
-
-
-
- {{ 'No clearnet domains' | i18n }}
-
-
-
- } @else {
- @for (_ of [0, 1]; track $index) {
-
-
- {{ 'Loading' | i18n }}
-
-
- }
- }
- }
-
- `,
- styles: `
- :host {
- grid-column: span 3;
- }
- `,
- host: { class: 'g-card' },
- imports: [
- TuiButton,
- TableComponent,
- PlaceholderComponent,
- i18nPipe,
- DocsLinkDirective,
- DomainComponent,
- TuiSkeleton,
- ],
- changeDetection: ChangeDetectionStrategy.OnPush,
-})
-export class InterfaceClearnetDomainsComponent {
- readonly clearnetDomains = input.required<
- readonly ClearnetDomain[] | undefined
- >()
-
- open = false
-
- add() {}
-}
diff --git a/web/projects/ui/src/app/routes/portal/components/interfaces/clearnet-domains/clearnet-domains.component.ts b/web/projects/ui/src/app/routes/portal/components/interfaces/clearnet-domains/clearnet-domains.component.ts
new file mode 100644
index 000000000..60dd8da91
--- /dev/null
+++ b/web/projects/ui/src/app/routes/portal/components/interfaces/clearnet-domains/clearnet-domains.component.ts
@@ -0,0 +1,236 @@
+import {
+ ChangeDetectionStrategy,
+ Component,
+ computed,
+ inject,
+ input,
+} from '@angular/core'
+import {
+ DocsLinkDirective,
+ ErrorService,
+ i18nPipe,
+ LoadingService,
+} from '@start9labs/shared'
+import { TuiButton } from '@taiga-ui/core'
+import { TuiSkeleton } from '@taiga-ui/kit'
+import { PlaceholderComponent } from 'src/app/routes/portal/components/placeholder.component'
+import { TableComponent } from 'src/app/routes/portal/components/table.component'
+import { InterfaceClearnetDomainsItemComponent } from './item.component'
+import { ClearnetDomain } from '../interface.service'
+import { ISB, utils } from '@start9labs/start-sdk'
+import { FormDialogService } from 'src/app/services/form-dialog.service'
+import { FormComponent } from '../../form.component'
+import { configBuilderToSpec } from 'src/app/utils/configBuilderToSpec'
+import { PatchDB } from 'patch-db-client'
+import { DataModel } from 'src/app/services/patch-db/data-model'
+import { toSignal } from '@angular/core/rxjs-interop'
+import { map } from 'rxjs'
+import { toAuthorityName } from 'src/app/utils/acme'
+import { ApiService } from 'src/app/services/api/embassy-api.service'
+import { InterfaceComponent } from '../interface.component'
+
+// @TODO translations
+
+@Component({
+ selector: 'section[clearnetDomains]',
+ template: `
+
+
+ @for (domain of clearnetDomains(); track $index) {
+
+ } @empty {
+ @if (clearnetDomains()) {
+
+
+
+ {{ 'No clearnet domains' | i18n }}
+
+
+
+ } @else {
+ @for (_ of [0, 1]; track $index) {
+
+
+ {{ 'Loading' | i18n }}
+
+
+ }
+ }
+ }
+
+ `,
+ styles: `
+ :host {
+ grid-column: span 3;
+ }
+ `,
+ host: { class: 'g-card' },
+ imports: [
+ TuiButton,
+ TableComponent,
+ PlaceholderComponent,
+ i18nPipe,
+ DocsLinkDirective,
+ InterfaceClearnetDomainsItemComponent,
+ TuiSkeleton,
+ ],
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class InterfaceClearnetDomainsComponent {
+ private readonly formDialog = inject(FormDialogService)
+ private readonly patch = inject>(PatchDB)
+ private readonly api = inject(ApiService)
+ private readonly loader = inject(LoadingService)
+ private readonly errorService = inject(ErrorService)
+ private readonly interface = inject(InterfaceComponent)
+
+ readonly clearnetDomains = input.required<
+ readonly ClearnetDomain[] | undefined
+ >()
+
+ private readonly domains = toSignal(
+ this.patch.watch$('serverInfo', 'network', 'domains'),
+ )
+
+ private readonly acme = toSignal(
+ this.patch.watch$('serverInfo', 'network', 'acme').pipe(
+ map(acme =>
+ Object.keys(acme).reduce>(
+ (obj, url) => ({
+ ...obj,
+ [url]: toAuthorityName(url),
+ }),
+ { local: toAuthorityName(null) },
+ ),
+ ),
+ ),
+ )
+
+ async add() {
+ const addSpec = ISB.InputSpec.of({
+ type: ISB.Value.union({
+ name: 'Type',
+ default: 'public',
+ description:
+ '- **Public**: the domain can be accessed by anyone with an Internet connection.\n- **Private**: the domain can only be accessed by people connected to the same Local Area Network (LAN) as the server, either physically or via VPN.',
+ variants: ISB.Variants.of({
+ public: {
+ name: 'Public',
+ spec: ISB.InputSpec.of({
+ domain: ISB.Value.select({
+ name: 'Domain',
+ default: '',
+ values: Object.keys(this.domains() || {}).reduce<
+ Record
+ >(
+ (obj, domain) => ({
+ ...obj,
+ [domain]: domain,
+ }),
+ {},
+ ),
+ }),
+ subdomain: ISB.Value.text({
+ name: 'Subdomain',
+ description: 'Optionally enter a subdomain',
+ required: false,
+ default: null,
+ patterns: [], // @TODO subdomain pattern
+ }),
+ ...this.acmeSpec(true),
+ }),
+ },
+ private: {
+ name: 'Private',
+ spec: ISB.InputSpec.of({
+ fqdn: ISB.Value.text({
+ name: 'Domain',
+ description:
+ 'Enter a fully qualified domain name. For example, if you control domain.com, you could enter domain.com or subdomain.domain.com or another.subdomain.domain.com.',
+ required: true,
+ default: null,
+ patterns: [utils.Patterns.domain],
+ }),
+ ...this.acmeSpec(false),
+ }),
+ },
+ }),
+ }),
+ })
+
+ this.formDialog.open(FormComponent, {
+ label: 'Add domain',
+ data: {
+ spec: await configBuilderToSpec(addSpec),
+ buttons: [
+ {
+ text: 'Save',
+ handler: async (input: typeof addSpec._TYPE) => {
+ const loader = this.loader.open('Removing').subscribe()
+ const type = input.type.selection
+ const params = {
+ private: type === 'private',
+ fqdn:
+ type === 'public'
+ ? `${input.type.value.subdomain}.${input.type.value.domain}`
+ : input.type.value.fqdn,
+ acme:
+ input.type.value.authority === 'local'
+ ? null
+ : input.type.value.authority,
+ }
+ try {
+ if (this.interface.packageId()) {
+ await this.api.pkgAddDomain({
+ ...params,
+ package: this.interface.packageId(),
+ host: this.interface.value()?.addressInfo.hostId || '',
+ })
+ } else {
+ await this.api.osUiAddDomain(params)
+ }
+ return true
+ } catch (e: any) {
+ this.errorService.handleError(e)
+ return false
+ } finally {
+ loader.unsubscribe()
+ }
+ },
+ },
+ ],
+ },
+ })
+ }
+
+ private acmeSpec(isPublic: boolean) {
+ return {
+ authority: ISB.Value.select({
+ name: 'Certificate Authority',
+ description:
+ 'Select the Certificate Authority that will issue the SSL/TLS certificate for this domain',
+ values: this.acme()!,
+ default: isPublic ? '' : 'local',
+ }),
+ }
+ }
+}
diff --git a/web/projects/ui/src/app/routes/portal/components/interfaces/domain.component.ts b/web/projects/ui/src/app/routes/portal/components/interfaces/clearnet-domains/item.component.ts
similarity index 63%
rename from web/projects/ui/src/app/routes/portal/components/interfaces/domain.component.ts
rename to web/projects/ui/src/app/routes/portal/components/interfaces/clearnet-domains/item.component.ts
index fb4f27d8c..9528ed61d 100644
--- a/web/projects/ui/src/app/routes/portal/components/interfaces/domain.component.ts
+++ b/web/projects/ui/src/app/routes/portal/components/interfaces/clearnet-domains/item.component.ts
@@ -19,66 +19,33 @@ import {
import { TuiBadge } from '@taiga-ui/kit'
import { filter } from 'rxjs'
import { ApiService } from 'src/app/services/api/embassy-api.service'
-import { InterfaceComponent } from './interface.component'
-import { ClearnetDomain } from './interface.service'
+import { InterfaceComponent } from '../interface.component'
+import { ClearnetDomain } from '../interface.service'
@Component({
selector: 'tr[domain]',
template: `
{{ domain().fqdn }}
- {{ domain().authority || '-' }}
+ {{ domain().authority }}
@if (domain().public) {
- {{ 'Public' | i18n }}
+ {{ 'public' | i18n }}
} @else {
- {{ 'Private' | i18n }}
+ {{ 'private' | i18n }}
}
- {{ 'More' | i18n }}
-
-
-
- @if (domain().public) {
- {{ 'Make private' | i18n }}
- } @else {
- {{ 'Make public' | i18n }}
- }
-
-
- {{ 'Edit' | i18n }}
-
-
-
-
- {{ 'Delete' | i18n }}
-
-
-
+ {{ 'Delete' | i18n }}
`,
@@ -115,7 +82,7 @@ import { ClearnetDomain } from './interface.service'
TuiBadge,
],
})
-export class DomainComponent {
+export class InterfaceClearnetDomainsItemComponent {
private readonly dialog = inject(DialogService)
private readonly loader = inject(LoadingService)
private readonly errorService = inject(ErrorService)
@@ -124,12 +91,6 @@ export class DomainComponent {
readonly domain = input.required()
- open = false
-
- toggle() {}
-
- edit() {}
-
remove() {
this.dialog
.openConfirm({ label: 'Are you sure?', size: 's' })
diff --git a/web/projects/ui/src/app/routes/portal/components/interfaces/interface.component.ts b/web/projects/ui/src/app/routes/portal/components/interfaces/interface.component.ts
index a06a4d91a..86769f728 100644
--- a/web/projects/ui/src/app/routes/portal/components/interfaces/interface.component.ts
+++ b/web/projects/ui/src/app/routes/portal/components/interfaces/interface.component.ts
@@ -3,7 +3,7 @@ import { tuiButtonOptionsProvider } from '@taiga-ui/core'
import { MappedServiceInterface } from './interface.service'
import { InterfaceGatewaysComponent } from './gateways.component'
import { InterfaceTorDomainsComponent } from './tor-domains.component'
-import { InterfaceClearnetDomainsComponent } from './clearnet-domains.component'
+import { InterfaceClearnetDomainsComponent } from './clearnet-domains/clearnet-domains.component'
import { InterfaceAddressesComponent } from './addresses/addresses.component'
@Component({
diff --git a/web/projects/ui/src/app/routes/portal/components/interfaces/interface.service.ts b/web/projects/ui/src/app/routes/portal/components/interfaces/interface.service.ts
index 2fd09fd7b..a6ae68b49 100644
--- a/web/projects/ui/src/app/routes/portal/components/interfaces/interface.service.ts
+++ b/web/projects/ui/src/app/routes/portal/components/interfaces/interface.service.ts
@@ -3,7 +3,7 @@ import { T, utils } from '@start9labs/start-sdk'
import { ConfigService } from 'src/app/services/config.service'
import { toAuthorityName } from 'src/app/utils/acme'
import { GatewayPlus } from 'src/app/services/gateway.service'
-import { DialogService, i18nKey } from '@start9labs/shared'
+import { i18nKey } from '@start9labs/shared'
type AddressWithInfo = {
url: URL
@@ -134,7 +134,9 @@ function toDisplayAddress(
]
// Tor (HTTP)
} else {
- bullets.unshift('Ideal for anonymous, remote connectivity')
+ bullets.unshift(
+ 'Ideal for anonymous, censorship-resistant hosting and remote access',
+ )
type = `${type} (HTTP)`
}
// ** Not Tor **
@@ -143,7 +145,7 @@ function toDisplayAddress(
const gateway = gateways.find(g => g.id === info.gatewayId)!
gatewayName = gateway.ipInfo.name
- const gatewayIpv4 = gateway.ipInfo.subnets[0]
+ const gatewayIpv4 = gateway.ipv4[0]
const isWireguard = gateway.ipInfo.deviceType === 'wireguard'
const localIdeal = 'Ideal for local access'
@@ -158,7 +160,7 @@ function toDisplayAddress(
access = 'private'
bullets = [
localIdeal,
- 'Not recommended for VPN access. VPNs do not support ".local" domains without extra configuration',
+ 'Not recommended for VPN access. VPNs do not support ".local" domains without advanced configuration',
lanRequired,
rootCaRequired,
]
@@ -174,7 +176,7 @@ function toDisplayAddress(
]
if (!gateway.public) {
bullets.push(
- `Requires creating a port forwarding rule in gateway "${gatewayName}": ${port} -> ${info.hostname.value}:${port}`,
+ `Requires port forwarding in gateway "${gatewayName}": ${port} -> ${info.hostname.value}:${port}`,
)
}
} else {
@@ -203,8 +205,8 @@ function toDisplayAddress(
if (info.public) {
access = 'public'
bullets = [
- `Requires creating DNS records for "${domains[info.hostname.value]?.root}", as shown in System -> Domains`,
- `Requires creating a port forwarding rule in gateway "${gatewayName}": ${port} -> ${info.hostname.value}:${port === 443 ? 5443 : port}`,
+ `Requires DNS record(s) for ${domains[info.hostname.value]?.root}, as shown in System -> Domains`,
+ `Requires port forwarding in gateway "${gatewayName}": ${port} -> ${info.hostname.value}:${port === 443 ? 5443 : port}`,
]
if (domain.acme) {
bullets.unshift('Ideal for public access via the Internet')
@@ -218,7 +220,7 @@ function toDisplayAddress(
} else {
access = 'private'
const ipPortBad = 'when using IP addresses and ports is undesirable'
- const customDnsRequired = `Requires creating custom DNS records for ${info.hostname.value} that resolve to ${gatewayIpv4}`
+ const customDnsRequired = `Requires DNS record for ${info.hostname.value} that resolve to ${gatewayIpv4}`
if (isWireguard) {
bullets = [
`${vpnAccess} StartTunnel (or similar) ${ipPortBad}`,
@@ -463,18 +465,9 @@ export type InterfaceGateway = GatewayPlus & {
enabled: boolean
}
-// export type InterfaceGateway = {
-// id: string
-// name: string
-// enabled: boolean
-// public: boolean
-// type: T.NetworkInterfaceType
-// lanIpv4: string | null
-// }
-
export type ClearnetDomain = {
fqdn: string
- authority: string | null
+ authority: string
public: boolean
}
diff --git a/web/projects/ui/src/app/routes/portal/routes/sideload/sideload.component.ts b/web/projects/ui/src/app/routes/portal/routes/sideload/sideload.component.ts
index 091fabd3d..8673e7c95 100644
--- a/web/projects/ui/src/app/routes/portal/routes/sideload/sideload.component.ts
+++ b/web/projects/ui/src/app/routes/portal/routes/sideload/sideload.component.ts
@@ -57,10 +57,7 @@ import { MarketplacePkgSideload, validateS9pk } from './sideload.utils'
{{ 'Upload .s9pk package file' | i18n }}
@if (isTor) {
- {{
- 'Warning: package upload will be slow over Tor. Switch to local for a better experience.'
- | i18n
- }}
+ {{ 'Warning: package upload will be slow over Tor.' | i18n }}
}
{{ 'Upload' | i18n }}
diff --git a/web/projects/ui/src/app/routes/portal/routes/system/routes/domains/dns.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/routes/domains/dns.component.ts
index 2c8175c3d..12bde76db 100644
--- a/web/projects/ui/src/app/routes/portal/routes/system/routes/domains/dns.component.ts
+++ b/web/projects/ui/src/app/routes/portal/routes/system/routes/domains/dns.component.ts
@@ -1,55 +1,57 @@
-import { ChangeDetectionStrategy, Component, inject } from '@angular/core'
-import { ErrorService, i18nPipe, LoadingService } from '@start9labs/shared'
+import {
+ ChangeDetectionStrategy,
+ Component,
+ computed,
+ inject,
+} from '@angular/core'
+import {
+ DialogService,
+ ErrorService,
+ i18nKey,
+ i18nPipe,
+ LoadingService,
+} from '@start9labs/shared'
import { TuiButton, TuiDialogContext } from '@taiga-ui/core'
import { ApiService } from 'src/app/services/api/embassy-api.service'
import { MappedDomain } from './domain.service'
import { injectContext, PolymorpheusComponent } from '@taiga-ui/polymorpheus'
import { TableComponent } from 'src/app/routes/portal/components/table.component'
+import { parse } from 'tldts'
+
+// @TODO translations
@Component({
selector: 'dns',
template: `
-
- {{ $any('Using IP') | i18n }}
+ @let wanIp = context.data.gateway.ipInfo?.wanIp || ('Error' | i18n);
- @let subdomain = context.data.subdomain;
- @let wanIp = context.data.gateway.ipInfo?.wanIp || ('Error' | i18n);
+
+
+ A
+ {{ subdomain() || '@' }}
+ {{ wanIp }}
+
+
+
+ A
+ {{ subdomain() ? '*.' + subdomain() : '*' }}
+ {{ wanIp }}
+
+
-
-
- A
- {{ subdomain || '@' }}
- {{ wanIp }}
-
-
-
- A
- {{ subdomain ? '*.' + subdomain : '*' }}
- {{ wanIp }}
-
-
-
-
-
- @if (context.data.gateway.ipInfo?.deviceType !== 'wireguard') {
-
- {{ $any('Using Dynamic DNS') | i18n }}
-
-
- ALIAS
- {{ subdomain || '@' }}
- [Dynamic DNS Address]
-
-
-
- ALIAS
- {{ subdomain ? '*.' + subdomain : '*' }}
- [Dynamic DNS Address]
-
-
-
-
- }
+
+
`,
+ styles: `
+ :host {
+ max-width: 50rem;
+ }
+ `,
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [
TuiButton,
diff --git a/web/projects/ui/src/app/routes/portal/routes/system/routes/domains/item.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/routes/domains/item.component.ts
index 7023f0b20..13f07c06e 100644
--- a/web/projects/ui/src/app/routes/portal/routes/system/routes/domains/item.component.ts
+++ b/web/projects/ui/src/app/routes/portal/routes/system/routes/domains/item.component.ts
@@ -18,7 +18,7 @@ import { DomainService, MappedDomain } from './domain.service'
template: `
@if (domain(); as domain) {
{{ domain.fqdn }}
- {{ domain.gateway.ipInfo?.name || '-' }}
+ {{ domain.gateway.ipInfo?.name || '-' }}
- {{ 'Edit' | i18n }}
+ {{ 'View DNS' | i18n }}
- {{ 'Manage DNS' | i18n }}
+ {{ 'Edit' | i18n }}
diff --git a/web/projects/ui/src/app/routes/portal/routes/system/routes/domains/table.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/routes/domains/table.component.ts
index 307cb4e07..a67389ccd 100644
--- a/web/projects/ui/src/app/routes/portal/routes/system/routes/domains/table.component.ts
+++ b/web/projects/ui/src/app/routes/portal/routes/system/routes/domains/table.component.ts
@@ -14,7 +14,7 @@ import { DomainService } from './domain.service'
} @empty {
-
+
@if (domainService.data()?.domains) {
{{ 'No domains' | i18n }}
diff --git a/web/projects/ui/src/app/routes/portal/routes/system/routes/gateways/item.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/routes/gateways/item.component.ts
index 41ccfe97c..3968c55b5 100644
--- a/web/projects/ui/src/app/routes/portal/routes/system/routes/gateways/item.component.ts
+++ b/web/projects/ui/src/app/routes/portal/routes/system/routes/gateways/item.component.ts
@@ -30,9 +30,14 @@ import { GatewayPlus } from 'src/app/services/gateway.service'
template: `
@if (gateway(); as gateway) {
{{ gateway.ipInfo.name }}
- {{ gateway.ipInfo.deviceType || '-' }}
-
- {{ gateway.public ? ('Public' | i18n) : ('Private' | i18n) }}
+
+ @if (gateway.ipInfo.deviceType; as type) {
+ {{ type }} ({{
+ gateway.public ? ('public' | i18n) : ('private' | i18n)
+ }})
+ } @else {
+ -
+ }
{{ gateway.ipv4.join(', ') }}
+
@for (gateway of gatewayService.gateways(); track $index) {
} @empty {
diff --git a/web/projects/ui/src/app/routes/portal/routes/updates/item.component.ts b/web/projects/ui/src/app/routes/portal/routes/updates/item.component.ts
index 401be58af..d1daea3c5 100644
--- a/web/projects/ui/src/app/routes/portal/routes/updates/item.component.ts
+++ b/web/projects/ui/src/app/routes/portal/routes/updates/item.component.ts
@@ -70,36 +70,34 @@ import UpdatesComponent from './updates.component'
{{ item().gitHash }}
{{ item().s9pk.publishedAt | date }}
-
+
+ {{ 'Show more' | i18n }}
+
+ @if (local().stateInfo.state === 'updating') {
+
+ } @else {
- {{ 'Show more' | i18n }}
+ {{ error() ? ('Retry' | i18n) : ('Update' | i18n) }}
- @if (local().stateInfo.state === 'updating') {
-
- } @else {
-
- {{ error() ? ('Retry' | i18n) : ('Update' | i18n) }}
-
- }
-
+ }