mirror of
https://github.com/outbackdingo/pangolin.git
synced 2026-01-27 10:20:03 +00:00
2
.github/workflows/cicd.yml
vendored
2
.github/workflows/cicd.yml
vendored
@@ -31,7 +31,7 @@ jobs:
|
||||
timeout-minutes: 120
|
||||
env:
|
||||
# Target images
|
||||
DOCKERHUB_IMAGE: docker.io/${{ secrets.DOCKER_HUB_USERNAME }}/${{ github.event.repository.name }}
|
||||
DOCKERHUB_IMAGE: docker.io/fosrl/${{ github.event.repository.name }}
|
||||
GHCR_IMAGE: ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}
|
||||
|
||||
steps:
|
||||
|
||||
@@ -73,7 +73,7 @@ func installDocker() error {
|
||||
case strings.Contains(osRelease, "ID=ubuntu"):
|
||||
installCmd = exec.Command("bash", "-c", fmt.Sprintf(`
|
||||
apt-get update &&
|
||||
apt-get install -y apt-transport-https ca-certificates curl software-properties-common &&
|
||||
apt-get install -y apt-transport-https ca-certificates curl &&
|
||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg &&
|
||||
echo "deb [arch=%s signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" > /etc/apt/sources.list.d/docker.list &&
|
||||
apt-get update &&
|
||||
@@ -82,7 +82,7 @@ func installDocker() error {
|
||||
case strings.Contains(osRelease, "ID=debian"):
|
||||
installCmd = exec.Command("bash", "-c", fmt.Sprintf(`
|
||||
apt-get update &&
|
||||
apt-get install -y apt-transport-https ca-certificates curl software-properties-common &&
|
||||
apt-get install -y apt-transport-https ca-certificates curl &&
|
||||
curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg &&
|
||||
echo "deb [arch=%s signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian $(lsb_release -cs) stable" > /etc/apt/sources.list.d/docker.list &&
|
||||
apt-get update &&
|
||||
|
||||
@@ -131,7 +131,7 @@
|
||||
"expireIn": "Изтече",
|
||||
"neverExpire": "Никога не изтича",
|
||||
"shareExpireDescription": "Времето на изтичане е колко дълго връзката ще бъде използваема и ще предоставя достъп до ресурса. След това време, връзката няма да работи и потребителите, които са я използвали, ще загубят достъп до ресурса.",
|
||||
"shareSeeOnce": "Ще можете да видите тази връзка само веднъж. Уверете се да я копирате.",
|
||||
"shareSeeOnce": "Ще можете да видите този линк само веднъж. Уверете се, че го копирате.",
|
||||
"shareAccessHint": "Всеки с тази връзка може да има достъп до ресурса. Споделяйте я с внимание.",
|
||||
"shareTokenUsage": "Вижте използването на токена за достъп",
|
||||
"createLink": "Създаване на връзка",
|
||||
@@ -179,7 +179,7 @@
|
||||
"baseDomain": "Базов домейн",
|
||||
"subdomnainDescription": "Субдомейнът, в който ще бъде достъпен вашият ресурс.",
|
||||
"resourceRawSettings": "TCP/UDP настройки",
|
||||
"resourceRawSettingsDescription": "Конфигурирайте как вашият ресурс ще бъде достъпен през TCP/UDP",
|
||||
"resourceRawSettingsDescription": "Настройте как ресурсът ви ще бъде достъпен през TCP/UDP. Свързвате ресурса към порт на хост сървъра Pangolin, за да го достъпвате от server-public-ip:mapped-port.",
|
||||
"protocol": "Протокол",
|
||||
"protocolSelect": "Изберете протокол",
|
||||
"resourcePortNumber": "Номер на порт",
|
||||
@@ -1165,13 +1165,13 @@
|
||||
"sidebarDomains": "Домейни",
|
||||
"sidebarBluePrints": "Чертежи",
|
||||
"blueprints": "Чертежи",
|
||||
"blueprintsDescription": "Чертежите са декларативни YAML конфигурации, които определят вашите ресурси и техните настройки",
|
||||
"blueprintsDescription": "Прилагайте декларативни конфигурации и преглеждайте предишни изпълнения",
|
||||
"blueprintAdd": "Добави Чертеж",
|
||||
"blueprintGoBack": "Виж всички Чертежи",
|
||||
"blueprintCreate": "Създай Чертеж",
|
||||
"blueprintCreateDescription2": "Следвайте стъпките по-долу, за да създадете и приложите нов чертеж",
|
||||
"blueprintDetails": "Детайли за Чертежа",
|
||||
"blueprintDetailsDescription": "Вижте детайлите за изпълнението на чертежа",
|
||||
"blueprintDetails": "Детайли на чертежа",
|
||||
"blueprintDetailsDescription": "Вижте резултата от приложените чертежи и всички възникнали грешки",
|
||||
"blueprintInfo": "Информация за Чертежа",
|
||||
"message": "Съобщение",
|
||||
"blueprintContentsDescription": "Дефинирайте YAML съдържанието, описващо вашата инфраструктура",
|
||||
@@ -1181,7 +1181,7 @@
|
||||
"appliedAt": "Приложено във",
|
||||
"source": "Източник",
|
||||
"contents": "Съдържание",
|
||||
"parsedContents": "Анализирано съдържание",
|
||||
"parsedContents": "Парсирано съдържание (само за четене)",
|
||||
"enableDockerSocket": "Активиране на Docker Чернова",
|
||||
"enableDockerSocketDescription": "Активиране на Docker Socket маркировка за изтегляне на етикети на чернова. Пътят на гнездото трябва да бъде предоставен на Newt.",
|
||||
"enableDockerSocketLink": "Научете повече",
|
||||
@@ -2080,5 +2080,20 @@
|
||||
"supportSending": "Изпращане...",
|
||||
"supportSend": "Изпрати",
|
||||
"supportMessageSent": "Съобщението е изпратено!",
|
||||
"supportWillContact": "Ще се свържем с вас скоро!"
|
||||
}
|
||||
"supportWillContact": "Ще се свържем с вас скоро!",
|
||||
"selectLogRetention": "Изберете съхранение на логовете",
|
||||
"showColumns": "Покажи колони",
|
||||
"hideColumns": "Скрий колони",
|
||||
"columnVisibility": "Видимост на колоните",
|
||||
"toggleColumn": "Превключване на колоната {columnName}",
|
||||
"allColumns": "Всички колони",
|
||||
"defaultColumns": "По подразбиране колони",
|
||||
"customizeView": "Персонализиране на изгледа",
|
||||
"viewOptions": "Опции за изгледа",
|
||||
"selectAll": "Избери всички",
|
||||
"selectNone": "Избери нищо",
|
||||
"selectedResources": "Избрани ресурси",
|
||||
"enableSelected": "Разреши избраните",
|
||||
"disableSelected": "Забрани избраните",
|
||||
"checkSelectedStatus": "Проверете състоянието на избраните"
|
||||
}
|
||||
@@ -131,7 +131,7 @@
|
||||
"expireIn": "Platnost vyprší za",
|
||||
"neverExpire": "Nikdy nevyprší",
|
||||
"shareExpireDescription": "Doba platnosti určuje, jak dlouho bude odkaz použitelný a bude poskytovat přístup ke zdroji. Po této době odkaz již nebude fungovat a uživatelé kteří tento odkaz používali ztratí přístup ke zdroji.",
|
||||
"shareSeeOnce": "Tento odkaz uvidíte pouze jednou. Ujistěte se, že jste jej zkopírovali.",
|
||||
"shareSeeOnce": "Tento odkaz uvidíte pouze jednou. Nezapomeňte jej zkopírovat.",
|
||||
"shareAccessHint": "Kdokoli s tímto odkazem může přistupovat ke zdroji. Sdílejte jej s rozvahou.",
|
||||
"shareTokenUsage": "Zobrazit využití přístupového tokenu",
|
||||
"createLink": "Vytvořit odkaz",
|
||||
@@ -179,7 +179,7 @@
|
||||
"baseDomain": "Základní doména",
|
||||
"subdomnainDescription": "Subdoména, kde bude váš zdroj přístupný.",
|
||||
"resourceRawSettings": "Nastavení TCP/UDP",
|
||||
"resourceRawSettingsDescription": "Nakonfigurujte, jak bude váš dokument přístupný přes TCP/UDP",
|
||||
"resourceRawSettingsDescription": "Nakonfigurujte, jak bude váš dokument přístupný přes TCP/UDP. Mapováte zdroj na port na serveru Pangolin, takže můžete přistupovat ke zdroji ze serveru-veřejné ip:mapped-port.",
|
||||
"protocol": "Protokol",
|
||||
"protocolSelect": "Vybrat protokol",
|
||||
"resourcePortNumber": "Číslo portu",
|
||||
@@ -1165,13 +1165,13 @@
|
||||
"sidebarDomains": "Domény",
|
||||
"sidebarBluePrints": "Plány",
|
||||
"blueprints": "Plány",
|
||||
"blueprintsDescription": "Plány jsou deklarativní YAML konfigurace, které definují vaše zdroje a jejich nastavení",
|
||||
"blueprintsDescription": "Použít deklarativní konfigurace a zobrazit předchozí běhy",
|
||||
"blueprintAdd": "Přidat plán",
|
||||
"blueprintGoBack": "Zobrazit všechny plány",
|
||||
"blueprintCreate": "Vytvořit plán",
|
||||
"blueprintCreateDescription2": "Postupujte podle níže uvedených kroků pro vytvoření a použití nového plánu",
|
||||
"blueprintDetails": "Podrobnosti plánu",
|
||||
"blueprintDetailsDescription": "Podívejte se na detaily běhu plánu",
|
||||
"blueprintDetailsDescription": "Podívejte se na výsledek použitého plánu a případné chyby, které se vyskytly",
|
||||
"blueprintInfo": "Informace o plánu",
|
||||
"message": "Zpráva",
|
||||
"blueprintContentsDescription": "Definujte obsah YAML popisující vaši infrastrukturu",
|
||||
@@ -1181,7 +1181,7 @@
|
||||
"appliedAt": "Použito v",
|
||||
"source": "Zdroj",
|
||||
"contents": "Obsah",
|
||||
"parsedContents": "Parsovaný obsah",
|
||||
"parsedContents": "Parsed content (Pouze pro čtení)",
|
||||
"enableDockerSocket": "Povolit Docker plán",
|
||||
"enableDockerSocketDescription": "Povolte seškrábání štítků na Docker Socket pro popisky plánů. Nová cesta musí být k dispozici.",
|
||||
"enableDockerSocketLink": "Zjistit více",
|
||||
@@ -2080,5 +2080,20 @@
|
||||
"supportSending": "Odesílání...",
|
||||
"supportSend": "Poslat",
|
||||
"supportMessageSent": "Zpráva odeslána!",
|
||||
"supportWillContact": "Brzy budeme v kontaktu!"
|
||||
}
|
||||
"supportWillContact": "Brzy budeme v kontaktu!",
|
||||
"selectLogRetention": "Vyberte záznam",
|
||||
"showColumns": "Zobrazit sloupce",
|
||||
"hideColumns": "Skrýt sloupce",
|
||||
"columnVisibility": "Viditelnost sloupců",
|
||||
"toggleColumn": "Přepnout sloupec {columnName}",
|
||||
"allColumns": "Všechny sloupce",
|
||||
"defaultColumns": "Výchozí sloupce",
|
||||
"customizeView": "Přizpůsobit zobrazení",
|
||||
"viewOptions": "Možnosti zobrazení",
|
||||
"selectAll": "Vybrat vše",
|
||||
"selectNone": "Nevybrat žádný",
|
||||
"selectedResources": "Vybrané zdroje",
|
||||
"enableSelected": "Povolit vybrané",
|
||||
"disableSelected": "Zakázat vybrané",
|
||||
"checkSelectedStatus": "Zkontrolovat stav vybraného"
|
||||
}
|
||||
@@ -131,7 +131,7 @@
|
||||
"expireIn": "Verfällt in",
|
||||
"neverExpire": "Nie ablaufen",
|
||||
"shareExpireDescription": "Ablaufzeit ist, wie lange der Link verwendet werden kann und bietet Zugriff auf die Ressource. Nach dieser Zeit wird der Link nicht mehr funktionieren und Benutzer, die diesen Link benutzt haben, verlieren den Zugriff auf die Ressource.",
|
||||
"shareSeeOnce": "Sie können diesen Link nur ein einziges Mal sehen. Bitte kopieren Sie ihn.",
|
||||
"shareSeeOnce": "Sie können diesen Link nur einmal sehen. Bitte kopieren Sie ihn.",
|
||||
"shareAccessHint": "Jeder mit diesem Link kann auf die Ressource zugreifen. Teilen Sie sie mit Vorsicht.",
|
||||
"shareTokenUsage": "Zugriffstoken-Nutzung anzeigen",
|
||||
"createLink": "Link erstellen",
|
||||
@@ -179,7 +179,7 @@
|
||||
"baseDomain": "Basis-Domain",
|
||||
"subdomnainDescription": "Die Subdomain, auf der Ihre Ressource erreichbar sein soll.",
|
||||
"resourceRawSettings": "TCP/UDP Einstellungen",
|
||||
"resourceRawSettingsDescription": "Konfigurieren Sie den Zugriff auf Ihre Ressource über TCP/UDP",
|
||||
"resourceRawSettingsDescription": "Legen Sie fest, wie auf Ihre Ressource über TCP/UDP zugegriffen wird. Sie ordnen die Ressource einem Port auf dem Pangolin-Server zu, so dass Sie auf die Ressource von server-public-ip:mapped-port zugreifen können.",
|
||||
"protocol": "Protokoll",
|
||||
"protocolSelect": "Wählen Sie ein Protokoll",
|
||||
"resourcePortNumber": "Portnummer",
|
||||
@@ -1021,7 +1021,7 @@
|
||||
"actionDeleteSite": "Standort löschen",
|
||||
"actionGetSite": "Standort abrufen",
|
||||
"actionListSites": "Standorte auflisten",
|
||||
"actionApplyBlueprint": "Blueprint anwenden",
|
||||
"actionApplyBlueprint": "Blaupause anwenden",
|
||||
"setupToken": "Setup-Token",
|
||||
"setupTokenDescription": "Geben Sie das Setup-Token von der Serverkonsole ein.",
|
||||
"setupTokenRequired": "Setup-Token ist erforderlich",
|
||||
@@ -1080,11 +1080,11 @@
|
||||
"actionDeleteIdpOrg": "IDP-Organisationsrichtlinie löschen",
|
||||
"actionListIdpOrgs": "IDP-Organisationen auflisten",
|
||||
"actionUpdateIdpOrg": "IDP-Organisation aktualisieren",
|
||||
"actionCreateClient": "Client erstellen",
|
||||
"actionDeleteClient": "Client löschen",
|
||||
"actionUpdateClient": "Client aktualisieren",
|
||||
"actionListClients": "Clientsn auflisten",
|
||||
"actionGetClient": "Client abrufen",
|
||||
"actionCreateClient": "Kunde erstellen",
|
||||
"actionDeleteClient": "Kunde löschen",
|
||||
"actionUpdateClient": "Kunde aktualisieren",
|
||||
"actionListClients": "Kunden auflisten",
|
||||
"actionGetClient": "Kunde holen",
|
||||
"actionCreateSiteResource": "Site-Ressource erstellen",
|
||||
"actionDeleteSiteResource": "Site-Ressource löschen",
|
||||
"actionGetSiteResource": "Site-Ressource abrufen",
|
||||
@@ -1161,29 +1161,29 @@
|
||||
"sidebarAllUsers": "Alle Benutzer",
|
||||
"sidebarIdentityProviders": "Identitätsanbieter",
|
||||
"sidebarLicense": "Lizenz",
|
||||
"sidebarClients": "Clients",
|
||||
"sidebarClients": "Kunden",
|
||||
"sidebarDomains": "Domänen",
|
||||
"sidebarBluePrints": "Blueprints",
|
||||
"blueprints": "Blueprints",
|
||||
"blueprintsDescription": "Blueprints sind deklarative YAML-Konfigurationen, die deine Ressourcen und deren Einstellungen definieren",
|
||||
"blueprintAdd": "Blueprint hinzufügen",
|
||||
"blueprintGoBack": "Alle Blueprints ansehen",
|
||||
"blueprintCreate": "Blueprint erstellen",
|
||||
"blueprintCreateDescription2": "Folge den Schritten unten, um einen neuen Blueprint zu erstellen und anzuwenden",
|
||||
"blueprintDetails": "Blueprintdetails",
|
||||
"blueprintDetailsDescription": "Siehe die Blueprint Details",
|
||||
"blueprintInfo": "Blueprint Information",
|
||||
"sidebarBluePrints": "Baupläne",
|
||||
"blueprints": "Baupläne",
|
||||
"blueprintsDescription": "Deklarative Konfigurationen anwenden und vorherige Abläufe anzeigen",
|
||||
"blueprintAdd": "Blaupause hinzufügen",
|
||||
"blueprintGoBack": "Alle Blaupausen ansehen",
|
||||
"blueprintCreate": "Blaupause erstellen",
|
||||
"blueprintCreateDescription2": "Folge den Schritten unten, um eine neue Blaupause zu erstellen und anzuwenden",
|
||||
"blueprintDetails": "Blaupausendetails",
|
||||
"blueprintDetailsDescription": "Siehe das Ergebnis der angewendeten Blaupause und alle aufgetretenen Fehler",
|
||||
"blueprintInfo": "Blaupauseninformation",
|
||||
"message": "Nachricht",
|
||||
"blueprintContentsDescription": "Definieren Sie den YAML-Inhalt, der Ihre Infrastruktur beschreibt",
|
||||
"blueprintErrorCreateDescription": "Fehler beim Anwenden des Blueprints",
|
||||
"blueprintErrorCreate": "Fehler beim Erstellen des Blueprints",
|
||||
"searchBlueprintProgress": "Blueprints suchen...",
|
||||
"blueprintErrorCreateDescription": "Fehler beim Anwenden der Blaupause",
|
||||
"blueprintErrorCreate": "Fehler beim Erstellen der Blaupause",
|
||||
"searchBlueprintProgress": "Blaupausen suchen...",
|
||||
"appliedAt": "Angewandt am",
|
||||
"source": "Quelle",
|
||||
"contents": "Inhalt",
|
||||
"parsedContents": "Analysierte Inhalte",
|
||||
"enableDockerSocket": "Docker Blueprints aktivieren",
|
||||
"enableDockerSocketDescription": "Aktiviere Docker-Socket-Label-Scraping für Blueprintbeschriftungen. Der Socket-Pfad muss neu angegeben werden.",
|
||||
"parsedContents": "Analysierte Inhalte (Nur lesen)",
|
||||
"enableDockerSocket": "Docker Blaupause aktivieren",
|
||||
"enableDockerSocketDescription": "Aktiviere Docker-Socket-Label-Scraping für Blaupausenbeschriftungen. Der Socket-Pfad muss neu angegeben werden.",
|
||||
"enableDockerSocketLink": "Mehr erfahren",
|
||||
"viewDockerContainers": "Docker Container anzeigen",
|
||||
"containersIn": "Container in {siteName}",
|
||||
@@ -1423,14 +1423,14 @@
|
||||
},
|
||||
"siteRequired": "Standort ist erforderlich.",
|
||||
"olmTunnel": "Olm-Tunnel",
|
||||
"olmTunnelDescription": "Nutzen Sie Olm für die Clientverbindung",
|
||||
"olmTunnelDescription": "Nutzen Sie Olm für die Kundenverbindung",
|
||||
"errorCreatingClient": "Fehler beim Erstellen des Clients",
|
||||
"clientDefaultsNotFound": "Standardeinstellungen des Clients nicht gefunden",
|
||||
"createClient": "Client erstellen",
|
||||
"createClientDescription": "Erstellen Sie einen neuen Client für die Verbindung zu Ihren Standorten.",
|
||||
"seeAllClients": "Alle Clients anzeigen",
|
||||
"clientInformation": "Clientninformationen",
|
||||
"clientNamePlaceholder": "Clientname",
|
||||
"clientInformation": "Kundeninformationen",
|
||||
"clientNamePlaceholder": "Kundenname",
|
||||
"address": "Adresse",
|
||||
"subnetPlaceholder": "Subnetz",
|
||||
"addressDescription": "Die Adresse, die dieser Client für die Verbindung verwenden wird.",
|
||||
@@ -2049,7 +2049,7 @@
|
||||
"orgOrDomainIdMissing": "Organisation oder Domänen-ID fehlt",
|
||||
"loadingDNSRecords": "Lade DNS-Einträge...",
|
||||
"olmUpdateAvailableInfo": "Eine aktualisierte Version von Olm ist verfügbar. Bitte aktualisieren Sie auf die neueste Version für die beste Erfahrung.",
|
||||
"client": "Client",
|
||||
"client": "Kunde",
|
||||
"proxyProtocol": "Proxy-Protokoll-Einstellungen",
|
||||
"proxyProtocolDescription": "Konfigurieren Sie das Proxy-Protokoll, um die IP-Adressen des Clients für TCP/UDP-Dienste zu erhalten.",
|
||||
"enableProxyProtocol": "Proxy-Protokoll aktivieren",
|
||||
@@ -2080,5 +2080,20 @@
|
||||
"supportSending": "Senden...",
|
||||
"supportSend": "Senden",
|
||||
"supportMessageSent": "Nachricht gesendet!",
|
||||
"supportWillContact": "Wir werden in Kürze kontaktieren!"
|
||||
}
|
||||
"supportWillContact": "Wir werden in Kürze kontaktieren!",
|
||||
"selectLogRetention": "Log-Speicherung auswählen",
|
||||
"showColumns": "Spalten anzeigen",
|
||||
"hideColumns": "Spalten ausblenden",
|
||||
"columnVisibility": "Spaltensichtbarkeit",
|
||||
"toggleColumn": "{columnName} Spalte umschalten",
|
||||
"allColumns": "Alle Spalten",
|
||||
"defaultColumns": "Standardspalten",
|
||||
"customizeView": "Ansicht anpassen",
|
||||
"viewOptions": "Optionen anzeigen",
|
||||
"selectAll": "Alle auswählen",
|
||||
"selectNone": "Nichts auswählen",
|
||||
"selectedResources": "Ausgewählte Ressourcen",
|
||||
"enableSelected": "Ausgewählte aktivieren",
|
||||
"disableSelected": "Ausgewählte deaktivieren",
|
||||
"checkSelectedStatus": "Status der Auswahl überprüfen"
|
||||
}
|
||||
@@ -179,7 +179,7 @@
|
||||
"baseDomain": "Base Domain",
|
||||
"subdomnainDescription": "The subdomain where your resource will be accessible.",
|
||||
"resourceRawSettings": "TCP/UDP Settings",
|
||||
"resourceRawSettingsDescription": "Configure how your resource will be accessed over TCP/UDP. You map the resource to a port on the host Pangolin server, so you can access the resource from <server-public-ip>:<mapped-port>. (https://docs.pangolin.net/manage/resources/tcp-udp-resources)",
|
||||
"resourceRawSettingsDescription": "Configure how your resource will be accessed over TCP/UDP. You map the resource to a port on the host Pangolin server, so you can access the resource from server-public-ip:mapped-port.",
|
||||
"protocol": "Protocol",
|
||||
"protocolSelect": "Select a protocol",
|
||||
"resourcePortNumber": "Port Number",
|
||||
@@ -1165,13 +1165,13 @@
|
||||
"sidebarDomains": "Domains",
|
||||
"sidebarBluePrints": "Blueprints",
|
||||
"blueprints": "Blueprints",
|
||||
"blueprintsDescription": "Blueprints are declarative YAML configurations that define your resources and their settings",
|
||||
"blueprintsDescription": "Apply declarative configurations and view previous runs",
|
||||
"blueprintAdd": "Add Blueprint",
|
||||
"blueprintGoBack": "See all Blueprints",
|
||||
"blueprintCreate": "Create Blueprint",
|
||||
"blueprintCreateDescription2": "Follow the steps below to create and apply a new blueprint",
|
||||
"blueprintDetails": "Blueprint details",
|
||||
"blueprintDetailsDescription": "See the blueprint run details",
|
||||
"blueprintDetails": "Blueprint Details",
|
||||
"blueprintDetailsDescription": "See the result of the applied blueprint and any errors that occurred",
|
||||
"blueprintInfo": "Blueprint Information",
|
||||
"message": "Message",
|
||||
"blueprintContentsDescription": "Define the YAML content describing your infrastructure",
|
||||
@@ -1181,7 +1181,7 @@
|
||||
"appliedAt": "Applied At",
|
||||
"source": "Source",
|
||||
"contents": "Contents",
|
||||
"parsedContents": "Parsed Contents",
|
||||
"parsedContents": "Parsed Contents (Read Only)",
|
||||
"enableDockerSocket": "Enable Docker Blueprint",
|
||||
"enableDockerSocketDescription": "Enable Docker Socket label scraping for blueprint labels. Socket path must be provided to Newt.",
|
||||
"enableDockerSocketLink": "Learn More",
|
||||
@@ -2081,5 +2081,19 @@
|
||||
"supportSend": "Send",
|
||||
"supportMessageSent": "Message Sent!",
|
||||
"supportWillContact": "We'll be in touch shortly!",
|
||||
"selectLogRetention": "Select log retention"
|
||||
}
|
||||
"selectLogRetention": "Select log retention",
|
||||
"showColumns": "Show Columns",
|
||||
"hideColumns": "Hide Columns",
|
||||
"columnVisibility": "Column Visibility",
|
||||
"toggleColumn": "Toggle {columnName} column",
|
||||
"allColumns": "All Columns",
|
||||
"defaultColumns": "Default Columns",
|
||||
"customizeView": "Customize View",
|
||||
"viewOptions": "View Options",
|
||||
"selectAll": "Select All",
|
||||
"selectNone": "Select None",
|
||||
"selectedResources": "Selected Resources",
|
||||
"enableSelected": "Enable Selected",
|
||||
"disableSelected": "Disable Selected",
|
||||
"checkSelectedStatus": "Check Status of Selected"
|
||||
}
|
||||
@@ -131,7 +131,7 @@
|
||||
"expireIn": "Caduca en",
|
||||
"neverExpire": "Nunca expirar",
|
||||
"shareExpireDescription": "El tiempo de caducidad es cuánto tiempo el enlace será utilizable y proporcionará acceso al recurso. Después de este tiempo, el enlace ya no funcionará, y los usuarios que usaron este enlace perderán el acceso al recurso.",
|
||||
"shareSeeOnce": "Sólo podrá ver este enlace una vez. Asegúrese de copiarlo.",
|
||||
"shareSeeOnce": "Sólo podrás ver este enlace una vez. Asegúrate de copiarlo.",
|
||||
"shareAccessHint": "Cualquiera con este enlace puede acceder al recurso. Compártelo con cuidado.",
|
||||
"shareTokenUsage": "Ver Uso de Token de Acceso",
|
||||
"createLink": "Crear enlace",
|
||||
@@ -179,7 +179,7 @@
|
||||
"baseDomain": "Dominio base",
|
||||
"subdomnainDescription": "El subdominio al que su recurso será accesible.",
|
||||
"resourceRawSettings": "Configuración TCP/UDP",
|
||||
"resourceRawSettingsDescription": "Configurar cómo se accederá a su recurso a través de TCP/UDP",
|
||||
"resourceRawSettingsDescription": "Configure cómo se accederá a su recurso a través de TCP/UDP. Mapeas el recurso a un puerto en el servidor Pangolin host, así puedes acceder al recurso desde el servidor-public-ip:mapped-port.",
|
||||
"protocol": "Protocolo",
|
||||
"protocolSelect": "Seleccionar un protocolo",
|
||||
"resourcePortNumber": "Número de puerto",
|
||||
@@ -1165,13 +1165,13 @@
|
||||
"sidebarDomains": "Dominios",
|
||||
"sidebarBluePrints": "Planos",
|
||||
"blueprints": "Planos",
|
||||
"blueprintsDescription": "Los planos son configuraciones YAML declarativas que definen sus recursos y sus configuraciones",
|
||||
"blueprintsDescription": "Aplicar configuraciones declarativas y ver ejecuciones anteriores",
|
||||
"blueprintAdd": "Añadir plano",
|
||||
"blueprintGoBack": "Ver todos los Planos",
|
||||
"blueprintCreate": "Crear Plano",
|
||||
"blueprintCreateDescription2": "Siga los siguientes pasos para crear y aplicar un nuevo plano",
|
||||
"blueprintDetails": "Detalles del plano",
|
||||
"blueprintDetailsDescription": "Ver los detalles de la ejecución del plano",
|
||||
"blueprintDetailsDescription": "Ver el resultado del plano aplicado y cualquier error que haya ocurrido",
|
||||
"blueprintInfo": "Información del plano",
|
||||
"message": "Mensaje",
|
||||
"blueprintContentsDescription": "Defina el contenido YAML describiendo su infraestructura",
|
||||
@@ -1181,7 +1181,7 @@
|
||||
"appliedAt": "Aplicado en",
|
||||
"source": "Fuente",
|
||||
"contents": "Contenido",
|
||||
"parsedContents": "Contenido analizado",
|
||||
"parsedContents": "Contenido analizado (Sólo lectura)",
|
||||
"enableDockerSocket": "Habilitar Plano Docker",
|
||||
"enableDockerSocketDescription": "Activar el raspado de etiquetas de Socket Docker para etiquetas de planos. La ruta del Socket debe proporcionarse a Newt.",
|
||||
"enableDockerSocketLink": "Saber más",
|
||||
@@ -2080,5 +2080,20 @@
|
||||
"supportSending": "Enviando...",
|
||||
"supportSend": "Enviar",
|
||||
"supportMessageSent": "¡Mensaje enviado!",
|
||||
"supportWillContact": "¡Estaremos en contacto en breve!"
|
||||
}
|
||||
"supportWillContact": "¡Estaremos en contacto en breve!",
|
||||
"selectLogRetention": "Seleccionar retención de registro",
|
||||
"showColumns": "Mostrar columnas",
|
||||
"hideColumns": "Ocultar columnas",
|
||||
"columnVisibility": "Visibilidad de la columna",
|
||||
"toggleColumn": "Cambiar columna {columnName}",
|
||||
"allColumns": "Todas las columnas",
|
||||
"defaultColumns": "Columnas por defecto",
|
||||
"customizeView": "Personalizar vista",
|
||||
"viewOptions": "Ver opciones",
|
||||
"selectAll": "Seleccionar todo",
|
||||
"selectNone": "No seleccionar",
|
||||
"selectedResources": "Recursos seleccionados",
|
||||
"enableSelected": "Habilitar seleccionados",
|
||||
"disableSelected": "Desactivar Seleccionado",
|
||||
"checkSelectedStatus": "Comprobar el estado de selección"
|
||||
}
|
||||
@@ -10,20 +10,20 @@
|
||||
"setupErrorIdentifier": "L'ID de l'organisation est déjà pris. Veuillez en choisir un autre.",
|
||||
"componentsErrorNoMemberCreate": "Vous n'êtes actuellement membre d'aucune organisation. Créez une organisation pour commencer.",
|
||||
"componentsErrorNoMember": "Vous n'êtes actuellement membre d'aucune organisation.",
|
||||
"welcome": "Bienvenue à Pangolin",
|
||||
"welcome": "Bienvenue sur Pangolin !",
|
||||
"welcomeTo": "Bienvenue chez",
|
||||
"componentsCreateOrg": "Créer une organisation",
|
||||
"componentsMember": "Vous êtes membre de {count, plural, =0 {aucune organisation} one {une organisation} other {# organisations}}.",
|
||||
"componentsInvalidKey": "Clés de licence invalides ou expirées détectées. Suivez les conditions de licence pour continuer à utiliser toutes les fonctionnalités.",
|
||||
"dismiss": "Refuser",
|
||||
"componentsLicenseViolation": "Violation de licence : Ce serveur utilise des sites {usedSites} qui dépassent la limite autorisée des sites {maxSites} . Suivez les conditions de licence pour continuer à utiliser toutes les fonctionnalités.",
|
||||
"componentsInvalidKey": "Clés de licence invalides ou expirées détectées. Veuillez respecter les conditions de licence pour continuer à utiliser toutes les fonctionnalités.",
|
||||
"dismiss": "Rejeter",
|
||||
"componentsLicenseViolation": "Violation de licence : ce serveur utilise {usedSites} sites, ce qui dépasse la limite autorisée de {maxSites} sites. Respectez les conditions de licence pour continuer à utiliser toutes les fonctionnalités.",
|
||||
"componentsSupporterMessage": "Merci de soutenir Pangolin en tant que {tier}!",
|
||||
"inviteErrorNotValid": "Nous sommes désolés, mais il semble que l'invitation que vous essayez d'accéder n'ait pas été acceptée ou n'est plus valide.",
|
||||
"inviteErrorUser": "Nous sommes désolés, mais il semble que l'invitation que vous essayez d'accéder ne soit pas pour cet utilisateur.",
|
||||
"inviteLoginUser": "Assurez-vous que vous êtes bien connecté en tant qu'utilisateur correct.",
|
||||
"inviteErrorNoUser": "Nous sommes désolés, mais il semble que l'invitation que vous essayez d'accéder ne soit pas pour un utilisateur qui existe.",
|
||||
"inviteErrorNotValid": "Nous sommes désolés, mais il semble que l'invitation à laquelle vous essayez d'accéder n'ait pas été acceptée ou ne soit plus valide.",
|
||||
"inviteErrorUser": "Nous sommes désolés, mais il semble que l'invitation à laquelle vous essayez d'accéder ne soit pas pour cet utilisateur.",
|
||||
"inviteLoginUser": "Veuillez vous assurer que vous êtes connecté avec le bon utilisateur.",
|
||||
"inviteErrorNoUser": "Nous sommes désolés, mais il semble que l'invitation à laquelle vous essayez d'accéder ne concerne pas un utilisateur existant.",
|
||||
"inviteCreateUser": "Veuillez d'abord créer un compte.",
|
||||
"goHome": "Retour à la maison",
|
||||
"goHome": "Retour à l'accueil",
|
||||
"inviteLogInOtherUser": "Se connecter en tant qu'utilisateur différent",
|
||||
"createAnAccount": "Créer un compte",
|
||||
"inviteNotAccepted": "Invitation non acceptée",
|
||||
@@ -39,12 +39,12 @@
|
||||
"online": "En ligne",
|
||||
"offline": "Hors ligne",
|
||||
"site": "Site",
|
||||
"dataIn": "Données dans",
|
||||
"dataOut": "Données épuisées",
|
||||
"dataIn": "Données entrantes",
|
||||
"dataOut": "Données sortantes",
|
||||
"connectionType": "Type de connexion",
|
||||
"tunnelType": "Type de tunnel",
|
||||
"local": "Locale",
|
||||
"edit": "Editer",
|
||||
"edit": "Éditer",
|
||||
"siteConfirmDelete": "Confirmer la suppression du site",
|
||||
"siteDelete": "Supprimer le site",
|
||||
"siteMessageRemove": "Une fois supprimé, le site ne sera plus accessible. Toutes les cibles associées au site seront également supprimées.",
|
||||
@@ -63,11 +63,11 @@
|
||||
"siteLearnNewt": "Apprenez à installer Newt sur votre système",
|
||||
"siteSeeConfigOnce": "Vous ne pourrez voir la configuration qu'une seule fois.",
|
||||
"siteLoadWGConfig": "Chargement de la configuration WireGuard...",
|
||||
"siteDocker": "Développer les détails du déploiement Docker",
|
||||
"siteDocker": "Développer pour obtenir plus de détails sur le déploiement Docker",
|
||||
"toggle": "Activer/désactiver",
|
||||
"dockerCompose": "Composition Docker",
|
||||
"dockerRun": "Exécution Docker",
|
||||
"siteLearnLocal": "Les sites locaux ne tunnel, en savoir plus",
|
||||
"siteLearnLocal": "Les sites locaux ne font pas de tunnel, en savoir plus",
|
||||
"siteConfirmCopy": "J'ai copié la configuration",
|
||||
"searchSitesProgress": "Rechercher des sites...",
|
||||
"siteAdd": "Ajouter un site",
|
||||
@@ -78,7 +78,7 @@
|
||||
"operatingSystem": "Système d'exploitation",
|
||||
"commands": "Commandes",
|
||||
"recommended": "Recommandé",
|
||||
"siteNewtDescription": "Pour une meilleure expérience d'utilisateur, utilisez Newt. Il utilise WireGuard sous le capot et vous permet d'adresser vos ressources privées par leur adresse LAN sur votre réseau privé à partir du tableau de bord Pangolin.",
|
||||
"siteNewtDescription": "Pour une meilleure expérience d'utilisateur, utilisez Newt. Il utilise WireGuard sous le capot et vous permet de vous connecter à vos ressources privées par leur adresse LAN sur votre réseau privé à partir du tableau de bord Pangolin.",
|
||||
"siteRunsInDocker": "Exécute dans Docker",
|
||||
"siteRunsInShell": "Exécute en shell sur macOS, Linux et Windows",
|
||||
"siteErrorDelete": "Erreur lors de la suppression du site",
|
||||
@@ -93,7 +93,7 @@
|
||||
"siteNewtTunnelDescription": "La façon la plus simple de créer un point d'entrée dans votre réseau. Pas de configuration supplémentaire.",
|
||||
"siteWg": "WireGuard basique",
|
||||
"siteWgDescription": "Utilisez n'importe quel client WireGuard pour établir un tunnel. Configuration NAT manuelle requise.",
|
||||
"siteWgDescriptionSaas": "Utilisez n'importe quel client WireGuard pour établir un tunnel. Configuration NAT manuelle requise. FONCTIONNE UNIQUEMENT SUR DES NŒUDS AUTONOMES",
|
||||
"siteWgDescriptionSaas": "Utilisez n'importe quel client WireGuard pour établir un tunnel. Configuration NAT manuelle requise. FONCTIONNE UNIQUEMENT SUR DES NŒUDS AUTONOMES.",
|
||||
"siteLocalDescription": "Ressources locales seulement. Pas de tunneling.",
|
||||
"siteLocalDescriptionSaas": "Ressources locales uniquement. Pas de tunneling. Disponible uniquement sur les nœuds distants.",
|
||||
"siteSeeAll": "Voir tous les sites",
|
||||
@@ -130,9 +130,9 @@
|
||||
"shareTitleOptional": "Titre (facultatif)",
|
||||
"expireIn": "Expire dans",
|
||||
"neverExpire": "N'expire jamais",
|
||||
"shareExpireDescription": "Le temps d'expiration est combien de temps le lien sera utilisable et fournira un accès à la ressource. Après cette période, le lien ne fonctionnera plus et les utilisateurs qui ont utilisé ce lien perdront l'accès à la ressource.",
|
||||
"shareSeeOnce": "Vous ne pourrez voir ce lien. Assurez-vous de le copier.",
|
||||
"shareAccessHint": "N'importe qui avec ce lien peut accéder à la ressource. Partagez-le avec soin.",
|
||||
"shareExpireDescription": "La durée d'expiration correspond à la période pendant laquelle le lien sera utilisable et permettra d'accéder à la ressource. Passé ce délai, le lien ne fonctionnera plus et les utilisateurs qui l'ont utilisé perdront l'accès à la ressource.",
|
||||
"shareSeeOnce": "Vous ne pourrez voir ce lien qu'une seule fois. Assurez-vous de le copier.",
|
||||
"shareAccessHint": "N'importe qui avec ce lien peut accéder à la ressource. Partagez-le avec précaution.",
|
||||
"shareTokenUsage": "Voir Utilisation du jeton d'accès",
|
||||
"createLink": "Créer un lien",
|
||||
"resourcesNotFound": "Aucune ressource trouvée",
|
||||
@@ -155,9 +155,9 @@
|
||||
"resourceMessageRemove": "Une fois supprimée, la ressource ne sera plus accessible. Toutes les cibles associées à la ressource seront également supprimées.",
|
||||
"resourceQuestionRemove": "Êtes-vous sûr de vouloir supprimer la ressource de l'organisation ?",
|
||||
"resourceHTTP": "Ressource HTTPS",
|
||||
"resourceHTTPDescription": "Requêtes de proxy à votre application via HTTPS en utilisant un sous-domaine ou un domaine de base.",
|
||||
"resourceHTTPDescription": "Requêtes de proxy vers votre application via HTTPS en utilisant un sous-domaine ou un domaine de base.",
|
||||
"resourceRaw": "Ressource TCP/UDP brute",
|
||||
"resourceRawDescription": "Demandes de proxy à votre application via TCP/UDP en utilisant un numéro de port.",
|
||||
"resourceRawDescription": "Demandes de proxy vers votre application via TCP/UDP en utilisant un numéro de port.",
|
||||
"resourceCreate": "Créer une ressource",
|
||||
"resourceCreateDescription": "Suivez les étapes ci-dessous pour créer une nouvelle ressource",
|
||||
"resourceSeeAll": "Voir toutes les ressources",
|
||||
@@ -179,7 +179,7 @@
|
||||
"baseDomain": "Domaine de base",
|
||||
"subdomnainDescription": "Le sous-domaine où votre ressource sera accessible.",
|
||||
"resourceRawSettings": "Paramètres TCP/UDP",
|
||||
"resourceRawSettingsDescription": "Configurer comment votre ressource sera accédée via TCP/UDP",
|
||||
"resourceRawSettingsDescription": "Configurer comment votre ressource sera accédée via TCP/UDP. Vous mappez la ressource à un port sur le serveur Pangolin, de sorte que vous puissiez accéder à la ressource depuis server-public-ip:mapped-port.",
|
||||
"protocol": "Protocole",
|
||||
"protocolSelect": "Sélectionner un protocole",
|
||||
"resourcePortNumber": "Numéro de port",
|
||||
@@ -206,7 +206,7 @@
|
||||
"resourceSetting": "Réglages {resourceName}",
|
||||
"alwaysAllow": "Toujours autoriser",
|
||||
"alwaysDeny": "Toujours refuser",
|
||||
"passToAuth": "Paser à l'authentification",
|
||||
"passToAuth": "Passer à l'authentification",
|
||||
"orgSettingsDescription": "Configurer les paramètres généraux de votre organisation",
|
||||
"orgGeneralSettings": "Paramètres de l'organisation",
|
||||
"orgGeneralSettingsDescription": "Gérer les détails et la configuration de votre organisation",
|
||||
@@ -342,7 +342,7 @@
|
||||
"licenseTitleDescription": "Voir et gérer les clés de licence dans le système",
|
||||
"licenseHost": "Licence Hôte",
|
||||
"licenseHostDescription": "Gérer la clé de licence principale de l'hôte.",
|
||||
"licensedNot": "Non licencié",
|
||||
"licensedNot": "Pas de licence",
|
||||
"hostId": "ID de l'hôte",
|
||||
"licenseReckeckAll": "Revérifier toutes les clés",
|
||||
"licenseSiteUsage": "Utilisation des sites",
|
||||
@@ -350,7 +350,7 @@
|
||||
"licenseNoSiteLimit": "Il n'y a pas de limite sur le nombre de sites utilisant un hôte non autorisé.",
|
||||
"licensePurchase": "Acheter une licence",
|
||||
"licensePurchaseSites": "Acheter des sites supplémentaires",
|
||||
"licenseSitesUsedMax": "{usedSites} des sites {maxSites} utilisés",
|
||||
"licenseSitesUsedMax": "{usedSites} des {maxSites} sites utilisés",
|
||||
"licenseSitesUsed": "{count, plural, =0 {# sites} one {# site} other {# sites}} dans le système.",
|
||||
"licensePurchaseDescription": "Choisissez le nombre de sites que vous voulez {selectedMode, select, license {achetez une licence. Vous pouvez toujours ajouter plus de sites plus tard.} other {ajouter à votre licence existante.}}",
|
||||
"licenseFee": "Frais de licence",
|
||||
@@ -371,7 +371,7 @@
|
||||
"inviteQuestionRemove": "Êtes-vous sûr de vouloir supprimer l'invitation?",
|
||||
"inviteMessageRemove": "Une fois supprimée, cette invitation ne sera plus valide. Vous pourrez toujours réinviter l'utilisateur plus tard.",
|
||||
"inviteMessageConfirm": "Pour confirmer, veuillez saisir l'adresse e-mail de l'invitation ci-dessous.",
|
||||
"inviteQuestionRegenerate": "Êtes-vous sûr de vouloir régénérer l'invitation {email}? Cela révoquera l'invitation précédente.",
|
||||
"inviteQuestionRegenerate": "Êtes-vous sûr de vouloir régénérer l'invitation pour {email}? Cela révoquera l'invitation précédente.",
|
||||
"inviteRemoveConfirm": "Confirmer la suppression de l'invitation",
|
||||
"inviteRegenerated": "Invitation régénérée",
|
||||
"inviteSent": "Une nouvelle invitation a été envoyée à {email}.",
|
||||
@@ -465,7 +465,7 @@
|
||||
"proxyErrorTls": "Nom de serveur TLS invalide. Utilisez le format de nom de domaine, ou laissez vide pour supprimer le nom de serveur TLS.",
|
||||
"proxyEnableSSL": "Activer SSL",
|
||||
"proxyEnableSSLDescription": "Activez le cryptage SSL/TLS pour des connexions HTTPS sécurisées vers vos cibles.",
|
||||
"target": "Target",
|
||||
"target": "Cible",
|
||||
"configureTarget": "Configurer les cibles",
|
||||
"targetErrorFetch": "Échec de la récupération des cibles",
|
||||
"targetErrorFetchDescription": "Une erreur s'est produite lors de la récupération des cibles",
|
||||
@@ -1165,13 +1165,13 @@
|
||||
"sidebarDomains": "Domaines",
|
||||
"sidebarBluePrints": "Plans",
|
||||
"blueprints": "Plans",
|
||||
"blueprintsDescription": "Les plans sont des configurations YAML déclaratives qui définissent vos ressources et leurs paramètres",
|
||||
"blueprintsDescription": "Appliquer les configurations déclaratives et afficher les exécutions précédentes",
|
||||
"blueprintAdd": "Ajouter un Plan",
|
||||
"blueprintGoBack": "Voir tous les plans",
|
||||
"blueprintCreate": "Créer un Plan",
|
||||
"blueprintCreateDescription2": "Suivez les étapes ci-dessous pour créer et appliquer un nouveau plan",
|
||||
"blueprintDetails": "Détails du Plan",
|
||||
"blueprintDetailsDescription": "Voir les détails de l'exécution des plans",
|
||||
"blueprintDetailsDescription": "Voir le résultat du plan appliqué et les erreurs qui se sont produites",
|
||||
"blueprintInfo": "Informations sur le Plan",
|
||||
"message": "Message",
|
||||
"blueprintContentsDescription": "Définissez le contenu YAML décrivant votre infrastructure",
|
||||
@@ -1181,7 +1181,7 @@
|
||||
"appliedAt": "Appliqué à",
|
||||
"source": "Source",
|
||||
"contents": "Contenus",
|
||||
"parsedContents": "Contenu analysé",
|
||||
"parsedContents": "Contenu analysé (lecture seule)",
|
||||
"enableDockerSocket": "Activer le Plan Docker",
|
||||
"enableDockerSocketDescription": "Activer le ramassage d'étiquettes de socket Docker pour les étiquettes de plan. Le chemin de socket doit être fourni à Newt.",
|
||||
"enableDockerSocketLink": "En savoir plus",
|
||||
@@ -2080,5 +2080,20 @@
|
||||
"supportSending": "Envoi...",
|
||||
"supportSend": "Envoyer",
|
||||
"supportMessageSent": "Message envoyé !",
|
||||
"supportWillContact": "Nous vous contacterons sous peu!"
|
||||
}
|
||||
"supportWillContact": "Nous vous contacterons sous peu!",
|
||||
"selectLogRetention": "Sélectionner la durée de rétention du journal",
|
||||
"showColumns": "Afficher les colonnes",
|
||||
"hideColumns": "Cacher les colonnes",
|
||||
"columnVisibility": "Visibilité des colonnes",
|
||||
"toggleColumn": "Activer/désactiver la colonne {columnName}",
|
||||
"allColumns": "Toutes les colonnes",
|
||||
"defaultColumns": "Colonnes par défaut",
|
||||
"customizeView": "Personnaliser la vue",
|
||||
"viewOptions": "Voir les options",
|
||||
"selectAll": "Tout sélectionner",
|
||||
"selectNone": "Ne rien sélectionner",
|
||||
"selectedResources": "Ressources sélectionnées",
|
||||
"enableSelected": "Activer la sélection",
|
||||
"disableSelected": "Désactiver la sélection",
|
||||
"checkSelectedStatus": "Vérifier le statut de la sélection"
|
||||
}
|
||||
@@ -131,7 +131,7 @@
|
||||
"expireIn": "Scadenza In",
|
||||
"neverExpire": "Mai scadere",
|
||||
"shareExpireDescription": "Il tempo di scadenza è per quanto tempo il link sarà utilizzabile e fornirà accesso alla risorsa. Dopo questo tempo, il link non funzionerà più e gli utenti che hanno utilizzato questo link perderanno l'accesso alla risorsa.",
|
||||
"shareSeeOnce": "Potrai vedere solo questo linkonce. Assicurati di copiarlo.",
|
||||
"shareSeeOnce": "Potrai vedere questo link solo una volta. Assicurati di copiarlo.",
|
||||
"shareAccessHint": "Chiunque abbia questo link può accedere alla risorsa. Condividilo con cura.",
|
||||
"shareTokenUsage": "Vedi Utilizzo Token Di Accesso",
|
||||
"createLink": "Crea Collegamento",
|
||||
@@ -179,7 +179,7 @@
|
||||
"baseDomain": "Dominio Base",
|
||||
"subdomnainDescription": "Il sottodominio in cui la tua risorsa sarà accessibile.",
|
||||
"resourceRawSettings": "Impostazioni TCP/UDP",
|
||||
"resourceRawSettingsDescription": "Configura come accedere alla tua risorsa tramite TCP/UDP",
|
||||
"resourceRawSettingsDescription": "Configura come sarà possibile accedere alla tua risorsa tramite TCP/UDP. Mappare la risorsa a una porta sul server host Pangolin, in modo da poter accedere alla risorsa dal server-public ip:mapped-port.",
|
||||
"protocol": "Protocollo",
|
||||
"protocolSelect": "Seleziona un protocollo",
|
||||
"resourcePortNumber": "Numero Porta",
|
||||
@@ -1165,13 +1165,13 @@
|
||||
"sidebarDomains": "Domini",
|
||||
"sidebarBluePrints": "Progetti",
|
||||
"blueprints": "Progetti",
|
||||
"blueprintsDescription": "I progetti sono configurazioni YAML dichiarative che definiscono le tue risorse e le loro impostazioni",
|
||||
"blueprintsDescription": "Applica le configurazioni dichiarative e visualizza le partite precedenti",
|
||||
"blueprintAdd": "Aggiungi Progetto",
|
||||
"blueprintGoBack": "Vedi tutti i progetti",
|
||||
"blueprintCreate": "Crea Progetto",
|
||||
"blueprintCreateDescription2": "Segui i passaggi qui sotto per creare e applicare un nuovo progetto",
|
||||
"blueprintDetails": "Dettagli progetto",
|
||||
"blueprintDetailsDescription": "Vedi i dettagli dell'esecuzione del progetto",
|
||||
"blueprintDetails": "Dettagli Progetto",
|
||||
"blueprintDetailsDescription": "Vedere il risultato del progetto applicato e gli eventuali errori verificatisi",
|
||||
"blueprintInfo": "Informazioni Sul Progetto",
|
||||
"message": "Messaggio",
|
||||
"blueprintContentsDescription": "Definisci il contenuto di YAML che descrive la tua infrastruttura",
|
||||
@@ -1181,7 +1181,7 @@
|
||||
"appliedAt": "Applicato Il",
|
||||
"source": "Fonte",
|
||||
"contents": "Contenuti",
|
||||
"parsedContents": "Sommario Analizzato",
|
||||
"parsedContents": "Sommario Analizzato (Solo Lettura)",
|
||||
"enableDockerSocket": "Abilita Progetto Docker",
|
||||
"enableDockerSocketDescription": "Abilita la raschiatura dell'etichetta Docker Socket per le etichette dei progetti. Il percorso del socket deve essere fornito a Newt.",
|
||||
"enableDockerSocketLink": "Scopri di più",
|
||||
@@ -2080,5 +2080,20 @@
|
||||
"supportSending": "Invio...",
|
||||
"supportSend": "Invia",
|
||||
"supportMessageSent": "Messaggio Inviato!",
|
||||
"supportWillContact": "Saremo in contatto a breve!"
|
||||
}
|
||||
"supportWillContact": "Saremo in contatto a breve!",
|
||||
"selectLogRetention": "Seleziona ritenzione log",
|
||||
"showColumns": "Mostra Colonne",
|
||||
"hideColumns": "Nascondi Colonne",
|
||||
"columnVisibility": "Visibilità Colonna",
|
||||
"toggleColumn": "Attiva/disattiva colonna {columnName}",
|
||||
"allColumns": "Tutte Le Colonne",
|
||||
"defaultColumns": "Colonne Predefinite",
|
||||
"customizeView": "Personalizza Vista",
|
||||
"viewOptions": "Opzioni Visualizzazione",
|
||||
"selectAll": "Seleziona Tutto",
|
||||
"selectNone": "Seleziona Nessuno",
|
||||
"selectedResources": "Risorse Selezionate",
|
||||
"enableSelected": "Abilita Selezionati",
|
||||
"disableSelected": "Disabilita Selezionati",
|
||||
"checkSelectedStatus": "Controlla lo stato dei selezionati"
|
||||
}
|
||||
@@ -179,7 +179,7 @@
|
||||
"baseDomain": "기본 도메인",
|
||||
"subdomnainDescription": "리소스에 접근할 수 있는 하위 도메인입니다.",
|
||||
"resourceRawSettings": "TCP/UDP 설정",
|
||||
"resourceRawSettingsDescription": "TCP/UDP를 통해 리소스에 접근하는 방법을 구성하세요.",
|
||||
"resourceRawSettingsDescription": "리소스를 TCP/UDP를 통해 액세스하는 방법을 구성합니다. 리소스를 호스트 Pangolin 서버의 포트에 매핑하여 서버-public-ip:매핑된 포트에서 리소스에 액세스할 수 있습니다.",
|
||||
"protocol": "프로토콜",
|
||||
"protocolSelect": "프로토콜 선택",
|
||||
"resourcePortNumber": "포트 번호",
|
||||
@@ -1165,13 +1165,13 @@
|
||||
"sidebarDomains": "도메인",
|
||||
"sidebarBluePrints": "청사진",
|
||||
"blueprints": "청사진",
|
||||
"blueprintsDescription": "청사진은 리소스와 그 설정을 정의하는 선언적인 YAML 구성입니다",
|
||||
"blueprintsDescription": "선언적 구성을 적용하고 이전 실행을 봅니다",
|
||||
"blueprintAdd": "청사진 추가",
|
||||
"blueprintGoBack": "모든 청사진 보기",
|
||||
"blueprintCreate": "청사진 생성",
|
||||
"blueprintCreateDescription2": "새 청사진을 생성하고 적용하려면 아래 단계를 따르십시오",
|
||||
"blueprintDetails": "청사진 세부 사항",
|
||||
"blueprintDetailsDescription": "청사진 실행 세부 정보 보기",
|
||||
"blueprintDetails": "청사진 세부사항",
|
||||
"blueprintDetailsDescription": "적용된 청사진의 결과와 발생한 오류를 확인합니다",
|
||||
"blueprintInfo": "청사진 정보",
|
||||
"message": "메시지",
|
||||
"blueprintContentsDescription": "인프라를 설명하는 YAML 콘텐츠를 정의하십시오",
|
||||
@@ -1181,7 +1181,7 @@
|
||||
"appliedAt": "적용 시점",
|
||||
"source": "출처",
|
||||
"contents": "콘텐츠",
|
||||
"parsedContents": "구문 분석된 콘텐츠",
|
||||
"parsedContents": "구문 분석된 콘텐츠 (읽기 전용)",
|
||||
"enableDockerSocket": "Docker 청사진 활성화",
|
||||
"enableDockerSocketDescription": "블루프린트 레이블을 위한 Docker 소켓 레이블 수집을 활성화합니다. 소켓 경로는 Newt에 제공되어야 합니다.",
|
||||
"enableDockerSocketLink": "자세히 알아보기",
|
||||
@@ -2080,5 +2080,20 @@
|
||||
"supportSending": "발송 중...",
|
||||
"supportSend": "보내기",
|
||||
"supportMessageSent": "메시지 전송 완료!",
|
||||
"supportWillContact": "곧 연락드리겠습니다!"
|
||||
}
|
||||
"supportWillContact": "곧 연락드리겠습니다!",
|
||||
"selectLogRetention": "로그 보존 선택",
|
||||
"showColumns": "열 표시",
|
||||
"hideColumns": "열 숨기기",
|
||||
"columnVisibility": "열 가시성",
|
||||
"toggleColumn": "{columnName} 열 토글",
|
||||
"allColumns": "모든 열",
|
||||
"defaultColumns": "기본 열",
|
||||
"customizeView": "보기 사용자 지정",
|
||||
"viewOptions": "보기 옵션",
|
||||
"selectAll": "모두 선택",
|
||||
"selectNone": "선택하지 않음",
|
||||
"selectedResources": "선택된 리소스",
|
||||
"enableSelected": "선택된 항목 활성화",
|
||||
"disableSelected": "선택된 항목 비활성화",
|
||||
"checkSelectedStatus": "선택된 항목 상태 확인"
|
||||
}
|
||||
@@ -131,7 +131,7 @@
|
||||
"expireIn": "Utløper om",
|
||||
"neverExpire": "Utløper aldri",
|
||||
"shareExpireDescription": "Utløpstid er hvor lenge lenken vil være brukbar og gi tilgang til ressursen. Etter denne tiden vil lenken ikke lenger fungere, og brukere som brukte denne lenken vil miste tilgangen til ressursen.",
|
||||
"shareSeeOnce": "Du får bare se denne lenken én gang. Pass på å kopiere den.",
|
||||
"shareSeeOnce": "Du vil bare kunne se denne linken én gang. Pass på å kopiere den.",
|
||||
"shareAccessHint": "Alle med denne lenken kan få tilgang til ressursen. Del forsiktig.",
|
||||
"shareTokenUsage": "Se tilgangstokenbruk",
|
||||
"createLink": "Opprett lenke",
|
||||
@@ -179,7 +179,7 @@
|
||||
"baseDomain": "Grunndomene",
|
||||
"subdomnainDescription": "Underdomenet der ressursen din vil være tilgjengelig.",
|
||||
"resourceRawSettings": "TCP/UDP-innstillinger",
|
||||
"resourceRawSettingsDescription": "Konfigurer tilgang til ressursen din over TCP/UDP",
|
||||
"resourceRawSettingsDescription": "Konfigurer hvordan din ressurs vil bli tilgjengelig over TCP/UDP. Du kartlegger ressursen til en port på vertsserveren Pangolin slik at du får tilgang til ressursene fra server-ip:mappet port.",
|
||||
"protocol": "Protokoll",
|
||||
"protocolSelect": "Velg en protokoll",
|
||||
"resourcePortNumber": "Portnummer",
|
||||
@@ -1165,13 +1165,13 @@
|
||||
"sidebarDomains": "Domener",
|
||||
"sidebarBluePrints": "Tegninger",
|
||||
"blueprints": "Tegninger",
|
||||
"blueprintsDescription": "Tegninger er deklarative YAML konfigurasjoner som definerer dine ressurser og deres innstillinger",
|
||||
"blueprintsDescription": "Bruk deklarative konfigurasjoner og vis tidligere kjøringer",
|
||||
"blueprintAdd": "Legg til blåkopi",
|
||||
"blueprintGoBack": "Se alle blåkopier",
|
||||
"blueprintCreate": "Opprette mal",
|
||||
"blueprintCreateDescription2": "Følg trinnene nedenfor for å opprette og bruke en ny plantegning",
|
||||
"blueprintDetails": "Blåkopi detaljer",
|
||||
"blueprintDetailsDescription": "Se detaljer om plantegning",
|
||||
"blueprintDetailsDescription": "Se resultatet av den påførte blåkopien og alle feil som oppstod",
|
||||
"blueprintInfo": "Blåkopi informasjon",
|
||||
"message": "Melding",
|
||||
"blueprintContentsDescription": "Definer innhold av YAML som beskriver din infrastruktur",
|
||||
@@ -1181,7 +1181,7 @@
|
||||
"appliedAt": "Anvendt på",
|
||||
"source": "Kilde",
|
||||
"contents": "Innhold",
|
||||
"parsedContents": "Parket innhold",
|
||||
"parsedContents": "Parastinnhold (kun lese)",
|
||||
"enableDockerSocket": "Aktiver Docker blåkopi",
|
||||
"enableDockerSocketDescription": "Aktiver skraping av Docker Socket for blueprint Etiketter. Socket bane må brukes for nye.",
|
||||
"enableDockerSocketLink": "Lær mer",
|
||||
@@ -2080,5 +2080,20 @@
|
||||
"supportSending": "Sender...",
|
||||
"supportSend": "Sende",
|
||||
"supportMessageSent": "Melding sendt!",
|
||||
"supportWillContact": "Vi kommer raskt til å ta kontakt!"
|
||||
}
|
||||
"supportWillContact": "Vi kommer raskt til å ta kontakt!",
|
||||
"selectLogRetention": "Velg oppbevaring av logg",
|
||||
"showColumns": "Vis kolonner",
|
||||
"hideColumns": "Skjul kolonner",
|
||||
"columnVisibility": "Kolonne Synlighet",
|
||||
"toggleColumn": "Veksle {columnName} kolonne",
|
||||
"allColumns": "Alle kolonner",
|
||||
"defaultColumns": "Standard kolonner",
|
||||
"customizeView": "Tilpass visning",
|
||||
"viewOptions": "Vis alternativer",
|
||||
"selectAll": "Velg alle",
|
||||
"selectNone": "Velg ingen",
|
||||
"selectedResources": "Valgte ressurser",
|
||||
"enableSelected": "Aktiver valgte",
|
||||
"disableSelected": "Deaktiver valgte",
|
||||
"checkSelectedStatus": "Kontroller status for valgte"
|
||||
}
|
||||
@@ -131,7 +131,7 @@
|
||||
"expireIn": "Vervalt in",
|
||||
"neverExpire": "Nooit verlopen",
|
||||
"shareExpireDescription": "Vervaltijd is hoe lang de link bruikbaar is en geeft toegang tot de bron. Na deze tijd zal de link niet meer werken en zullen gebruikers die deze link hebben gebruikt de toegang tot de pagina verliezen.",
|
||||
"shareSeeOnce": "Je kunt deze koppeling alleen zien. Zorg ervoor dat je het kopieert.",
|
||||
"shareSeeOnce": "U kunt deze link slechts één keer zien. Zorg ervoor dat u deze kopieert.",
|
||||
"shareAccessHint": "Iedereen met deze link heeft toegang tot de bron. Deel deze met zorg.",
|
||||
"shareTokenUsage": "Zie Toegangstoken Gebruik",
|
||||
"createLink": "Koppeling aanmaken",
|
||||
@@ -179,7 +179,7 @@
|
||||
"baseDomain": "Basis domein",
|
||||
"subdomnainDescription": "Het subdomein waar de bron toegankelijk is.",
|
||||
"resourceRawSettings": "TCP/UDP instellingen",
|
||||
"resourceRawSettingsDescription": "Stel in hoe je bron wordt benaderd via TCP/UDP",
|
||||
"resourceRawSettingsDescription": "Stel in hoe uw bron wordt benaderd via TCP/UDP. Je gooit de bron toe aan een poort op de host-Pangolin server, zodat je de bron kan bereiken vanaf server-public-ip:mapped-port.",
|
||||
"protocol": "Protocol",
|
||||
"protocolSelect": "Selecteer een protocol",
|
||||
"resourcePortNumber": "Nummer van poort",
|
||||
@@ -1165,13 +1165,13 @@
|
||||
"sidebarDomains": "Domeinen",
|
||||
"sidebarBluePrints": "Blauwdrukken",
|
||||
"blueprints": "Blauwdrukken",
|
||||
"blueprintsDescription": "Blauwdrukken zijn declaratieve YAML-configuraties die je bronnen en hun instellingen bepalen",
|
||||
"blueprintsDescription": "Gebruik declaratieve configuraties en bekijk vorige uitvoeringen.",
|
||||
"blueprintAdd": "Blauwdruk toevoegen",
|
||||
"blueprintGoBack": "Bekijk alle Blauwdrukken",
|
||||
"blueprintCreate": "Creëer blauwdruk",
|
||||
"blueprintCreateDescription2": "Volg de onderstaande stappen om een nieuwe blauwdruk te maken en toe te passen",
|
||||
"blueprintDetails": "Blauwdruk details",
|
||||
"blueprintDetailsDescription": "Bekijk de blauwdruk run details",
|
||||
"blueprintDetails": "Blauwdruk Details",
|
||||
"blueprintDetailsDescription": "Bekijk het resultaat van de toegepaste blauwdruk en eventuele fouten",
|
||||
"blueprintInfo": "Blauwdruk Informatie",
|
||||
"message": "bericht",
|
||||
"blueprintContentsDescription": "Definieer de YAML content die je infrastructuur beschrijft",
|
||||
@@ -1181,7 +1181,7 @@
|
||||
"appliedAt": "Toegepast op",
|
||||
"source": "Bron",
|
||||
"contents": "Inhoud",
|
||||
"parsedContents": "Geparseerde inhoud",
|
||||
"parsedContents": "Geparseerde inhoud (alleen lezen)",
|
||||
"enableDockerSocket": "Schakel Docker Blauwdruk in",
|
||||
"enableDockerSocketDescription": "Schakel Docker Socket label in voor blauwdruk labels. Pad naar Nieuw.",
|
||||
"enableDockerSocketLink": "Meer informatie",
|
||||
@@ -2080,5 +2080,20 @@
|
||||
"supportSending": "Verzenden...",
|
||||
"supportSend": "Verzenden",
|
||||
"supportMessageSent": "Bericht verzonden!",
|
||||
"supportWillContact": "We nemen binnenkort contact met u op!"
|
||||
}
|
||||
"supportWillContact": "We nemen binnenkort contact met u op!",
|
||||
"selectLogRetention": "Selecteer log retentie",
|
||||
"showColumns": "Kolommen weergeven",
|
||||
"hideColumns": "Kolommen verbergen",
|
||||
"columnVisibility": "Zichtbaarheid kolommen",
|
||||
"toggleColumn": "{columnName} kolom in-/uitschakelen",
|
||||
"allColumns": "Alle kolommen",
|
||||
"defaultColumns": "Standaard Kolommen",
|
||||
"customizeView": "Weergave aanpassen",
|
||||
"viewOptions": "Bekijk opties",
|
||||
"selectAll": "Alles selecteren",
|
||||
"selectNone": "Niets selecteren",
|
||||
"selectedResources": "Geselecteerde bronnen",
|
||||
"enableSelected": "Selectie inschakelen",
|
||||
"disableSelected": "Selectie uitschakelen",
|
||||
"checkSelectedStatus": "Controleer de status van de geselecteerde"
|
||||
}
|
||||
@@ -131,7 +131,7 @@
|
||||
"expireIn": "Wygasa za",
|
||||
"neverExpire": "Nigdy nie wygasa",
|
||||
"shareExpireDescription": "Czas wygaśnięcia to jak długo link będzie mógł być użyty i zapewni dostęp do zasobu. Po tym czasie link nie będzie już działał, a użytkownicy, którzy użyli tego linku, utracą dostęp do zasobu.",
|
||||
"shareSeeOnce": "Możesz zobaczyć tylko ten link. Upewnij się, że go skopiowało.",
|
||||
"shareSeeOnce": "Możesz zobaczyć ten link tylko raz. Pamiętaj, aby go skopiować.",
|
||||
"shareAccessHint": "Każdy z tym linkiem może uzyskać dostęp do zasobu. Podziel się nim ostrożnie.",
|
||||
"shareTokenUsage": "Zobacz użycie tokenu dostępu",
|
||||
"createLink": "Utwórz link",
|
||||
@@ -179,7 +179,7 @@
|
||||
"baseDomain": "Bazowa domena",
|
||||
"subdomnainDescription": "Poddomena, w której twój zasób będzie dostępny.",
|
||||
"resourceRawSettings": "Ustawienia TCP/UDP",
|
||||
"resourceRawSettingsDescription": "Skonfiguruj jak twój zasób będzie dostępny przez TCP/UDP",
|
||||
"resourceRawSettingsDescription": "Skonfiguruj jak twój zasób będzie dostępny przez TCP/UDP. Zmapujesz zasób do portu na serwerze hosta Pangolin, dzięki czemu możesz uzyskać dostęp do zasobu z serwera-public ip:mapped-port.",
|
||||
"protocol": "Protokół",
|
||||
"protocolSelect": "Wybierz protokół",
|
||||
"resourcePortNumber": "Numer portu",
|
||||
@@ -1165,13 +1165,13 @@
|
||||
"sidebarDomains": "Domeny",
|
||||
"sidebarBluePrints": "Schematy",
|
||||
"blueprints": "Schematy",
|
||||
"blueprintsDescription": "Plany to deklaratywne konfiguracje YAML, które definiują twoje zasoby i ich ustawienia",
|
||||
"blueprintsDescription": "Zastosuj konfiguracje deklaracyjne i wyświetl poprzednie operacje",
|
||||
"blueprintAdd": "Dodaj schemat",
|
||||
"blueprintGoBack": "Zobacz wszystkie schematy",
|
||||
"blueprintCreate": "Utwórz schemat",
|
||||
"blueprintCreateDescription2": "Wykonaj poniższe kroki, aby utworzyć i zastosować nowy schemat",
|
||||
"blueprintDetails": "Szczegóły projektu",
|
||||
"blueprintDetailsDescription": "Zobacz szczegóły uruchomienia schematu",
|
||||
"blueprintDetails": "Szczegóły Projektu",
|
||||
"blueprintDetailsDescription": "Zobacz wynik zastosowanego schematu i wszelkie błędy, które wystąpiły",
|
||||
"blueprintInfo": "Informacje o projekcie",
|
||||
"message": "Wiadomość",
|
||||
"blueprintContentsDescription": "Zdefiniuj zawartość YAML opisującą Twoją infrastrukturę",
|
||||
@@ -1181,7 +1181,7 @@
|
||||
"appliedAt": "Zastosowano",
|
||||
"source": "Źródło",
|
||||
"contents": "Treść",
|
||||
"parsedContents": "Przetworzona zawartość",
|
||||
"parsedContents": "Przetworzona zawartość (tylko do odczytu)",
|
||||
"enableDockerSocket": "Włącz schemat dokera",
|
||||
"enableDockerSocketDescription": "Włącz etykietowanie kieszeni dokującej dla etykiet schematów. Ścieżka do gniazda musi być dostarczona do Newt.",
|
||||
"enableDockerSocketLink": "Dowiedz się więcej",
|
||||
@@ -2080,5 +2080,20 @@
|
||||
"supportSending": "Wysyłanie...",
|
||||
"supportSend": "Wyślij",
|
||||
"supportMessageSent": "Wiadomość wysłana!",
|
||||
"supportWillContact": "Wkrótce będziemy w kontakcie!"
|
||||
}
|
||||
"supportWillContact": "Wkrótce będziemy w kontakcie!",
|
||||
"selectLogRetention": "Wybierz zatrzymanie dziennika",
|
||||
"showColumns": "Pokaż kolumny",
|
||||
"hideColumns": "Ukryj kolumny",
|
||||
"columnVisibility": "Widoczność kolumn",
|
||||
"toggleColumn": "Przełącz kolumnę {columnName}",
|
||||
"allColumns": "Wszystkie kolumny",
|
||||
"defaultColumns": "Kolumny domyślne",
|
||||
"customizeView": "Dostosuj widok",
|
||||
"viewOptions": "Opcje widoku",
|
||||
"selectAll": "Zaznacz wszystko",
|
||||
"selectNone": "Nie wybierz żadnego",
|
||||
"selectedResources": "Wybrane Zasoby",
|
||||
"enableSelected": "Włącz zaznaczone",
|
||||
"disableSelected": "Wyłącz zaznaczone",
|
||||
"checkSelectedStatus": "Sprawdź status zaznaczonych"
|
||||
}
|
||||
@@ -179,7 +179,7 @@
|
||||
"baseDomain": "Domínio Base",
|
||||
"subdomnainDescription": "O subdomínio onde seu recurso estará acessível.",
|
||||
"resourceRawSettings": "Configurações TCP/UDP",
|
||||
"resourceRawSettingsDescription": "Configure como seu recurso será acessado sobre TCP/UDP",
|
||||
"resourceRawSettingsDescription": "Configure como seu recurso será acessado sobre TCP/UDP. Você mapeia o recurso para uma porta no servidor Pangolin do hospedeiro, para que você possa acessar o recurso do server-public-ip:mapped-port.",
|
||||
"protocol": "Protocolo",
|
||||
"protocolSelect": "Selecione um protocolo",
|
||||
"resourcePortNumber": "Número da Porta",
|
||||
@@ -1165,13 +1165,13 @@
|
||||
"sidebarDomains": "Domínios",
|
||||
"sidebarBluePrints": "Diagramas",
|
||||
"blueprints": "Diagramas",
|
||||
"blueprintsDescription": "Diagramas são configurações declarativas YAML que definem seus recursos e suas configurações",
|
||||
"blueprintsDescription": "Aplicar configurações declarativas e ver execuções anteriores",
|
||||
"blueprintAdd": "Adicionar Diagrama",
|
||||
"blueprintGoBack": "Ver todos os Diagramas",
|
||||
"blueprintCreate": "Criar Diagrama",
|
||||
"blueprintCreateDescription2": "Siga as etapas abaixo para criar e aplicar um novo diagrama",
|
||||
"blueprintDetails": "Detalhes do Diagrama",
|
||||
"blueprintDetailsDescription": "Veja os detalhes da execução do diagrama",
|
||||
"blueprintDetailsDescription": "Veja o resultado do diagrama aplicado e todos os erros que ocorreram",
|
||||
"blueprintInfo": "Informação do Diagrama",
|
||||
"message": "mensagem",
|
||||
"blueprintContentsDescription": "Defina o conteúdo YAML descrevendo a sua infraestrutura",
|
||||
@@ -1181,7 +1181,7 @@
|
||||
"appliedAt": "Aplicado em",
|
||||
"source": "fonte",
|
||||
"contents": "Conteúdo",
|
||||
"parsedContents": "Conteúdo analisado",
|
||||
"parsedContents": "Conteúdo analisado (Somente Leitura)",
|
||||
"enableDockerSocket": "Habilitar o Diagrama Docker",
|
||||
"enableDockerSocketDescription": "Ativar a scraping de rótulo Docker para rótulos de diagramas. Caminho de Socket deve ser fornecido para Newt.",
|
||||
"enableDockerSocketLink": "Saiba mais",
|
||||
@@ -2080,5 +2080,20 @@
|
||||
"supportSending": "Enviando...",
|
||||
"supportSend": "Mandar",
|
||||
"supportMessageSent": "Mensagem enviada!",
|
||||
"supportWillContact": "Entraremos em contato em breve!"
|
||||
}
|
||||
"supportWillContact": "Entraremos em contato em breve!",
|
||||
"selectLogRetention": "Selecionar retenção de log",
|
||||
"showColumns": "Exibir Colunas",
|
||||
"hideColumns": "Ocultar colunas",
|
||||
"columnVisibility": "Visibilidade da Coluna",
|
||||
"toggleColumn": "Alternar coluna {columnName}",
|
||||
"allColumns": "Todas as colunas",
|
||||
"defaultColumns": "Colunas padrão",
|
||||
"customizeView": "Personalizar visualização",
|
||||
"viewOptions": "Opções de visualização",
|
||||
"selectAll": "Selecionar Todos",
|
||||
"selectNone": "Não selecionar nada",
|
||||
"selectedResources": "Recursos Selecionados",
|
||||
"enableSelected": "Habilitar Selecionados",
|
||||
"disableSelected": "Desativar Selecionados",
|
||||
"checkSelectedStatus": "Status de Verificação dos Selecionados"
|
||||
}
|
||||
@@ -131,7 +131,7 @@
|
||||
"expireIn": "Срок действия",
|
||||
"neverExpire": "Бессрочный доступ",
|
||||
"shareExpireDescription": "Срок действия - это период, в течение которого ссылка будет работать и предоставлять доступ к ресурсу. После этого времени ссылка перестанет работать, и пользователи, использовавшие эту ссылку, потеряют доступ к ресурсу.",
|
||||
"shareSeeOnce": "Вы сможете увидеть эту ссылку только один раз. Обязательно скопируйте её.",
|
||||
"shareSeeOnce": "Вы сможете увидеть эту ссылку только один раз. Обязательно скопируйте ее.",
|
||||
"shareAccessHint": "Любой, у кого есть эта ссылка, может получить доступ к ресурсу. Делитесь ею с осторожностью.",
|
||||
"shareTokenUsage": "Посмотреть использование токена доступа",
|
||||
"createLink": "Создать ссылку",
|
||||
@@ -179,7 +179,7 @@
|
||||
"baseDomain": "Базовый домен",
|
||||
"subdomnainDescription": "Поддомен, на котором будет доступен ресурс.",
|
||||
"resourceRawSettings": "Настройки TCP/UDP",
|
||||
"resourceRawSettingsDescription": "Настройте, как будет осуществляться доступ к вашему ресурсу через TCP/UDP",
|
||||
"resourceRawSettingsDescription": "Настройте доступ к вашему ресурсу по TCP/UDP. Вы соотносите ресурс с портом на сервере хоста Pangolin, так что вы можете получить доступ к ресурсу с сервера server-public-ip:mapped-порта.",
|
||||
"protocol": "Протокол",
|
||||
"protocolSelect": "Выберите протокол",
|
||||
"resourcePortNumber": "Номер порта",
|
||||
@@ -1165,13 +1165,13 @@
|
||||
"sidebarDomains": "Домены",
|
||||
"sidebarBluePrints": "Чертежи",
|
||||
"blueprints": "Чертежи",
|
||||
"blueprintsDescription": "Чертежи являются декларативными конфигурациями YAML, которые определяют ваши ресурсы и их настройки",
|
||||
"blueprintsDescription": "Применить декларирующие конфигурации и просмотреть предыдущие запуски",
|
||||
"blueprintAdd": "Добавить чертёж",
|
||||
"blueprintGoBack": "Посмотреть все чертежи",
|
||||
"blueprintCreate": "Создать чертёж",
|
||||
"blueprintCreateDescription2": "Для создания и применения нового чертежа выполните следующие шаги",
|
||||
"blueprintDetails": "Подробности чертежа",
|
||||
"blueprintDetailsDescription": "Посмотреть детали запуска чертежа",
|
||||
"blueprintDetails": "Детали чертежа",
|
||||
"blueprintDetailsDescription": "Посмотреть результат примененного чертежа и все возникшие ошибки",
|
||||
"blueprintInfo": "Информация о чертеже",
|
||||
"message": "Сообщение",
|
||||
"blueprintContentsDescription": "Определите содержимое YAML, описывающее вашу инфраструктуру",
|
||||
@@ -1181,7 +1181,7 @@
|
||||
"appliedAt": "Заявка на",
|
||||
"source": "Источник",
|
||||
"contents": "Содержание",
|
||||
"parsedContents": "Обработанное содержимое",
|
||||
"parsedContents": "Переработанное содержимое (только для чтения)",
|
||||
"enableDockerSocket": "Включить чертёж Docker",
|
||||
"enableDockerSocketDescription": "Включить scraping ярлыка Docker Socket для ярлыков чертежей. Путь к сокету должен быть предоставлен в Newt.",
|
||||
"enableDockerSocketLink": "Узнать больше",
|
||||
@@ -2080,5 +2080,20 @@
|
||||
"supportSending": "Отправка...",
|
||||
"supportSend": "Отправить",
|
||||
"supportMessageSent": "Сообщение отправлено!",
|
||||
"supportWillContact": "Мы скоро свяжемся с Вами!"
|
||||
}
|
||||
"supportWillContact": "Мы скоро свяжемся с Вами!",
|
||||
"selectLogRetention": "Выберите удержание журнала",
|
||||
"showColumns": "Показать колонки",
|
||||
"hideColumns": "Скрыть столбцы",
|
||||
"columnVisibility": "Видимость столбцов",
|
||||
"toggleColumn": "Столбец {columnName}",
|
||||
"allColumns": "Все колонки",
|
||||
"defaultColumns": "Столбцы по умолчанию",
|
||||
"customizeView": "Настроить вид",
|
||||
"viewOptions": "Параметры просмотра",
|
||||
"selectAll": "Выделить все",
|
||||
"selectNone": "Не выбирать",
|
||||
"selectedResources": "Выбранные ресурсы",
|
||||
"enableSelected": "Включить выбранные",
|
||||
"disableSelected": "Отключить выбранные",
|
||||
"checkSelectedStatus": "Проверить статус выбранных"
|
||||
}
|
||||
@@ -179,7 +179,7 @@
|
||||
"baseDomain": "Temel Alan Adı",
|
||||
"subdomnainDescription": "Kaynağınızın erişilebileceği alt alan adı.",
|
||||
"resourceRawSettings": "TCP/UDP Ayarları",
|
||||
"resourceRawSettingsDescription": "Kaynağınıza TCP/UDP üzerinden erişimin nasıl sağlanacağını yapılandırın",
|
||||
"resourceRawSettingsDescription": "Kaynağınızın TCP/UDP üzerinden nasıl erişileceğini yapılandırın. Kaynağı, sunucudan erişebilmeniz için bir ana bilgisayar Pangolin sunucusundaki bir bağlantı noktasına eşlersiniz: sunucu genel-IP: eşlenen-bağlantı-noktası.",
|
||||
"protocol": "Protokol",
|
||||
"protocolSelect": "Bir protokol seçin",
|
||||
"resourcePortNumber": "Port Numarası",
|
||||
@@ -1165,13 +1165,13 @@
|
||||
"sidebarDomains": "Alan Adları",
|
||||
"sidebarBluePrints": "Planlar",
|
||||
"blueprints": "Planlar",
|
||||
"blueprintsDescription": "Planlar, kaynaklarınızı ve ayarlarını tanımlayan bildirimsel YAML yapılandırmalarıdır",
|
||||
"blueprintsDescription": "Deklaratif yapılandırmaları uygulayın ve önceki çalışmaları görüntüleyin",
|
||||
"blueprintAdd": "Plan Ekle",
|
||||
"blueprintGoBack": "Tüm Planları Gör",
|
||||
"blueprintCreate": "Plan Oluştur",
|
||||
"blueprintCreateDescription2": "Yeni bir plan oluşturup uygulamak için aşağıdaki adımları izleyin",
|
||||
"blueprintDetails": "Plan Detayları",
|
||||
"blueprintDetailsDescription": "Plan çalıştırma detaylarını görün",
|
||||
"blueprintDetails": "Mavi Yazılım Detayları",
|
||||
"blueprintDetailsDescription": "Uygulanan mavi yazılımın sonucunu ve oluşan hataları görün",
|
||||
"blueprintInfo": "Plan Bilgileri",
|
||||
"message": "Mesaj",
|
||||
"blueprintContentsDescription": "Altyapınızı tanımlayan YAML içeriğini tanımlayın",
|
||||
@@ -1181,7 +1181,7 @@
|
||||
"appliedAt": "Uygulama Zamanı",
|
||||
"source": "Kaynak",
|
||||
"contents": "İçerik",
|
||||
"parsedContents": "Ayrıştırılmış İçerik",
|
||||
"parsedContents": "Verilerin Ayrıştırılmış İçeriği (Salt Okunur)",
|
||||
"enableDockerSocket": "Docker Soketini Etkinleştir",
|
||||
"enableDockerSocketDescription": "Plan etiketleri için Docker Socket etiket toplamasını etkinleştirin. Newt'e soket yolu sağlanmalıdır.",
|
||||
"enableDockerSocketLink": "Daha fazla bilgi",
|
||||
@@ -2080,5 +2080,20 @@
|
||||
"supportSending": "Gönderiliyor...",
|
||||
"supportSend": "Gönder",
|
||||
"supportMessageSent": "Mesaj Gönderildi!",
|
||||
"supportWillContact": "En kısa sürede size geri döneceğiz!"
|
||||
}
|
||||
"supportWillContact": "En kısa sürede size geri döneceğiz!",
|
||||
"selectLogRetention": "Kayıt saklama seç",
|
||||
"showColumns": "Sütunları Göster",
|
||||
"hideColumns": "Sütunları Gizle",
|
||||
"columnVisibility": "Sütun Görünürlüğü",
|
||||
"toggleColumn": "{columnName} sütununu aç/kapat",
|
||||
"allColumns": "Tüm Sütunlar",
|
||||
"defaultColumns": "Varsayılan Sütunlar",
|
||||
"customizeView": "Görünümü Özelleştir",
|
||||
"viewOptions": "Görünüm Seçenekleri",
|
||||
"selectAll": "Tümünü Seç",
|
||||
"selectNone": "Hiçbirini Seçme",
|
||||
"selectedResources": "Seçilen Kaynaklar",
|
||||
"enableSelected": "Seçilenleri Etkinleştir",
|
||||
"disableSelected": "Seçilenleri Devre Dışı Bırak",
|
||||
"checkSelectedStatus": "Seçilenlerin Durumunu Kontrol Et"
|
||||
}
|
||||
@@ -131,7 +131,7 @@
|
||||
"expireIn": "过期时间",
|
||||
"neverExpire": "永不过期",
|
||||
"shareExpireDescription": "过期时间是链接可以使用并提供对资源的访问时间。 此时间后,链接将不再工作,使用此链接的用户将失去对资源的访问。",
|
||||
"shareSeeOnce": "您只能看到此链接。请确保复制它。",
|
||||
"shareSeeOnce": "您只能看到一次此链接。请确保复制它。",
|
||||
"shareAccessHint": "任何具有此链接的人都可以访问该资源。小心地分享它。",
|
||||
"shareTokenUsage": "查看访问令牌使用情况",
|
||||
"createLink": "创建链接",
|
||||
@@ -179,7 +179,7 @@
|
||||
"baseDomain": "根域名",
|
||||
"subdomnainDescription": "您的资源可以访问的子域名。",
|
||||
"resourceRawSettings": "TCP/UDP 设置",
|
||||
"resourceRawSettingsDescription": "配置如何通过 TCP/UDP 访问您的资源",
|
||||
"resourceRawSettingsDescription": "配置如何通过 TCP/UDP 访问您的资源。 您映射资源到主机Pangolin服务器上的端口,这样您就可以访问服务器-公共-ip:mapped端口的资源。",
|
||||
"protocol": "协议",
|
||||
"protocolSelect": "选择协议",
|
||||
"resourcePortNumber": "端口号",
|
||||
@@ -1165,13 +1165,13 @@
|
||||
"sidebarDomains": "域",
|
||||
"sidebarBluePrints": "蓝图",
|
||||
"blueprints": "蓝图",
|
||||
"blueprintsDescription": "蓝图是用于定义资源及其设置的 YAML 声明配置",
|
||||
"blueprintsDescription": "应用声明配置并查看先前运行的",
|
||||
"blueprintAdd": "添加蓝图",
|
||||
"blueprintGoBack": "查看所有蓝图",
|
||||
"blueprintCreate": "创建蓝图",
|
||||
"blueprintCreateDescription2": "按照下面的步骤创建和应用新的蓝图",
|
||||
"blueprintDetails": "蓝图详细信息",
|
||||
"blueprintDetailsDescription": "查看蓝图运行详情",
|
||||
"blueprintDetailsDescription": "查看应用蓝图的结果和发生的任何错误",
|
||||
"blueprintInfo": "蓝图信息",
|
||||
"message": "留言",
|
||||
"blueprintContentsDescription": "定义描述您基础设施的 YAML 内容",
|
||||
@@ -1181,7 +1181,7 @@
|
||||
"appliedAt": "应用于",
|
||||
"source": "来源",
|
||||
"contents": "目录",
|
||||
"parsedContents": "解析内容",
|
||||
"parsedContents": "解析内容 (只读)",
|
||||
"enableDockerSocket": "启用 Docker 蓝图",
|
||||
"enableDockerSocketDescription": "启用 Docker Socket 标签擦除蓝图标签。套接字路径必须提供给新的。",
|
||||
"enableDockerSocketLink": "了解更多",
|
||||
@@ -2080,5 +2080,20 @@
|
||||
"supportSending": "正在发送...",
|
||||
"supportSend": "发送",
|
||||
"supportMessageSent": "消息已发送!",
|
||||
"supportWillContact": "我们很快就会联系起来!"
|
||||
}
|
||||
"supportWillContact": "我们很快就会联系起来!",
|
||||
"selectLogRetention": "选择保留日志",
|
||||
"showColumns": "显示列",
|
||||
"hideColumns": "隐藏列",
|
||||
"columnVisibility": "列可见性",
|
||||
"toggleColumn": "切换 {columnName} 列",
|
||||
"allColumns": "全部列",
|
||||
"defaultColumns": "默认列",
|
||||
"customizeView": "自定义视图",
|
||||
"viewOptions": "查看选项",
|
||||
"selectAll": "选择所有",
|
||||
"selectNone": "没有选择",
|
||||
"selectedResources": "选定的资源",
|
||||
"enableSelected": "启用选中的",
|
||||
"disableSelected": "禁用选中的",
|
||||
"checkSelectedStatus": "检查选中的状态"
|
||||
}
|
||||
@@ -30,6 +30,7 @@ import { pickPort } from "@server/routers/target/helpers";
|
||||
import { resourcePassword } from "@server/db";
|
||||
import { hashPassword } from "@server/auth/password";
|
||||
import { isValidCIDR, isValidIP, isValidUrlGlobPattern } from "../validators";
|
||||
import { get } from "http";
|
||||
|
||||
export type ProxyResourcesResults = {
|
||||
proxyResource: Resource;
|
||||
@@ -544,7 +545,7 @@ export async function updateProxyResources(
|
||||
if (
|
||||
existingRule.action !== getRuleAction(rule.action) ||
|
||||
existingRule.match !== rule.match.toUpperCase() ||
|
||||
existingRule.value !== rule.value.toUpperCase()
|
||||
existingRule.value !== getRuleValue(rule.match.toUpperCase(), rule.value)
|
||||
) {
|
||||
validateRule(rule);
|
||||
await trx
|
||||
@@ -552,7 +553,7 @@ export async function updateProxyResources(
|
||||
.set({
|
||||
action: getRuleAction(rule.action),
|
||||
match: rule.match.toUpperCase(),
|
||||
value: rule.value.toUpperCase()
|
||||
value: getRuleValue(rule.match.toUpperCase(), rule.value),
|
||||
})
|
||||
.where(
|
||||
eq(resourceRules.ruleId, existingRule.ruleId)
|
||||
@@ -564,7 +565,7 @@ export async function updateProxyResources(
|
||||
resourceId: existingResource.resourceId,
|
||||
action: getRuleAction(rule.action),
|
||||
match: rule.match.toUpperCase(),
|
||||
value: rule.value.toUpperCase(),
|
||||
value: getRuleValue(rule.match.toUpperCase(), rule.value),
|
||||
priority: index + 1 // start priorities at 1
|
||||
});
|
||||
}
|
||||
@@ -722,7 +723,7 @@ export async function updateProxyResources(
|
||||
resourceId: newResource.resourceId,
|
||||
action: getRuleAction(rule.action),
|
||||
match: rule.match.toUpperCase(),
|
||||
value: rule.value.toUpperCase(),
|
||||
value: getRuleValue(rule.match.toUpperCase(), rule.value),
|
||||
priority: index + 1 // start priorities at 1
|
||||
});
|
||||
}
|
||||
@@ -752,6 +753,14 @@ function getRuleAction(input: string) {
|
||||
return action;
|
||||
}
|
||||
|
||||
function getRuleValue(match: string, value: string) {
|
||||
// if the match is a country, uppercase the value
|
||||
if (match == "COUNTRY") {
|
||||
return value.toUpperCase();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function validateRule(rule: any) {
|
||||
if (rule.match === "cidr") {
|
||||
if (!isValidCIDR(rule.value)) {
|
||||
|
||||
@@ -2,7 +2,7 @@ import path from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
|
||||
// This is a placeholder value replaced by the build process
|
||||
export const APP_VERSION = "1.12.0-rc.0";
|
||||
export const APP_VERSION = "1.12.1";
|
||||
|
||||
export const __FILENAME = fileURLToPath(import.meta.url);
|
||||
export const __DIRNAME = path.dirname(__FILENAME);
|
||||
|
||||
@@ -80,7 +80,8 @@ export async function getTraefikConfig(
|
||||
subnet: sites.subnet,
|
||||
exitNodeId: sites.exitNodeId,
|
||||
// Domain cert resolver fields
|
||||
domainCertResolver: domains.certResolver
|
||||
domainCertResolver: domains.certResolver,
|
||||
preferWildcardCert: domains.preferWildcardCert
|
||||
})
|
||||
.from(sites)
|
||||
.innerJoin(targets, eq(targets.siteId, sites.siteId))
|
||||
@@ -178,7 +179,8 @@ export async function getTraefikConfig(
|
||||
rewritePathType: row.rewritePathType,
|
||||
priority: priority,
|
||||
// Store domain cert resolver fields
|
||||
domainCertResolver: row.domainCertResolver
|
||||
domainCertResolver: row.domainCertResolver,
|
||||
preferWildcardCert: row.preferWildcardCert
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -111,7 +111,8 @@ export async function getTraefikConfig(
|
||||
domainNamespaceId: domainNamespaces.domainNamespaceId,
|
||||
// Certificate
|
||||
certificateStatus: certificates.status,
|
||||
domainCertResolver: domains.certResolver
|
||||
domainCertResolver: domains.certResolver,
|
||||
preferWildcardCert: domains.preferWildcardCert
|
||||
})
|
||||
.from(sites)
|
||||
.innerJoin(targets, eq(targets.siteId, sites.siteId))
|
||||
@@ -218,7 +219,8 @@ export async function getTraefikConfig(
|
||||
rewritePath: row.rewritePath,
|
||||
rewritePathType: row.rewritePathType,
|
||||
priority: priority, // may be null, we fallback later
|
||||
domainCertResolver: row.domainCertResolver
|
||||
domainCertResolver: row.domainCertResolver,
|
||||
preferWildcardCert: row.preferWildcardCert
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -352,20 +352,38 @@ export async function validateOidcCallback(
|
||||
|
||||
if (!userOrgInfo.length) {
|
||||
if (existingUser) {
|
||||
// delete the user
|
||||
// cascade will also delete org users
|
||||
// get existing user orgs
|
||||
const existingUserOrgs = await db
|
||||
.select()
|
||||
.from(userOrgs)
|
||||
.where(
|
||||
and(
|
||||
eq(userOrgs.userId, existingUser.userId),
|
||||
eq(userOrgs.autoProvisioned, false)
|
||||
)
|
||||
);
|
||||
|
||||
await db
|
||||
.delete(users)
|
||||
.where(eq(users.userId, existingUser.userId));
|
||||
if (!existingUserOrgs.length) {
|
||||
// delete the user
|
||||
// await db
|
||||
// .delete(users)
|
||||
// .where(eq(users.userId, existingUser.userId));
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.UNAUTHORIZED,
|
||||
`No policies matched for ${userIdentifier}. This user must be added to an organization before logging in.`
|
||||
)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// no orgs to provision and user doesn't exist
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.UNAUTHORIZED,
|
||||
`No policies matched for ${userIdentifier}. This user must be added to an organization before logging in.`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.UNAUTHORIZED,
|
||||
`No policies matched for ${userIdentifier}. This user must be added to an organization before logging in.`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const orgUserCounts: { orgId: string; userCount: number }[] = [];
|
||||
|
||||
@@ -92,7 +92,7 @@ export async function updateOrg(
|
||||
const { orgId } = parsedParams.data;
|
||||
|
||||
const isLicensed = await isLicensedOrSubscribed(orgId);
|
||||
if (!isLicensed) {
|
||||
if (build == "enterprise" && !isLicensed) {
|
||||
parsedBody.data.requireTwoFactor = undefined;
|
||||
parsedBody.data.maxSessionLengthHours = undefined;
|
||||
parsedBody.data.passwordExpiryDays = undefined;
|
||||
@@ -100,6 +100,7 @@ export async function updateOrg(
|
||||
|
||||
const { tier } = await getOrgTierData(orgId);
|
||||
if (
|
||||
build == "saas" &&
|
||||
tier != TierId.STANDARD &&
|
||||
parsedBody.data.settingsLogRetentionDaysRequest &&
|
||||
parsedBody.data.settingsLogRetentionDaysRequest > 30
|
||||
|
||||
@@ -6,7 +6,9 @@ import {
|
||||
userResources,
|
||||
roleResources,
|
||||
resourcePassword,
|
||||
resourcePincode
|
||||
resourcePincode,
|
||||
targets,
|
||||
targetHealthCheck,
|
||||
} from "@server/db";
|
||||
import response from "@server/lib/response";
|
||||
import HttpCode from "@server/types/HttpCode";
|
||||
@@ -40,6 +42,59 @@ const listResourcesSchema = z.object({
|
||||
.pipe(z.number().int().nonnegative())
|
||||
});
|
||||
|
||||
// (resource fields + a single joined target)
|
||||
type JoinedRow = {
|
||||
resourceId: number;
|
||||
niceId: string;
|
||||
name: string;
|
||||
ssl: boolean;
|
||||
fullDomain: string | null;
|
||||
passwordId: number | null;
|
||||
sso: boolean;
|
||||
pincodeId: number | null;
|
||||
whitelist: boolean;
|
||||
http: boolean;
|
||||
protocol: string;
|
||||
proxyPort: number | null;
|
||||
enabled: boolean;
|
||||
domainId: string | null;
|
||||
headerAuthId: number | null;
|
||||
|
||||
targetId: number | null;
|
||||
targetIp: string | null;
|
||||
targetPort: number | null;
|
||||
targetEnabled: boolean | null;
|
||||
|
||||
hcHealth: string | null;
|
||||
hcEnabled: boolean | null;
|
||||
};
|
||||
|
||||
// grouped by resource with targets[])
|
||||
export type ResourceWithTargets = {
|
||||
resourceId: number;
|
||||
name: string;
|
||||
ssl: boolean;
|
||||
fullDomain: string | null;
|
||||
passwordId: number | null;
|
||||
sso: boolean;
|
||||
pincodeId: number | null;
|
||||
whitelist: boolean;
|
||||
http: boolean;
|
||||
protocol: string;
|
||||
proxyPort: number | null;
|
||||
enabled: boolean;
|
||||
domainId: string | null;
|
||||
niceId: string;
|
||||
headerAuthId: number | null;
|
||||
targets: Array<{
|
||||
targetId: number;
|
||||
ip: string;
|
||||
port: number;
|
||||
enabled: boolean;
|
||||
healthStatus?: 'healthy' | 'unhealthy' | 'unknown';
|
||||
}>;
|
||||
};
|
||||
|
||||
function queryResources(accessibleResourceIds: number[], orgId: string) {
|
||||
return db
|
||||
.select({
|
||||
@@ -57,7 +112,15 @@ function queryResources(accessibleResourceIds: number[], orgId: string) {
|
||||
enabled: resources.enabled,
|
||||
domainId: resources.domainId,
|
||||
niceId: resources.niceId,
|
||||
headerAuthId: resourceHeaderAuth.headerAuthId
|
||||
headerAuthId: resourceHeaderAuth.headerAuthId,
|
||||
|
||||
targetId: targets.targetId,
|
||||
targetIp: targets.ip,
|
||||
targetPort: targets.port,
|
||||
targetEnabled: targets.enabled,
|
||||
|
||||
hcHealth: targetHealthCheck.hcHealth,
|
||||
hcEnabled: targetHealthCheck.hcEnabled,
|
||||
})
|
||||
.from(resources)
|
||||
.leftJoin(
|
||||
@@ -72,6 +135,11 @@ function queryResources(accessibleResourceIds: number[], orgId: string) {
|
||||
resourceHeaderAuth,
|
||||
eq(resourceHeaderAuth.resourceId, resources.resourceId)
|
||||
)
|
||||
.leftJoin(targets, eq(targets.resourceId, resources.resourceId))
|
||||
.leftJoin(
|
||||
targetHealthCheck,
|
||||
eq(targetHealthCheck.targetId, targets.targetId)
|
||||
)
|
||||
.where(
|
||||
and(
|
||||
inArray(resources.resourceId, accessibleResourceIds),
|
||||
@@ -81,7 +149,7 @@ function queryResources(accessibleResourceIds: number[], orgId: string) {
|
||||
}
|
||||
|
||||
export type ListResourcesResponse = {
|
||||
resources: NonNullable<Awaited<ReturnType<typeof queryResources>>>;
|
||||
resources: ResourceWithTargets[];
|
||||
pagination: { total: number; limit: number; offset: number };
|
||||
};
|
||||
|
||||
@@ -146,7 +214,7 @@ export async function listResources(
|
||||
);
|
||||
}
|
||||
|
||||
let accessibleResources;
|
||||
let accessibleResources: Array<{ resourceId: number }>;
|
||||
if (req.user) {
|
||||
accessibleResources = await db
|
||||
.select({
|
||||
@@ -183,9 +251,56 @@ export async function listResources(
|
||||
|
||||
const baseQuery = queryResources(accessibleResourceIds, orgId);
|
||||
|
||||
const resourcesList = await baseQuery!.limit(limit).offset(offset);
|
||||
const rows: JoinedRow[] = await baseQuery.limit(limit).offset(offset);
|
||||
|
||||
// avoids TS issues with reduce/never[]
|
||||
const map = new Map<number, ResourceWithTargets>();
|
||||
|
||||
for (const row of rows) {
|
||||
let entry = map.get(row.resourceId);
|
||||
if (!entry) {
|
||||
entry = {
|
||||
resourceId: row.resourceId,
|
||||
niceId: row.niceId,
|
||||
name: row.name,
|
||||
ssl: row.ssl,
|
||||
fullDomain: row.fullDomain,
|
||||
passwordId: row.passwordId,
|
||||
sso: row.sso,
|
||||
pincodeId: row.pincodeId,
|
||||
whitelist: row.whitelist,
|
||||
http: row.http,
|
||||
protocol: row.protocol,
|
||||
proxyPort: row.proxyPort,
|
||||
enabled: row.enabled,
|
||||
domainId: row.domainId,
|
||||
headerAuthId: row.headerAuthId,
|
||||
targets: [],
|
||||
};
|
||||
map.set(row.resourceId, entry);
|
||||
}
|
||||
|
||||
if (row.targetId != null && row.targetIp && row.targetPort != null && row.targetEnabled != null) {
|
||||
let healthStatus: 'healthy' | 'unhealthy' | 'unknown' = 'unknown';
|
||||
|
||||
if (row.hcEnabled && row.hcHealth) {
|
||||
healthStatus = row.hcHealth as 'healthy' | 'unhealthy' | 'unknown';
|
||||
}
|
||||
|
||||
entry.targets.push({
|
||||
targetId: row.targetId,
|
||||
ip: row.targetIp,
|
||||
port: row.targetPort,
|
||||
enabled: row.targetEnabled,
|
||||
healthStatus: healthStatus,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const resourcesList: ResourceWithTargets[] = Array.from(map.values());
|
||||
|
||||
const totalCountResult = await countQuery;
|
||||
const totalCount = totalCountResult[0].count;
|
||||
const totalCount = totalCountResult[0]?.count ?? 0;
|
||||
|
||||
return response<ListResourcesResponse>(res, {
|
||||
data: {
|
||||
|
||||
@@ -163,12 +163,8 @@ export async function createTarget(
|
||||
);
|
||||
|
||||
if (existingTarget) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
`Target with IP ${targetData.ip}, port ${targetData.port}, method ${targetData.method} already exists for resource ID ${resourceId}`
|
||||
)
|
||||
);
|
||||
// log a warning
|
||||
logger.warn(`Target with IP ${targetData.ip}, port ${targetData.port}, method ${targetData.method} already exists for resource ID ${resourceId}`);
|
||||
}
|
||||
|
||||
let newTarget: Target[] = [];
|
||||
|
||||
@@ -170,12 +170,8 @@ export async function updateTarget(
|
||||
);
|
||||
|
||||
if (foundTarget) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
`Target with IP ${targetData.ip}, port ${targetData.port}, and method ${targetData.method} already exists on the same site.`
|
||||
)
|
||||
);
|
||||
// log a warning
|
||||
logger.warn(`Target with IP ${targetData.ip}, port ${targetData.port}, method ${targetData.method} already exists for resource ID ${target.resourceId}`);
|
||||
}
|
||||
|
||||
const { internalPort, targetIps } = await pickPort(site.siteId!, db);
|
||||
|
||||
@@ -73,7 +73,7 @@ export default async function DomainSettingsPage({
|
||||
|
||||
<DNSRecordsTable records={dnsRecords} type={domain.type} />
|
||||
|
||||
{domain.type == "wildcard" && (
|
||||
{domain.type == "wildcard" && !domain.configManaged && (
|
||||
<DomainCertForm
|
||||
orgId={orgId}
|
||||
domainId={domain.domainId}
|
||||
|
||||
@@ -501,25 +501,6 @@ export default function ReverseProxyTargets(props: {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if target with same IP, port and method already exists
|
||||
const isDuplicate = targets.some(
|
||||
(t) =>
|
||||
t.targetId !== target.targetId &&
|
||||
t.ip === target.ip &&
|
||||
t.port === target.port &&
|
||||
t.method === target.method &&
|
||||
t.siteId === target.siteId
|
||||
);
|
||||
|
||||
if (isDuplicate) {
|
||||
toast({
|
||||
variant: "destructive",
|
||||
title: t("targetErrorDuplicate"),
|
||||
description: t("targetErrorDuplicateDescription")
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setTargetsLoading(true);
|
||||
|
||||
@@ -585,24 +566,6 @@ export default function ReverseProxyTargets(props: {
|
||||
}
|
||||
|
||||
async function addTarget(data: z.infer<typeof addTargetSchema>) {
|
||||
// Check if target with same IP, port and method already exists
|
||||
const isDuplicate = targets.some(
|
||||
(target) =>
|
||||
target.ip === data.ip &&
|
||||
target.port === data.port &&
|
||||
target.method === data.method &&
|
||||
target.siteId === data.siteId
|
||||
);
|
||||
|
||||
if (isDuplicate) {
|
||||
toast({
|
||||
variant: "destructive",
|
||||
title: t("targetErrorDuplicate"),
|
||||
description: t("targetErrorDuplicateDescription")
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// if (site && site.type == "wireguard" && site.subnet) {
|
||||
// // make sure that the target IP is within the site subnet
|
||||
// const targetIp = data.ip;
|
||||
@@ -899,7 +862,7 @@ export default function ReverseProxyTargets(props: {
|
||||
|
||||
const healthCheckColumn: ColumnDef<LocalTarget> = {
|
||||
accessorKey: "healthCheck",
|
||||
header: t("healthCheck"),
|
||||
header: () => (<span className="p-3">{t("healthCheck")}</span>),
|
||||
cell: ({ row }) => {
|
||||
const status = row.original.hcHealth || "unknown";
|
||||
const isEnabled = row.original.hcEnabled;
|
||||
@@ -971,7 +934,7 @@ export default function ReverseProxyTargets(props: {
|
||||
|
||||
const matchPathColumn: ColumnDef<LocalTarget> = {
|
||||
accessorKey: "path",
|
||||
header: t("matchPath"),
|
||||
header: () => (<span className="p-3">{t("matchPath")}</span>),
|
||||
cell: ({ row }) => {
|
||||
const hasPathMatch = !!(
|
||||
row.original.path || row.original.pathMatchType
|
||||
@@ -1033,7 +996,7 @@ export default function ReverseProxyTargets(props: {
|
||||
|
||||
const addressColumn: ColumnDef<LocalTarget> = {
|
||||
accessorKey: "address",
|
||||
header: t("address"),
|
||||
header: () => (<span className="p-3">{t("address")}</span>),
|
||||
cell: ({ row }) => {
|
||||
const selectedSite = sites.find(
|
||||
(site) => site.siteId === row.original.siteId
|
||||
@@ -1052,7 +1015,7 @@ export default function ReverseProxyTargets(props: {
|
||||
|
||||
return (
|
||||
<div className="flex items-center w-full">
|
||||
<div className="flex items-center w-full justify-start py-0 space-x-2 px-0 cursor-default border border-input shadow-2xs rounded-md">
|
||||
<div className="flex items-center w-full justify-start py-0 space-x-2 px-0 cursor-default border border-input rounded-md">
|
||||
{selectedSite &&
|
||||
selectedSite.type === "newt" &&
|
||||
(() => {
|
||||
@@ -1247,7 +1210,7 @@ export default function ReverseProxyTargets(props: {
|
||||
|
||||
const rewritePathColumn: ColumnDef<LocalTarget> = {
|
||||
accessorKey: "rewritePath",
|
||||
header: t("rewritePath"),
|
||||
header: () => (<span className="p-3">{t("rewritePath")}</span>),
|
||||
cell: ({ row }) => {
|
||||
const hasRewritePath = !!(
|
||||
row.original.rewritePath || row.original.rewritePathType
|
||||
@@ -1317,7 +1280,7 @@ export default function ReverseProxyTargets(props: {
|
||||
|
||||
const enabledColumn: ColumnDef<LocalTarget> = {
|
||||
accessorKey: "enabled",
|
||||
header: t("enabled"),
|
||||
header: () => (<span className="p-3">{t("enabled")}</span>),
|
||||
cell: ({ row }) => (
|
||||
<div className="flex items-center justify-center w-full">
|
||||
<Switch
|
||||
@@ -1338,8 +1301,9 @@ export default function ReverseProxyTargets(props: {
|
||||
|
||||
const actionsColumn: ColumnDef<LocalTarget> = {
|
||||
id: "actions",
|
||||
header: () => (<span className="p-3">{t("actions")}</span>),
|
||||
cell: ({ row }) => (
|
||||
<div className="flex items-center justify-end w-full">
|
||||
<div className="flex items-center w-full">
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => removeTarget(row.original.targetId)}
|
||||
|
||||
@@ -425,24 +425,6 @@ export default function Page() {
|
||||
};
|
||||
|
||||
async function addTarget(data: z.infer<typeof addTargetSchema>) {
|
||||
// Check if target with same IP, port and method already exists
|
||||
const isDuplicate = targets.some(
|
||||
(target) =>
|
||||
target.ip === data.ip &&
|
||||
target.port === data.port &&
|
||||
target.method === data.method &&
|
||||
target.siteId === data.siteId
|
||||
);
|
||||
|
||||
if (isDuplicate) {
|
||||
toast({
|
||||
variant: "destructive",
|
||||
title: t("targetErrorDuplicate"),
|
||||
description: t("targetErrorDuplicateDescription")
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const site = sites.find((site) => site.siteId === data.siteId);
|
||||
|
||||
const isHttp = baseForm.watch("http");
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { internal } from "@app/lib/api";
|
||||
import { authCookieHeader } from "@app/lib/api/cookies";
|
||||
import ResourcesTable, {
|
||||
ResourceRow,
|
||||
InternalResourceRow
|
||||
ResourceRow,
|
||||
InternalResourceRow
|
||||
} from "../../../../components/ResourcesTable";
|
||||
import { AxiosResponse } from "axios";
|
||||
import { ListResourcesResponse } from "@server/routers/resource";
|
||||
@@ -17,123 +17,130 @@ import { pullEnv } from "@app/lib/pullEnv";
|
||||
import { toUnicode } from "punycode";
|
||||
|
||||
type ResourcesPageProps = {
|
||||
params: Promise<{ orgId: string }>;
|
||||
searchParams: Promise<{ view?: string }>;
|
||||
params: Promise<{ orgId: string }>;
|
||||
searchParams: Promise<{ view?: string }>;
|
||||
};
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
export default async function ResourcesPage(props: ResourcesPageProps) {
|
||||
const params = await props.params;
|
||||
const searchParams = await props.searchParams;
|
||||
const t = await getTranslations();
|
||||
const params = await props.params;
|
||||
const searchParams = await props.searchParams;
|
||||
const t = await getTranslations();
|
||||
|
||||
const env = pullEnv();
|
||||
const env = pullEnv();
|
||||
|
||||
// Default to 'proxy' view, or use the query param if provided
|
||||
let defaultView: "proxy" | "internal" = "proxy";
|
||||
if (env.flags.enableClients) {
|
||||
defaultView = searchParams.view === "internal" ? "internal" : "proxy";
|
||||
}
|
||||
// Default to 'proxy' view, or use the query param if provided
|
||||
let defaultView: "proxy" | "internal" = "proxy";
|
||||
if (env.flags.enableClients) {
|
||||
defaultView = searchParams.view === "internal" ? "internal" : "proxy";
|
||||
}
|
||||
|
||||
let resources: ListResourcesResponse["resources"] = [];
|
||||
try {
|
||||
const res = await internal.get<AxiosResponse<ListResourcesResponse>>(
|
||||
`/org/${params.orgId}/resources`,
|
||||
await authCookieHeader()
|
||||
);
|
||||
resources = res.data.data.resources;
|
||||
} catch (e) { }
|
||||
|
||||
let siteResources: ListAllSiteResourcesByOrgResponse["siteResources"] = [];
|
||||
try {
|
||||
const res = await internal.get<
|
||||
AxiosResponse<ListAllSiteResourcesByOrgResponse>
|
||||
>(`/org/${params.orgId}/site-resources`, await authCookieHeader());
|
||||
siteResources = res.data.data.siteResources;
|
||||
} catch (e) { }
|
||||
|
||||
let org = null;
|
||||
try {
|
||||
const getOrg = cache(async () =>
|
||||
internal.get<AxiosResponse<GetOrgResponse>>(
|
||||
`/org/${params.orgId}`,
|
||||
await authCookieHeader()
|
||||
)
|
||||
);
|
||||
const res = await getOrg();
|
||||
org = res.data.data;
|
||||
} catch {
|
||||
redirect(`/${params.orgId}/settings/resources`);
|
||||
}
|
||||
|
||||
if (!org) {
|
||||
redirect(`/${params.orgId}/settings/resources`);
|
||||
}
|
||||
|
||||
const resourceRows: ResourceRow[] = resources.map((resource) => {
|
||||
return {
|
||||
id: resource.resourceId,
|
||||
name: resource.name,
|
||||
orgId: params.orgId,
|
||||
nice: resource.niceId,
|
||||
domain: `${resource.ssl ? "https://" : "http://"}${toUnicode(resource.fullDomain || "")}`,
|
||||
protocol: resource.protocol,
|
||||
proxyPort: resource.proxyPort,
|
||||
http: resource.http,
|
||||
authState: !resource.http
|
||||
? "none"
|
||||
: resource.sso ||
|
||||
resource.pincodeId !== null ||
|
||||
resource.passwordId !== null ||
|
||||
resource.whitelist ||
|
||||
resource.headerAuthId
|
||||
? "protected"
|
||||
: "not_protected",
|
||||
enabled: resource.enabled,
|
||||
domainId: resource.domainId || undefined,
|
||||
ssl: resource.ssl
|
||||
};
|
||||
});
|
||||
|
||||
const internalResourceRows: InternalResourceRow[] = siteResources.map(
|
||||
(siteResource) => {
|
||||
return {
|
||||
id: siteResource.siteResourceId,
|
||||
name: siteResource.name,
|
||||
orgId: params.orgId,
|
||||
siteName: siteResource.siteName,
|
||||
protocol: siteResource.protocol,
|
||||
proxyPort: siteResource.proxyPort,
|
||||
siteId: siteResource.siteId,
|
||||
destinationIp: siteResource.destinationIp,
|
||||
destinationPort: siteResource.destinationPort,
|
||||
siteNiceId: siteResource.siteNiceId
|
||||
};
|
||||
}
|
||||
let resources: ListResourcesResponse["resources"] = [];
|
||||
try {
|
||||
const res = await internal.get<AxiosResponse<ListResourcesResponse>>(
|
||||
`/org/${params.orgId}/resources`,
|
||||
await authCookieHeader()
|
||||
);
|
||||
resources = res.data.data.resources;
|
||||
} catch (e) { }
|
||||
|
||||
return (
|
||||
<>
|
||||
<SettingsSectionTitle
|
||||
title={t("resourceTitle")}
|
||||
description={t("resourceDescription")}
|
||||
/>
|
||||
let siteResources: ListAllSiteResourcesByOrgResponse["siteResources"] = [];
|
||||
try {
|
||||
const res = await internal.get<
|
||||
AxiosResponse<ListAllSiteResourcesByOrgResponse>
|
||||
>(`/org/${params.orgId}/site-resources`, await authCookieHeader());
|
||||
siteResources = res.data.data.siteResources;
|
||||
} catch (e) { }
|
||||
|
||||
<OrgProvider org={org}>
|
||||
<ResourcesTable
|
||||
resources={resourceRows}
|
||||
internalResources={internalResourceRows}
|
||||
orgId={params.orgId}
|
||||
defaultView={
|
||||
env.flags.enableClients ? defaultView : "proxy"
|
||||
}
|
||||
defaultSort={{
|
||||
id: "name",
|
||||
desc: false
|
||||
}}
|
||||
/>
|
||||
</OrgProvider>
|
||||
</>
|
||||
let org = null;
|
||||
try {
|
||||
const getOrg = cache(async () =>
|
||||
internal.get<AxiosResponse<GetOrgResponse>>(
|
||||
`/org/${params.orgId}`,
|
||||
await authCookieHeader()
|
||||
)
|
||||
);
|
||||
}
|
||||
const res = await getOrg();
|
||||
org = res.data.data;
|
||||
} catch {
|
||||
redirect(`/${params.orgId}/settings/resources`);
|
||||
}
|
||||
|
||||
if (!org) {
|
||||
redirect(`/${params.orgId}/settings/resources`);
|
||||
}
|
||||
|
||||
const resourceRows: ResourceRow[] = resources.map((resource) => {
|
||||
return {
|
||||
id: resource.resourceId,
|
||||
name: resource.name,
|
||||
orgId: params.orgId,
|
||||
nice: resource.niceId,
|
||||
domain: `${resource.ssl ? "https://" : "http://"}${toUnicode(resource.fullDomain || "")}`,
|
||||
protocol: resource.protocol,
|
||||
proxyPort: resource.proxyPort,
|
||||
http: resource.http,
|
||||
authState: !resource.http
|
||||
? "none"
|
||||
: resource.sso ||
|
||||
resource.pincodeId !== null ||
|
||||
resource.passwordId !== null ||
|
||||
resource.whitelist ||
|
||||
resource.headerAuthId
|
||||
? "protected"
|
||||
: "not_protected",
|
||||
enabled: resource.enabled,
|
||||
domainId: resource.domainId || undefined,
|
||||
ssl: resource.ssl,
|
||||
targets: resource.targets?.map(target => ({
|
||||
targetId: target.targetId,
|
||||
ip: target.ip,
|
||||
port: target.port,
|
||||
enabled: target.enabled,
|
||||
healthStatus: target.healthStatus
|
||||
}))
|
||||
};
|
||||
});
|
||||
|
||||
const internalResourceRows: InternalResourceRow[] = siteResources.map(
|
||||
(siteResource) => {
|
||||
return {
|
||||
id: siteResource.siteResourceId,
|
||||
name: siteResource.name,
|
||||
orgId: params.orgId,
|
||||
siteName: siteResource.siteName,
|
||||
protocol: siteResource.protocol,
|
||||
proxyPort: siteResource.proxyPort,
|
||||
siteId: siteResource.siteId,
|
||||
destinationIp: siteResource.destinationIp,
|
||||
destinationPort: siteResource.destinationPort,
|
||||
siteNiceId: siteResource.siteNiceId
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<SettingsSectionTitle
|
||||
title={t("resourceTitle")}
|
||||
description={t("resourceDescription")}
|
||||
/>
|
||||
|
||||
<OrgProvider org={org}>
|
||||
<ResourcesTable
|
||||
resources={resourceRows}
|
||||
internalResources={internalResourceRows}
|
||||
orgId={params.orgId}
|
||||
defaultView={
|
||||
env.flags.enableClients ? defaultView : "proxy"
|
||||
}
|
||||
defaultSort={{
|
||||
id: "name",
|
||||
desc: false
|
||||
}}
|
||||
/>
|
||||
</OrgProvider>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -54,22 +54,7 @@ export default function BlueprintDetailsForm({
|
||||
<div className="flex flex-col gap-6">
|
||||
<Alert>
|
||||
<AlertDescription>
|
||||
<InfoSections cols={2}>
|
||||
<InfoSection>
|
||||
<InfoSectionTitle>
|
||||
{t("appliedAt")}
|
||||
</InfoSectionTitle>
|
||||
<InfoSectionContent>
|
||||
<time
|
||||
className="text-muted-foreground"
|
||||
dateTime={blueprint.createdAt.toString()}
|
||||
>
|
||||
{new Date(
|
||||
blueprint.createdAt * 1000
|
||||
).toLocaleString()}
|
||||
</time>
|
||||
</InfoSectionContent>
|
||||
</InfoSection>
|
||||
<InfoSections cols={3}>
|
||||
<InfoSection>
|
||||
<InfoSectionTitle>
|
||||
{t("status")}
|
||||
@@ -88,16 +73,6 @@ export default function BlueprintDetailsForm({
|
||||
)}
|
||||
</InfoSectionContent>
|
||||
</InfoSection>
|
||||
<InfoSection>
|
||||
<InfoSectionTitle>
|
||||
{t("message")}
|
||||
</InfoSectionTitle>
|
||||
<InfoSectionContent>
|
||||
<p className="text-muted-foreground">
|
||||
{blueprint.message}
|
||||
</p>
|
||||
</InfoSectionContent>
|
||||
</InfoSection>
|
||||
<InfoSection>
|
||||
<InfoSectionTitle>
|
||||
{t("source")}
|
||||
@@ -106,35 +81,59 @@ export default function BlueprintDetailsForm({
|
||||
{blueprint.source === "API" && (
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className="-mx-2"
|
||||
className="inline-flex items-center gap-1 "
|
||||
>
|
||||
<span className="inline-flex items-center gap-1 ">
|
||||
API
|
||||
<Webhook className="size-4 flex-none" />
|
||||
</span>
|
||||
API
|
||||
<Webhook className="w-3 h-3 flex-none" />
|
||||
</Badge>
|
||||
)}
|
||||
{blueprint.source === "NEWT" && (
|
||||
<Badge variant="secondary">
|
||||
<span className="inline-flex items-center gap-1 ">
|
||||
Newt CLI
|
||||
<Terminal className="size-4 flex-none" />
|
||||
</span>
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className="inline-flex items-center gap-1 "
|
||||
>
|
||||
<Terminal className="w-3 h-3 flex-none" />
|
||||
Newt CLI
|
||||
</Badge>
|
||||
)}
|
||||
{blueprint.source === "UI" && (
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className="-mx-1 py-1"
|
||||
className="inline-flex items-center gap-1 "
|
||||
>
|
||||
<span className="inline-flex items-center gap-1 ">
|
||||
Dashboard{" "}
|
||||
<Globe className="size-4 flex-none" />
|
||||
</span>
|
||||
<Globe className="w-3 h-3 flex-none" />
|
||||
Dashboard
|
||||
</Badge>
|
||||
)}{" "}
|
||||
</InfoSectionContent>
|
||||
</InfoSection>
|
||||
<InfoSection>
|
||||
<InfoSectionTitle>
|
||||
{t("appliedAt")}
|
||||
</InfoSectionTitle>
|
||||
<InfoSectionContent>
|
||||
<time
|
||||
className="text-muted-foreground"
|
||||
dateTime={blueprint.createdAt.toString()}
|
||||
>
|
||||
{new Date(
|
||||
blueprint.createdAt * 1000
|
||||
).toLocaleString()}
|
||||
</time>
|
||||
</InfoSectionContent>
|
||||
</InfoSection>
|
||||
{blueprint.message && (
|
||||
<InfoSection>
|
||||
<InfoSectionTitle>
|
||||
{t("message")}
|
||||
</InfoSectionTitle>
|
||||
<InfoSectionContent>
|
||||
<p className="text-muted-foreground">
|
||||
{blueprint.message}
|
||||
</p>
|
||||
</InfoSectionContent>
|
||||
</InfoSection>
|
||||
)}
|
||||
</InfoSections>
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
@@ -169,11 +168,6 @@ export default function BlueprintDetailsForm({
|
||||
<FormLabel>
|
||||
{t("parsedContents")}
|
||||
</FormLabel>
|
||||
<FormDescription>
|
||||
{t(
|
||||
"blueprintContentsDescription"
|
||||
)}
|
||||
</FormDescription>
|
||||
<FormControl>
|
||||
<div
|
||||
className={cn(
|
||||
|
||||
@@ -116,10 +116,13 @@ export default function BlueprintsTable({ blueprints, orgId }: Props) {
|
||||
}
|
||||
case "UI": {
|
||||
return (
|
||||
<Badge variant="secondary">
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className="inline-flex items-center gap-1"
|
||||
>
|
||||
<span className="inline-flex items-center gap-1 ">
|
||||
Dashboard{" "}
|
||||
<Globe className="size-4 flex-none" />
|
||||
<Globe className="w-3 h-3" />
|
||||
Dashboard
|
||||
</span>
|
||||
</Badge>
|
||||
);
|
||||
@@ -163,18 +166,14 @@ export default function BlueprintsTable({ blueprints, orgId }: Props) {
|
||||
cell: ({ row }) => {
|
||||
return (
|
||||
<div className="flex justify-end">
|
||||
<Button
|
||||
variant="outline"
|
||||
className="items-center"
|
||||
asChild
|
||||
<Link
|
||||
href={`/${orgId}/settings/blueprints/${row.original.blueprintId}`}
|
||||
>
|
||||
<Link
|
||||
href={`/${orgId}/settings/blueprints/${row.original.blueprintId}`}
|
||||
>
|
||||
View details{" "}
|
||||
<ArrowRight className="size-4 flex-none" />
|
||||
</Link>
|
||||
</Button>
|
||||
<Button variant="outline" className="items-center">
|
||||
View Details
|
||||
<ArrowRight className="ml-2 w-4 h-4" />
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -124,7 +124,6 @@ export function DNSRecordsDataTable<TData, TValue>({
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
<TableRow
|
||||
key={headerGroup.id}
|
||||
className="bg-secondary dark:bg-transparent"
|
||||
>
|
||||
{headerGroup.headers.map((header) => (
|
||||
<TableHead key={header.id}>
|
||||
|
||||
@@ -9,13 +9,15 @@ import {
|
||||
SortingState,
|
||||
getSortedRowModel,
|
||||
ColumnFiltersState,
|
||||
getFilteredRowModel
|
||||
getFilteredRowModel,
|
||||
VisibilityState
|
||||
} from "@tanstack/react-table";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger
|
||||
DropdownMenuTrigger,
|
||||
DropdownMenuCheckboxItem,
|
||||
} from "@app/components/ui/dropdown-menu";
|
||||
import { Button } from "@app/components/ui/button";
|
||||
import {
|
||||
@@ -25,7 +27,16 @@ import {
|
||||
ArrowUpRight,
|
||||
ShieldOff,
|
||||
ShieldCheck,
|
||||
RefreshCw
|
||||
RefreshCw,
|
||||
Settings2,
|
||||
Plus,
|
||||
Search,
|
||||
ChevronDown,
|
||||
Clock,
|
||||
Wifi,
|
||||
WifiOff,
|
||||
CheckCircle2,
|
||||
XCircle,
|
||||
} from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/navigation";
|
||||
@@ -44,7 +55,6 @@ import { useTranslations } from "next-intl";
|
||||
import { InfoPopup } from "@app/components/ui/info-popup";
|
||||
import { Input } from "@app/components/ui/input";
|
||||
import { DataTablePagination } from "@app/components/DataTablePagination";
|
||||
import { Plus, Search } from "lucide-react";
|
||||
import { Card, CardContent, CardHeader } from "@app/components/ui/card";
|
||||
import {
|
||||
Table,
|
||||
@@ -65,6 +75,15 @@ import EditInternalResourceDialog from "@app/components/EditInternalResourceDial
|
||||
import CreateInternalResourceDialog from "@app/components/CreateInternalResourceDialog";
|
||||
import { Alert, AlertDescription } from "@app/components/ui/alert";
|
||||
|
||||
|
||||
export type TargetHealth = {
|
||||
targetId: number;
|
||||
ip: string;
|
||||
port: number;
|
||||
enabled: boolean;
|
||||
healthStatus?: 'healthy' | 'unhealthy' | 'unknown';
|
||||
};
|
||||
|
||||
export type ResourceRow = {
|
||||
id: number;
|
||||
nice: string | null;
|
||||
@@ -78,8 +97,54 @@ export type ResourceRow = {
|
||||
enabled: boolean;
|
||||
domainId?: string;
|
||||
ssl: boolean;
|
||||
targetHost?: string;
|
||||
targetPort?: number;
|
||||
targets?: TargetHealth[];
|
||||
};
|
||||
|
||||
|
||||
function getOverallHealthStatus(targets?: TargetHealth[]): 'online' | 'degraded' | 'offline' | 'unknown' {
|
||||
if (!targets || targets.length === 0) {
|
||||
return 'unknown';
|
||||
}
|
||||
|
||||
const monitoredTargets = targets.filter(t => t.enabled && t.healthStatus && t.healthStatus !== 'unknown');
|
||||
|
||||
if (monitoredTargets.length === 0) {
|
||||
return 'unknown';
|
||||
}
|
||||
|
||||
const healthyCount = monitoredTargets.filter(t => t.healthStatus === 'healthy').length;
|
||||
const unhealthyCount = monitoredTargets.filter(t => t.healthStatus === 'unhealthy').length;
|
||||
|
||||
if (healthyCount === monitoredTargets.length) {
|
||||
return 'online';
|
||||
} else if (unhealthyCount === monitoredTargets.length) {
|
||||
return 'offline';
|
||||
} else {
|
||||
return 'degraded';
|
||||
}
|
||||
}
|
||||
|
||||
function StatusIcon({ status, className = "" }: {
|
||||
status: 'online' | 'degraded' | 'offline' | 'unknown';
|
||||
className?: string;
|
||||
}) {
|
||||
const iconClass = `h-4 w-4 ${className}`;
|
||||
|
||||
switch (status) {
|
||||
case 'online':
|
||||
return <CheckCircle2 className={`${iconClass} text-green-500`} />;
|
||||
case 'degraded':
|
||||
return <CheckCircle2 className={`${iconClass} text-yellow-500`} />;
|
||||
case 'offline':
|
||||
return <XCircle className={`${iconClass} text-destructive`} />;
|
||||
case 'unknown':
|
||||
return <Clock className={`${iconClass} text-gray-400`} />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
export type InternalResourceRow = {
|
||||
id: number;
|
||||
name: string;
|
||||
@@ -143,6 +208,7 @@ const setStoredPageSize = (pageSize: number, tableId?: string): void => {
|
||||
};
|
||||
|
||||
|
||||
|
||||
export default function ResourcesTable({
|
||||
resources,
|
||||
internalResources,
|
||||
@@ -158,6 +224,7 @@ export default function ResourcesTable({
|
||||
|
||||
const api = createApiClient({ env });
|
||||
|
||||
|
||||
const [proxyPageSize, setProxyPageSize] = useState<number>(() =>
|
||||
getStoredPageSize('proxy-resources', 20)
|
||||
);
|
||||
@@ -179,6 +246,10 @@ export default function ResourcesTable({
|
||||
const [proxySorting, setProxySorting] = useState<SortingState>(
|
||||
defaultSort ? [defaultSort] : []
|
||||
);
|
||||
|
||||
const [proxyColumnVisibility, setProxyColumnVisibility] = useState<VisibilityState>({});
|
||||
const [internalColumnVisibility, setInternalColumnVisibility] = useState<VisibilityState>({});
|
||||
|
||||
const [proxyColumnFilters, setProxyColumnFilters] =
|
||||
useState<ColumnFiltersState>([]);
|
||||
const [proxyGlobalFilter, setProxyGlobalFilter] = useState<any>([]);
|
||||
@@ -349,6 +420,76 @@ export default function ResourcesTable({
|
||||
});
|
||||
}
|
||||
|
||||
function TargetStatusCell({ targets }: { targets?: TargetHealth[] }) {
|
||||
const overallStatus = getOverallHealthStatus(targets);
|
||||
|
||||
if (!targets || targets.length === 0) {
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<StatusIcon status="unknown" />
|
||||
<span className="text-sm text-muted-foreground">No targets</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const monitoredTargets = targets.filter(t => t.enabled && t.healthStatus && t.healthStatus !== 'unknown');
|
||||
const unknownTargets = targets.filter(t => !t.enabled || !t.healthStatus || t.healthStatus === 'unknown');
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="ghost" size="sm" className="flex items-center gap-2 h-8">
|
||||
<StatusIcon status={overallStatus} />
|
||||
<span className="text-sm">
|
||||
{overallStatus === 'online' && 'Healthy'}
|
||||
{overallStatus === 'degraded' && 'Degraded'}
|
||||
{overallStatus === 'offline' && 'Offline'}
|
||||
{overallStatus === 'unknown' && 'Unknown'}
|
||||
</span>
|
||||
<ChevronDown className="h-3 w-3" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="start" className="min-w-[280px]">
|
||||
{monitoredTargets.length > 0 && (
|
||||
<>
|
||||
{monitoredTargets.map((target) => (
|
||||
<DropdownMenuItem key={target.targetId} className="flex items-center justify-between gap-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<StatusIcon
|
||||
status={target.healthStatus === 'healthy' ? 'online' : 'offline'}
|
||||
className="h-3 w-3"
|
||||
/>
|
||||
{`${target.ip}:${target.port}`}
|
||||
</div>
|
||||
<span className={`capitalize ${target.healthStatus === 'healthy' ? 'text-green-500' : 'text-destructive'
|
||||
}`}>
|
||||
{target.healthStatus}
|
||||
</span>
|
||||
</DropdownMenuItem>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
{unknownTargets.length > 0 && (
|
||||
<>
|
||||
{unknownTargets.map((target) => (
|
||||
<DropdownMenuItem key={target.targetId} className="flex items-center justify-between gap-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<StatusIcon status="unknown" className="h-3 w-3" />
|
||||
{`${target.ip}:${target.port}`}
|
||||
</div>
|
||||
<span className="text-muted-foreground">
|
||||
{!target.enabled ? 'Disabled' : 'Not monitored'}
|
||||
</span>
|
||||
</DropdownMenuItem>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
const proxyColumns: ColumnDef<ResourceRow>[] = [
|
||||
{
|
||||
accessorKey: "name",
|
||||
@@ -390,6 +531,33 @@ export default function ResourcesTable({
|
||||
return <span>{resourceRow.http ? (resourceRow.ssl ? "HTTPS" : "HTTP") : resourceRow.protocol.toUpperCase()}</span>;
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "status",
|
||||
accessorKey: "status",
|
||||
header: ({ column }) => {
|
||||
return (
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() =>
|
||||
column.toggleSorting(column.getIsSorted() === "asc")
|
||||
}
|
||||
>
|
||||
{t("status")}
|
||||
<ArrowUpDown className="ml-2 h-4 w-4" />
|
||||
</Button>
|
||||
);
|
||||
},
|
||||
cell: ({ row }) => {
|
||||
const resourceRow = row.original;
|
||||
return <TargetStatusCell targets={resourceRow.targets} />;
|
||||
},
|
||||
sortingFn: (rowA, rowB) => {
|
||||
const statusA = getOverallHealthStatus(rowA.original.targets);
|
||||
const statusB = getOverallHealthStatus(rowB.original.targets);
|
||||
const statusOrder = { online: 3, degraded: 2, offline: 1, unknown: 0 };
|
||||
return statusOrder[statusA] - statusOrder[statusB];
|
||||
}
|
||||
},
|
||||
{
|
||||
accessorKey: "domain",
|
||||
header: t("access"),
|
||||
@@ -647,6 +815,7 @@ export default function ResourcesTable({
|
||||
onColumnFiltersChange: setProxyColumnFilters,
|
||||
getFilteredRowModel: getFilteredRowModel(),
|
||||
onGlobalFilterChange: setProxyGlobalFilter,
|
||||
onColumnVisibilityChange: setProxyColumnVisibility,
|
||||
initialState: {
|
||||
pagination: {
|
||||
pageSize: proxyPageSize,
|
||||
@@ -656,7 +825,8 @@ export default function ResourcesTable({
|
||||
state: {
|
||||
sorting: proxySorting,
|
||||
columnFilters: proxyColumnFilters,
|
||||
globalFilter: proxyGlobalFilter
|
||||
globalFilter: proxyGlobalFilter,
|
||||
columnVisibility: proxyColumnVisibility
|
||||
}
|
||||
});
|
||||
|
||||
@@ -670,6 +840,7 @@ export default function ResourcesTable({
|
||||
onColumnFiltersChange: setInternalColumnFilters,
|
||||
getFilteredRowModel: getFilteredRowModel(),
|
||||
onGlobalFilterChange: setInternalGlobalFilter,
|
||||
onColumnVisibilityChange: setInternalColumnVisibility,
|
||||
initialState: {
|
||||
pagination: {
|
||||
pageSize: internalPageSize,
|
||||
@@ -679,7 +850,8 @@ export default function ResourcesTable({
|
||||
state: {
|
||||
sorting: internalSorting,
|
||||
columnFilters: internalColumnFilters,
|
||||
globalFilter: internalGlobalFilter
|
||||
globalFilter: internalGlobalFilter,
|
||||
columnVisibility: internalColumnVisibility
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user