diff --git a/web/projects/shared/src/i18n/dictionaries/de.ts b/web/projects/shared/src/i18n/dictionaries/de.ts index 0ec4883d0..30067076e 100644 --- a/web/projects/shared/src/i18n/dictionaries/de.ts +++ b/web/projects/shared/src/i18n/dictionaries/de.ts @@ -15,7 +15,6 @@ export default { 12: 'Aktive Sitzungen', 13: 'Passwort ändern', 14: 'Allgemeine Einstellungen', - 15: 'Verwalten Sie Ihre Gesamteinrichtung und Einstellungen', 16: 'Browser-Tab Titel', 17: 'Sprache', 18: 'Festplattenreparatur', @@ -91,8 +90,6 @@ export default { 88: 'Aktionen', 89: 'nicht empfohlen', 90: 'Root-CA ist vertrauenswürdig!', - 91: 'Fügen Sie eine Clearnet-Adresse hinzu, um diese Oberfläche im Internet verfügbar zu machen. Clearnet-Adressen sind vollständig öffentlich und nicht anonym.', - 92: 'Mehr erfahren', 93: 'Öffentlich machen', 94: 'Privat machen', 95: 'Keine öffentlichen Adressen', @@ -105,16 +102,12 @@ export default { 102: 'Verlassen', 103: 'Sind Sie sicher?', 104: 'Domain auswählen', - 105: 'Lokal', - 106: 'Lokale Adressen sind nur von Geräten erreichbar, die direkt oder über VPN mit demselben LAN wie Ihr Server verbunden sind.', - 107: 'Mehr erfahren', 108: 'Öffentlich', 109: 'Privat', - 110: 'Fügen Sie eine Onion-Adresse hinzu, um dieses Interface anonym im Darknet verfügbar zu machen. Onion-Adressen sind nur über das Tor-Netzwerk erreichbar.', - 111: 'Keine Onion-Adressen', - 112: 'Neue onion-adresse', + 111: 'Keine Onion-Domains', + 112: 'Neue Onion-Domain', 113: 'Privater Schlüssel (optional)', - 114: 'Optional können Sie einen base64-codierten ed25519-Schlüssel angeben, um die Tor V3 (.onion)-Adresse zu generieren. Wenn nicht angegeben, wird ein zufälliger Schlüssel erstellt.', + 114: '', 115: 'Verarbeite 10.000 Logs', 116: 'Ältere Logs werden geladen', 117: 'Warten auf Netzwerkverbindung', @@ -241,7 +234,7 @@ export default { 240: 'Name', 241: 'Status', 242: 'Öffnen', - 243: 'Schnittstellen', + 243: '', 244: 'Hosting', 245: 'Installation läuft', 246: 'Siehe unten', @@ -293,7 +286,6 @@ export default { 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', - 299: 'Das Hinzufügen einer Domain zu StartOS bedeutet, dass du sie und ihre Subdomains verwenden kannst, um Service-Oberflächen im öffentlichen Internet zu hosten.', 300: 'Anleitung anzeigen', 303: 'Kontakt', 304: 'Bearbeiten', @@ -324,8 +316,6 @@ export default { 329: 'Hostname', 330: 'Pfad', 331: 'URL', - 332: 'Netzwerkschnittstelle', - 333: 'Protokoll', 334: 'Modell', 335: 'User-Agent', 336: 'Plattform', @@ -372,7 +362,6 @@ export default { 377: 'StartOS-Sicherungen erkannt', 378: 'Keine StartOS-Sicherungen erkannt', 379: 'StartOS-Version', - 380: 'Die Verbindung zu einem externen SMTP-Server ermöglicht es StartOS und seinen Diensten, E-Mails zu senden.', 381: 'SMTP-Zugangsdaten', 382: 'Test-E-Mail senden', 383: 'Senden', @@ -380,7 +369,6 @@ export default { 385: 'Eine Test-E-Mail wurde gesendet an', 386: 'Prüfen Sie Ihren Spam-Ordner und markieren Sie die Nachricht als „kein Spam“.', 387: 'Die Web-Benutzeroberfläche Ihres StartOS-Servers, zugänglich über jeden Browser.', - 388: 'Ändern Sie Ihr Master-Passwort für StartOS.', 389: 'Sie benötigen weiterhin Ihr aktuelles Passwort, um bestehende Sicherungen zu entschlüsseln!', 390: 'Neue Passwörter stimmen nicht überein', 391: 'Neues Passwort muss mindestens 12 Zeichen lang sein', @@ -390,7 +378,6 @@ export default { 395: 'Aktuelles Passwort', 396: 'Neues Passwort', 397: 'Neues Passwort erneut eingeben', - 398: 'Eine Sitzung ist ein Gerät, das aktuell bei StartOS angemeldet ist. Beenden Sie Sitzungen, die Sie nicht kennen oder nicht mehr verwenden.', 399: 'Aktuelle Sitzung', 400: 'Weitere Sitzungen', 401: 'Ausgewählte beenden', @@ -497,7 +484,6 @@ export default { 502: 'souveränes computing', 503: 'Passen Sie den Namen an, der in Ihrem Browser-Tab erscheint', 504: 'Verwalten', - 505: 'Möchten Sie diese Adresse wirklich löschen?', 506: '"Weiches Deinstallieren" entfernt den Dienst aus StartOS, behält jedoch die Daten bei.', 507: 'Keine gespeicherten Anbieter', 508: 'Kiosk-Modus', @@ -511,22 +497,20 @@ export default { 516: 'Empfohlen', 517: 'Möchten Sie diese Aufgabe wirklich verwerfen?', 518: 'Verwerfen', - 519: 'Um Clearnet-Domains zu veröffentlichen, musst du oben auf „Öffentlich machen“ klicken.', 520: 'Update verfügbar', 521: 'Um das Problem zu beheben, siehe', 522: 'SDK Version', 523: 'Sicherungsbericht', 524: 'Ausgewählte löschen', - 525: 'Keine schlüssel', - 526: 'Öffentlichen SSH-Schlüssel hinzufügen', - 527: 'Standardmäßig kannst du dich per SSH von jedem Gerät aus mit deinem Server verbinden, indem du dein Master-Passwort verwendest. Optional kannst du SSH-öffentliche Schlüssel hinzufügen, um bestimmten Geräten den Zugriff ohne Passworteingabe zu ermöglichen.', + 525: '', + 526: '', + 527: '', 528: 'Quellcode', 529: 'Upstream-Dienst', 530: 'StartOS-Paket', 531: 'Fehler beim Initialisieren des Servers', 532: 'Abgeschlossen', 533: 'Gateways', - 534: 'Gateways verbinden Ihren Server mit dem Internet. Sie verarbeiten ausgehenden Datenverkehr und erlauben unter bestimmten Bedingungen auch eingehenden Verkehr.', 535: 'Gateway hinzufügen', 536: 'Umbenennen', 537: 'Zugriff', @@ -534,10 +518,15 @@ export default { 539: 'Zertifizierungsstellen', 540: 'Domain', 541: 'Gateway', - 542: 'Standard-Zertifizierungsstelle', 543: 'Zertifizierungsstelle', 544: 'Domain bearbeiten', 545: 'Keine Domains', 546: 'Anbieter', 547: 'DNS verwalten', + 548: '', + 549: '', + 550: '', + 551: '', + 552: '', + 553: '', } satisfies i18n diff --git a/web/projects/shared/src/i18n/dictionaries/en.ts b/web/projects/shared/src/i18n/dictionaries/en.ts index 4e7847cba..0d554bf34 100644 --- a/web/projects/shared/src/i18n/dictionaries/en.ts +++ b/web/projects/shared/src/i18n/dictionaries/en.ts @@ -14,7 +14,6 @@ export const ENGLISH = { 'Active Sessions': 12, 'Change Password': 13, 'General Settings': 14, - 'Manage your overall setup and preferences': 15, 'Browser tab title': 16, 'Language': 17, 'Disk Repair': 18, @@ -90,8 +89,6 @@ export const ENGLISH = { 'Actions': 88, // as in, actions available to the user 'not recommended': 89, 'Root CA Trusted!': 90, - 'Add a clearnet address to expose this interface on the Internet. Clearnet addresses are fully public and not anonymous.': 91, - 'Learn more': 92, 'Make public': 93, 'Make private': 94, 'No public addresses': 95, @@ -104,16 +101,12 @@ export const ENGLISH = { 'Leave': 102, 'Are you sure?': 103, 'Select domain': 104, - 'Local': 105, - 'Local addresses can only be accessed by devices connected to the same LAN as your server, either directly or using a VPN.': 106, - 'Learn More': 107, 'Public': 108, 'Private': 109, - 'Add an onion address to anonymously expose this interface on the darknet. Onion addresses can only be reached over the Tor network.': 110, - 'No onion addresses': 111, - 'New onion address': 112, + 'No Tor domains': 111, + 'New Tor domain': 112, 'Private Key (optional)': 113, - 'Optionally provide a base64-encoded ed25519 private key for generating the Tor V3 (.onion) address. If not provided, a random key will be generated and used.': 114, + 'Optionally provide a base64-encoded ed25519 private key for generating the Tor V3 (.onion) domain. If not provided, a random key will be generated.': 114, 'Processing 10,000 logs': 115, 'Loading older logs': 116, 'Waiting for network connectivity': 117, @@ -240,7 +233,7 @@ export const ENGLISH = { 'Name': 240, 'Status': 241, 'Open': 242, // verb - 'Interfaces': 243, // as in user interface or application program interface + 'Service Interfaces': 243, // as in, a UI or API for an application 'Hosting': 244, 'Installing': 245, 'See below': 246, @@ -292,7 +285,6 @@ export const ENGLISH = { '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, - 'Adding a domain to StartOS means you can use it and its subdomains to host service interfaces on the public Internet.': 299, 'View instructions': 300, 'Contact': 303, // as in, "contact us" 'Edit': 304, @@ -323,8 +315,6 @@ export const ENGLISH = { 'Hostname': 329, 'Path': 330, // as in, a URL path 'URL': 331, - 'Network Interface': 332, - 'Protocol': 333, // as in, http protocol 'Model': 334, // as in, a product model 'User Agent': 335, 'Platform': 336, // as in, OS platform, such as iOS, Android, Linux, etc @@ -371,7 +361,6 @@ export const ENGLISH = { 'StartOS backups detected': 377, 'No StartOS backups detected': 378, 'StartOS Version': 379, - 'Connecting an external SMTP server allows StartOS and your installed services to send you emails.': 380, 'SMTP Credentials': 381, 'Send test email': 382, 'Send': 383, @@ -379,7 +368,6 @@ export const ENGLISH = { 'A test email has been sent to': 385, 'Check your spam folder and mark as not spam.': 386, 'The web user interface for your StartOS server, accessible from any browser.': 387, - 'Change your StartOS master password.': 388, 'You will still need your current password to decrypt existing backups!': 389, 'New passwords do not match': 390, 'New password must be 12 characters or greater': 391, @@ -389,7 +377,6 @@ export const ENGLISH = { 'Current Password': 395, 'New Password': 396, 'Retype New Password': 397, - 'A session is a device that is currently logged into StartOS. For best security, terminate sessions you do not recognize or no longer use.': 398, 'Current session': 399, 'Other sessions': 400, 'Terminate selected': 401, @@ -496,7 +483,6 @@ export const ENGLISH = { 'sovereign computing': 502, 'Customize the name appearing in your browser tab': 503, 'Manage': 504, // as in, administer - 'Are you sure you want to delete this address?': 505, // this address referes to a domain or URL '"Soft uninstall" will remove the service from StartOS but preserve its data.': 506, 'No saved providers': 507, 'Kiosk Mode': 508, // an OS mode that permits attaching a monitor to the computer @@ -510,22 +496,20 @@ export const ENGLISH = { 'Recommended': 516, // as in, we recommend this 'Are you sure you want to dismiss this task?': 517, 'Dismiss': 518, // as in, dismiss or delete a task - 'To publish clearnet domains, you must click "Make Public", above.': 519, 'Update available': 520, 'To resolve the issue, refer to': 521, 'SDK Version': 522, 'Backup Report': 523, 'Delete selected': 524, - 'No keys': 525, - 'Add SSH Public Key': 526, - 'By default, you can SSH into your server from any device using your master password. Optionally add SSH public keys to grant specific devices access without needing to enter a password.': 527, + 'No SSH keys': 525, + 'Add SSH key': 526, + 'SSH Keys': 527, 'Source Code': 528, 'Upstream service': 529, // as in, the URL of the source code for the original software 'StartOS package': 530, // as in, the URL of the source code for the StartOS package 'Error initializing server': 531, 'Finished': 532, // an in, complete 'Gateways': 533, // as in, a device or software that connects two different networks - 'Gateways connect your server to the Internet. They process outbound traffic, and under certain conditions, they also permit inbound traffic.': 534, 'Add gateway': 535, // as in, add a new network gateway to StartOS 'Rename': 536, 'Access': 537, // as in, public or private access, almost "permission" @@ -533,10 +517,15 @@ export const ENGLISH = { 'Certificate Authorities': 539, 'Domain': 540, // as in, an internat domain name 'Gateway': 541, // as in, a device or software that connects two different networks - 'Default Certificate Authority': 542, 'Certificate Authority': 543, 'Edit domain': 544, 'No domains': 545, 'Provider': 546, 'Manage DNS': 547, + 'Clearnet Domains': 548, + 'No clearnet domains': 549, + 'Addresses': 550, + 'Common': 551, + 'Uncommon': 552, + 'No addresses': 553, } as const diff --git a/web/projects/shared/src/i18n/dictionaries/es.ts b/web/projects/shared/src/i18n/dictionaries/es.ts index 582c7389a..2e723c04f 100644 --- a/web/projects/shared/src/i18n/dictionaries/es.ts +++ b/web/projects/shared/src/i18n/dictionaries/es.ts @@ -15,7 +15,6 @@ export default { 12: 'Sesiones activas', 13: 'Cambiar contraseña', 14: 'Configuración general', - 15: 'Administra tu configuración y preferencias generales', 16: 'Título de la pestaña del navegador', 17: 'Idioma', 18: 'Reparación de disco', @@ -91,8 +90,6 @@ export default { 88: 'Acciones', 89: 'no recomendado', 90: '¡CA raíz confiable!', - 91: 'Agrega una dirección clearnet para exponer esta interfaz en Internet. Las direcciones clearnet son totalmente públicas y no anónimas.', - 92: 'Saber más', 93: 'Hacer público', 94: 'Hacer privado', 95: 'Sin direcciones públicas', @@ -105,16 +102,12 @@ export default { 102: 'Salir', 103: '¿Estás seguro?', 104: 'Seleccionar dominio', - 105: 'Local', - 106: 'Las direcciones locales solo pueden ser accedidas por dispositivos conectados a la misma red local que tu servidor, ya sea directamente o mediante una VPN.', - 107: 'Más información', 108: 'Público', 109: 'Privado', - 110: 'Agrega una dirección onion para exponer esta interfaz de forma anónima en la darknet. Las direcciones onion solo se pueden acceder a través de la red Tor.', - 111: 'Sin direcciones onion', - 112: 'Nueva dirección onion', + 111: 'Sin dominios onion', + 112: 'Nueva dominio onion', 113: 'Clave privada (opcional)', - 114: 'Opcionalmente proporciona una clave privada ed25519 codificada en base64 para generar la dirección Tor V3 (.onion). Si no se proporciona, se generará una clave aleatoria.', + 114: '', 115: 'Procesando 10,000 registros', 116: 'Cargando registros anteriores', 117: 'Esperando conectividad de red', @@ -241,7 +234,7 @@ export default { 240: 'Nombre', 241: 'Estado', 242: 'Abrir', - 243: 'Interfaces', + 243: '', 244: 'Alojamiento', 245: 'Instalando', 246: 'Ver abajo', @@ -293,7 +286,6 @@ export default { 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', - 299: 'Agregar un dominio a StartOS significa que puedes usarlo y sus subdominios para alojar interfaces de servicios en Internet público.', 300: 'Ver instrucciones', 303: 'Contacto', 304: 'Editar', @@ -324,8 +316,6 @@ export default { 329: 'Nombre del host', 330: 'Ruta', 331: 'URL', - 332: 'Interfaz de red', - 333: 'Protocolo', 334: 'Modelo', 335: 'Agente de usuario', 336: 'Plataforma', @@ -372,7 +362,6 @@ export default { 377: 'Copias de seguridad de StartOS detectadas', 378: 'No se detectaron copias de seguridad de StartOS', 379: 'Versión de StartOS', - 380: 'Conectar un servidor SMTP externo permite que StartOS y tus servicios instalados te envíen correos electrónicos.', 381: 'Credenciales SMTP', 382: 'Enviar correo de prueba', 383: 'Enviar', @@ -380,7 +369,6 @@ export default { 385: 'Se ha enviado un correo de prueba a', 386: 'Revisa tu carpeta de spam y márcalo como no spam.', 387: 'La interfaz web de tu servidor StartOS, accesible desde cualquier navegador.', - 388: 'Cambia tu contraseña maestra de StartOS.', 389: '¡Aún necesitarás tu contraseña actual para descifrar copias de seguridad existentes!', 390: 'Las nuevas contraseñas no coinciden', 391: 'La nueva contraseña debe tener al menos 12 caracteres', @@ -390,7 +378,6 @@ export default { 395: 'Contraseña actual', 396: 'Nueva contraseña', 397: 'Reingresa nueva contraseña', - 398: 'Una sesión es un dispositivo que actualmente ha iniciado sesión en StartOS. Para mayor seguridad, cierra las sesiones que no reconozcas o que ya no uses.', 399: 'Sesión actual', 400: 'Otras sesiones', 401: 'Terminar seleccionados', @@ -497,7 +484,6 @@ export default { 502: 'computación soberana', 503: 'Personaliza el nombre que aparece en la pestaña de tu navegador', 504: 'Administrar', - 505: '¿Estás seguro de que deseas eliminar esta dirección?', 506: '"Desinstalación suave" eliminará el servicio de StartOS pero conservará sus datos.', 507: 'No hay proveedores guardados', 508: 'Modo quiosco', @@ -511,22 +497,20 @@ export default { 516: 'Recomendado', 517: '¿Estás seguro de que deseas descartar esta tarea?', 518: 'Descartar', - 519: 'Para publicar dominios en clearnet, debes hacer clic en "Hacer público" arriba.', 520: 'Actualización disponible', 521: 'Para resolver el problema, consulta', 522: 'Versión de SDK', 523: 'Informe de respaldo', 524: 'Eliminar seleccionado', - 525: 'Sin llaves', - 526: 'Agregar clave pública SSH', - 527: 'De forma predeterminada, puedes conectarte por SSH a tu servidor desde cualquier dispositivo usando tu contraseña maestra. Opcionalmente, añade claves públicas SSH para otorgar acceso a dispositivos específicos sin necesidad de ingresar una contraseña.', + 525: '', + 526: '', + 527: '', 528: 'Código fuente', 529: 'Servicio original', 530: 'Paquete StartOS', 531: 'Error al inicializar el servidor', 532: 'Finalizado', 533: 'Puertas de enlace', - 534: 'Las puertas de enlace conectan su servidor a Internet. Procesan el tráfico saliente y, en ciertas condiciones, también permiten tráfico entrante.', 535: 'Agregar puerta de enlace', 536: 'Renombrar', 537: 'Acceso', @@ -534,10 +518,15 @@ export default { 539: 'Autoridades certificadoras', 540: 'Dominio', 541: 'Puerta de enlace', - 542: 'Autoridad certificadora predeterminada', 543: 'Autoridad certificadora', 544: 'Editar dominio', 545: 'Sin dominios', 546: 'Proveedor', 547: 'Administrar DNS', + 548: '', + 549: '', + 550: '', + 551: '', + 552: '', + 553: '', } satisfies i18n diff --git a/web/projects/shared/src/i18n/dictionaries/fr.ts b/web/projects/shared/src/i18n/dictionaries/fr.ts index f304788c1..0056431f1 100644 --- a/web/projects/shared/src/i18n/dictionaries/fr.ts +++ b/web/projects/shared/src/i18n/dictionaries/fr.ts @@ -15,7 +15,6 @@ export default { 12: 'Sessions actives', 13: 'Changer le mot de passe', 14: 'Paramètres généraux', - 15: 'Gérez votre configuration et vos préférences globales', 16: 'Titre de l’onglet du navigateur', 17: 'Langue', 18: 'Réparation du disque', @@ -91,8 +90,6 @@ export default { 88: 'Actions', 89: 'non recommandé', 90: 'Certificat racine approuvé !', - 91: 'Ajoutez une addresse clearnet pour exposer cette interface sur Internet. Les adresses clearnet sont entièrement publiques et non anonymes.', - 92: 'En savoir plus', 93: 'Rendre public', 94: 'Rendre privé', 95: 'Aucune adresse publique', @@ -105,16 +102,12 @@ export default { 102: 'Quitter', 103: 'Êtes-vous sûr ?', 104: 'Sélectionner un domaine', - 105: 'Local', - 106: 'Les adresses locales ne sont accessibles qu’aux appareils connectés au même réseau local (LAN) que votre serveur, directement ou via un VPN.', - 107: 'En savoir plus', 108: 'Public', 109: 'Privé', - 110: 'Ajoutez une adresse onion (tor) pour exposer cette interface anonymement sur le darknet. Les adresses onion sont accessibles uniquement via le réseau Tor.', - 111: 'Aucune adresse onion', - 112: 'Nouvelle adresse onion', + 111: 'Aucune domaine onion', + 112: 'Nouvelle domaine onion', 113: 'Clé privée (optionnel)', - 114: 'Vous pouvez fournir une clé privée ed25519 encodée en base64 pour générer l’adresse Tor V3 (.onion). Sinon, une clé aléatoire sera générée et utilisée.', + 114: '', 115: 'Traitement de 10 000 journaux', 116: 'Chargement des journaux plus anciens', 117: 'En attente d’une connexion réseau', @@ -241,7 +234,7 @@ export default { 240: 'Nom', 241: 'Statut', 242: 'Ouvrir', - 243: 'Interfaces', + 243: '', 244: 'Hébergement', 245: 'Installation', 246: 'Voir ci-dessous', @@ -293,7 +286,6 @@ export default { 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', - 299: 'Ajouter un domaine à StartOS signifie que vous pouvez l’utiliser, ainsi que ses sous-domaines, pour héberger des interfaces de services sur Internet public.', 300: 'Voir les instructions', 303: 'Contact', 304: 'Modifier', @@ -324,8 +316,6 @@ export default { 329: 'Nom d’hôte', 330: 'Chemin', 331: 'URL', - 332: 'Interface réseau', - 333: 'Protocole', 334: 'Modèle', 335: 'Agent utilisateur', 336: 'Plateforme', @@ -372,7 +362,6 @@ export default { 377: 'Sauvegardes StartOS détectées', 378: 'Aucune sauvegarde StartOS détectée', 379: 'Version de StartOS', - 380: 'Connecter un serveur SMTP externe permet à StartOS et à vos services installés de vous envoyer des emails.', 381: 'Identifiants SMTP', 382: 'Envoyer un email de test', 383: 'Envoyer', @@ -380,7 +369,6 @@ export default { 385: 'Un email de test a été envoyé à', 386: 'Vérifiez votre dossier spam et marquez-le comme non spam.', 387: 'L’interface web de votre serveur StartOS, accessible depuis n’importe quel navigateur.', - 388: 'Changez le mot de passe maître de StartOS.', 389: 'Vous aurez toujours besoin de votre mot de passe actuel pour déchiffrer les sauvegardes existantes !', 390: 'Les nouveaux mots de passe ne correspondent pas', 391: 'Le nouveau mot de passe doit comporter au moins 12 caractères', @@ -390,7 +378,6 @@ export default { 395: 'Mot de passe actuel', 396: 'Nouveau mot de passe', 397: 'Retapez le nouveau mot de passe', - 398: 'Une session correspond à un appareil actuellement connecté à StartOS. Pour plus de sécurité, terminez les sessions que vous ne reconnaissez pas ou que vous n’utilisez plus.', 399: 'Session en cours', 400: 'Autres sessions', 401: 'Terminer les sessions séléctionnées', @@ -497,7 +484,6 @@ export default { 502: 'informatique souveraine', 503: 'Personnalisez le nom qui apparaît dans l’onglet de votre navigateur', 504: 'Gérer', - 505: 'Êtes-vous sûr de vouloir supprimer cette adresse ?', 506: '« Désinstallation douce » supprimera le service de StartOS tout en conservant ses données.', 507: 'Aucun fournisseur enregistré', 508: 'Mode kiosque', @@ -511,22 +497,20 @@ export default { 516: 'Recommandé', 517: 'Êtes-vous sûr de vouloir ignorer cette tâche ?', 518: 'Ignorer', - 519: 'Pour publier des domaines clearnet, vous devez cliquer sur « Rendre public » ci-dessus.', 520: 'Mise à jour disponible', 521: 'Pour résoudre le problème, consultez', 522: 'Version de SDK', 523: 'Rapport de sauvegarde', 524: 'Supprimer la sélection', - 525: 'Pas de clés', - 526: 'Ajouter une clé publique SSH', - 527: 'Par défaut, vous pouvez accéder à votre serveur en SSH depuis n’importe quel appareil en utilisant votre mot de passe maître. Vous pouvez également ajouter des clés publiques SSH pour accorder l’accès à certains appareils sans avoir à saisir de mot de passe.', + 525: '', + 526: '', + 527: '', 528: 'Code source', 529: 'Service en amont', 530: 'Paquet StartOS', 531: "Erreur lors de l'initialisation du serveur", 532: 'Terminé', 533: 'Passerelles', - 534: 'Les passerelles connectent votre serveur à Internet. Elles traitent le trafic sortant et, dans certaines conditions, autorisent également le trafic entrant.', 535: 'Ajouter une passerelle', 536: 'Renommer', 537: 'Accès', @@ -534,10 +518,15 @@ export default { 539: 'Autorités de certification', 540: 'Domaine', 541: 'Passerelle', - 542: 'Autorité de certification par défaut', 543: 'Autorité de certification', 544: 'Modifier le domaine', 545: 'Aucun domaine', 546: 'Fournisseur', 547: 'Gérer le DNS', + 548: '', + 549: '', + 550: '', + 551: '', + 552: '', + 553: '', } satisfies i18n diff --git a/web/projects/shared/src/i18n/dictionaries/pl.ts b/web/projects/shared/src/i18n/dictionaries/pl.ts index fdd54bb55..2e6415a23 100644 --- a/web/projects/shared/src/i18n/dictionaries/pl.ts +++ b/web/projects/shared/src/i18n/dictionaries/pl.ts @@ -15,7 +15,6 @@ export default { 12: 'Aktywne sesje', 13: 'Zmień hasło', 14: 'Ustawienia ogólne', - 15: 'Zarządzaj ustawieniami i preferencjami systemu', 16: 'Tytuł karty przeglądarki', 17: 'Język', 18: 'Naprawa dysku', @@ -91,8 +90,6 @@ export default { 88: 'Akcje', 89: 'niezalecane', 90: 'Główny certyfikat CA zaufany!', - 91: 'Dodaj adres clearnet, aby udostępnić ten interfejs w Internecie. Adresy clearnet są w pełni publiczne i nie zapewniają anonimowości.', - 92: 'Dowiedz się więcej', 93: 'Upublicznij', 94: 'Ukryj', 95: 'Brak publicznych adresów', @@ -105,16 +102,12 @@ export default { 102: 'Opuść', 103: 'Czy jesteś pewien?', 104: 'Wybierz domenę', - 105: 'Lokalne', - 106: 'Adresy lokalne są dostępne tylko dla urządzeń podłączonych do tej samej sieci LAN co twój serwer, bezpośrednio lub przez VPN.', - 107: 'Dowiedz się więcej', 108: 'Publiczny', 109: 'Prywatny', - 110: 'Dodaj adres onion, aby anonimowo udostępnić ten interfejs w sieci Tor. Adresy onion są dostępne tylko przez sieć Tor.', - 111: 'Brak adresów onion', - 112: 'Nowy adres onion', + 111: 'Brak domeny onion', + 112: 'Nowy domenę onion', 113: 'Klucz prywatny (opcjonalnie)', - 114: 'Opcjonalnie podaj klucz prywatny ed25519 zakodowany w base64, aby wygenerować adres Tor V3 (.onion). Jeśli nie zostanie podany, zostanie wygenerowany i użyty losowy klucz.', + 114: '', 115: 'Przetwarzanie 10 000 logów', 116: 'Ładowanie starszych logów', 117: 'Oczekiwanie na połączenie sieciowe', @@ -241,7 +234,7 @@ export default { 240: 'Nazwa', 241: 'Stan', 242: 'Otwórz', - 243: 'Przyłącza', + 243: '', 244: 'Hosting', 245: 'Instalowanie', 246: 'Zobacz poniżej', @@ -293,7 +286,6 @@ export default { 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', - 299: 'Dodanie domeny do StartOS oznacza, że możesz używać jej i jej subdomen do hostowania interfejsów usług w publicznym Internecie.', 300: 'Zobacz instrukcje', 303: 'Kontakt', 304: 'Edytuj', @@ -324,8 +316,6 @@ export default { 329: 'Nazwa hosta', 330: 'Ścieżka', 331: 'URL', - 332: 'Interfejs sieciowy', - 333: 'Protokół', 334: 'Model', 335: 'Agent użytkownika', 336: 'Platforma', @@ -372,7 +362,6 @@ export default { 377: 'Wykryto kopie zapasowe StartOS', 378: 'Nie wykryto kopii zapasowych StartOS', 379: 'Wersja StartOS', - 380: 'Podłączenie zewnętrznego serwera SMTP umożliwia StartOS i zainstalowanym serwisom wysyłanie wiadomości e-mail.', 381: 'Dane logowania SMTP', 382: 'Wyślij e-mail testowy', 383: 'Wyślij', @@ -380,7 +369,6 @@ export default { 385: 'Wiadomość testowa została wysłana na adres', 386: 'Sprawdź folder spam i oznacz wiadomość jako "nie spam".', 387: 'Przyłącze użytkownika twojego serwera StartOS, dostępne z dowolnej przeglądarki.', - 388: 'Zmień swoje hasło główne StartOS.', 389: 'Nadal będziesz potrzebować aktualnego hasła, aby odszyfrować istniejące kopie zapasowe!', 390: 'Nowe hasła nie są zgodne', 391: 'Nowe hasło musi mieć co najmniej 12 znaków', @@ -390,7 +378,6 @@ export default { 395: 'Bieżące hasło', 396: 'Nowe hasło', 397: 'Powtórz nowe hasło', - 398: 'Sesja to urządzenie, które jest obecnie zalogowane do StartOS. Dla najlepszego bezpieczeństwa zakończ sesje, których nie rozpoznajesz lub już nie używasz.', 399: 'Bieżąca sesja', 400: 'Inne sesje', 401: 'Zakończ wybrane', @@ -497,7 +484,6 @@ export default { 502: 'suwerenne przetwarzanie', 503: 'Dostosuj nazwę wyświetlaną na karcie przeglądarki', 504: 'Zarządzać', - 505: 'Czy na pewno chcesz usunąć ten adres?', 506: '„Miękkie odinstalowanie” usunie usługę z StartOS, ale zachowa jej dane.', 507: 'Brak zapisanych dostawców', 508: 'Tryb kiosku', @@ -511,22 +497,20 @@ export default { 516: 'Zalecane', 517: 'Czy na pewno chcesz odrzucić to zadanie?', 518: 'Odrzuć', - 519: 'Aby opublikować domeny w clearnet, kliknij „Upublicznij” powyżej.', 520: 'Aktualizacja dostępna', 521: 'Aby rozwiązać problem, zapoznaj się z', 522: 'Wersja SDK', 523: 'Raport kopii zapasowej', 524: 'Usuń wybrane', - 525: 'Brak kluczy', - 526: 'Dodaj klucz publiczny SSH', - 527: 'Domyślnie możesz połączyć się z serwerem przez SSH z dowolnego urządzenia, używając hasła głównego. Opcjonalnie dodaj klucze publiczne SSH, aby przyznać dostęp określonym urządzeniom bez potrzeby wpisywania hasła.', + 525: '', + 526: '', + 527: '', 528: 'Kod źródłowy', 529: 'Usługa źródłowa', 530: 'Pakiet StartOS', 531: 'Błąd inicjalizacji serwera', 532: 'Zakończono', 533: 'Bramy sieciowe', - 534: 'Bramy łączą twój serwer z Internetem. Przetwarzają ruch wychodzący, a w pewnych warunkach również dopuszczają ruch przychodzący.', 535: 'Dodaj bramę', 536: 'Zmień nazwę', 537: 'Dostęp', @@ -534,10 +518,15 @@ export default { 539: 'Urzędy certyfikacji', 540: 'Domena', 541: 'Brama', - 542: 'Domyślny urząd certyfikacji', 543: 'Urząd certyfikacji', 544: 'Edytuj domenę', 545: 'Brak domen', 546: 'Dostawca', 547: 'Zarządzaj DNS', + 548: '', + 549: '', + 550: '', + 551: '', + 552: '', + 553: '', } satisfies i18n diff --git a/web/projects/ui/src/app/routes/portal/components/interfaces/actions.component.ts b/web/projects/ui/src/app/routes/portal/components/interfaces/addresses/actions.component.ts similarity index 97% rename from web/projects/ui/src/app/routes/portal/components/interfaces/actions.component.ts rename to web/projects/ui/src/app/routes/portal/components/interfaces/addresses/actions.component.ts index d19ae6f4e..4cdee19c9 100644 --- a/web/projects/ui/src/app/routes/portal/components/interfaces/actions.component.ts +++ b/web/projects/ui/src/app/routes/portal/components/interfaces/addresses/actions.component.ts @@ -15,7 +15,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 { InterfaceComponent } from '../interface.component' @Component({ selector: 'td[actions]', @@ -114,7 +114,7 @@ import { InterfaceComponent } from './interface.component' providers: [tuiButtonOptionsProvider({ appearance: 'icon' })], changeDetection: ChangeDetectionStrategy.OnPush, }) -export class InterfaceActionsComponent { +export class AddressActionsComponent { private readonly document = inject(DOCUMENT) readonly isMobile = inject(TUI_IS_MOBILE) 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 new file mode 100644 index 000000000..302362aea --- /dev/null +++ b/web/projects/ui/src/app/routes/portal/components/interfaces/addresses/addresses.component.ts @@ -0,0 +1,85 @@ +import { ChangeDetectionStrategy, Component, input } from '@angular/core' +import { i18nPipe } from '@start9labs/shared' +import { TuiButton, TuiDataList, TuiDropdown } from '@taiga-ui/core' +import { PlaceholderComponent } from 'src/app/routes/portal/components/placeholder.component' +import { TableComponent } from 'src/app/routes/portal/components/table.component' +import { MappedServiceInterface } from '../interface.utils' +import { AddressActionsComponent } from './actions.component' + +@Component({ + selector: 'section[addresses]', + template: ` +
{{ 'Addresses' | i18n }}
+ @if (addresses().common.length) { +
+
{{ 'Common' | i18n }}
+ + @for (address of addresses().common; track $index) { + + + + + + + + } +
+ + {{ address.type }}{{ address.gateway }}{{ address.url }}
+
+ } @else { + + {{ 'No addresses' | i18n }} + + } + + @if (addresses().uncommon.length) { +
+
{{ 'Uncommon' | i18n }}
+ + @for (address of addresses().uncommon; track $index) { + + + + + + + + } +
+ + {{ address.type }}{{ address.gateway }}{{ address.url }}
+
+ } + `, + imports: [ + TableComponent, + PlaceholderComponent, + i18nPipe, + TuiDropdown, + TuiDataList, + AddressActionsComponent, + TuiButton, + ], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class InterfaceAddressesComponent { + readonly addresses = input.required() + readonly isRunning = input.required() + + instructions() {} +} 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 new file mode 100644 index 000000000..50590cc76 --- /dev/null +++ b/web/projects/ui/src/app/routes/portal/components/interfaces/clearnet-domains.component.ts @@ -0,0 +1,166 @@ +import { + ChangeDetectionStrategy, + Component, + inject, + input, +} from '@angular/core' +import { + DialogService, + DocsLinkDirective, + ErrorService, + i18nPipe, + LoadingService, +} from '@start9labs/shared' +import { ISB, utils } from '@start9labs/start-sdk' +import { + TuiAppearance, + TuiButton, + TuiDataList, + TuiDropdown, + TuiLink, +} from '@taiga-ui/core' +import { filter } from 'rxjs' +import { + FormComponent, + FormContext, +} from 'src/app/routes/portal/components/form.component' +import { InterfaceComponent } from 'src/app/routes/portal/components/interfaces/interface.component' +import { PlaceholderComponent } from 'src/app/routes/portal/components/placeholder.component' +import { TableComponent } from 'src/app/routes/portal/components/table.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 { ClearnetDomain } from './interface.utils' + +@Component({ + selector: 'section[clearnetDomains]', + template: ` +
+ {{ 'Clearnet Domains' | i18n }} + + +
+ @if (clearnetDomains().length) { + + @for (domain of clearnetDomains(); track $index) { + + + + + + + } +
{{ domain.fqdn }}{{ domain.authority }}{{ domain.public ? 'public' : 'private' }} + + + + + + + +
+ } @else { + + {{ 'No clearnet domains' | i18n }} + + } + `, + imports: [ + TuiButton, + TuiLink, + TuiAppearance, + TableComponent, + PlaceholderComponent, + i18nPipe, + DocsLinkDirective, + TuiDropdown, + TuiDataList, + ], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class InterfaceClearnetDomainsComponent { + private readonly dialog = inject(DialogService) + private readonly formDialog = inject(FormDialogService) + private readonly loader = inject(LoadingService) + private readonly errorService = inject(ErrorService) + private readonly api = inject(ApiService) + private readonly interface = inject(InterfaceComponent) + private readonly i18n = inject(i18nPipe) + + readonly clearnetDomains = input.required() + + open = false + + async add() {} + + async edit(domain: ClearnetDomain) {} + + async remove(fqdn: string) { + this.dialog + .openConfirm({ label: 'Are you sure?', size: 's' }) + .pipe(filter(Boolean)) + .subscribe(async () => { + const loader = this.loader.open('Removing').subscribe() + const params = { fqdn } + + try { + if (this.interface.packageId()) { + await this.api.pkgRemoveDomain({ + ...params, + package: this.interface.packageId(), + host: this.interface.value().addressInfo.hostId, + }) + } else { + await this.api.osUiRemoveDomain(params) + } + return true + } catch (e: any) { + this.errorService.handleError(e) + return false + } finally { + loader.unsubscribe() + } + }) + } +} diff --git a/web/projects/ui/src/app/routes/portal/components/interfaces/clearnet.component.ts b/web/projects/ui/src/app/routes/portal/components/interfaces/clearnet.component.ts deleted file mode 100644 index 810275ca6..000000000 --- a/web/projects/ui/src/app/routes/portal/components/interfaces/clearnet.component.ts +++ /dev/null @@ -1,305 +0,0 @@ -import { - ChangeDetectionStrategy, - Component, - inject, - input, -} from '@angular/core' -import { toSignal } from '@angular/core/rxjs-interop' -import { - DialogService, - DocsLinkDirective, - ErrorService, - i18nPipe, - LoadingService, -} from '@start9labs/shared' -import { ISB, utils } from '@start9labs/start-sdk' -import { - TuiAppearance, - TuiButton, - TuiDataList, - TuiIcon, - TuiLink, - TuiNotification, -} from '@taiga-ui/core' -import { TuiTooltip } from '@taiga-ui/kit' -import { PatchDB } from 'patch-db-client' -import { defaultIfEmpty, firstValueFrom, map } from 'rxjs' -import { - FormComponent, - FormContext, -} from 'src/app/routes/portal/components/form.component' -import { AuthorityNamePipe } from 'src/app/routes/portal/components/interfaces/acme.pipe' -import { InterfaceComponent } from 'src/app/routes/portal/components/interfaces/interface.component' -import { PlaceholderComponent } from 'src/app/routes/portal/components/placeholder.component' -import { TableComponent } from 'src/app/routes/portal/components/table.component' -import { ApiService } from 'src/app/services/api/embassy-api.service' -import { FormDialogService } from 'src/app/services/form-dialog.service' -import { DataModel } from 'src/app/services/patch-db/data-model' -import { toAuthorityName } from 'src/app/utils/acme' -import { configBuilderToSpec } from 'src/app/utils/configBuilderToSpec' -import { InterfaceActionsComponent } from './actions.component' -import { ClearnetAddress } from './interface.utils' -import { MaskPipe } from './mask.pipe' - -type ClearnetForm = { - domain: string - authority: string -} - -@Component({ - selector: 'section[clearnet]', - template: ` -
- Clearnet - - - {{ - 'Add a clearnet address to expose this interface on the Internet. Clearnet addresses are fully public and not anonymous.' - | i18n - }} - - {{ 'Learn more' | i18n }} - - - @if (clearnet().length) { - - } -
- @if (clearnet().length) { - @if (!isPublic()) { - - {{ - 'To publish clearnet domains, you must click "Make Public", above.' - | i18n - }} - - } - - @for (address of clearnet(); track $index) { - - - - - - } -
- {{ - interface.value().addSsl - ? (address.authority | authorityName) - : '-' - }} - {{ address.url | mask }} - @if (address.isDomain) { - - } - @if (address.isDomain) { - - } -
- } @else { - - {{ 'No public addresses' | i18n }} - - - } - `, - styles: ` - :host-context(tui-root._mobile) { - td { - font-weight: bold; - color: var(--tui-text-primary); - - &:first-child { - font-weight: normal; - color: var(--tui-text-secondary); - } - } - } - `, - host: { class: 'g-card' }, - imports: [ - TuiButton, - TuiIcon, - TuiTooltip, - TuiLink, - TuiDataList, - TuiAppearance, - PlaceholderComponent, - TableComponent, - MaskPipe, - AuthorityNamePipe, - InterfaceActionsComponent, - i18nPipe, - DocsLinkDirective, - TuiNotification, - ], - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class InterfaceClearnetComponent { - private readonly dialog = inject(DialogService) - private readonly formDialog = inject(FormDialogService) - private readonly loader = inject(LoadingService) - private readonly errorService = inject(ErrorService) - private readonly api = inject(ApiService) - - readonly interface = inject(InterfaceComponent) - - readonly clearnet = input.required() - readonly isRunning = input.required() - readonly isPublic = input.required() - - readonly authorityUrls = toSignal( - inject>(PatchDB) - .watch$('serverInfo', 'network', 'acme') - .pipe(map(acme => Object.keys(acme))), - { initialValue: [] }, - ) - - async remove({ url }: ClearnetAddress) { - const confirm = await firstValueFrom( - this.dialog - .openConfirm({ - label: 'Confirm', - size: 's', - data: { - yes: 'Delete', - no: 'Cancel', - content: 'Are you sure you want to delete this address?', - }, - }) - .pipe(defaultIfEmpty(false)), - ) - - if (!confirm) { - return - } - - const loader = this.loader.open('Removing').subscribe() - - if (!/^[a-zA-Z][a-zA-Z\d+\-.]*:\/\//.test(url)) { - url = 'http://' + url - } - - const params = { domain: new URL(url).hostname } - - try { - if (this.interface.packageId()) { - await this.api.pkgRemoveDomain({ - ...params, - package: this.interface.packageId(), - host: this.interface.value().addressInfo.hostId, - }) - } else { - await this.api.osUiRemoveDomain(params) - } - return true - } catch (e: any) { - this.errorService.handleError(e) - return false - } finally { - loader.unsubscribe() - } - } - - async add() { - const domain = ISB.Value.text({ - name: 'Domain', - description: 'The domain or subdomain you want to use', - placeholder: `e.g. 'mydomain.com' or 'sub.mydomain.com'`, - required: true, - default: null, - patterns: [utils.Patterns.domain], - }) - const authority = ISB.Value.select({ - name: 'Certificate Authority', - description: - 'Select which Certificate authority to use for obtaining your SSL certificate. Add new authority in the System tab. Optionally use your local= Root CA. Note: only devices that have trusted your Root CA will be able to access the domain without security warnings.', - values: this.authorityUrls().reduce>( - (obj, url) => ({ - ...obj, - [url]: toAuthorityName(url), - }), - { local: toAuthorityName(null) }, - ), - default: '', - }) - - this.formDialog.open>(FormComponent, { - label: 'Select domain', - data: { - spec: await configBuilderToSpec( - ISB.InputSpec.of( - this.interface.value().addSsl ? { domain, authority } : { domain }, - ), - ), - buttons: [ - { - text: 'Save', - handler: async value => this.save(value), - }, - ], - }, - }) - } - - private async save(domainInfo: ClearnetForm): Promise { - const loader = this.loader.open('Saving').subscribe() - - const { domain, authority } = domainInfo - - const params = { - domain, - acme: authority === 'local' ? null : authority, - private: false, - } - - 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() - } - } -} diff --git a/web/projects/ui/src/app/routes/portal/components/interfaces/gateways.component.ts b/web/projects/ui/src/app/routes/portal/components/interfaces/gateways.component.ts new file mode 100644 index 000000000..bc209a02f --- /dev/null +++ b/web/projects/ui/src/app/routes/portal/components/interfaces/gateways.component.ts @@ -0,0 +1,35 @@ +import { CommonModule } from '@angular/common' +import { ChangeDetectionStrategy, Component, input } from '@angular/core' +import { TuiSwitch } from '@taiga-ui/kit' +import { FormsModule } from '@angular/forms' +import { i18nPipe } from '@start9labs/shared' + +@Component({ + selector: 'section[gateways]', + template: ` +
{{ 'Gateways' | i18n }}
+
    + @for (gateway of gateways(); track $index) { +
  • + {{ gateway.name }} + +
  • + } +
      +
    + `, + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [CommonModule, FormsModule, TuiSwitch, i18nPipe], +}) +export class InterfaceGatewaysComponent { + readonly gateways = input.required() + + async onToggle(event: any) {} +} 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 7486bfea8..35d54a6d1 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 @@ -1,43 +1,28 @@ -import { - ChangeDetectionStrategy, - Component, - inject, - input, -} from '@angular/core' -import { ErrorService, i18nPipe, LoadingService } from '@start9labs/shared' -import { TuiButton, tuiButtonOptionsProvider } from '@taiga-ui/core' -import { InterfaceClearnetComponent } from 'src/app/routes/portal/components/interfaces/clearnet.component' -import { InterfaceLocalComponent } from 'src/app/routes/portal/components/interfaces/local.component' -import { InterfaceTorComponent } from 'src/app/routes/portal/components/interfaces/tor.component' -import { ApiService } from 'src/app/services/api/embassy-api.service' +import { ChangeDetectionStrategy, Component, input } from '@angular/core' +import { tuiButtonOptionsProvider } from '@taiga-ui/core' import { MappedServiceInterface } from './interface.utils' +import { InterfaceGatewaysComponent } from './gateways.component' +import { InterfaceTorDomainsComponent } from './tor-domains.component' +import { InterfaceClearnetDomainsComponent } from './clearnet-domains.component' +import { InterfaceAddressesComponent } from './addresses/addresses.component' @Component({ - selector: 'app-interface', + selector: 'service-interface', template: ` - +
    +
    -
    `, styles: ` :host { - max-width: 56rem; display: flex; flex-direction: column; gap: 1rem; @@ -48,54 +33,18 @@ import { MappedServiceInterface } from './interface.utils' overflow-wrap: anywhere; } } - - button { - margin: -0.5rem auto 0 0; - } `, - providers: [tuiButtonOptionsProvider({ size: 'xs' })], changeDetection: ChangeDetectionStrategy.OnPush, + providers: [tuiButtonOptionsProvider({ size: 'xs' })], imports: [ - InterfaceClearnetComponent, - InterfaceTorComponent, - InterfaceLocalComponent, - TuiButton, - i18nPipe, + InterfaceGatewaysComponent, + InterfaceTorDomainsComponent, + InterfaceClearnetDomainsComponent, + InterfaceAddressesComponent, ], }) export class InterfaceComponent { - private readonly loader = inject(LoadingService) - private readonly errorService = inject(ErrorService) - private readonly api = inject(ApiService) - readonly packageId = input('') readonly value = input.required() readonly isRunning = input.required() - - async toggle() { - const loader = this.loader - .open(`Making ${this.value().public ? 'private' : 'public'}`) - .subscribe() - - const params = { - internalPort: this.value().addressInfo.internalPort, - public: !this.value().public, - } - - try { - if (this.packageId()) { - await this.api.pkgBindingSetPubic({ - ...params, - host: this.value().addressInfo.hostId, - package: this.packageId(), - }) - } else { - await this.api.serverBindingSetPubic(params) - } - } catch (e: any) { - this.errorService.handleError(e) - } finally { - loader.unsubscribe() - } - } } diff --git a/web/projects/ui/src/app/routes/portal/components/interfaces/interface.utils.ts b/web/projects/ui/src/app/routes/portal/components/interfaces/interface.utils.ts index 513dc7837..96f77e7aa 100644 --- a/web/projects/ui/src/app/routes/portal/components/interfaces/interface.utils.ts +++ b/web/projects/ui/src/app/routes/portal/components/interfaces/interface.utils.ts @@ -1,21 +1,11 @@ -import { T, utils } from '@start9labs/start-sdk' +import { T } from '@start9labs/start-sdk' import { ConfigService } from 'src/app/services/config.service' -export abstract class AddressesService { - abstract static: boolean - abstract add(): Promise - abstract remove(): Promise -} - export function getAddresses( serviceInterface: T.ServiceInterface, host: T.Host, config: ConfigService, -): { - clearnet: ClearnetAddress[] - local: LocalAddress[] - tor: TorAddress[] -} { +): MappedServiceInterface['addresses'] { const addressInfo = serviceInterface.addressInfo const hostnames = host.hostnameInfo[addressInfo.internalPort]?.filter( @@ -46,60 +36,75 @@ export function getAddresses( } } - const clearnet: ClearnetAddress[] = [] - const local: LocalAddress[] = [] - const tor: TorAddress[] = [] + const common: Address[] = [ + { + type: 'Local', + description: '', + gateway: 'Wire Conenction 1', + url: 'https://test.local:1234', + }, + { + type: 'IPv4 (LAN)', + description: '', + gateway: 'Wire Connction 1', + url: 'https://192.168.1.10.local:1234', + }, + ] + const uncommon: Address[] = [ + { + type: 'IPv4 (WAN)', + description: '', + gateway: 'Wire Conenction 1', + url: 'https://72.72.72.72', + }, + ] - hostnames.forEach(h => { - const addresses = utils.addressHostToUrl(addressInfo, h) + // hostnames.forEach(h => { + // const addresses = utils.addressHostToUrl(addressInfo, h) - addresses.forEach(url => { - if (h.kind === 'onion') { - tor.push({ - protocol: /^[a-zA-Z][a-zA-Z\d+\-.]*:\/\//.test(url) - ? new URL(url).protocol.replace(':', '').toUpperCase() - : null, - url, - }) - } else { - const hostnameKind = h.hostname.kind + // addresses.forEach(url => { + // if (h.kind === 'onion') { + // tor.push({ + // protocol: /^[a-zA-Z][a-zA-Z\d+\-.]*:\/\//.test(url) + // ? new URL(url).protocol.replace(':', '').toUpperCase() + // : null, + // url, + // }) + // } else { + // const hostnameKind = h.hostname.kind - if ( - h.public || - (hostnameKind === 'domain' && host.domains[h.hostname.domain]?.public) - ) { - clearnet.push({ - url, - disabled: !h.public, - isDomain: hostnameKind == 'domain', - authority: - hostnameKind == 'domain' - ? host.domains[h.hostname.domain]?.acme || null - : null, - }) - } else { - local.push({ - nid: - hostnameKind === 'local' - ? 'Local' - : `${h.gatewayId} (${hostnameKind})`, - url, - }) - } - } - }) - }) + // if ( + // h.public || + // (hostnameKind === 'domain' && host.domains[h.hostname.domain]?.public) + // ) { + // clearnet.push({ + // url, + // disabled: !h.public, + // isDomain: hostnameKind == 'domain', + // authority: + // hostnameKind == 'domain' + // ? host.domains[h.hostname.domain]?.acme || null + // : null, + // }) + // } else { + // local.push({ + // nid: + // hostnameKind === 'local' + // ? 'Local' + // : `${h.gatewayId} (${hostnameKind})`, + // url, + // }) + // } + // } + // }) + // }) return { - clearnet: clearnet.filter( + common: common.filter( (value, index, self) => index === self.findIndex(t => t.url === value.url), ), - local: local.filter( - (value, index, self) => - index === self.findIndex(t => t.url === value.url), - ), - tor: tor.filter( + uncommon: uncommon.filter( (value, index, self) => index === self.findIndex(t => t.url === value.url), ), @@ -107,28 +112,28 @@ export function getAddresses( } export type MappedServiceInterface = T.ServiceInterface & { - addSsl?: T.AddSslOptions | null - public: boolean + gateways: { + id: string + name: string + enabled: boolean + }[] + torDomains: string[] + clearnetDomains: ClearnetDomain[] addresses: { - clearnet: ClearnetAddress[] - local: LocalAddress[] - tor: TorAddress[] + common: Address[] + uncommon: Address[] } } -export type ClearnetAddress = { - url: string +export type ClearnetDomain = { + fqdn: string authority: string | null - isDomain: boolean - disabled: boolean + public: boolean } -export type LocalAddress = { +export type Address = { + type: string + gateway: string url: string - nid: string -} - -export type TorAddress = { - url: string - protocol: string | null + description: string } diff --git a/web/projects/ui/src/app/routes/portal/components/interfaces/local.component.ts b/web/projects/ui/src/app/routes/portal/components/interfaces/local.component.ts deleted file mode 100644 index 03f2b184b..000000000 --- a/web/projects/ui/src/app/routes/portal/components/interfaces/local.component.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { ChangeDetectionStrategy, Component, input } from '@angular/core' -import { TuiIcon, TuiLink } from '@taiga-ui/core' -import { TuiTooltip } from '@taiga-ui/kit' -import { TableComponent } from 'src/app/routes/portal/components/table.component' -import { InterfaceActionsComponent } from './actions.component' -import { LocalAddress } from './interface.utils' -import { MaskPipe } from './mask.pipe' -import { DocsLinkDirective, i18nPipe } from '@start9labs/shared' - -@Component({ - selector: 'section[local]', - template: ` -
    - {{ 'Local' | i18n }} - - - {{ - 'Local addresses can only be accessed by devices connected to the same LAN as your server, either directly or using a VPN.' - | i18n - }} - - {{ 'Learn More' | i18n }} - - -
    - - @for (address of local(); track $index) { - - - - - - } -
    {{ address.nid }}{{ address.url | mask }}
    - `, - host: { class: 'g-card' }, - imports: [ - TuiIcon, - TuiTooltip, - TuiLink, - TableComponent, - InterfaceActionsComponent, - MaskPipe, - i18nPipe, - DocsLinkDirective, - ], - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class InterfaceLocalComponent { - readonly local = input.required() - readonly isRunning = input.required() -} diff --git a/web/projects/ui/src/app/routes/portal/components/interfaces/status.component.ts b/web/projects/ui/src/app/routes/portal/components/interfaces/status.component.ts deleted file mode 100644 index 7c69d6958..000000000 --- a/web/projects/ui/src/app/routes/portal/components/interfaces/status.component.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { ChangeDetectionStrategy, Component, input } from '@angular/core' -import { i18nPipe } from '@start9labs/shared' -import { TuiBadge } from '@taiga-ui/kit' - -@Component({ - selector: 'interface-status', - template: ` - - {{ public() ? ('Public' | i18n) : ('Private' | i18n) }} - - `, - styles: ` - :host { - display: inline-flex; - } - `, - changeDetection: ChangeDetectionStrategy.OnPush, - imports: [TuiBadge, i18nPipe], -}) -export class InterfaceStatusComponent { - readonly public = input(false) -} diff --git a/web/projects/ui/src/app/routes/portal/components/interfaces/tor-domains.component.ts b/web/projects/ui/src/app/routes/portal/components/interfaces/tor-domains.component.ts new file mode 100644 index 000000000..dcaafbbb8 --- /dev/null +++ b/web/projects/ui/src/app/routes/portal/components/interfaces/tor-domains.component.ts @@ -0,0 +1,182 @@ +import { + ChangeDetectionStrategy, + Component, + inject, + input, +} from '@angular/core' +import { + DialogService, + DocsLinkDirective, + ErrorService, + i18nPipe, + LoadingService, +} from '@start9labs/shared' +import { ISB, utils } from '@start9labs/start-sdk' +import { TuiAppearance, TuiButton, TuiLink } from '@taiga-ui/core' +import { filter } from 'rxjs' +import { + FormComponent, + FormContext, +} from 'src/app/routes/portal/components/form.component' +import { InterfaceComponent } from 'src/app/routes/portal/components/interfaces/interface.component' +import { PlaceholderComponent } from 'src/app/routes/portal/components/placeholder.component' +import { TableComponent } from 'src/app/routes/portal/components/table.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' + +type OnionForm = { + key: string +} + +@Component({ + selector: 'section[torDomains]', + template: ` +
    + + Tor Domains + + +
    + @if (torDomains().length) { + + @for (domain of torDomains(); track $index) { + + + + + } +
    {{ domain }} + +
    + } @else { + + {{ 'No Tor domains' | i18n }} + + } + `, + imports: [ + TuiButton, + TuiLink, + TuiAppearance, + TableComponent, + PlaceholderComponent, + i18nPipe, + DocsLinkDirective, + ], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class InterfaceTorDomainsComponent { + private readonly dialog = inject(DialogService) + private readonly formDialog = inject(FormDialogService) + private readonly loader = inject(LoadingService) + private readonly errorService = inject(ErrorService) + private readonly api = inject(ApiService) + private readonly interface = inject(InterfaceComponent) + private readonly i18n = inject(i18nPipe) + + readonly torDomains = input.required() + + async remove(domain: string) { + this.dialog + .openConfirm({ label: 'Are you sure?', size: 's' }) + .pipe(filter(Boolean)) + .subscribe(async () => { + const loader = this.loader.open('Removing').subscribe() + const params = { onion: domain } + + try { + if (this.interface.packageId()) { + await this.api.pkgRemoveOnion({ + ...params, + package: this.interface.packageId(), + host: this.interface.value().addressInfo.hostId, + }) + } else { + await this.api.serverRemoveOnion(params) + } + return true + } catch (e: any) { + this.errorService.handleError(e) + return false + } finally { + loader.unsubscribe() + } + }) + } + + async add() { + this.formDialog.open>(FormComponent, { + label: 'New Tor domain', + data: { + spec: await configBuilderToSpec( + ISB.InputSpec.of({ + key: ISB.Value.text({ + name: this.i18n.transform('Private Key (optional)')!, + description: this.i18n.transform( + 'Optionally provide a base64-encoded ed25519 private key for generating the Tor V3 (.onion) domain. If not provided, a random key will be generated.', + ), + required: false, + default: null, + patterns: [utils.Patterns.base64], + }), + }), + ), + buttons: [ + { + text: this.i18n.transform('Save')!, + handler: async value => this.save(value), + }, + ], + }, + }) + } + + private async save(form: OnionForm): Promise { + const loader = this.loader.open('Saving').subscribe() + + try { + let onion = form.key + ? await this.api.addTorKey({ key: form.key }) + : await this.api.generateTorKey({}) + onion = `${onion}.onion` + + if (this.interface.packageId) { + await this.api.pkgAddOnion({ + onion, + package: this.interface.packageId(), + host: this.interface.value().addressInfo.hostId, + }) + } else { + await this.api.serverAddOnion({ onion }) + } + return true + } catch (e: any) { + this.errorService.handleError(e) + return false + } finally { + loader.unsubscribe() + } + } +} diff --git a/web/projects/ui/src/app/routes/portal/components/interfaces/tor.component.ts b/web/projects/ui/src/app/routes/portal/components/interfaces/tor.component.ts deleted file mode 100644 index 9a13bc631..000000000 --- a/web/projects/ui/src/app/routes/portal/components/interfaces/tor.component.ts +++ /dev/null @@ -1,233 +0,0 @@ -import { - ChangeDetectionStrategy, - Component, - inject, - input, -} from '@angular/core' -import { - DialogService, - DocsLinkDirective, - ErrorService, - i18nPipe, - LoadingService, -} from '@start9labs/shared' -import { ISB, utils } from '@start9labs/start-sdk' -import { - TuiAppearance, - TuiButton, - TuiIcon, - TuiLink, - TuiOption, -} from '@taiga-ui/core' -import { TuiTooltip } from '@taiga-ui/kit' -import { defaultIfEmpty, firstValueFrom } from 'rxjs' -import { - FormComponent, - FormContext, -} from 'src/app/routes/portal/components/form.component' -import { InterfaceComponent } from 'src/app/routes/portal/components/interfaces/interface.component' -import { PlaceholderComponent } from 'src/app/routes/portal/components/placeholder.component' -import { TableComponent } from 'src/app/routes/portal/components/table.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 { InterfaceActionsComponent } from './actions.component' -import { TorAddress } from './interface.utils' -import { MaskPipe } from './mask.pipe' - -type OnionForm = { - key: string -} - -@Component({ - selector: 'section[tor]', - template: ` -
    - Tor - - - {{ - 'Add an onion address to anonymously expose this interface on the darknet. Onion addresses can only be reached over the Tor network.' - | i18n - }} - - {{ 'Learn More' | i18n }} - - - @if (tor().length) { - - } -
    - @if (tor().length) { - - @for (address of tor(); track $index) { - - - - - - } -
    {{ address.protocol || '-' }}{{ address.url | mask }} - - -
    - } @else { - - {{ 'No onion addresses' | i18n }} - - - } - `, - styles: ` - [tuiFade] { - white-space: nowrap; - max-width: 30rem; - } - `, - host: { class: 'g-card' }, - imports: [ - TuiButton, - TuiIcon, - TuiTooltip, - TuiLink, - TuiAppearance, - TuiOption, - TableComponent, - PlaceholderComponent, - MaskPipe, - InterfaceActionsComponent, - i18nPipe, - DocsLinkDirective, - ], - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class InterfaceTorComponent { - private readonly dialog = inject(DialogService) - private readonly formDialog = inject(FormDialogService) - private readonly loader = inject(LoadingService) - private readonly errorService = inject(ErrorService) - private readonly api = inject(ApiService) - private readonly interface = inject(InterfaceComponent) - private readonly i18n = inject(i18nPipe) - - readonly tor = input.required() - readonly isRunning = input.required() - - async remove({ url }: TorAddress) { - const confirm = await firstValueFrom( - this.dialog - .openConfirm({ - label: 'Confirm', - size: 's', - data: { - yes: 'Delete', - no: 'Cancel', - content: 'Are you sure you want to delete this address?', - }, - }) - .pipe(defaultIfEmpty(false)), - ) - - if (!confirm) { - return - } - - const loader = this.loader.open('Removing').subscribe() - const params = { onion: new URL(url).hostname } - - try { - if (this.interface.packageId()) { - await this.api.pkgRemoveOnion({ - ...params, - package: this.interface.packageId(), - host: this.interface.value().addressInfo.hostId, - }) - } else { - await this.api.serverRemoveOnion(params) - } - return true - } catch (e: any) { - this.errorService.handleError(e) - return false - } finally { - loader.unsubscribe() - } - } - - async add() { - this.formDialog.open>(FormComponent, { - label: 'New onion address', - data: { - spec: await configBuilderToSpec( - ISB.InputSpec.of({ - key: ISB.Value.text({ - name: this.i18n.transform('Private Key (optional)')!, - description: this.i18n.transform( - 'Optionally provide a base64-encoded ed25519 private key for generating the Tor V3 (.onion) address. If not provided, a random key will be generated and used.', - ), - required: false, - default: null, - patterns: [utils.Patterns.base64], - }), - }), - ), - buttons: [ - { - text: this.i18n.transform('Save')!, - handler: async value => this.save(value), - }, - ], - }, - }) - } - - private async save(form: OnionForm): Promise { - const loader = this.loader.open('Saving').subscribe() - - try { - let onion = form.key - ? await this.api.addTorKey({ key: form.key }) - : await this.api.generateTorKey({}) - onion = `${onion}.onion` - - if (this.interface.packageId) { - await this.api.pkgAddOnion({ - onion, - package: this.interface.packageId(), - host: this.interface.value().addressInfo.hostId, - }) - } else { - await this.api.serverAddOnion({ onion }) - } - return true - } catch (e: any) { - this.errorService.handleError(e) - return false - } finally { - loader.unsubscribe() - } - } -} diff --git a/web/projects/ui/src/app/routes/portal/routes/services/components/interface-item.component.ts b/web/projects/ui/src/app/routes/portal/routes/services/components/interface-item.component.ts index a29f2f268..3f44f8969 100644 --- a/web/projects/ui/src/app/routes/portal/routes/services/components/interface-item.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/services/components/interface-item.component.ts @@ -7,7 +7,7 @@ import { } from '@angular/core' import { RouterLink } from '@angular/router' import { T } from '@start9labs/start-sdk' -import { TuiButton, TuiIcon } from '@taiga-ui/core' +import { TuiButton } from '@taiga-ui/core' import { TuiBadge } from '@taiga-ui/kit' import { ConfigService } from 'src/app/services/config.service' import { PackageDataEntry } from 'src/app/services/patch-db/data-model' @@ -21,13 +21,6 @@ import { PackageDataEntry } from 'src/app/services/patch-db/data-model' {{ info.type }} - - @if (info.public) { - - } @else { - - } - {{ info.description }} @@ -86,7 +79,7 @@ import { PackageDataEntry } from 'src/app/services/patch-db/data-model' } `, changeDetection: ChangeDetectionStrategy.OnPush, - imports: [TuiButton, TuiBadge, TuiIcon, RouterLink], + imports: [TuiButton, TuiBadge, RouterLink], }) export class ServiceInterfaceItemComponent { private readonly config = inject(ConfigService) @@ -94,7 +87,6 @@ export class ServiceInterfaceItemComponent { @Input({ required: true }) info!: T.ServiceInterface & { - public: boolean routerLink: string } diff --git a/web/projects/ui/src/app/routes/portal/routes/services/components/interfaces.component.ts b/web/projects/ui/src/app/routes/portal/routes/services/components/interfaces.component.ts index e64792fee..59772ef01 100644 --- a/web/projects/ui/src/app/routes/portal/routes/services/components/interfaces.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/services/components/interfaces.component.ts @@ -2,27 +2,23 @@ import { ChangeDetectionStrategy, Component, computed, - inject, input, } from '@angular/core' import { TuiTable } from '@taiga-ui/addon-table' import { tuiDefaultSort } from '@taiga-ui/cdk' -import { ConfigService } from 'src/app/services/config.service' import { PackageDataEntry } from 'src/app/services/patch-db/data-model' -import { getAddresses } from '../../../components/interfaces/interface.utils' import { ServiceInterfaceItemComponent } from './interface-item.component' import { i18nPipe } from '@start9labs/shared' @Component({ selector: 'service-interfaces', template: ` -
    {{ 'Interfaces' | i18n }}
    +
    {{ 'Service Interfaces' | i18n }}
    - @@ -49,8 +45,6 @@ import { i18nPipe } from '@start9labs/shared' imports: [ServiceInterfaceItemComponent, TuiTable, i18nPipe], }) export class ServiceInterfacesComponent { - private readonly config = inject(ConfigService) - readonly pkg = input.required() readonly disabled = input(false) @@ -58,14 +52,8 @@ export class ServiceInterfacesComponent { Object.entries(serviceInterfaces) .sort((a, b) => tuiDefaultSort(a[1], b[1])) .map(([id, value]) => { - const host = hosts[value.addressInfo.hostId] - const port = value.addressInfo.internalPort - return { ...value, - addSsl: host?.bindings[port]?.options.addSsl, - public: !!host?.bindings[port]?.net.public, - addresses: host ? getAddresses(value, host, this.config) : {}, routerLink: `./interface/${id}`, } }), diff --git a/web/projects/ui/src/app/routes/portal/routes/services/routes/interface.component.ts b/web/projects/ui/src/app/routes/portal/routes/services/routes/interface.component.ts index 753d2245d..a27745c58 100644 --- a/web/projects/ui/src/app/routes/portal/routes/services/routes/interface.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/services/routes/interface.component.ts @@ -16,7 +16,6 @@ import { TuiHeader } from '@taiga-ui/layout' import { PatchDB } from 'patch-db-client' import { InterfaceComponent } from 'src/app/routes/portal/components/interfaces/interface.component' import { getAddresses } from 'src/app/routes/portal/components/interfaces/interface.utils' -import { InterfaceStatusComponent } from 'src/app/routes/portal/components/interfaces/status.component' import { ConfigService } from 'src/app/services/config.service' import { DataModel } from 'src/app/services/patch-db/data-model' import { TitleDirective } from 'src/app/services/title.service' @@ -28,10 +27,6 @@ import { TitleDirective } from 'src/app/services/title.service' {{ 'Back' | i18n }} {{ interface()?.name }} - @@ -47,12 +42,11 @@ import { TitleDirective } from 'src/app/services/title.service' {{ value.type }} -

    {{ value.description }}

    - +
    + {{ 'Back' | i18n }} + + {{ 'Certificate Authorities' | i18n }} + +
    +
    + {{ 'Certificate Authorities' | i18n }} + + @if (authorityService.authorities(); as authorities) { + + } +
    + +
    + `, + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + TuiButton, + TuiLink, + RouterLink, + TitleDirective, + i18nPipe, + DocsLinkDirective, + AuthoritiesTableComponent, + ], + providers: [AuthorityService], +}) +export default class SystemAuthoritiesComponent { + protected readonly authorityService = inject(AuthorityService) +} diff --git a/web/projects/ui/src/app/routes/portal/routes/system/routes/domains/authorities/authority.service.ts b/web/projects/ui/src/app/routes/portal/routes/system/routes/authorities/authority.service.ts similarity index 100% rename from web/projects/ui/src/app/routes/portal/routes/system/routes/domains/authorities/authority.service.ts rename to web/projects/ui/src/app/routes/portal/routes/system/routes/authorities/authority.service.ts diff --git a/web/projects/ui/src/app/routes/portal/routes/system/routes/domains/authorities/item.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/routes/authorities/item.component.ts similarity index 100% rename from web/projects/ui/src/app/routes/portal/routes/system/routes/domains/authorities/item.component.ts rename to web/projects/ui/src/app/routes/portal/routes/system/routes/authorities/item.component.ts diff --git a/web/projects/ui/src/app/routes/portal/routes/system/routes/domains/authorities/table.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/routes/authorities/table.component.ts similarity index 100% rename from web/projects/ui/src/app/routes/portal/routes/system/routes/domains/authorities/table.component.ts rename to web/projects/ui/src/app/routes/portal/routes/system/routes/authorities/table.component.ts diff --git a/web/projects/ui/src/app/routes/portal/routes/system/routes/domains/domains/dns.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/routes/domains/dns.component.ts similarity index 100% rename from web/projects/ui/src/app/routes/portal/routes/system/routes/domains/domains/dns.component.ts rename to web/projects/ui/src/app/routes/portal/routes/system/routes/domains/dns.component.ts diff --git a/web/projects/ui/src/app/routes/portal/routes/system/routes/domains/domains/domain.service.ts b/web/projects/ui/src/app/routes/portal/routes/system/routes/domains/domain.service.ts similarity index 78% rename from web/projects/ui/src/app/routes/portal/routes/system/routes/domains/domains/domain.service.ts rename to web/projects/ui/src/app/routes/portal/routes/system/routes/domains/domain.service.ts index 88209a029..eb0fe0c28 100644 --- a/web/projects/ui/src/app/routes/portal/routes/system/routes/domains/domains/domain.service.ts +++ b/web/projects/ui/src/app/routes/portal/routes/system/routes/domains/domain.service.ts @@ -14,7 +14,6 @@ import { FormDialogService } from 'src/app/services/form-dialog.service' import { configBuilderToSpec } from 'src/app/utils/configBuilderToSpec' import { PatchDB } from 'patch-db-client' import { DataModel } from 'src/app/services/patch-db/data-model' -import { toAuthorityName } from 'src/app/utils/acme' import { parse } from 'tldts' import { RR } from 'src/app/services/api/api.types' import { DNS } from './dns.component' @@ -29,10 +28,6 @@ export type MappedDomain = { name: string | null ipInfo: T.IpInfo | null } - authority: { - url: string | null - name: string | null - } } @Injectable() @@ -64,19 +59,8 @@ export class DomainService { id: gateway, ipInfo: gateways[gateway]?.ipInfo || null, }, - authority: { - url: acme, - name: toAuthorityName(acme), - }, }) as MappedDomain, ), - authorities: Object.keys(acme).reduce>( - (obj, url) => ({ - ...obj, - [url]: toAuthorityName(url), - }), - { local: toAuthorityName(null) }, - ), })), ), ) @@ -91,7 +75,7 @@ export class DomainService { default: null, patterns: [utils.Patterns.domain], }), - ...this.gatewaysAndAuthorities(), + ...this.gatewaysSpec(), }) this.formDialog.open(FormComponent, { @@ -105,7 +89,6 @@ export class DomainService { this.save({ fqdn: input.fqdn, gateway: input.gateway, - acme: input.authority === 'local' ? null : input.authority, }), }, ], @@ -115,7 +98,7 @@ export class DomainService { async edit(domain: MappedDomain) { const editSpec = ISB.InputSpec.of({ - ...this.gatewaysAndAuthorities(), + ...this.gatewaysSpec(), }) this.formDialog.open(FormComponent, { @@ -129,13 +112,11 @@ export class DomainService { this.save({ fqdn: domain.fqdn, gateway: input.gateway, - acme: input.authority === 'local' ? null : input.authority, }), }, ], value: { gateway: domain.gateway.id, - authority: domain.authority.url || 'local', }, }, }) @@ -178,22 +159,14 @@ export class DomainService { } } - private gatewaysAndAuthorities() { + private gatewaysSpec() { return { gateway: ISB.Value.select({ name: 'Gateway', - description: - 'Select the public gateway for this domain. Whichever gateway you select is the IP address that will be exposed to the Internet.', + description: 'Select which gateway to use for this domain.', values: this.data()!.gateways, default: '', }), - authority: ISB.Value.select({ - name: 'Default Certificate Authority', - description: - 'Select the default certificate authority that will sign certificates for this domain. You can override this on a case-by-case basis.', - values: this.data()!.authorities, - default: '', - }), } } } diff --git a/web/projects/ui/src/app/routes/portal/routes/system/routes/domains/domains.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/routes/domains/domains.component.ts index 61f2c208e..3f0399f50 100644 --- a/web/projects/ui/src/app/routes/portal/routes/system/routes/domains/domains.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/system/routes/domains/domains.component.ts @@ -1,13 +1,10 @@ import { ChangeDetectionStrategy, Component, inject } from '@angular/core' import { RouterLink } from '@angular/router' import { DocsLinkDirective, i18nPipe } from '@start9labs/shared' -import { TuiButton, TuiLink, TuiTitle } from '@taiga-ui/core' -import { TuiHeader } from '@taiga-ui/layout' +import { TuiButton, TuiLink } from '@taiga-ui/core' import { TitleDirective } from 'src/app/services/title.service' -import { AuthorityService } from './authorities/authority.service' -import { DomainService } from './domains/domain.service' -import { DomainsTableComponent } from './domains/table.component' -import { AuthoritiesTableComponent } from './authorities/table.component' +import { DomainService } from './domain.service' +import { DomainsTableComponent } from './table.component' @Component({ template: ` @@ -17,48 +14,18 @@ import { AuthoritiesTableComponent } from './authorities/table.component' {{ 'Domains' | i18n }} -
    -
    -

    {{ 'Domains' | i18n }}

    -

    - {{ - 'Adding a domain to StartOS means you can use it and its subdomains to host service interfaces on the public Internet.' - | i18n - }} - -

    -
    -
    - -
    -
    - {{ 'Certificate Authorities' | i18n }} - @if (authorityService.authorities(); as authorities) { - - } -
    - -
    {{ 'Domains' | i18n }} + @if (domainService.data(); as value) {
    -
    {{ 'Name' | i18n }} {{ 'Type' | i18n }}{{ 'Hosting' | i18n }} {{ 'Description' | i18n }}
    {{ domain.gateway.ipInfo?.name || '-' }}{{ domain.authority.name }}