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: `
{{ 'Addresses' | i18n }}
- +
@for (address of addresses()?.common; track $index) { } @empty { @@ -27,7 +27,7 @@ import { InterfaceAddressItemComponent } from './item.component' } @else { @for (_ of [0, 1]; track $index) { - 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' + @@ -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: ` -
- {{ 'Clearnet Domains' | i18n }} - - {{ 'Documentation' | i18n }} - - -
-
+
{{ 'Loading' | i18n }}
{{ address.type }} + @if (address.access === 'public') { + + {{ 'public' | i18n }} + + } @else if (address.access === 'private') { + + {{ 'private' | i18n }} + + } @else { + - + } + {{ address.gatewayName || '-' }}
- @for (domain of clearnetDomains(); track $index) { - - } @empty { - @if (clearnetDomains()) { - - - - } @else { - @for (_ of [0, 1]; track $index) { - - - - } - } - } -
- - {{ 'No clearnet domains' | i18n }} - -
-
{{ '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: ` +
+ {{ 'Clearnet Domains' | i18n }} + + {{ 'Documentation' | i18n }} + + +
+ + @for (domain of clearnetDomains(); track $index) { + + } @empty { + @if (clearnetDomains()) { + + + + } @else { + @for (_ of [0, 1]; track $index) { + + + + } + } + } +
+ + {{ 'No clearnet domains' | i18n }} + +
+
{{ '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 }} } - - - - - - + {{ '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 }}

} 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]
-
- } + +