mirror of
https://github.com/fosrl/pangolin.git
synced 2026-02-18 02:46:37 +00:00
Compare commits
73 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b0ff50a76f | ||
|
|
37acdc2796 | ||
|
|
f3d31cb6de | ||
|
|
a336955066 | ||
|
|
a229fc1c61 | ||
|
|
7995fd364e | ||
|
|
5e0d822d45 | ||
|
|
4fddaa8f11 | ||
|
|
4a87cecf89 | ||
|
|
ac5ee5c7ca | ||
|
|
8a8c357563 | ||
|
|
263fd80c18 | ||
|
|
7bdf05bdf5 | ||
|
|
d00f12967d | ||
|
|
d9991a18e2 | ||
|
|
a51c21cdd2 | ||
|
|
265cab5b64 | ||
|
|
da15e5e77b | ||
|
|
a717ca2675 | ||
|
|
564b290244 | ||
|
|
84d78df67e | ||
|
|
107053a98f | ||
|
|
6422a78e6f | ||
|
|
5f11630e27 | ||
|
|
a776b2ea94 | ||
|
|
b83ec1b503 | ||
|
|
83bd5957cd | ||
|
|
66c14c2d09 | ||
|
|
51db267a4a | ||
|
|
669817818a | ||
|
|
b84453bfbe | ||
|
|
15d561f59f | ||
|
|
749cea5a4d | ||
|
|
2a7529c39e | ||
|
|
fce887436d | ||
|
|
3489107a49 | ||
|
|
296b220bf3 | ||
|
|
0a9f37c44d | ||
|
|
ef5d72663f | ||
|
|
6ddfc9b8fe | ||
|
|
301654b63e | ||
|
|
3b12a77cf0 | ||
|
|
7cd31313d8 | ||
|
|
9822deb4bf | ||
|
|
8942cb7aa7 | ||
|
|
f0f219f293 | ||
|
|
6174599754 | ||
|
|
8ba04aeb74 | ||
|
|
43590896e9 | ||
|
|
3547c4832b | ||
|
|
1cd098252e | ||
|
|
4adbc31dae | ||
|
|
99031feb35 | ||
|
|
d363b06d0e | ||
|
|
2af100cc86 | ||
|
|
3e90211108 | ||
|
|
6dd161fe17 | ||
|
|
558bd040c6 | ||
|
|
f2c48975f6 | ||
|
|
fc43a56bb3 | ||
|
|
ca7f557a3c | ||
|
|
7477713eef | ||
|
|
c16e762fa4 | ||
|
|
41592133a6 | ||
|
|
54f7525f1b | ||
|
|
ad6bb3da9f | ||
|
|
49bc2dc5da | ||
|
|
cdf77087cd | ||
|
|
8e5dde887c | ||
|
|
f21188000e | ||
|
|
1b3eb32bf4 | ||
|
|
eec3f183e6 | ||
|
|
ad425e8d9e |
2
.github/workflows/cicd.yml
vendored
2
.github/workflows/cicd.yml
vendored
@@ -31,7 +31,7 @@ jobs:
|
|||||||
timeout-minutes: 120
|
timeout-minutes: 120
|
||||||
env:
|
env:
|
||||||
# Target images
|
# 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 }}
|
GHCR_IMAGE: ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
|||||||
@@ -89,3 +89,7 @@ Pangolin is dual licensed under the AGPL-3 and the [Fossorial Commercial License
|
|||||||
## Contributions
|
## Contributions
|
||||||
|
|
||||||
Please see [CONTRIBUTING](./CONTRIBUTING.md) in the repository for guidelines and best practices.
|
Please see [CONTRIBUTING](./CONTRIBUTING.md) in the repository for guidelines and best practices.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
WireGuard® is a registered trademark of Jason A. Donenfeld.
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ func installDocker() error {
|
|||||||
case strings.Contains(osRelease, "ID=ubuntu"):
|
case strings.Contains(osRelease, "ID=ubuntu"):
|
||||||
installCmd = exec.Command("bash", "-c", fmt.Sprintf(`
|
installCmd = exec.Command("bash", "-c", fmt.Sprintf(`
|
||||||
apt-get update &&
|
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 &&
|
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 &&
|
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 &&
|
apt-get update &&
|
||||||
@@ -82,7 +82,7 @@ func installDocker() error {
|
|||||||
case strings.Contains(osRelease, "ID=debian"):
|
case strings.Contains(osRelease, "ID=debian"):
|
||||||
installCmd = exec.Command("bash", "-c", fmt.Sprintf(`
|
installCmd = exec.Command("bash", "-c", fmt.Sprintf(`
|
||||||
apt-get update &&
|
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 &&
|
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 &&
|
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 &&
|
apt-get update &&
|
||||||
|
|||||||
@@ -131,7 +131,7 @@
|
|||||||
"expireIn": "Изтече",
|
"expireIn": "Изтече",
|
||||||
"neverExpire": "Никога не изтича",
|
"neverExpire": "Никога не изтича",
|
||||||
"shareExpireDescription": "Времето на изтичане е колко дълго връзката ще бъде използваема и ще предоставя достъп до ресурса. След това време, връзката няма да работи и потребителите, които са я използвали, ще загубят достъп до ресурса.",
|
"shareExpireDescription": "Времето на изтичане е колко дълго връзката ще бъде използваема и ще предоставя достъп до ресурса. След това време, връзката няма да работи и потребителите, които са я използвали, ще загубят достъп до ресурса.",
|
||||||
"shareSeeOnce": "Ще можете да видите тази връзка само веднъж. Уверете се да я копирате.",
|
"shareSeeOnce": "Ще можете да видите този линк само веднъж. Уверете се, че го копирате.",
|
||||||
"shareAccessHint": "Всеки с тази връзка може да има достъп до ресурса. Споделяйте я с внимание.",
|
"shareAccessHint": "Всеки с тази връзка може да има достъп до ресурса. Споделяйте я с внимание.",
|
||||||
"shareTokenUsage": "Вижте използването на токена за достъп",
|
"shareTokenUsage": "Вижте използването на токена за достъп",
|
||||||
"createLink": "Създаване на връзка",
|
"createLink": "Създаване на връзка",
|
||||||
@@ -179,7 +179,7 @@
|
|||||||
"baseDomain": "Базов домейн",
|
"baseDomain": "Базов домейн",
|
||||||
"subdomnainDescription": "Субдомейнът, в който ще бъде достъпен вашият ресурс.",
|
"subdomnainDescription": "Субдомейнът, в който ще бъде достъпен вашият ресурс.",
|
||||||
"resourceRawSettings": "TCP/UDP настройки",
|
"resourceRawSettings": "TCP/UDP настройки",
|
||||||
"resourceRawSettingsDescription": "Конфигурирайте как вашият ресурс ще бъде достъпен през TCP/UDP",
|
"resourceRawSettingsDescription": "Настройте как ресурсът ви ще бъде достъпен през TCP/UDP. Свързвате ресурса към порт на хост сървъра Pangolin, за да го достъпвате от server-public-ip:mapped-port.",
|
||||||
"protocol": "Протокол",
|
"protocol": "Протокол",
|
||||||
"protocolSelect": "Изберете протокол",
|
"protocolSelect": "Изберете протокол",
|
||||||
"resourcePortNumber": "Номер на порт",
|
"resourcePortNumber": "Номер на порт",
|
||||||
@@ -1165,13 +1165,13 @@
|
|||||||
"sidebarDomains": "Домейни",
|
"sidebarDomains": "Домейни",
|
||||||
"sidebarBluePrints": "Чертежи",
|
"sidebarBluePrints": "Чертежи",
|
||||||
"blueprints": "Чертежи",
|
"blueprints": "Чертежи",
|
||||||
"blueprintsDescription": "Чертежите са декларативни YAML конфигурации, които определят вашите ресурси и техните настройки",
|
"blueprintsDescription": "Прилагайте декларативни конфигурации и преглеждайте предишни изпълнения",
|
||||||
"blueprintAdd": "Добави Чертеж",
|
"blueprintAdd": "Добави Чертеж",
|
||||||
"blueprintGoBack": "Виж всички Чертежи",
|
"blueprintGoBack": "Виж всички Чертежи",
|
||||||
"blueprintCreate": "Създай Чертеж",
|
"blueprintCreate": "Създай Чертеж",
|
||||||
"blueprintCreateDescription2": "Следвайте стъпките по-долу, за да създадете и приложите нов чертеж",
|
"blueprintCreateDescription2": "Следвайте стъпките по-долу, за да създадете и приложите нов чертеж",
|
||||||
"blueprintDetails": "Детайли за Чертежа",
|
"blueprintDetails": "Детайли на чертежа",
|
||||||
"blueprintDetailsDescription": "Вижте детайлите за изпълнението на чертежа",
|
"blueprintDetailsDescription": "Вижте резултата от приложените чертежи и всички възникнали грешки",
|
||||||
"blueprintInfo": "Информация за Чертежа",
|
"blueprintInfo": "Информация за Чертежа",
|
||||||
"message": "Съобщение",
|
"message": "Съобщение",
|
||||||
"blueprintContentsDescription": "Дефинирайте YAML съдържанието, описващо вашата инфраструктура",
|
"blueprintContentsDescription": "Дефинирайте YAML съдържанието, описващо вашата инфраструктура",
|
||||||
@@ -1181,7 +1181,7 @@
|
|||||||
"appliedAt": "Приложено във",
|
"appliedAt": "Приложено във",
|
||||||
"source": "Източник",
|
"source": "Източник",
|
||||||
"contents": "Съдържание",
|
"contents": "Съдържание",
|
||||||
"parsedContents": "Анализирано съдържание",
|
"parsedContents": "Парсирано съдържание (само за четене)",
|
||||||
"enableDockerSocket": "Активиране на Docker Чернова",
|
"enableDockerSocket": "Активиране на Docker Чернова",
|
||||||
"enableDockerSocketDescription": "Активиране на Docker Socket маркировка за изтегляне на етикети на чернова. Пътят на гнездото трябва да бъде предоставен на Newt.",
|
"enableDockerSocketDescription": "Активиране на Docker Socket маркировка за изтегляне на етикети на чернова. Пътят на гнездото трябва да бъде предоставен на Newt.",
|
||||||
"enableDockerSocketLink": "Научете повече",
|
"enableDockerSocketLink": "Научете повече",
|
||||||
@@ -2080,5 +2080,20 @@
|
|||||||
"supportSending": "Изпращане...",
|
"supportSending": "Изпращане...",
|
||||||
"supportSend": "Изпрати",
|
"supportSend": "Изпрати",
|
||||||
"supportMessageSent": "Съобщението е изпратено!",
|
"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",
|
"expireIn": "Platnost vyprší za",
|
||||||
"neverExpire": "Nikdy nevyprší",
|
"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.",
|
"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.",
|
"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",
|
"shareTokenUsage": "Zobrazit využití přístupového tokenu",
|
||||||
"createLink": "Vytvořit odkaz",
|
"createLink": "Vytvořit odkaz",
|
||||||
@@ -179,7 +179,7 @@
|
|||||||
"baseDomain": "Základní doména",
|
"baseDomain": "Základní doména",
|
||||||
"subdomnainDescription": "Subdoména, kde bude váš zdroj přístupný.",
|
"subdomnainDescription": "Subdoména, kde bude váš zdroj přístupný.",
|
||||||
"resourceRawSettings": "Nastavení TCP/UDP",
|
"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",
|
"protocol": "Protokol",
|
||||||
"protocolSelect": "Vybrat protokol",
|
"protocolSelect": "Vybrat protokol",
|
||||||
"resourcePortNumber": "Číslo portu",
|
"resourcePortNumber": "Číslo portu",
|
||||||
@@ -1165,13 +1165,13 @@
|
|||||||
"sidebarDomains": "Domény",
|
"sidebarDomains": "Domény",
|
||||||
"sidebarBluePrints": "Plány",
|
"sidebarBluePrints": "Plány",
|
||||||
"blueprints": "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",
|
"blueprintAdd": "Přidat plán",
|
||||||
"blueprintGoBack": "Zobrazit všechny plány",
|
"blueprintGoBack": "Zobrazit všechny plány",
|
||||||
"blueprintCreate": "Vytvořit plán",
|
"blueprintCreate": "Vytvořit plán",
|
||||||
"blueprintCreateDescription2": "Postupujte podle níže uvedených kroků pro vytvoření a použití nového plánu",
|
"blueprintCreateDescription2": "Postupujte podle níže uvedených kroků pro vytvoření a použití nového plánu",
|
||||||
"blueprintDetails": "Podrobnosti 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",
|
"blueprintInfo": "Informace o plánu",
|
||||||
"message": "Zpráva",
|
"message": "Zpráva",
|
||||||
"blueprintContentsDescription": "Definujte obsah YAML popisující vaši infrastrukturu",
|
"blueprintContentsDescription": "Definujte obsah YAML popisující vaši infrastrukturu",
|
||||||
@@ -1181,7 +1181,7 @@
|
|||||||
"appliedAt": "Použito v",
|
"appliedAt": "Použito v",
|
||||||
"source": "Zdroj",
|
"source": "Zdroj",
|
||||||
"contents": "Obsah",
|
"contents": "Obsah",
|
||||||
"parsedContents": "Parsovaný obsah",
|
"parsedContents": "Parsed content (Pouze pro čtení)",
|
||||||
"enableDockerSocket": "Povolit Docker plán",
|
"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.",
|
"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",
|
"enableDockerSocketLink": "Zjistit více",
|
||||||
@@ -2080,5 +2080,20 @@
|
|||||||
"supportSending": "Odesílání...",
|
"supportSending": "Odesílání...",
|
||||||
"supportSend": "Poslat",
|
"supportSend": "Poslat",
|
||||||
"supportMessageSent": "Zpráva odeslána!",
|
"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",
|
"expireIn": "Verfällt in",
|
||||||
"neverExpire": "Nie ablaufen",
|
"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.",
|
"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.",
|
"shareAccessHint": "Jeder mit diesem Link kann auf die Ressource zugreifen. Teilen Sie sie mit Vorsicht.",
|
||||||
"shareTokenUsage": "Zugriffstoken-Nutzung anzeigen",
|
"shareTokenUsage": "Zugriffstoken-Nutzung anzeigen",
|
||||||
"createLink": "Link erstellen",
|
"createLink": "Link erstellen",
|
||||||
@@ -179,7 +179,7 @@
|
|||||||
"baseDomain": "Basis-Domain",
|
"baseDomain": "Basis-Domain",
|
||||||
"subdomnainDescription": "Die Subdomain, auf der Ihre Ressource erreichbar sein soll.",
|
"subdomnainDescription": "Die Subdomain, auf der Ihre Ressource erreichbar sein soll.",
|
||||||
"resourceRawSettings": "TCP/UDP Einstellungen",
|
"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",
|
"protocol": "Protokoll",
|
||||||
"protocolSelect": "Wählen Sie ein Protokoll",
|
"protocolSelect": "Wählen Sie ein Protokoll",
|
||||||
"resourcePortNumber": "Portnummer",
|
"resourcePortNumber": "Portnummer",
|
||||||
@@ -1165,13 +1165,13 @@
|
|||||||
"sidebarDomains": "Domänen",
|
"sidebarDomains": "Domänen",
|
||||||
"sidebarBluePrints": "Baupläne",
|
"sidebarBluePrints": "Baupläne",
|
||||||
"blueprints": "Baupläne",
|
"blueprints": "Baupläne",
|
||||||
"blueprintsDescription": "Blaupausen sind deklarative YAML-Konfigurationen, die deine Ressourcen und deren Einstellungen definieren",
|
"blueprintsDescription": "Deklarative Konfigurationen anwenden und vorherige Abläufe anzeigen",
|
||||||
"blueprintAdd": "Blaupause hinzufügen",
|
"blueprintAdd": "Blaupause hinzufügen",
|
||||||
"blueprintGoBack": "Alle Blaupausen ansehen",
|
"blueprintGoBack": "Alle Blaupausen ansehen",
|
||||||
"blueprintCreate": "Blaupause erstellen",
|
"blueprintCreate": "Blaupause erstellen",
|
||||||
"blueprintCreateDescription2": "Folge den Schritten unten, um eine neue Blaupause zu erstellen und anzuwenden",
|
"blueprintCreateDescription2": "Folge den Schritten unten, um eine neue Blaupause zu erstellen und anzuwenden",
|
||||||
"blueprintDetails": "Blaupausendetails",
|
"blueprintDetails": "Blaupausendetails",
|
||||||
"blueprintDetailsDescription": "Siehe die Blaupausenlauf-Details",
|
"blueprintDetailsDescription": "Siehe das Ergebnis der angewendeten Blaupause und alle aufgetretenen Fehler",
|
||||||
"blueprintInfo": "Blaupauseninformation",
|
"blueprintInfo": "Blaupauseninformation",
|
||||||
"message": "Nachricht",
|
"message": "Nachricht",
|
||||||
"blueprintContentsDescription": "Definieren Sie den YAML-Inhalt, der Ihre Infrastruktur beschreibt",
|
"blueprintContentsDescription": "Definieren Sie den YAML-Inhalt, der Ihre Infrastruktur beschreibt",
|
||||||
@@ -1181,7 +1181,7 @@
|
|||||||
"appliedAt": "Angewandt am",
|
"appliedAt": "Angewandt am",
|
||||||
"source": "Quelle",
|
"source": "Quelle",
|
||||||
"contents": "Inhalt",
|
"contents": "Inhalt",
|
||||||
"parsedContents": "Analysierte Inhalte",
|
"parsedContents": "Analysierte Inhalte (Nur lesen)",
|
||||||
"enableDockerSocket": "Docker Blaupause aktivieren",
|
"enableDockerSocket": "Docker Blaupause aktivieren",
|
||||||
"enableDockerSocketDescription": "Aktiviere Docker-Socket-Label-Scraping für Blaupausenbeschriftungen. Der Socket-Pfad muss neu angegeben werden.",
|
"enableDockerSocketDescription": "Aktiviere Docker-Socket-Label-Scraping für Blaupausenbeschriftungen. Der Socket-Pfad muss neu angegeben werden.",
|
||||||
"enableDockerSocketLink": "Mehr erfahren",
|
"enableDockerSocketLink": "Mehr erfahren",
|
||||||
@@ -2080,5 +2080,20 @@
|
|||||||
"supportSending": "Senden...",
|
"supportSending": "Senden...",
|
||||||
"supportSend": "Senden",
|
"supportSend": "Senden",
|
||||||
"supportMessageSent": "Nachricht gesendet!",
|
"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"
|
||||||
}
|
}
|
||||||
@@ -131,7 +131,7 @@
|
|||||||
"expireIn": "Expire In",
|
"expireIn": "Expire In",
|
||||||
"neverExpire": "Never expire",
|
"neverExpire": "Never expire",
|
||||||
"shareExpireDescription": "Expiration time is how long the link will be usable and provide access to the resource. After this time, the link will no longer work, and users who used this link will lose access to the resource.",
|
"shareExpireDescription": "Expiration time is how long the link will be usable and provide access to the resource. After this time, the link will no longer work, and users who used this link will lose access to the resource.",
|
||||||
"shareSeeOnce": "You will only be able to see this linkonce. Make sure to copy it.",
|
"shareSeeOnce": "You will only be able to see this link once. Make sure to copy it.",
|
||||||
"shareAccessHint": "Anyone with this link can access the resource. Share it with care.",
|
"shareAccessHint": "Anyone with this link can access the resource. Share it with care.",
|
||||||
"shareTokenUsage": "See Access Token Usage",
|
"shareTokenUsage": "See Access Token Usage",
|
||||||
"createLink": "Create Link",
|
"createLink": "Create Link",
|
||||||
@@ -179,7 +179,7 @@
|
|||||||
"baseDomain": "Base Domain",
|
"baseDomain": "Base Domain",
|
||||||
"subdomnainDescription": "The subdomain where your resource will be accessible.",
|
"subdomnainDescription": "The subdomain where your resource will be accessible.",
|
||||||
"resourceRawSettings": "TCP/UDP Settings",
|
"resourceRawSettings": "TCP/UDP Settings",
|
||||||
"resourceRawSettingsDescription": "Configure how your resource will be accessed over TCP/UDP",
|
"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",
|
"protocol": "Protocol",
|
||||||
"protocolSelect": "Select a protocol",
|
"protocolSelect": "Select a protocol",
|
||||||
"resourcePortNumber": "Port Number",
|
"resourcePortNumber": "Port Number",
|
||||||
@@ -1165,13 +1165,13 @@
|
|||||||
"sidebarDomains": "Domains",
|
"sidebarDomains": "Domains",
|
||||||
"sidebarBluePrints": "Blueprints",
|
"sidebarBluePrints": "Blueprints",
|
||||||
"blueprints": "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",
|
"blueprintAdd": "Add Blueprint",
|
||||||
"blueprintGoBack": "See all Blueprints",
|
"blueprintGoBack": "See all Blueprints",
|
||||||
"blueprintCreate": "Create Blueprint",
|
"blueprintCreate": "Create Blueprint",
|
||||||
"blueprintCreateDescription2": "Follow the steps below to create and apply a new blueprint",
|
"blueprintCreateDescription2": "Follow the steps below to create and apply a new blueprint",
|
||||||
"blueprintDetails": "Blueprint details",
|
"blueprintDetails": "Blueprint Details",
|
||||||
"blueprintDetailsDescription": "See the blueprint run details",
|
"blueprintDetailsDescription": "See the result of the applied blueprint and any errors that occurred",
|
||||||
"blueprintInfo": "Blueprint Information",
|
"blueprintInfo": "Blueprint Information",
|
||||||
"message": "Message",
|
"message": "Message",
|
||||||
"blueprintContentsDescription": "Define the YAML content describing your infrastructure",
|
"blueprintContentsDescription": "Define the YAML content describing your infrastructure",
|
||||||
@@ -1181,7 +1181,7 @@
|
|||||||
"appliedAt": "Applied At",
|
"appliedAt": "Applied At",
|
||||||
"source": "Source",
|
"source": "Source",
|
||||||
"contents": "Contents",
|
"contents": "Contents",
|
||||||
"parsedContents": "Parsed Contents",
|
"parsedContents": "Parsed Contents (Read Only)",
|
||||||
"enableDockerSocket": "Enable Docker Blueprint",
|
"enableDockerSocket": "Enable Docker Blueprint",
|
||||||
"enableDockerSocketDescription": "Enable Docker Socket label scraping for blueprint labels. Socket path must be provided to Newt.",
|
"enableDockerSocketDescription": "Enable Docker Socket label scraping for blueprint labels. Socket path must be provided to Newt.",
|
||||||
"enableDockerSocketLink": "Learn More",
|
"enableDockerSocketLink": "Learn More",
|
||||||
@@ -2081,5 +2081,19 @@
|
|||||||
"supportSend": "Send",
|
"supportSend": "Send",
|
||||||
"supportMessageSent": "Message Sent!",
|
"supportMessageSent": "Message Sent!",
|
||||||
"supportWillContact": "We'll be in touch shortly!",
|
"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",
|
"expireIn": "Caduca en",
|
||||||
"neverExpire": "Nunca expirar",
|
"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.",
|
"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.",
|
"shareAccessHint": "Cualquiera con este enlace puede acceder al recurso. Compártelo con cuidado.",
|
||||||
"shareTokenUsage": "Ver Uso de Token de Acceso",
|
"shareTokenUsage": "Ver Uso de Token de Acceso",
|
||||||
"createLink": "Crear enlace",
|
"createLink": "Crear enlace",
|
||||||
@@ -179,7 +179,7 @@
|
|||||||
"baseDomain": "Dominio base",
|
"baseDomain": "Dominio base",
|
||||||
"subdomnainDescription": "El subdominio al que su recurso será accesible.",
|
"subdomnainDescription": "El subdominio al que su recurso será accesible.",
|
||||||
"resourceRawSettings": "Configuración TCP/UDP",
|
"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",
|
"protocol": "Protocolo",
|
||||||
"protocolSelect": "Seleccionar un protocolo",
|
"protocolSelect": "Seleccionar un protocolo",
|
||||||
"resourcePortNumber": "Número de puerto",
|
"resourcePortNumber": "Número de puerto",
|
||||||
@@ -1165,13 +1165,13 @@
|
|||||||
"sidebarDomains": "Dominios",
|
"sidebarDomains": "Dominios",
|
||||||
"sidebarBluePrints": "Planos",
|
"sidebarBluePrints": "Planos",
|
||||||
"blueprints": "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",
|
"blueprintAdd": "Añadir plano",
|
||||||
"blueprintGoBack": "Ver todos los Planos",
|
"blueprintGoBack": "Ver todos los Planos",
|
||||||
"blueprintCreate": "Crear Plano",
|
"blueprintCreate": "Crear Plano",
|
||||||
"blueprintCreateDescription2": "Siga los siguientes pasos para crear y aplicar un nuevo plano",
|
"blueprintCreateDescription2": "Siga los siguientes pasos para crear y aplicar un nuevo plano",
|
||||||
"blueprintDetails": "Detalles del 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",
|
"blueprintInfo": "Información del plano",
|
||||||
"message": "Mensaje",
|
"message": "Mensaje",
|
||||||
"blueprintContentsDescription": "Defina el contenido YAML describiendo su infraestructura",
|
"blueprintContentsDescription": "Defina el contenido YAML describiendo su infraestructura",
|
||||||
@@ -1181,7 +1181,7 @@
|
|||||||
"appliedAt": "Aplicado en",
|
"appliedAt": "Aplicado en",
|
||||||
"source": "Fuente",
|
"source": "Fuente",
|
||||||
"contents": "Contenido",
|
"contents": "Contenido",
|
||||||
"parsedContents": "Contenido analizado",
|
"parsedContents": "Contenido analizado (Sólo lectura)",
|
||||||
"enableDockerSocket": "Habilitar Plano Docker",
|
"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.",
|
"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",
|
"enableDockerSocketLink": "Saber más",
|
||||||
@@ -2080,5 +2080,20 @@
|
|||||||
"supportSending": "Enviando...",
|
"supportSending": "Enviando...",
|
||||||
"supportSend": "Enviar",
|
"supportSend": "Enviar",
|
||||||
"supportMessageSent": "¡Mensaje enviado!",
|
"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.",
|
"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.",
|
"componentsErrorNoMemberCreate": "Vous n'êtes actuellement membre d'aucune organisation. Créez une organisation pour commencer.",
|
||||||
"componentsErrorNoMember": "Vous n'êtes actuellement membre d'aucune organisation.",
|
"componentsErrorNoMember": "Vous n'êtes actuellement membre d'aucune organisation.",
|
||||||
"welcome": "Bienvenue à Pangolin",
|
"welcome": "Bienvenue sur Pangolin !",
|
||||||
"welcomeTo": "Bienvenue chez",
|
"welcomeTo": "Bienvenue chez",
|
||||||
"componentsCreateOrg": "Créer une organisation",
|
"componentsCreateOrg": "Créer une organisation",
|
||||||
"componentsMember": "Vous êtes membre de {count, plural, =0 {aucune organisation} one {une organisation} other {# organisations}}.",
|
"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.",
|
"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": "Refuser",
|
"dismiss": "Rejeter",
|
||||||
"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.",
|
"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}!",
|
"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.",
|
"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 que vous essayez d'accéder ne soit pas pour cet utilisateur.",
|
"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": "Assurez-vous que vous êtes bien connecté en tant qu'utilisateur correct.",
|
"inviteLoginUser": "Veuillez vous assurer que vous êtes connecté avec le bon utilisateur.",
|
||||||
"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.",
|
"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.",
|
"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",
|
"inviteLogInOtherUser": "Se connecter en tant qu'utilisateur différent",
|
||||||
"createAnAccount": "Créer un compte",
|
"createAnAccount": "Créer un compte",
|
||||||
"inviteNotAccepted": "Invitation non acceptée",
|
"inviteNotAccepted": "Invitation non acceptée",
|
||||||
@@ -39,12 +39,12 @@
|
|||||||
"online": "En ligne",
|
"online": "En ligne",
|
||||||
"offline": "Hors ligne",
|
"offline": "Hors ligne",
|
||||||
"site": "Site",
|
"site": "Site",
|
||||||
"dataIn": "Données dans",
|
"dataIn": "Données entrantes",
|
||||||
"dataOut": "Données épuisées",
|
"dataOut": "Données sortantes",
|
||||||
"connectionType": "Type de connexion",
|
"connectionType": "Type de connexion",
|
||||||
"tunnelType": "Type de tunnel",
|
"tunnelType": "Type de tunnel",
|
||||||
"local": "Locale",
|
"local": "Locale",
|
||||||
"edit": "Editer",
|
"edit": "Éditer",
|
||||||
"siteConfirmDelete": "Confirmer la suppression du site",
|
"siteConfirmDelete": "Confirmer la suppression du site",
|
||||||
"siteDelete": "Supprimer le 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.",
|
"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",
|
"siteLearnNewt": "Apprenez à installer Newt sur votre système",
|
||||||
"siteSeeConfigOnce": "Vous ne pourrez voir la configuration qu'une seule fois.",
|
"siteSeeConfigOnce": "Vous ne pourrez voir la configuration qu'une seule fois.",
|
||||||
"siteLoadWGConfig": "Chargement de la configuration WireGuard...",
|
"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",
|
"toggle": "Activer/désactiver",
|
||||||
"dockerCompose": "Composition Docker",
|
"dockerCompose": "Composition Docker",
|
||||||
"dockerRun": "Exécution 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",
|
"siteConfirmCopy": "J'ai copié la configuration",
|
||||||
"searchSitesProgress": "Rechercher des sites...",
|
"searchSitesProgress": "Rechercher des sites...",
|
||||||
"siteAdd": "Ajouter un site",
|
"siteAdd": "Ajouter un site",
|
||||||
@@ -78,7 +78,7 @@
|
|||||||
"operatingSystem": "Système d'exploitation",
|
"operatingSystem": "Système d'exploitation",
|
||||||
"commands": "Commandes",
|
"commands": "Commandes",
|
||||||
"recommended": "Recommandé",
|
"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",
|
"siteRunsInDocker": "Exécute dans Docker",
|
||||||
"siteRunsInShell": "Exécute en shell sur macOS, Linux et Windows",
|
"siteRunsInShell": "Exécute en shell sur macOS, Linux et Windows",
|
||||||
"siteErrorDelete": "Erreur lors de la suppression du site",
|
"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.",
|
"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",
|
"siteWg": "WireGuard basique",
|
||||||
"siteWgDescription": "Utilisez n'importe quel client WireGuard pour établir un tunnel. Configuration NAT manuelle requise.",
|
"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.",
|
"siteLocalDescription": "Ressources locales seulement. Pas de tunneling.",
|
||||||
"siteLocalDescriptionSaas": "Ressources locales uniquement. Pas de tunneling. Disponible uniquement sur les nœuds distants.",
|
"siteLocalDescriptionSaas": "Ressources locales uniquement. Pas de tunneling. Disponible uniquement sur les nœuds distants.",
|
||||||
"siteSeeAll": "Voir tous les sites",
|
"siteSeeAll": "Voir tous les sites",
|
||||||
@@ -130,9 +130,9 @@
|
|||||||
"shareTitleOptional": "Titre (facultatif)",
|
"shareTitleOptional": "Titre (facultatif)",
|
||||||
"expireIn": "Expire dans",
|
"expireIn": "Expire dans",
|
||||||
"neverExpire": "N'expire jamais",
|
"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.",
|
"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. Assurez-vous de le copier.",
|
"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 soin.",
|
"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",
|
"shareTokenUsage": "Voir Utilisation du jeton d'accès",
|
||||||
"createLink": "Créer un lien",
|
"createLink": "Créer un lien",
|
||||||
"resourcesNotFound": "Aucune ressource trouvée",
|
"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.",
|
"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 ?",
|
"resourceQuestionRemove": "Êtes-vous sûr de vouloir supprimer la ressource de l'organisation ?",
|
||||||
"resourceHTTP": "Ressource HTTPS",
|
"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",
|
"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",
|
"resourceCreate": "Créer une ressource",
|
||||||
"resourceCreateDescription": "Suivez les étapes ci-dessous pour créer une nouvelle ressource",
|
"resourceCreateDescription": "Suivez les étapes ci-dessous pour créer une nouvelle ressource",
|
||||||
"resourceSeeAll": "Voir toutes les ressources",
|
"resourceSeeAll": "Voir toutes les ressources",
|
||||||
@@ -179,7 +179,7 @@
|
|||||||
"baseDomain": "Domaine de base",
|
"baseDomain": "Domaine de base",
|
||||||
"subdomnainDescription": "Le sous-domaine où votre ressource sera accessible.",
|
"subdomnainDescription": "Le sous-domaine où votre ressource sera accessible.",
|
||||||
"resourceRawSettings": "Paramètres TCP/UDP",
|
"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",
|
"protocol": "Protocole",
|
||||||
"protocolSelect": "Sélectionner un protocole",
|
"protocolSelect": "Sélectionner un protocole",
|
||||||
"resourcePortNumber": "Numéro de port",
|
"resourcePortNumber": "Numéro de port",
|
||||||
@@ -206,7 +206,7 @@
|
|||||||
"resourceSetting": "Réglages {resourceName}",
|
"resourceSetting": "Réglages {resourceName}",
|
||||||
"alwaysAllow": "Toujours autoriser",
|
"alwaysAllow": "Toujours autoriser",
|
||||||
"alwaysDeny": "Toujours refuser",
|
"alwaysDeny": "Toujours refuser",
|
||||||
"passToAuth": "Paser à l'authentification",
|
"passToAuth": "Passer à l'authentification",
|
||||||
"orgSettingsDescription": "Configurer les paramètres généraux de votre organisation",
|
"orgSettingsDescription": "Configurer les paramètres généraux de votre organisation",
|
||||||
"orgGeneralSettings": "Paramètres de l'organisation",
|
"orgGeneralSettings": "Paramètres de l'organisation",
|
||||||
"orgGeneralSettingsDescription": "Gérer les détails et la configuration de votre 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",
|
"licenseTitleDescription": "Voir et gérer les clés de licence dans le système",
|
||||||
"licenseHost": "Licence Hôte",
|
"licenseHost": "Licence Hôte",
|
||||||
"licenseHostDescription": "Gérer la clé de licence principale de l'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",
|
"hostId": "ID de l'hôte",
|
||||||
"licenseReckeckAll": "Revérifier toutes les clés",
|
"licenseReckeckAll": "Revérifier toutes les clés",
|
||||||
"licenseSiteUsage": "Utilisation des sites",
|
"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é.",
|
"licenseNoSiteLimit": "Il n'y a pas de limite sur le nombre de sites utilisant un hôte non autorisé.",
|
||||||
"licensePurchase": "Acheter une licence",
|
"licensePurchase": "Acheter une licence",
|
||||||
"licensePurchaseSites": "Acheter des sites supplémentaires",
|
"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.",
|
"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.}}",
|
"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",
|
"licenseFee": "Frais de licence",
|
||||||
@@ -371,7 +371,7 @@
|
|||||||
"inviteQuestionRemove": "Êtes-vous sûr de vouloir supprimer l'invitation?",
|
"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.",
|
"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.",
|
"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",
|
"inviteRemoveConfirm": "Confirmer la suppression de l'invitation",
|
||||||
"inviteRegenerated": "Invitation régénérée",
|
"inviteRegenerated": "Invitation régénérée",
|
||||||
"inviteSent": "Une nouvelle invitation a été envoyée à {email}.",
|
"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.",
|
"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",
|
"proxyEnableSSL": "Activer SSL",
|
||||||
"proxyEnableSSLDescription": "Activez le cryptage SSL/TLS pour des connexions HTTPS sécurisées vers vos cibles.",
|
"proxyEnableSSLDescription": "Activez le cryptage SSL/TLS pour des connexions HTTPS sécurisées vers vos cibles.",
|
||||||
"target": "Target",
|
"target": "Cible",
|
||||||
"configureTarget": "Configurer les cibles",
|
"configureTarget": "Configurer les cibles",
|
||||||
"targetErrorFetch": "Échec de la récupération des cibles",
|
"targetErrorFetch": "Échec de la récupération des cibles",
|
||||||
"targetErrorFetchDescription": "Une erreur s'est produite lors 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",
|
"sidebarDomains": "Domaines",
|
||||||
"sidebarBluePrints": "Plans",
|
"sidebarBluePrints": "Plans",
|
||||||
"blueprints": "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",
|
"blueprintAdd": "Ajouter un Plan",
|
||||||
"blueprintGoBack": "Voir tous les plans",
|
"blueprintGoBack": "Voir tous les plans",
|
||||||
"blueprintCreate": "Créer un Plan",
|
"blueprintCreate": "Créer un Plan",
|
||||||
"blueprintCreateDescription2": "Suivez les étapes ci-dessous pour créer et appliquer un nouveau plan",
|
"blueprintCreateDescription2": "Suivez les étapes ci-dessous pour créer et appliquer un nouveau plan",
|
||||||
"blueprintDetails": "Détails du 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",
|
"blueprintInfo": "Informations sur le Plan",
|
||||||
"message": "Message",
|
"message": "Message",
|
||||||
"blueprintContentsDescription": "Définissez le contenu YAML décrivant votre infrastructure",
|
"blueprintContentsDescription": "Définissez le contenu YAML décrivant votre infrastructure",
|
||||||
@@ -1181,7 +1181,7 @@
|
|||||||
"appliedAt": "Appliqué à",
|
"appliedAt": "Appliqué à",
|
||||||
"source": "Source",
|
"source": "Source",
|
||||||
"contents": "Contenus",
|
"contents": "Contenus",
|
||||||
"parsedContents": "Contenu analysé",
|
"parsedContents": "Contenu analysé (lecture seule)",
|
||||||
"enableDockerSocket": "Activer le Plan Docker",
|
"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.",
|
"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",
|
"enableDockerSocketLink": "En savoir plus",
|
||||||
@@ -2080,5 +2080,20 @@
|
|||||||
"supportSending": "Envoi...",
|
"supportSending": "Envoi...",
|
||||||
"supportSend": "Envoyer",
|
"supportSend": "Envoyer",
|
||||||
"supportMessageSent": "Message envoyé !",
|
"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",
|
"expireIn": "Scadenza In",
|
||||||
"neverExpire": "Mai scadere",
|
"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.",
|
"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.",
|
"shareAccessHint": "Chiunque abbia questo link può accedere alla risorsa. Condividilo con cura.",
|
||||||
"shareTokenUsage": "Vedi Utilizzo Token Di Accesso",
|
"shareTokenUsage": "Vedi Utilizzo Token Di Accesso",
|
||||||
"createLink": "Crea Collegamento",
|
"createLink": "Crea Collegamento",
|
||||||
@@ -179,7 +179,7 @@
|
|||||||
"baseDomain": "Dominio Base",
|
"baseDomain": "Dominio Base",
|
||||||
"subdomnainDescription": "Il sottodominio in cui la tua risorsa sarà accessibile.",
|
"subdomnainDescription": "Il sottodominio in cui la tua risorsa sarà accessibile.",
|
||||||
"resourceRawSettings": "Impostazioni TCP/UDP",
|
"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",
|
"protocol": "Protocollo",
|
||||||
"protocolSelect": "Seleziona un protocollo",
|
"protocolSelect": "Seleziona un protocollo",
|
||||||
"resourcePortNumber": "Numero Porta",
|
"resourcePortNumber": "Numero Porta",
|
||||||
@@ -1165,13 +1165,13 @@
|
|||||||
"sidebarDomains": "Domini",
|
"sidebarDomains": "Domini",
|
||||||
"sidebarBluePrints": "Progetti",
|
"sidebarBluePrints": "Progetti",
|
||||||
"blueprints": "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",
|
"blueprintAdd": "Aggiungi Progetto",
|
||||||
"blueprintGoBack": "Vedi tutti i progetti",
|
"blueprintGoBack": "Vedi tutti i progetti",
|
||||||
"blueprintCreate": "Crea Progetto",
|
"blueprintCreate": "Crea Progetto",
|
||||||
"blueprintCreateDescription2": "Segui i passaggi qui sotto per creare e applicare un nuovo progetto",
|
"blueprintCreateDescription2": "Segui i passaggi qui sotto per creare e applicare un nuovo progetto",
|
||||||
"blueprintDetails": "Dettagli progetto",
|
"blueprintDetails": "Dettagli Progetto",
|
||||||
"blueprintDetailsDescription": "Vedi i dettagli dell'esecuzione del progetto",
|
"blueprintDetailsDescription": "Vedere il risultato del progetto applicato e gli eventuali errori verificatisi",
|
||||||
"blueprintInfo": "Informazioni Sul Progetto",
|
"blueprintInfo": "Informazioni Sul Progetto",
|
||||||
"message": "Messaggio",
|
"message": "Messaggio",
|
||||||
"blueprintContentsDescription": "Definisci il contenuto di YAML che descrive la tua infrastruttura",
|
"blueprintContentsDescription": "Definisci il contenuto di YAML che descrive la tua infrastruttura",
|
||||||
@@ -1181,7 +1181,7 @@
|
|||||||
"appliedAt": "Applicato Il",
|
"appliedAt": "Applicato Il",
|
||||||
"source": "Fonte",
|
"source": "Fonte",
|
||||||
"contents": "Contenuti",
|
"contents": "Contenuti",
|
||||||
"parsedContents": "Sommario Analizzato",
|
"parsedContents": "Sommario Analizzato (Solo Lettura)",
|
||||||
"enableDockerSocket": "Abilita Progetto Docker",
|
"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.",
|
"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ù",
|
"enableDockerSocketLink": "Scopri di più",
|
||||||
@@ -2080,5 +2080,20 @@
|
|||||||
"supportSending": "Invio...",
|
"supportSending": "Invio...",
|
||||||
"supportSend": "Invia",
|
"supportSend": "Invia",
|
||||||
"supportMessageSent": "Messaggio Inviato!",
|
"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": "기본 도메인",
|
"baseDomain": "기본 도메인",
|
||||||
"subdomnainDescription": "리소스에 접근할 수 있는 하위 도메인입니다.",
|
"subdomnainDescription": "리소스에 접근할 수 있는 하위 도메인입니다.",
|
||||||
"resourceRawSettings": "TCP/UDP 설정",
|
"resourceRawSettings": "TCP/UDP 설정",
|
||||||
"resourceRawSettingsDescription": "TCP/UDP를 통해 리소스에 접근하는 방법을 구성하세요.",
|
"resourceRawSettingsDescription": "리소스를 TCP/UDP를 통해 액세스하는 방법을 구성합니다. 리소스를 호스트 Pangolin 서버의 포트에 매핑하여 서버-public-ip:매핑된 포트에서 리소스에 액세스할 수 있습니다.",
|
||||||
"protocol": "프로토콜",
|
"protocol": "프로토콜",
|
||||||
"protocolSelect": "프로토콜 선택",
|
"protocolSelect": "프로토콜 선택",
|
||||||
"resourcePortNumber": "포트 번호",
|
"resourcePortNumber": "포트 번호",
|
||||||
@@ -1165,13 +1165,13 @@
|
|||||||
"sidebarDomains": "도메인",
|
"sidebarDomains": "도메인",
|
||||||
"sidebarBluePrints": "청사진",
|
"sidebarBluePrints": "청사진",
|
||||||
"blueprints": "청사진",
|
"blueprints": "청사진",
|
||||||
"blueprintsDescription": "청사진은 리소스와 그 설정을 정의하는 선언적인 YAML 구성입니다",
|
"blueprintsDescription": "선언적 구성을 적용하고 이전 실행을 봅니다",
|
||||||
"blueprintAdd": "청사진 추가",
|
"blueprintAdd": "청사진 추가",
|
||||||
"blueprintGoBack": "모든 청사진 보기",
|
"blueprintGoBack": "모든 청사진 보기",
|
||||||
"blueprintCreate": "청사진 생성",
|
"blueprintCreate": "청사진 생성",
|
||||||
"blueprintCreateDescription2": "새 청사진을 생성하고 적용하려면 아래 단계를 따르십시오",
|
"blueprintCreateDescription2": "새 청사진을 생성하고 적용하려면 아래 단계를 따르십시오",
|
||||||
"blueprintDetails": "청사진 세부 사항",
|
"blueprintDetails": "청사진 세부사항",
|
||||||
"blueprintDetailsDescription": "청사진 실행 세부 정보 보기",
|
"blueprintDetailsDescription": "적용된 청사진의 결과와 발생한 오류를 확인합니다",
|
||||||
"blueprintInfo": "청사진 정보",
|
"blueprintInfo": "청사진 정보",
|
||||||
"message": "메시지",
|
"message": "메시지",
|
||||||
"blueprintContentsDescription": "인프라를 설명하는 YAML 콘텐츠를 정의하십시오",
|
"blueprintContentsDescription": "인프라를 설명하는 YAML 콘텐츠를 정의하십시오",
|
||||||
@@ -1181,7 +1181,7 @@
|
|||||||
"appliedAt": "적용 시점",
|
"appliedAt": "적용 시점",
|
||||||
"source": "출처",
|
"source": "출처",
|
||||||
"contents": "콘텐츠",
|
"contents": "콘텐츠",
|
||||||
"parsedContents": "구문 분석된 콘텐츠",
|
"parsedContents": "구문 분석된 콘텐츠 (읽기 전용)",
|
||||||
"enableDockerSocket": "Docker 청사진 활성화",
|
"enableDockerSocket": "Docker 청사진 활성화",
|
||||||
"enableDockerSocketDescription": "블루프린트 레이블을 위한 Docker 소켓 레이블 수집을 활성화합니다. 소켓 경로는 Newt에 제공되어야 합니다.",
|
"enableDockerSocketDescription": "블루프린트 레이블을 위한 Docker 소켓 레이블 수집을 활성화합니다. 소켓 경로는 Newt에 제공되어야 합니다.",
|
||||||
"enableDockerSocketLink": "자세히 알아보기",
|
"enableDockerSocketLink": "자세히 알아보기",
|
||||||
@@ -2080,5 +2080,20 @@
|
|||||||
"supportSending": "발송 중...",
|
"supportSending": "발송 중...",
|
||||||
"supportSend": "보내기",
|
"supportSend": "보내기",
|
||||||
"supportMessageSent": "메시지 전송 완료!",
|
"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",
|
"expireIn": "Utløper om",
|
||||||
"neverExpire": "Utløper aldri",
|
"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.",
|
"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.",
|
"shareAccessHint": "Alle med denne lenken kan få tilgang til ressursen. Del forsiktig.",
|
||||||
"shareTokenUsage": "Se tilgangstokenbruk",
|
"shareTokenUsage": "Se tilgangstokenbruk",
|
||||||
"createLink": "Opprett lenke",
|
"createLink": "Opprett lenke",
|
||||||
@@ -179,7 +179,7 @@
|
|||||||
"baseDomain": "Grunndomene",
|
"baseDomain": "Grunndomene",
|
||||||
"subdomnainDescription": "Underdomenet der ressursen din vil være tilgjengelig.",
|
"subdomnainDescription": "Underdomenet der ressursen din vil være tilgjengelig.",
|
||||||
"resourceRawSettings": "TCP/UDP-innstillinger",
|
"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",
|
"protocol": "Protokoll",
|
||||||
"protocolSelect": "Velg en protokoll",
|
"protocolSelect": "Velg en protokoll",
|
||||||
"resourcePortNumber": "Portnummer",
|
"resourcePortNumber": "Portnummer",
|
||||||
@@ -1165,13 +1165,13 @@
|
|||||||
"sidebarDomains": "Domener",
|
"sidebarDomains": "Domener",
|
||||||
"sidebarBluePrints": "Tegninger",
|
"sidebarBluePrints": "Tegninger",
|
||||||
"blueprints": "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",
|
"blueprintAdd": "Legg til blåkopi",
|
||||||
"blueprintGoBack": "Se alle blåkopier",
|
"blueprintGoBack": "Se alle blåkopier",
|
||||||
"blueprintCreate": "Opprette mal",
|
"blueprintCreate": "Opprette mal",
|
||||||
"blueprintCreateDescription2": "Følg trinnene nedenfor for å opprette og bruke en ny plantegning",
|
"blueprintCreateDescription2": "Følg trinnene nedenfor for å opprette og bruke en ny plantegning",
|
||||||
"blueprintDetails": "Blåkopi detaljer",
|
"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",
|
"blueprintInfo": "Blåkopi informasjon",
|
||||||
"message": "Melding",
|
"message": "Melding",
|
||||||
"blueprintContentsDescription": "Definer innhold av YAML som beskriver din infrastruktur",
|
"blueprintContentsDescription": "Definer innhold av YAML som beskriver din infrastruktur",
|
||||||
@@ -1181,7 +1181,7 @@
|
|||||||
"appliedAt": "Anvendt på",
|
"appliedAt": "Anvendt på",
|
||||||
"source": "Kilde",
|
"source": "Kilde",
|
||||||
"contents": "Innhold",
|
"contents": "Innhold",
|
||||||
"parsedContents": "Parket innhold",
|
"parsedContents": "Parastinnhold (kun lese)",
|
||||||
"enableDockerSocket": "Aktiver Docker blåkopi",
|
"enableDockerSocket": "Aktiver Docker blåkopi",
|
||||||
"enableDockerSocketDescription": "Aktiver skraping av Docker Socket for blueprint Etiketter. Socket bane må brukes for nye.",
|
"enableDockerSocketDescription": "Aktiver skraping av Docker Socket for blueprint Etiketter. Socket bane må brukes for nye.",
|
||||||
"enableDockerSocketLink": "Lær mer",
|
"enableDockerSocketLink": "Lær mer",
|
||||||
@@ -2080,5 +2080,20 @@
|
|||||||
"supportSending": "Sender...",
|
"supportSending": "Sender...",
|
||||||
"supportSend": "Sende",
|
"supportSend": "Sende",
|
||||||
"supportMessageSent": "Melding sendt!",
|
"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",
|
"expireIn": "Vervalt in",
|
||||||
"neverExpire": "Nooit verlopen",
|
"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.",
|
"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.",
|
"shareAccessHint": "Iedereen met deze link heeft toegang tot de bron. Deel deze met zorg.",
|
||||||
"shareTokenUsage": "Zie Toegangstoken Gebruik",
|
"shareTokenUsage": "Zie Toegangstoken Gebruik",
|
||||||
"createLink": "Koppeling aanmaken",
|
"createLink": "Koppeling aanmaken",
|
||||||
@@ -179,7 +179,7 @@
|
|||||||
"baseDomain": "Basis domein",
|
"baseDomain": "Basis domein",
|
||||||
"subdomnainDescription": "Het subdomein waar de bron toegankelijk is.",
|
"subdomnainDescription": "Het subdomein waar de bron toegankelijk is.",
|
||||||
"resourceRawSettings": "TCP/UDP instellingen",
|
"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",
|
"protocol": "Protocol",
|
||||||
"protocolSelect": "Selecteer een protocol",
|
"protocolSelect": "Selecteer een protocol",
|
||||||
"resourcePortNumber": "Nummer van poort",
|
"resourcePortNumber": "Nummer van poort",
|
||||||
@@ -1165,13 +1165,13 @@
|
|||||||
"sidebarDomains": "Domeinen",
|
"sidebarDomains": "Domeinen",
|
||||||
"sidebarBluePrints": "Blauwdrukken",
|
"sidebarBluePrints": "Blauwdrukken",
|
||||||
"blueprints": "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",
|
"blueprintAdd": "Blauwdruk toevoegen",
|
||||||
"blueprintGoBack": "Bekijk alle Blauwdrukken",
|
"blueprintGoBack": "Bekijk alle Blauwdrukken",
|
||||||
"blueprintCreate": "Creëer blauwdruk",
|
"blueprintCreate": "Creëer blauwdruk",
|
||||||
"blueprintCreateDescription2": "Volg de onderstaande stappen om een nieuwe blauwdruk te maken en toe te passen",
|
"blueprintCreateDescription2": "Volg de onderstaande stappen om een nieuwe blauwdruk te maken en toe te passen",
|
||||||
"blueprintDetails": "Blauwdruk details",
|
"blueprintDetails": "Blauwdruk Details",
|
||||||
"blueprintDetailsDescription": "Bekijk de blauwdruk run details",
|
"blueprintDetailsDescription": "Bekijk het resultaat van de toegepaste blauwdruk en eventuele fouten",
|
||||||
"blueprintInfo": "Blauwdruk Informatie",
|
"blueprintInfo": "Blauwdruk Informatie",
|
||||||
"message": "bericht",
|
"message": "bericht",
|
||||||
"blueprintContentsDescription": "Definieer de YAML content die je infrastructuur beschrijft",
|
"blueprintContentsDescription": "Definieer de YAML content die je infrastructuur beschrijft",
|
||||||
@@ -1181,7 +1181,7 @@
|
|||||||
"appliedAt": "Toegepast op",
|
"appliedAt": "Toegepast op",
|
||||||
"source": "Bron",
|
"source": "Bron",
|
||||||
"contents": "Inhoud",
|
"contents": "Inhoud",
|
||||||
"parsedContents": "Geparseerde inhoud",
|
"parsedContents": "Geparseerde inhoud (alleen lezen)",
|
||||||
"enableDockerSocket": "Schakel Docker Blauwdruk in",
|
"enableDockerSocket": "Schakel Docker Blauwdruk in",
|
||||||
"enableDockerSocketDescription": "Schakel Docker Socket label in voor blauwdruk labels. Pad naar Nieuw.",
|
"enableDockerSocketDescription": "Schakel Docker Socket label in voor blauwdruk labels. Pad naar Nieuw.",
|
||||||
"enableDockerSocketLink": "Meer informatie",
|
"enableDockerSocketLink": "Meer informatie",
|
||||||
@@ -2080,5 +2080,20 @@
|
|||||||
"supportSending": "Verzenden...",
|
"supportSending": "Verzenden...",
|
||||||
"supportSend": "Verzenden",
|
"supportSend": "Verzenden",
|
||||||
"supportMessageSent": "Bericht verzonden!",
|
"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",
|
"expireIn": "Wygasa za",
|
||||||
"neverExpire": "Nigdy nie wygasa",
|
"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.",
|
"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.",
|
"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",
|
"shareTokenUsage": "Zobacz użycie tokenu dostępu",
|
||||||
"createLink": "Utwórz link",
|
"createLink": "Utwórz link",
|
||||||
@@ -179,7 +179,7 @@
|
|||||||
"baseDomain": "Bazowa domena",
|
"baseDomain": "Bazowa domena",
|
||||||
"subdomnainDescription": "Poddomena, w której twój zasób będzie dostępny.",
|
"subdomnainDescription": "Poddomena, w której twój zasób będzie dostępny.",
|
||||||
"resourceRawSettings": "Ustawienia TCP/UDP",
|
"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ół",
|
"protocol": "Protokół",
|
||||||
"protocolSelect": "Wybierz protokół",
|
"protocolSelect": "Wybierz protokół",
|
||||||
"resourcePortNumber": "Numer portu",
|
"resourcePortNumber": "Numer portu",
|
||||||
@@ -1165,13 +1165,13 @@
|
|||||||
"sidebarDomains": "Domeny",
|
"sidebarDomains": "Domeny",
|
||||||
"sidebarBluePrints": "Schematy",
|
"sidebarBluePrints": "Schematy",
|
||||||
"blueprints": "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",
|
"blueprintAdd": "Dodaj schemat",
|
||||||
"blueprintGoBack": "Zobacz wszystkie schematy",
|
"blueprintGoBack": "Zobacz wszystkie schematy",
|
||||||
"blueprintCreate": "Utwórz schemat",
|
"blueprintCreate": "Utwórz schemat",
|
||||||
"blueprintCreateDescription2": "Wykonaj poniższe kroki, aby utworzyć i zastosować nowy schemat",
|
"blueprintCreateDescription2": "Wykonaj poniższe kroki, aby utworzyć i zastosować nowy schemat",
|
||||||
"blueprintDetails": "Szczegóły projektu",
|
"blueprintDetails": "Szczegóły Projektu",
|
||||||
"blueprintDetailsDescription": "Zobacz szczegóły uruchomienia schematu",
|
"blueprintDetailsDescription": "Zobacz wynik zastosowanego schematu i wszelkie błędy, które wystąpiły",
|
||||||
"blueprintInfo": "Informacje o projekcie",
|
"blueprintInfo": "Informacje o projekcie",
|
||||||
"message": "Wiadomość",
|
"message": "Wiadomość",
|
||||||
"blueprintContentsDescription": "Zdefiniuj zawartość YAML opisującą Twoją infrastrukturę",
|
"blueprintContentsDescription": "Zdefiniuj zawartość YAML opisującą Twoją infrastrukturę",
|
||||||
@@ -1181,7 +1181,7 @@
|
|||||||
"appliedAt": "Zastosowano",
|
"appliedAt": "Zastosowano",
|
||||||
"source": "Źródło",
|
"source": "Źródło",
|
||||||
"contents": "Treść",
|
"contents": "Treść",
|
||||||
"parsedContents": "Przetworzona zawartość",
|
"parsedContents": "Przetworzona zawartość (tylko do odczytu)",
|
||||||
"enableDockerSocket": "Włącz schemat dokera",
|
"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.",
|
"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",
|
"enableDockerSocketLink": "Dowiedz się więcej",
|
||||||
@@ -2080,5 +2080,20 @@
|
|||||||
"supportSending": "Wysyłanie...",
|
"supportSending": "Wysyłanie...",
|
||||||
"supportSend": "Wyślij",
|
"supportSend": "Wyślij",
|
||||||
"supportMessageSent": "Wiadomość wysłana!",
|
"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",
|
"baseDomain": "Domínio Base",
|
||||||
"subdomnainDescription": "O subdomínio onde seu recurso estará acessível.",
|
"subdomnainDescription": "O subdomínio onde seu recurso estará acessível.",
|
||||||
"resourceRawSettings": "Configurações TCP/UDP",
|
"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",
|
"protocol": "Protocolo",
|
||||||
"protocolSelect": "Selecione um protocolo",
|
"protocolSelect": "Selecione um protocolo",
|
||||||
"resourcePortNumber": "Número da Porta",
|
"resourcePortNumber": "Número da Porta",
|
||||||
@@ -1165,13 +1165,13 @@
|
|||||||
"sidebarDomains": "Domínios",
|
"sidebarDomains": "Domínios",
|
||||||
"sidebarBluePrints": "Diagramas",
|
"sidebarBluePrints": "Diagramas",
|
||||||
"blueprints": "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",
|
"blueprintAdd": "Adicionar Diagrama",
|
||||||
"blueprintGoBack": "Ver todos os Diagramas",
|
"blueprintGoBack": "Ver todos os Diagramas",
|
||||||
"blueprintCreate": "Criar Diagrama",
|
"blueprintCreate": "Criar Diagrama",
|
||||||
"blueprintCreateDescription2": "Siga as etapas abaixo para criar e aplicar um novo diagrama",
|
"blueprintCreateDescription2": "Siga as etapas abaixo para criar e aplicar um novo diagrama",
|
||||||
"blueprintDetails": "Detalhes do 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",
|
"blueprintInfo": "Informação do Diagrama",
|
||||||
"message": "mensagem",
|
"message": "mensagem",
|
||||||
"blueprintContentsDescription": "Defina o conteúdo YAML descrevendo a sua infraestrutura",
|
"blueprintContentsDescription": "Defina o conteúdo YAML descrevendo a sua infraestrutura",
|
||||||
@@ -1181,7 +1181,7 @@
|
|||||||
"appliedAt": "Aplicado em",
|
"appliedAt": "Aplicado em",
|
||||||
"source": "fonte",
|
"source": "fonte",
|
||||||
"contents": "Conteúdo",
|
"contents": "Conteúdo",
|
||||||
"parsedContents": "Conteúdo analisado",
|
"parsedContents": "Conteúdo analisado (Somente Leitura)",
|
||||||
"enableDockerSocket": "Habilitar o Diagrama Docker",
|
"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.",
|
"enableDockerSocketDescription": "Ativar a scraping de rótulo Docker para rótulos de diagramas. Caminho de Socket deve ser fornecido para Newt.",
|
||||||
"enableDockerSocketLink": "Saiba mais",
|
"enableDockerSocketLink": "Saiba mais",
|
||||||
@@ -2080,5 +2080,20 @@
|
|||||||
"supportSending": "Enviando...",
|
"supportSending": "Enviando...",
|
||||||
"supportSend": "Mandar",
|
"supportSend": "Mandar",
|
||||||
"supportMessageSent": "Mensagem enviada!",
|
"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": "Срок действия",
|
"expireIn": "Срок действия",
|
||||||
"neverExpire": "Бессрочный доступ",
|
"neverExpire": "Бессрочный доступ",
|
||||||
"shareExpireDescription": "Срок действия - это период, в течение которого ссылка будет работать и предоставлять доступ к ресурсу. После этого времени ссылка перестанет работать, и пользователи, использовавшие эту ссылку, потеряют доступ к ресурсу.",
|
"shareExpireDescription": "Срок действия - это период, в течение которого ссылка будет работать и предоставлять доступ к ресурсу. После этого времени ссылка перестанет работать, и пользователи, использовавшие эту ссылку, потеряют доступ к ресурсу.",
|
||||||
"shareSeeOnce": "Вы сможете увидеть эту ссылку только один раз. Обязательно скопируйте её.",
|
"shareSeeOnce": "Вы сможете увидеть эту ссылку только один раз. Обязательно скопируйте ее.",
|
||||||
"shareAccessHint": "Любой, у кого есть эта ссылка, может получить доступ к ресурсу. Делитесь ею с осторожностью.",
|
"shareAccessHint": "Любой, у кого есть эта ссылка, может получить доступ к ресурсу. Делитесь ею с осторожностью.",
|
||||||
"shareTokenUsage": "Посмотреть использование токена доступа",
|
"shareTokenUsage": "Посмотреть использование токена доступа",
|
||||||
"createLink": "Создать ссылку",
|
"createLink": "Создать ссылку",
|
||||||
@@ -179,7 +179,7 @@
|
|||||||
"baseDomain": "Базовый домен",
|
"baseDomain": "Базовый домен",
|
||||||
"subdomnainDescription": "Поддомен, на котором будет доступен ресурс.",
|
"subdomnainDescription": "Поддомен, на котором будет доступен ресурс.",
|
||||||
"resourceRawSettings": "Настройки TCP/UDP",
|
"resourceRawSettings": "Настройки TCP/UDP",
|
||||||
"resourceRawSettingsDescription": "Настройте, как будет осуществляться доступ к вашему ресурсу через TCP/UDP",
|
"resourceRawSettingsDescription": "Настройте доступ к вашему ресурсу по TCP/UDP. Вы соотносите ресурс с портом на сервере хоста Pangolin, так что вы можете получить доступ к ресурсу с сервера server-public-ip:mapped-порта.",
|
||||||
"protocol": "Протокол",
|
"protocol": "Протокол",
|
||||||
"protocolSelect": "Выберите протокол",
|
"protocolSelect": "Выберите протокол",
|
||||||
"resourcePortNumber": "Номер порта",
|
"resourcePortNumber": "Номер порта",
|
||||||
@@ -1165,13 +1165,13 @@
|
|||||||
"sidebarDomains": "Домены",
|
"sidebarDomains": "Домены",
|
||||||
"sidebarBluePrints": "Чертежи",
|
"sidebarBluePrints": "Чертежи",
|
||||||
"blueprints": "Чертежи",
|
"blueprints": "Чертежи",
|
||||||
"blueprintsDescription": "Чертежи являются декларативными конфигурациями YAML, которые определяют ваши ресурсы и их настройки",
|
"blueprintsDescription": "Применить декларирующие конфигурации и просмотреть предыдущие запуски",
|
||||||
"blueprintAdd": "Добавить чертёж",
|
"blueprintAdd": "Добавить чертёж",
|
||||||
"blueprintGoBack": "Посмотреть все чертежи",
|
"blueprintGoBack": "Посмотреть все чертежи",
|
||||||
"blueprintCreate": "Создать чертёж",
|
"blueprintCreate": "Создать чертёж",
|
||||||
"blueprintCreateDescription2": "Для создания и применения нового чертежа выполните следующие шаги",
|
"blueprintCreateDescription2": "Для создания и применения нового чертежа выполните следующие шаги",
|
||||||
"blueprintDetails": "Подробности чертежа",
|
"blueprintDetails": "Детали чертежа",
|
||||||
"blueprintDetailsDescription": "Посмотреть детали запуска чертежа",
|
"blueprintDetailsDescription": "Посмотреть результат примененного чертежа и все возникшие ошибки",
|
||||||
"blueprintInfo": "Информация о чертеже",
|
"blueprintInfo": "Информация о чертеже",
|
||||||
"message": "Сообщение",
|
"message": "Сообщение",
|
||||||
"blueprintContentsDescription": "Определите содержимое YAML, описывающее вашу инфраструктуру",
|
"blueprintContentsDescription": "Определите содержимое YAML, описывающее вашу инфраструктуру",
|
||||||
@@ -1181,7 +1181,7 @@
|
|||||||
"appliedAt": "Заявка на",
|
"appliedAt": "Заявка на",
|
||||||
"source": "Источник",
|
"source": "Источник",
|
||||||
"contents": "Содержание",
|
"contents": "Содержание",
|
||||||
"parsedContents": "Обработанное содержимое",
|
"parsedContents": "Переработанное содержимое (только для чтения)",
|
||||||
"enableDockerSocket": "Включить чертёж Docker",
|
"enableDockerSocket": "Включить чертёж Docker",
|
||||||
"enableDockerSocketDescription": "Включить scraping ярлыка Docker Socket для ярлыков чертежей. Путь к сокету должен быть предоставлен в Newt.",
|
"enableDockerSocketDescription": "Включить scraping ярлыка Docker Socket для ярлыков чертежей. Путь к сокету должен быть предоставлен в Newt.",
|
||||||
"enableDockerSocketLink": "Узнать больше",
|
"enableDockerSocketLink": "Узнать больше",
|
||||||
@@ -2080,5 +2080,20 @@
|
|||||||
"supportSending": "Отправка...",
|
"supportSending": "Отправка...",
|
||||||
"supportSend": "Отправить",
|
"supportSend": "Отправить",
|
||||||
"supportMessageSent": "Сообщение отправлено!",
|
"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ı",
|
"baseDomain": "Temel Alan Adı",
|
||||||
"subdomnainDescription": "Kaynağınızın erişilebileceği alt alan adı.",
|
"subdomnainDescription": "Kaynağınızın erişilebileceği alt alan adı.",
|
||||||
"resourceRawSettings": "TCP/UDP Ayarları",
|
"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",
|
"protocol": "Protokol",
|
||||||
"protocolSelect": "Bir protokol seçin",
|
"protocolSelect": "Bir protokol seçin",
|
||||||
"resourcePortNumber": "Port Numarası",
|
"resourcePortNumber": "Port Numarası",
|
||||||
@@ -1165,13 +1165,13 @@
|
|||||||
"sidebarDomains": "Alan Adları",
|
"sidebarDomains": "Alan Adları",
|
||||||
"sidebarBluePrints": "Planlar",
|
"sidebarBluePrints": "Planlar",
|
||||||
"blueprints": "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",
|
"blueprintAdd": "Plan Ekle",
|
||||||
"blueprintGoBack": "Tüm Planları Gör",
|
"blueprintGoBack": "Tüm Planları Gör",
|
||||||
"blueprintCreate": "Plan Oluştur",
|
"blueprintCreate": "Plan Oluştur",
|
||||||
"blueprintCreateDescription2": "Yeni bir plan oluşturup uygulamak için aşağıdaki adımları izleyin",
|
"blueprintCreateDescription2": "Yeni bir plan oluşturup uygulamak için aşağıdaki adımları izleyin",
|
||||||
"blueprintDetails": "Plan Detayları",
|
"blueprintDetails": "Mavi Yazılım Detayları",
|
||||||
"blueprintDetailsDescription": "Plan çalıştırma detaylarını görün",
|
"blueprintDetailsDescription": "Uygulanan mavi yazılımın sonucunu ve oluşan hataları görün",
|
||||||
"blueprintInfo": "Plan Bilgileri",
|
"blueprintInfo": "Plan Bilgileri",
|
||||||
"message": "Mesaj",
|
"message": "Mesaj",
|
||||||
"blueprintContentsDescription": "Altyapınızı tanımlayan YAML içeriğini tanımlayın",
|
"blueprintContentsDescription": "Altyapınızı tanımlayan YAML içeriğini tanımlayın",
|
||||||
@@ -1181,7 +1181,7 @@
|
|||||||
"appliedAt": "Uygulama Zamanı",
|
"appliedAt": "Uygulama Zamanı",
|
||||||
"source": "Kaynak",
|
"source": "Kaynak",
|
||||||
"contents": "İçerik",
|
"contents": "İçerik",
|
||||||
"parsedContents": "Ayrıştırılmış İçerik",
|
"parsedContents": "Verilerin Ayrıştırılmış İçeriği (Salt Okunur)",
|
||||||
"enableDockerSocket": "Docker Soketini Etkinleştir",
|
"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.",
|
"enableDockerSocketDescription": "Plan etiketleri için Docker Socket etiket toplamasını etkinleştirin. Newt'e soket yolu sağlanmalıdır.",
|
||||||
"enableDockerSocketLink": "Daha fazla bilgi",
|
"enableDockerSocketLink": "Daha fazla bilgi",
|
||||||
@@ -2080,5 +2080,20 @@
|
|||||||
"supportSending": "Gönderiliyor...",
|
"supportSending": "Gönderiliyor...",
|
||||||
"supportSend": "Gönder",
|
"supportSend": "Gönder",
|
||||||
"supportMessageSent": "Mesaj Gönderildi!",
|
"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": "过期时间",
|
"expireIn": "过期时间",
|
||||||
"neverExpire": "永不过期",
|
"neverExpire": "永不过期",
|
||||||
"shareExpireDescription": "过期时间是链接可以使用并提供对资源的访问时间。 此时间后,链接将不再工作,使用此链接的用户将失去对资源的访问。",
|
"shareExpireDescription": "过期时间是链接可以使用并提供对资源的访问时间。 此时间后,链接将不再工作,使用此链接的用户将失去对资源的访问。",
|
||||||
"shareSeeOnce": "您只能看到此链接。请确保复制它。",
|
"shareSeeOnce": "您只能看到一次此链接。请确保复制它。",
|
||||||
"shareAccessHint": "任何具有此链接的人都可以访问该资源。小心地分享它。",
|
"shareAccessHint": "任何具有此链接的人都可以访问该资源。小心地分享它。",
|
||||||
"shareTokenUsage": "查看访问令牌使用情况",
|
"shareTokenUsage": "查看访问令牌使用情况",
|
||||||
"createLink": "创建链接",
|
"createLink": "创建链接",
|
||||||
@@ -179,7 +179,7 @@
|
|||||||
"baseDomain": "根域名",
|
"baseDomain": "根域名",
|
||||||
"subdomnainDescription": "您的资源可以访问的子域名。",
|
"subdomnainDescription": "您的资源可以访问的子域名。",
|
||||||
"resourceRawSettings": "TCP/UDP 设置",
|
"resourceRawSettings": "TCP/UDP 设置",
|
||||||
"resourceRawSettingsDescription": "配置如何通过 TCP/UDP 访问您的资源",
|
"resourceRawSettingsDescription": "配置如何通过 TCP/UDP 访问您的资源。 您映射资源到主机Pangolin服务器上的端口,这样您就可以访问服务器-公共-ip:mapped端口的资源。",
|
||||||
"protocol": "协议",
|
"protocol": "协议",
|
||||||
"protocolSelect": "选择协议",
|
"protocolSelect": "选择协议",
|
||||||
"resourcePortNumber": "端口号",
|
"resourcePortNumber": "端口号",
|
||||||
@@ -1165,13 +1165,13 @@
|
|||||||
"sidebarDomains": "域",
|
"sidebarDomains": "域",
|
||||||
"sidebarBluePrints": "蓝图",
|
"sidebarBluePrints": "蓝图",
|
||||||
"blueprints": "蓝图",
|
"blueprints": "蓝图",
|
||||||
"blueprintsDescription": "蓝图是用于定义资源及其设置的 YAML 声明配置",
|
"blueprintsDescription": "应用声明配置并查看先前运行的",
|
||||||
"blueprintAdd": "添加蓝图",
|
"blueprintAdd": "添加蓝图",
|
||||||
"blueprintGoBack": "查看所有蓝图",
|
"blueprintGoBack": "查看所有蓝图",
|
||||||
"blueprintCreate": "创建蓝图",
|
"blueprintCreate": "创建蓝图",
|
||||||
"blueprintCreateDescription2": "按照下面的步骤创建和应用新的蓝图",
|
"blueprintCreateDescription2": "按照下面的步骤创建和应用新的蓝图",
|
||||||
"blueprintDetails": "蓝图详细信息",
|
"blueprintDetails": "蓝图详细信息",
|
||||||
"blueprintDetailsDescription": "查看蓝图运行详情",
|
"blueprintDetailsDescription": "查看应用蓝图的结果和发生的任何错误",
|
||||||
"blueprintInfo": "蓝图信息",
|
"blueprintInfo": "蓝图信息",
|
||||||
"message": "留言",
|
"message": "留言",
|
||||||
"blueprintContentsDescription": "定义描述您基础设施的 YAML 内容",
|
"blueprintContentsDescription": "定义描述您基础设施的 YAML 内容",
|
||||||
@@ -1181,7 +1181,7 @@
|
|||||||
"appliedAt": "应用于",
|
"appliedAt": "应用于",
|
||||||
"source": "来源",
|
"source": "来源",
|
||||||
"contents": "目录",
|
"contents": "目录",
|
||||||
"parsedContents": "解析内容",
|
"parsedContents": "解析内容 (只读)",
|
||||||
"enableDockerSocket": "启用 Docker 蓝图",
|
"enableDockerSocket": "启用 Docker 蓝图",
|
||||||
"enableDockerSocketDescription": "启用 Docker Socket 标签擦除蓝图标签。套接字路径必须提供给新的。",
|
"enableDockerSocketDescription": "启用 Docker Socket 标签擦除蓝图标签。套接字路径必须提供给新的。",
|
||||||
"enableDockerSocketLink": "了解更多",
|
"enableDockerSocketLink": "了解更多",
|
||||||
@@ -2080,5 +2080,20 @@
|
|||||||
"supportSending": "正在发送...",
|
"supportSending": "正在发送...",
|
||||||
"supportSend": "发送",
|
"supportSend": "发送",
|
||||||
"supportMessageSent": "消息已发送!",
|
"supportMessageSent": "消息已发送!",
|
||||||
"supportWillContact": "我们很快就会联系起来!"
|
"supportWillContact": "我们很快就会联系起来!",
|
||||||
|
"selectLogRetention": "选择保留日志",
|
||||||
|
"showColumns": "显示列",
|
||||||
|
"hideColumns": "隐藏列",
|
||||||
|
"columnVisibility": "列可见性",
|
||||||
|
"toggleColumn": "切换 {columnName} 列",
|
||||||
|
"allColumns": "全部列",
|
||||||
|
"defaultColumns": "默认列",
|
||||||
|
"customizeView": "自定义视图",
|
||||||
|
"viewOptions": "查看选项",
|
||||||
|
"selectAll": "选择所有",
|
||||||
|
"selectNone": "没有选择",
|
||||||
|
"selectedResources": "选定的资源",
|
||||||
|
"enableSelected": "启用选中的",
|
||||||
|
"disableSelected": "禁用选中的",
|
||||||
|
"checkSelectedStatus": "检查选中的状态"
|
||||||
}
|
}
|
||||||
1917
package-lock.json
generated
1917
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
28
package.json
28
package.json
@@ -33,7 +33,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@asteasolutions/zod-to-openapi": "^7.3.4",
|
"@asteasolutions/zod-to-openapi": "^7.3.4",
|
||||||
"@aws-sdk/client-s3": "3.908.0",
|
"@aws-sdk/client-s3": "3.922.0",
|
||||||
"@hookform/resolvers": "5.2.2",
|
"@hookform/resolvers": "5.2.2",
|
||||||
"@monaco-editor/react": "^4.7.0",
|
"@monaco-editor/react": "^4.7.0",
|
||||||
"@node-rs/argon2": "^2.0.2",
|
"@node-rs/argon2": "^2.0.2",
|
||||||
@@ -65,7 +65,7 @@
|
|||||||
"@tailwindcss/forms": "^0.5.10",
|
"@tailwindcss/forms": "^0.5.10",
|
||||||
"@tanstack/react-table": "8.21.3",
|
"@tanstack/react-table": "8.21.3",
|
||||||
"arctic": "^3.7.0",
|
"arctic": "^3.7.0",
|
||||||
"axios": "^1.12.2",
|
"axios": "^1.13.1",
|
||||||
"better-sqlite3": "11.7.0",
|
"better-sqlite3": "11.7.0",
|
||||||
"canvas-confetti": "1.9.4",
|
"canvas-confetti": "1.9.4",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
@@ -78,10 +78,10 @@
|
|||||||
"crypto-js": "^4.2.0",
|
"crypto-js": "^4.2.0",
|
||||||
"date-fns": "4.1.0",
|
"date-fns": "4.1.0",
|
||||||
"drizzle-orm": "0.44.7",
|
"drizzle-orm": "0.44.7",
|
||||||
"eslint": "9.37.0",
|
"eslint": "9.39.0",
|
||||||
"eslint-config-next": "15.5.6",
|
"eslint-config-next": "16.0.1",
|
||||||
"express": "5.1.0",
|
"express": "5.1.0",
|
||||||
"express-rate-limit": "8.1.0",
|
"express-rate-limit": "8.2.1",
|
||||||
"glob": "11.0.3",
|
"glob": "11.0.3",
|
||||||
"helmet": "8.1.0",
|
"helmet": "8.1.0",
|
||||||
"http-errors": "2.0.0",
|
"http-errors": "2.0.0",
|
||||||
@@ -91,11 +91,11 @@
|
|||||||
"jmespath": "^0.16.0",
|
"jmespath": "^0.16.0",
|
||||||
"js-yaml": "4.1.0",
|
"js-yaml": "4.1.0",
|
||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
"lucide-react": "^0.545.0",
|
"lucide-react": "^0.552.0",
|
||||||
"maxmind": "5.0.0",
|
"maxmind": "5.0.0",
|
||||||
"moment": "2.30.1",
|
"moment": "2.30.1",
|
||||||
"next": "15.5.6",
|
"next": "15.5.6",
|
||||||
"next-intl": "^4.3.12",
|
"next-intl": "^4.4.0",
|
||||||
"next-themes": "0.4.6",
|
"next-themes": "0.4.6",
|
||||||
"nextjs-toploader": "^3.9.17",
|
"nextjs-toploader": "^3.9.17",
|
||||||
"node-cache": "5.1.2",
|
"node-cache": "5.1.2",
|
||||||
@@ -105,17 +105,17 @@
|
|||||||
"nprogress": "^0.2.0",
|
"nprogress": "^0.2.0",
|
||||||
"oslo": "1.2.1",
|
"oslo": "1.2.1",
|
||||||
"pg": "^8.16.2",
|
"pg": "^8.16.2",
|
||||||
"posthog-node": "^5.10.4",
|
"posthog-node": "^5.11.0",
|
||||||
"qrcode.react": "4.2.0",
|
"qrcode.react": "4.2.0",
|
||||||
"react": "19.2.0",
|
"react": "19.2.0",
|
||||||
"react-day-picker": "9.11.1",
|
"react-day-picker": "9.11.1",
|
||||||
"react-dom": "19.2.0",
|
"react-dom": "19.2.0",
|
||||||
"react-easy-sort": "^1.8.0",
|
"react-easy-sort": "^1.8.0",
|
||||||
"react-hook-form": "7.65.0",
|
"react-hook-form": "7.66.0",
|
||||||
"react-icons": "^5.5.0",
|
"react-icons": "^5.5.0",
|
||||||
"rebuild": "0.1.2",
|
"rebuild": "0.1.2",
|
||||||
"reodotdev": "^1.0.0",
|
"reodotdev": "^1.0.0",
|
||||||
"resend": "^6.1.2",
|
"resend": "^6.4.0",
|
||||||
"semver": "^7.7.3",
|
"semver": "^7.7.3",
|
||||||
"stripe": "18.2.1",
|
"stripe": "18.2.1",
|
||||||
"swagger-ui-express": "^5.0.1",
|
"swagger-ui-express": "^5.0.1",
|
||||||
@@ -133,10 +133,10 @@
|
|||||||
"@faker-js/faker": "^10.1.0"
|
"@faker-js/faker": "^10.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@dotenvx/dotenvx": "1.51.0",
|
"@dotenvx/dotenvx": "1.51.1",
|
||||||
"@esbuild-plugins/tsconfig-paths": "0.1.2",
|
"@esbuild-plugins/tsconfig-paths": "0.1.2",
|
||||||
"@react-email/preview-server": "4.3.2",
|
"@react-email/preview-server": "4.3.2",
|
||||||
"@tailwindcss/postcss": "^4.1.16",
|
"@tailwindcss/postcss": "^4.1.17",
|
||||||
"@types/better-sqlite3": "7.6.12",
|
"@types/better-sqlite3": "7.6.12",
|
||||||
"@types/cookie-parser": "1.4.10",
|
"@types/cookie-parser": "1.4.10",
|
||||||
"@types/cors": "2.8.19",
|
"@types/cors": "2.8.19",
|
||||||
@@ -157,7 +157,7 @@
|
|||||||
"@types/ws": "8.18.1",
|
"@types/ws": "8.18.1",
|
||||||
"@types/yargs": "17.0.34",
|
"@types/yargs": "17.0.34",
|
||||||
"drizzle-kit": "0.31.6",
|
"drizzle-kit": "0.31.6",
|
||||||
"esbuild": "0.25.11",
|
"esbuild": "0.25.12",
|
||||||
"esbuild-node-externals": "1.18.0",
|
"esbuild-node-externals": "1.18.0",
|
||||||
"postcss": "^8",
|
"postcss": "^8",
|
||||||
"react-email": "4.3.2",
|
"react-email": "4.3.2",
|
||||||
@@ -165,7 +165,7 @@
|
|||||||
"tsc-alias": "1.8.16",
|
"tsc-alias": "1.8.16",
|
||||||
"tsx": "4.20.6",
|
"tsx": "4.20.6",
|
||||||
"typescript": "^5",
|
"typescript": "^5",
|
||||||
"typescript-eslint": "^8.46.2"
|
"typescript-eslint": "^8.46.3"
|
||||||
},
|
},
|
||||||
"overrides": {
|
"overrides": {
|
||||||
"emblor": {
|
"emblor": {
|
||||||
|
|||||||
@@ -34,11 +34,7 @@ export async function applyNewtDockerBlueprint(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isEmptyObject(blueprint["proxy-resources"])) {
|
if (isEmptyObject(blueprint["proxy-resources"]) && isEmptyObject(blueprint["client-resources"])) {
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isEmptyObject(blueprint["client-resources"])) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import { pickPort } from "@server/routers/target/helpers";
|
|||||||
import { resourcePassword } from "@server/db";
|
import { resourcePassword } from "@server/db";
|
||||||
import { hashPassword } from "@server/auth/password";
|
import { hashPassword } from "@server/auth/password";
|
||||||
import { isValidCIDR, isValidIP, isValidUrlGlobPattern } from "../validators";
|
import { isValidCIDR, isValidIP, isValidUrlGlobPattern } from "../validators";
|
||||||
|
import { get } from "http";
|
||||||
|
|
||||||
export type ProxyResourcesResults = {
|
export type ProxyResourcesResults = {
|
||||||
proxyResource: Resource;
|
proxyResource: Resource;
|
||||||
@@ -114,7 +115,12 @@ export async function updateProxyResources(
|
|||||||
internalPort: internalPortToCreate,
|
internalPort: internalPortToCreate,
|
||||||
path: targetData.path,
|
path: targetData.path,
|
||||||
pathMatchType: targetData["path-match"],
|
pathMatchType: targetData["path-match"],
|
||||||
rewritePath: targetData.rewritePath,
|
rewritePath:
|
||||||
|
targetData.rewritePath ||
|
||||||
|
targetData["rewrite-path"] ||
|
||||||
|
(targetData["rewrite-match"] === "stripPrefix"
|
||||||
|
? "/"
|
||||||
|
: undefined),
|
||||||
rewritePathType: targetData["rewrite-match"],
|
rewritePathType: targetData["rewrite-match"],
|
||||||
priority: targetData.priority
|
priority: targetData.priority
|
||||||
})
|
})
|
||||||
@@ -139,10 +145,14 @@ export async function updateProxyResources(
|
|||||||
hcHostname: healthcheckData?.hostname,
|
hcHostname: healthcheckData?.hostname,
|
||||||
hcPort: healthcheckData?.port,
|
hcPort: healthcheckData?.port,
|
||||||
hcInterval: healthcheckData?.interval,
|
hcInterval: healthcheckData?.interval,
|
||||||
hcUnhealthyInterval: healthcheckData?.unhealthyInterval,
|
hcUnhealthyInterval:
|
||||||
|
healthcheckData?.unhealthyInterval ||
|
||||||
|
healthcheckData?.["unhealthy-interval"],
|
||||||
hcTimeout: healthcheckData?.timeout,
|
hcTimeout: healthcheckData?.timeout,
|
||||||
hcHeaders: hcHeaders,
|
hcHeaders: hcHeaders,
|
||||||
hcFollowRedirects: healthcheckData?.followRedirects,
|
hcFollowRedirects:
|
||||||
|
healthcheckData?.followRedirects ||
|
||||||
|
healthcheckData?.["follow-redirects"],
|
||||||
hcMethod: healthcheckData?.method,
|
hcMethod: healthcheckData?.method,
|
||||||
hcStatus: healthcheckData?.status,
|
hcStatus: healthcheckData?.status,
|
||||||
hcHealth: "unknown"
|
hcHealth: "unknown"
|
||||||
@@ -392,7 +402,12 @@ export async function updateProxyResources(
|
|||||||
enabled: targetData.enabled,
|
enabled: targetData.enabled,
|
||||||
path: targetData.path,
|
path: targetData.path,
|
||||||
pathMatchType: targetData["path-match"],
|
pathMatchType: targetData["path-match"],
|
||||||
rewritePath: targetData.rewritePath,
|
rewritePath:
|
||||||
|
targetData.rewritePath ||
|
||||||
|
targetData["rewrite-path"] ||
|
||||||
|
(targetData["rewrite-match"] === "stripPrefix"
|
||||||
|
? "/"
|
||||||
|
: undefined),
|
||||||
rewritePathType: targetData["rewrite-match"],
|
rewritePathType: targetData["rewrite-match"],
|
||||||
priority: targetData.priority
|
priority: targetData.priority
|
||||||
})
|
})
|
||||||
@@ -452,10 +467,13 @@ export async function updateProxyResources(
|
|||||||
hcPort: healthcheckData?.port,
|
hcPort: healthcheckData?.port,
|
||||||
hcInterval: healthcheckData?.interval,
|
hcInterval: healthcheckData?.interval,
|
||||||
hcUnhealthyInterval:
|
hcUnhealthyInterval:
|
||||||
healthcheckData?.unhealthyInterval,
|
healthcheckData?.unhealthyInterval ||
|
||||||
|
healthcheckData?.["unhealthy-interval"],
|
||||||
hcTimeout: healthcheckData?.timeout,
|
hcTimeout: healthcheckData?.timeout,
|
||||||
hcHeaders: hcHeaders,
|
hcHeaders: hcHeaders,
|
||||||
hcFollowRedirects: healthcheckData?.followRedirects,
|
hcFollowRedirects:
|
||||||
|
healthcheckData?.followRedirects ||
|
||||||
|
healthcheckData?.["follow-redirects"],
|
||||||
hcMethod: healthcheckData?.method,
|
hcMethod: healthcheckData?.method,
|
||||||
hcStatus: healthcheckData?.status
|
hcStatus: healthcheckData?.status
|
||||||
})
|
})
|
||||||
@@ -527,7 +545,7 @@ export async function updateProxyResources(
|
|||||||
if (
|
if (
|
||||||
existingRule.action !== getRuleAction(rule.action) ||
|
existingRule.action !== getRuleAction(rule.action) ||
|
||||||
existingRule.match !== rule.match.toUpperCase() ||
|
existingRule.match !== rule.match.toUpperCase() ||
|
||||||
existingRule.value !== rule.value.toUpperCase()
|
existingRule.value !== getRuleValue(rule.match.toUpperCase(), rule.value)
|
||||||
) {
|
) {
|
||||||
validateRule(rule);
|
validateRule(rule);
|
||||||
await trx
|
await trx
|
||||||
@@ -535,7 +553,7 @@ export async function updateProxyResources(
|
|||||||
.set({
|
.set({
|
||||||
action: getRuleAction(rule.action),
|
action: getRuleAction(rule.action),
|
||||||
match: rule.match.toUpperCase(),
|
match: rule.match.toUpperCase(),
|
||||||
value: rule.value.toUpperCase(),
|
value: getRuleValue(rule.match.toUpperCase(), rule.value),
|
||||||
})
|
})
|
||||||
.where(
|
.where(
|
||||||
eq(resourceRules.ruleId, existingRule.ruleId)
|
eq(resourceRules.ruleId, existingRule.ruleId)
|
||||||
@@ -547,7 +565,7 @@ export async function updateProxyResources(
|
|||||||
resourceId: existingResource.resourceId,
|
resourceId: existingResource.resourceId,
|
||||||
action: getRuleAction(rule.action),
|
action: getRuleAction(rule.action),
|
||||||
match: rule.match.toUpperCase(),
|
match: rule.match.toUpperCase(),
|
||||||
value: rule.value.toUpperCase(),
|
value: getRuleValue(rule.match.toUpperCase(), rule.value),
|
||||||
priority: index + 1 // start priorities at 1
|
priority: index + 1 // start priorities at 1
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -705,7 +723,7 @@ export async function updateProxyResources(
|
|||||||
resourceId: newResource.resourceId,
|
resourceId: newResource.resourceId,
|
||||||
action: getRuleAction(rule.action),
|
action: getRuleAction(rule.action),
|
||||||
match: rule.match.toUpperCase(),
|
match: rule.match.toUpperCase(),
|
||||||
value: rule.value.toUpperCase(),
|
value: getRuleValue(rule.match.toUpperCase(), rule.value),
|
||||||
priority: index + 1 // start priorities at 1
|
priority: index + 1 // start priorities at 1
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -735,6 +753,14 @@ function getRuleAction(input: string) {
|
|||||||
return action;
|
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) {
|
function validateRule(rule: any) {
|
||||||
if (rule.match === "cidr") {
|
if (rule.match === "cidr") {
|
||||||
if (!isValidCIDR(rule.value)) {
|
if (!isValidCIDR(rule.value)) {
|
||||||
|
|||||||
@@ -13,10 +13,12 @@ export const TargetHealthCheckSchema = z.object({
|
|||||||
scheme: z.string().optional(),
|
scheme: z.string().optional(),
|
||||||
mode: z.string().default("http"),
|
mode: z.string().default("http"),
|
||||||
interval: z.number().int().default(30),
|
interval: z.number().int().default(30),
|
||||||
unhealthyInterval: z.number().int().default(30),
|
"unhealthy-interval": z.number().int().default(30),
|
||||||
|
unhealthyInterval: z.number().int().optional(), // deprecated alias
|
||||||
timeout: z.number().int().default(5),
|
timeout: z.number().int().default(5),
|
||||||
headers: z.array(z.object({ name: z.string(), value: z.string() })).nullable().optional().default(null),
|
headers: z.array(z.object({ name: z.string(), value: z.string() })).nullable().optional().default(null),
|
||||||
followRedirects: z.boolean().default(true),
|
"follow-redirects": z.boolean().default(true),
|
||||||
|
followRedirects: z.boolean().optional(), // deprecated alias
|
||||||
method: z.string().default("GET"),
|
method: z.string().default("GET"),
|
||||||
status: z.number().int().optional()
|
status: z.number().int().optional()
|
||||||
});
|
});
|
||||||
@@ -32,7 +34,8 @@ export const TargetSchema = z.object({
|
|||||||
path: z.string().optional(),
|
path: z.string().optional(),
|
||||||
"path-match": z.enum(["exact", "prefix", "regex"]).optional().nullable(),
|
"path-match": z.enum(["exact", "prefix", "regex"]).optional().nullable(),
|
||||||
healthcheck: TargetHealthCheckSchema.optional(),
|
healthcheck: TargetHealthCheckSchema.optional(),
|
||||||
rewritePath: z.string().optional(),
|
rewritePath: z.string().optional(), // deprecated alias
|
||||||
|
"rewrite-path": z.string().optional(),
|
||||||
"rewrite-match": z.enum(["exact", "prefix", "regex", "stripPrefix"]).optional().nullable(),
|
"rewrite-match": z.enum(["exact", "prefix", "regex", "stripPrefix"]).optional().nullable(),
|
||||||
priority: z.number().int().min(1).max(1000).optional().default(100)
|
priority: z.number().int().min(1).max(1000).optional().default(100)
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import path from "path";
|
|||||||
import { fileURLToPath } from "url";
|
import { fileURLToPath } from "url";
|
||||||
|
|
||||||
// This is a placeholder value replaced by the build process
|
// 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 __FILENAME = fileURLToPath(import.meta.url);
|
||||||
export const __DIRNAME = path.dirname(__FILENAME);
|
export const __DIRNAME = path.dirname(__FILENAME);
|
||||||
|
|||||||
@@ -80,7 +80,8 @@ export async function getTraefikConfig(
|
|||||||
subnet: sites.subnet,
|
subnet: sites.subnet,
|
||||||
exitNodeId: sites.exitNodeId,
|
exitNodeId: sites.exitNodeId,
|
||||||
// Domain cert resolver fields
|
// Domain cert resolver fields
|
||||||
domainCertResolver: domains.certResolver
|
domainCertResolver: domains.certResolver,
|
||||||
|
preferWildcardCert: domains.preferWildcardCert
|
||||||
})
|
})
|
||||||
.from(sites)
|
.from(sites)
|
||||||
.innerJoin(targets, eq(targets.siteId, sites.siteId))
|
.innerJoin(targets, eq(targets.siteId, sites.siteId))
|
||||||
@@ -178,7 +179,8 @@ export async function getTraefikConfig(
|
|||||||
rewritePathType: row.rewritePathType,
|
rewritePathType: row.rewritePathType,
|
||||||
priority: priority,
|
priority: priority,
|
||||||
// Store domain cert resolver fields
|
// 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,
|
domainNamespaceId: domainNamespaces.domainNamespaceId,
|
||||||
// Certificate
|
// Certificate
|
||||||
certificateStatus: certificates.status,
|
certificateStatus: certificates.status,
|
||||||
domainCertResolver: domains.certResolver
|
domainCertResolver: domains.certResolver,
|
||||||
|
preferWildcardCert: domains.preferWildcardCert
|
||||||
})
|
})
|
||||||
.from(sites)
|
.from(sites)
|
||||||
.innerJoin(targets, eq(targets.siteId, sites.siteId))
|
.innerJoin(targets, eq(targets.siteId, sites.siteId))
|
||||||
@@ -218,7 +219,8 @@ export async function getTraefikConfig(
|
|||||||
rewritePath: row.rewritePath,
|
rewritePath: row.rewritePath,
|
||||||
rewritePathType: row.rewritePathType,
|
rewritePathType: row.rewritePathType,
|
||||||
priority: priority, // may be null, we fallback later
|
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 (!userOrgInfo.length) {
|
||||||
if (existingUser) {
|
if (existingUser) {
|
||||||
// delete the user
|
// get existing user orgs
|
||||||
// cascade will also delete org users
|
const existingUserOrgs = await db
|
||||||
|
.select()
|
||||||
|
.from(userOrgs)
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(userOrgs.userId, existingUser.userId),
|
||||||
|
eq(userOrgs.autoProvisioned, false)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
await db
|
if (!existingUserOrgs.length) {
|
||||||
.delete(users)
|
// delete the user
|
||||||
.where(eq(users.userId, existingUser.userId));
|
// 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 }[] = [];
|
const orgUserCounts: { orgId: string; userCount: number }[] = [];
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ export async function updateOrg(
|
|||||||
const { orgId } = parsedParams.data;
|
const { orgId } = parsedParams.data;
|
||||||
|
|
||||||
const isLicensed = await isLicensedOrSubscribed(orgId);
|
const isLicensed = await isLicensedOrSubscribed(orgId);
|
||||||
if (!isLicensed) {
|
if (build == "enterprise" && !isLicensed) {
|
||||||
parsedBody.data.requireTwoFactor = undefined;
|
parsedBody.data.requireTwoFactor = undefined;
|
||||||
parsedBody.data.maxSessionLengthHours = undefined;
|
parsedBody.data.maxSessionLengthHours = undefined;
|
||||||
parsedBody.data.passwordExpiryDays = undefined;
|
parsedBody.data.passwordExpiryDays = undefined;
|
||||||
@@ -100,6 +100,7 @@ export async function updateOrg(
|
|||||||
|
|
||||||
const { tier } = await getOrgTierData(orgId);
|
const { tier } = await getOrgTierData(orgId);
|
||||||
if (
|
if (
|
||||||
|
build == "saas" &&
|
||||||
tier != TierId.STANDARD &&
|
tier != TierId.STANDARD &&
|
||||||
parsedBody.data.settingsLogRetentionDaysRequest &&
|
parsedBody.data.settingsLogRetentionDaysRequest &&
|
||||||
parsedBody.data.settingsLogRetentionDaysRequest > 30
|
parsedBody.data.settingsLogRetentionDaysRequest > 30
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ import {
|
|||||||
userResources,
|
userResources,
|
||||||
roleResources,
|
roleResources,
|
||||||
resourcePassword,
|
resourcePassword,
|
||||||
resourcePincode
|
resourcePincode,
|
||||||
|
targets,
|
||||||
|
targetHealthCheck,
|
||||||
} from "@server/db";
|
} from "@server/db";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
@@ -40,6 +42,59 @@ const listResourcesSchema = z.object({
|
|||||||
.pipe(z.number().int().nonnegative())
|
.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) {
|
function queryResources(accessibleResourceIds: number[], orgId: string) {
|
||||||
return db
|
return db
|
||||||
.select({
|
.select({
|
||||||
@@ -57,7 +112,15 @@ function queryResources(accessibleResourceIds: number[], orgId: string) {
|
|||||||
enabled: resources.enabled,
|
enabled: resources.enabled,
|
||||||
domainId: resources.domainId,
|
domainId: resources.domainId,
|
||||||
niceId: resources.niceId,
|
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)
|
.from(resources)
|
||||||
.leftJoin(
|
.leftJoin(
|
||||||
@@ -72,6 +135,11 @@ function queryResources(accessibleResourceIds: number[], orgId: string) {
|
|||||||
resourceHeaderAuth,
|
resourceHeaderAuth,
|
||||||
eq(resourceHeaderAuth.resourceId, resources.resourceId)
|
eq(resourceHeaderAuth.resourceId, resources.resourceId)
|
||||||
)
|
)
|
||||||
|
.leftJoin(targets, eq(targets.resourceId, resources.resourceId))
|
||||||
|
.leftJoin(
|
||||||
|
targetHealthCheck,
|
||||||
|
eq(targetHealthCheck.targetId, targets.targetId)
|
||||||
|
)
|
||||||
.where(
|
.where(
|
||||||
and(
|
and(
|
||||||
inArray(resources.resourceId, accessibleResourceIds),
|
inArray(resources.resourceId, accessibleResourceIds),
|
||||||
@@ -81,7 +149,7 @@ function queryResources(accessibleResourceIds: number[], orgId: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type ListResourcesResponse = {
|
export type ListResourcesResponse = {
|
||||||
resources: NonNullable<Awaited<ReturnType<typeof queryResources>>>;
|
resources: ResourceWithTargets[];
|
||||||
pagination: { total: number; limit: number; offset: number };
|
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) {
|
if (req.user) {
|
||||||
accessibleResources = await db
|
accessibleResources = await db
|
||||||
.select({
|
.select({
|
||||||
@@ -183,9 +251,56 @@ export async function listResources(
|
|||||||
|
|
||||||
const baseQuery = queryResources(accessibleResourceIds, orgId);
|
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 totalCountResult = await countQuery;
|
||||||
const totalCount = totalCountResult[0].count;
|
const totalCount = totalCountResult[0]?.count ?? 0;
|
||||||
|
|
||||||
return response<ListResourcesResponse>(res, {
|
return response<ListResourcesResponse>(res, {
|
||||||
data: {
|
data: {
|
||||||
|
|||||||
@@ -163,12 +163,8 @@ export async function createTarget(
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (existingTarget) {
|
if (existingTarget) {
|
||||||
return next(
|
// log a warning
|
||||||
createHttpError(
|
logger.warn(`Target with IP ${targetData.ip}, port ${targetData.port}, method ${targetData.method} already exists for resource ID ${resourceId}`);
|
||||||
HttpCode.BAD_REQUEST,
|
|
||||||
`Target with IP ${targetData.ip}, port ${targetData.port}, method ${targetData.method} already exists for resource ID ${resourceId}`
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let newTarget: Target[] = [];
|
let newTarget: Target[] = [];
|
||||||
|
|||||||
@@ -170,12 +170,8 @@ export async function updateTarget(
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (foundTarget) {
|
if (foundTarget) {
|
||||||
return next(
|
// log a warning
|
||||||
createHttpError(
|
logger.warn(`Target with IP ${targetData.ip}, port ${targetData.port}, method ${targetData.method} already exists for resource ID ${target.resourceId}`);
|
||||||
HttpCode.BAD_REQUEST,
|
|
||||||
`Target with IP ${targetData.ip}, port ${targetData.port}, and method ${targetData.method} already exists on the same site.`
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const { internalPort, targetIps } = await pickPort(site.siteId!, db);
|
const { internalPort, targetIps } = await pickPort(site.siteId!, db);
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ export default async function DomainSettingsPage({
|
|||||||
|
|
||||||
<DNSRecordsTable records={dnsRecords} type={domain.type} />
|
<DNSRecordsTable records={dnsRecords} type={domain.type} />
|
||||||
|
|
||||||
{domain.type == "wildcard" && (
|
{domain.type == "wildcard" && !domain.configManaged && (
|
||||||
<DomainCertForm
|
<DomainCertForm
|
||||||
orgId={orgId}
|
orgId={orgId}
|
||||||
domainId={domain.domainId}
|
domainId={domain.domainId}
|
||||||
|
|||||||
@@ -501,25 +501,6 @@ export default function ReverseProxyTargets(props: {
|
|||||||
return;
|
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 {
|
try {
|
||||||
setTargetsLoading(true);
|
setTargetsLoading(true);
|
||||||
|
|
||||||
@@ -585,24 +566,6 @@ export default function ReverseProxyTargets(props: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function addTarget(data: z.infer<typeof addTargetSchema>) {
|
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) {
|
// if (site && site.type == "wireguard" && site.subnet) {
|
||||||
// // make sure that the target IP is within the site subnet
|
// // make sure that the target IP is within the site subnet
|
||||||
// const targetIp = data.ip;
|
// const targetIp = data.ip;
|
||||||
@@ -899,7 +862,7 @@ export default function ReverseProxyTargets(props: {
|
|||||||
|
|
||||||
const healthCheckColumn: ColumnDef<LocalTarget> = {
|
const healthCheckColumn: ColumnDef<LocalTarget> = {
|
||||||
accessorKey: "healthCheck",
|
accessorKey: "healthCheck",
|
||||||
header: t("healthCheck"),
|
header: () => (<span className="p-3">{t("healthCheck")}</span>),
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
const status = row.original.hcHealth || "unknown";
|
const status = row.original.hcHealth || "unknown";
|
||||||
const isEnabled = row.original.hcEnabled;
|
const isEnabled = row.original.hcEnabled;
|
||||||
@@ -971,7 +934,7 @@ export default function ReverseProxyTargets(props: {
|
|||||||
|
|
||||||
const matchPathColumn: ColumnDef<LocalTarget> = {
|
const matchPathColumn: ColumnDef<LocalTarget> = {
|
||||||
accessorKey: "path",
|
accessorKey: "path",
|
||||||
header: t("matchPath"),
|
header: () => (<span className="p-3">{t("matchPath")}</span>),
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
const hasPathMatch = !!(
|
const hasPathMatch = !!(
|
||||||
row.original.path || row.original.pathMatchType
|
row.original.path || row.original.pathMatchType
|
||||||
@@ -1033,7 +996,7 @@ export default function ReverseProxyTargets(props: {
|
|||||||
|
|
||||||
const addressColumn: ColumnDef<LocalTarget> = {
|
const addressColumn: ColumnDef<LocalTarget> = {
|
||||||
accessorKey: "address",
|
accessorKey: "address",
|
||||||
header: t("address"),
|
header: () => (<span className="p-3">{t("address")}</span>),
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
const selectedSite = sites.find(
|
const selectedSite = sites.find(
|
||||||
(site) => site.siteId === row.original.siteId
|
(site) => site.siteId === row.original.siteId
|
||||||
@@ -1052,7 +1015,7 @@ export default function ReverseProxyTargets(props: {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center w-full">
|
<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 &&
|
||||||
selectedSite.type === "newt" &&
|
selectedSite.type === "newt" &&
|
||||||
(() => {
|
(() => {
|
||||||
@@ -1247,7 +1210,7 @@ export default function ReverseProxyTargets(props: {
|
|||||||
|
|
||||||
const rewritePathColumn: ColumnDef<LocalTarget> = {
|
const rewritePathColumn: ColumnDef<LocalTarget> = {
|
||||||
accessorKey: "rewritePath",
|
accessorKey: "rewritePath",
|
||||||
header: t("rewritePath"),
|
header: () => (<span className="p-3">{t("rewritePath")}</span>),
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
const hasRewritePath = !!(
|
const hasRewritePath = !!(
|
||||||
row.original.rewritePath || row.original.rewritePathType
|
row.original.rewritePath || row.original.rewritePathType
|
||||||
@@ -1317,7 +1280,7 @@ export default function ReverseProxyTargets(props: {
|
|||||||
|
|
||||||
const enabledColumn: ColumnDef<LocalTarget> = {
|
const enabledColumn: ColumnDef<LocalTarget> = {
|
||||||
accessorKey: "enabled",
|
accessorKey: "enabled",
|
||||||
header: t("enabled"),
|
header: () => (<span className="p-3">{t("enabled")}</span>),
|
||||||
cell: ({ row }) => (
|
cell: ({ row }) => (
|
||||||
<div className="flex items-center justify-center w-full">
|
<div className="flex items-center justify-center w-full">
|
||||||
<Switch
|
<Switch
|
||||||
@@ -1338,8 +1301,9 @@ export default function ReverseProxyTargets(props: {
|
|||||||
|
|
||||||
const actionsColumn: ColumnDef<LocalTarget> = {
|
const actionsColumn: ColumnDef<LocalTarget> = {
|
||||||
id: "actions",
|
id: "actions",
|
||||||
|
header: () => (<span className="p-3">{t("actions")}</span>),
|
||||||
cell: ({ row }) => (
|
cell: ({ row }) => (
|
||||||
<div className="flex items-center justify-end w-full">
|
<div className="flex items-center w-full">
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
onClick={() => removeTarget(row.original.targetId)}
|
onClick={() => removeTarget(row.original.targetId)}
|
||||||
|
|||||||
@@ -425,24 +425,6 @@ export default function Page() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
async function addTarget(data: z.infer<typeof addTargetSchema>) {
|
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 site = sites.find((site) => site.siteId === data.siteId);
|
||||||
|
|
||||||
const isHttp = baseForm.watch("http");
|
const isHttp = baseForm.watch("http");
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { internal } from "@app/lib/api";
|
import { internal } from "@app/lib/api";
|
||||||
import { authCookieHeader } from "@app/lib/api/cookies";
|
import { authCookieHeader } from "@app/lib/api/cookies";
|
||||||
import ResourcesTable, {
|
import ResourcesTable, {
|
||||||
ResourceRow,
|
ResourceRow,
|
||||||
InternalResourceRow
|
InternalResourceRow
|
||||||
} from "../../../../components/ResourcesTable";
|
} from "../../../../components/ResourcesTable";
|
||||||
import { AxiosResponse } from "axios";
|
import { AxiosResponse } from "axios";
|
||||||
import { ListResourcesResponse } from "@server/routers/resource";
|
import { ListResourcesResponse } from "@server/routers/resource";
|
||||||
@@ -17,123 +17,130 @@ import { pullEnv } from "@app/lib/pullEnv";
|
|||||||
import { toUnicode } from "punycode";
|
import { toUnicode } from "punycode";
|
||||||
|
|
||||||
type ResourcesPageProps = {
|
type ResourcesPageProps = {
|
||||||
params: Promise<{ orgId: string }>;
|
params: Promise<{ orgId: string }>;
|
||||||
searchParams: Promise<{ view?: string }>;
|
searchParams: Promise<{ view?: string }>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const dynamic = "force-dynamic";
|
export const dynamic = "force-dynamic";
|
||||||
|
|
||||||
export default async function ResourcesPage(props: ResourcesPageProps) {
|
export default async function ResourcesPage(props: ResourcesPageProps) {
|
||||||
const params = await props.params;
|
const params = await props.params;
|
||||||
const searchParams = await props.searchParams;
|
const searchParams = await props.searchParams;
|
||||||
const t = await getTranslations();
|
const t = await getTranslations();
|
||||||
|
|
||||||
const env = pullEnv();
|
const env = pullEnv();
|
||||||
|
|
||||||
// Default to 'proxy' view, or use the query param if provided
|
// Default to 'proxy' view, or use the query param if provided
|
||||||
let defaultView: "proxy" | "internal" = "proxy";
|
let defaultView: "proxy" | "internal" = "proxy";
|
||||||
if (env.flags.enableClients) {
|
if (env.flags.enableClients) {
|
||||||
defaultView = searchParams.view === "internal" ? "internal" : "proxy";
|
defaultView = searchParams.view === "internal" ? "internal" : "proxy";
|
||||||
}
|
}
|
||||||
|
|
||||||
let resources: ListResourcesResponse["resources"] = [];
|
let resources: ListResourcesResponse["resources"] = [];
|
||||||
try {
|
try {
|
||||||
const res = await internal.get<AxiosResponse<ListResourcesResponse>>(
|
const res = await internal.get<AxiosResponse<ListResourcesResponse>>(
|
||||||
`/org/${params.orgId}/resources`,
|
`/org/${params.orgId}/resources`,
|
||||||
await authCookieHeader()
|
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
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
resources = res.data.data.resources;
|
||||||
|
} catch (e) { }
|
||||||
|
|
||||||
return (
|
let siteResources: ListAllSiteResourcesByOrgResponse["siteResources"] = [];
|
||||||
<>
|
try {
|
||||||
<SettingsSectionTitle
|
const res = await internal.get<
|
||||||
title={t("resourceTitle")}
|
AxiosResponse<ListAllSiteResourcesByOrgResponse>
|
||||||
description={t("resourceDescription")}
|
>(`/org/${params.orgId}/site-resources`, await authCookieHeader());
|
||||||
/>
|
siteResources = res.data.data.siteResources;
|
||||||
|
} catch (e) { }
|
||||||
|
|
||||||
<OrgProvider org={org}>
|
let org = null;
|
||||||
<ResourcesTable
|
try {
|
||||||
resources={resourceRows}
|
const getOrg = cache(async () =>
|
||||||
internalResources={internalResourceRows}
|
internal.get<AxiosResponse<GetOrgResponse>>(
|
||||||
orgId={params.orgId}
|
`/org/${params.orgId}`,
|
||||||
defaultView={
|
await authCookieHeader()
|
||||||
env.flags.enableClients ? defaultView : "proxy"
|
)
|
||||||
}
|
|
||||||
defaultSort={{
|
|
||||||
id: "name",
|
|
||||||
desc: false
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</OrgProvider>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
|
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">
|
<div className="flex flex-col gap-6">
|
||||||
<Alert>
|
<Alert>
|
||||||
<AlertDescription>
|
<AlertDescription>
|
||||||
<InfoSections cols={2}>
|
<InfoSections cols={3}>
|
||||||
<InfoSection>
|
|
||||||
<InfoSectionTitle>
|
|
||||||
{t("appliedAt")}
|
|
||||||
</InfoSectionTitle>
|
|
||||||
<InfoSectionContent>
|
|
||||||
<time
|
|
||||||
className="text-muted-foreground"
|
|
||||||
dateTime={blueprint.createdAt.toString()}
|
|
||||||
>
|
|
||||||
{new Date(
|
|
||||||
blueprint.createdAt * 1000
|
|
||||||
).toLocaleString()}
|
|
||||||
</time>
|
|
||||||
</InfoSectionContent>
|
|
||||||
</InfoSection>
|
|
||||||
<InfoSection>
|
<InfoSection>
|
||||||
<InfoSectionTitle>
|
<InfoSectionTitle>
|
||||||
{t("status")}
|
{t("status")}
|
||||||
@@ -88,16 +73,6 @@ export default function BlueprintDetailsForm({
|
|||||||
)}
|
)}
|
||||||
</InfoSectionContent>
|
</InfoSectionContent>
|
||||||
</InfoSection>
|
</InfoSection>
|
||||||
<InfoSection>
|
|
||||||
<InfoSectionTitle>
|
|
||||||
{t("message")}
|
|
||||||
</InfoSectionTitle>
|
|
||||||
<InfoSectionContent>
|
|
||||||
<p className="text-muted-foreground">
|
|
||||||
{blueprint.message}
|
|
||||||
</p>
|
|
||||||
</InfoSectionContent>
|
|
||||||
</InfoSection>
|
|
||||||
<InfoSection>
|
<InfoSection>
|
||||||
<InfoSectionTitle>
|
<InfoSectionTitle>
|
||||||
{t("source")}
|
{t("source")}
|
||||||
@@ -106,35 +81,59 @@ export default function BlueprintDetailsForm({
|
|||||||
{blueprint.source === "API" && (
|
{blueprint.source === "API" && (
|
||||||
<Badge
|
<Badge
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
className="-mx-2"
|
className="inline-flex items-center gap-1 "
|
||||||
>
|
>
|
||||||
<span className="inline-flex items-center gap-1 ">
|
API
|
||||||
API
|
<Webhook className="w-3 h-3 flex-none" />
|
||||||
<Webhook className="size-4 flex-none" />
|
|
||||||
</span>
|
|
||||||
</Badge>
|
</Badge>
|
||||||
)}
|
)}
|
||||||
{blueprint.source === "NEWT" && (
|
{blueprint.source === "NEWT" && (
|
||||||
<Badge variant="secondary">
|
<Badge
|
||||||
<span className="inline-flex items-center gap-1 ">
|
variant="secondary"
|
||||||
Newt CLI
|
className="inline-flex items-center gap-1 "
|
||||||
<Terminal className="size-4 flex-none" />
|
>
|
||||||
</span>
|
<Terminal className="w-3 h-3 flex-none" />
|
||||||
|
Newt CLI
|
||||||
</Badge>
|
</Badge>
|
||||||
)}
|
)}
|
||||||
{blueprint.source === "UI" && (
|
{blueprint.source === "UI" && (
|
||||||
<Badge
|
<Badge
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
className="-mx-1 py-1"
|
className="inline-flex items-center gap-1 "
|
||||||
>
|
>
|
||||||
<span className="inline-flex items-center gap-1 ">
|
<Globe className="w-3 h-3 flex-none" />
|
||||||
Dashboard{" "}
|
Dashboard
|
||||||
<Globe className="size-4 flex-none" />
|
|
||||||
</span>
|
|
||||||
</Badge>
|
</Badge>
|
||||||
)}{" "}
|
)}{" "}
|
||||||
</InfoSectionContent>
|
</InfoSectionContent>
|
||||||
</InfoSection>
|
</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>
|
</InfoSections>
|
||||||
</AlertDescription>
|
</AlertDescription>
|
||||||
</Alert>
|
</Alert>
|
||||||
@@ -169,11 +168,6 @@ export default function BlueprintDetailsForm({
|
|||||||
<FormLabel>
|
<FormLabel>
|
||||||
{t("parsedContents")}
|
{t("parsedContents")}
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<FormDescription>
|
|
||||||
{t(
|
|
||||||
"blueprintContentsDescription"
|
|
||||||
)}
|
|
||||||
</FormDescription>
|
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
|
|||||||
@@ -116,10 +116,13 @@ export default function BlueprintsTable({ blueprints, orgId }: Props) {
|
|||||||
}
|
}
|
||||||
case "UI": {
|
case "UI": {
|
||||||
return (
|
return (
|
||||||
<Badge variant="secondary">
|
<Badge
|
||||||
|
variant="secondary"
|
||||||
|
className="inline-flex items-center gap-1"
|
||||||
|
>
|
||||||
<span className="inline-flex items-center gap-1 ">
|
<span className="inline-flex items-center gap-1 ">
|
||||||
Dashboard{" "}
|
<Globe className="w-3 h-3" />
|
||||||
<Globe className="size-4 flex-none" />
|
Dashboard
|
||||||
</span>
|
</span>
|
||||||
</Badge>
|
</Badge>
|
||||||
);
|
);
|
||||||
@@ -163,18 +166,14 @@ export default function BlueprintsTable({ blueprints, orgId }: Props) {
|
|||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
return (
|
return (
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<Button
|
<Link
|
||||||
variant="outline"
|
href={`/${orgId}/settings/blueprints/${row.original.blueprintId}`}
|
||||||
className="items-center"
|
|
||||||
asChild
|
|
||||||
>
|
>
|
||||||
<Link
|
<Button variant="outline" className="items-center">
|
||||||
href={`/${orgId}/settings/blueprints/${row.original.blueprintId}`}
|
View Details
|
||||||
>
|
<ArrowRight className="ml-2 w-4 h-4" />
|
||||||
View details{" "}
|
</Button>
|
||||||
<ArrowRight className="size-4 flex-none" />
|
</Link>
|
||||||
</Link>
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -124,7 +124,6 @@ export function DNSRecordsDataTable<TData, TValue>({
|
|||||||
{table.getHeaderGroups().map((headerGroup) => (
|
{table.getHeaderGroups().map((headerGroup) => (
|
||||||
<TableRow
|
<TableRow
|
||||||
key={headerGroup.id}
|
key={headerGroup.id}
|
||||||
className="bg-secondary dark:bg-transparent"
|
|
||||||
>
|
>
|
||||||
{headerGroup.headers.map((header) => (
|
{headerGroup.headers.map((header) => (
|
||||||
<TableHead key={header.id}>
|
<TableHead key={header.id}>
|
||||||
|
|||||||
@@ -9,13 +9,15 @@ import {
|
|||||||
SortingState,
|
SortingState,
|
||||||
getSortedRowModel,
|
getSortedRowModel,
|
||||||
ColumnFiltersState,
|
ColumnFiltersState,
|
||||||
getFilteredRowModel
|
getFilteredRowModel,
|
||||||
|
VisibilityState
|
||||||
} from "@tanstack/react-table";
|
} from "@tanstack/react-table";
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
DropdownMenuItem,
|
DropdownMenuItem,
|
||||||
DropdownMenuTrigger
|
DropdownMenuTrigger,
|
||||||
|
DropdownMenuCheckboxItem,
|
||||||
} from "@app/components/ui/dropdown-menu";
|
} from "@app/components/ui/dropdown-menu";
|
||||||
import { Button } from "@app/components/ui/button";
|
import { Button } from "@app/components/ui/button";
|
||||||
import {
|
import {
|
||||||
@@ -25,7 +27,16 @@ import {
|
|||||||
ArrowUpRight,
|
ArrowUpRight,
|
||||||
ShieldOff,
|
ShieldOff,
|
||||||
ShieldCheck,
|
ShieldCheck,
|
||||||
RefreshCw
|
RefreshCw,
|
||||||
|
Settings2,
|
||||||
|
Plus,
|
||||||
|
Search,
|
||||||
|
ChevronDown,
|
||||||
|
Clock,
|
||||||
|
Wifi,
|
||||||
|
WifiOff,
|
||||||
|
CheckCircle2,
|
||||||
|
XCircle,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
@@ -44,7 +55,6 @@ import { useTranslations } from "next-intl";
|
|||||||
import { InfoPopup } from "@app/components/ui/info-popup";
|
import { InfoPopup } from "@app/components/ui/info-popup";
|
||||||
import { Input } from "@app/components/ui/input";
|
import { Input } from "@app/components/ui/input";
|
||||||
import { DataTablePagination } from "@app/components/DataTablePagination";
|
import { DataTablePagination } from "@app/components/DataTablePagination";
|
||||||
import { Plus, Search } from "lucide-react";
|
|
||||||
import { Card, CardContent, CardHeader } from "@app/components/ui/card";
|
import { Card, CardContent, CardHeader } from "@app/components/ui/card";
|
||||||
import {
|
import {
|
||||||
Table,
|
Table,
|
||||||
@@ -65,6 +75,15 @@ import EditInternalResourceDialog from "@app/components/EditInternalResourceDial
|
|||||||
import CreateInternalResourceDialog from "@app/components/CreateInternalResourceDialog";
|
import CreateInternalResourceDialog from "@app/components/CreateInternalResourceDialog";
|
||||||
import { Alert, AlertDescription } from "@app/components/ui/alert";
|
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 = {
|
export type ResourceRow = {
|
||||||
id: number;
|
id: number;
|
||||||
nice: string | null;
|
nice: string | null;
|
||||||
@@ -78,8 +97,54 @@ export type ResourceRow = {
|
|||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
domainId?: string;
|
domainId?: string;
|
||||||
ssl: boolean;
|
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 = {
|
export type InternalResourceRow = {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
@@ -143,6 +208,7 @@ const setStoredPageSize = (pageSize: number, tableId?: string): void => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export default function ResourcesTable({
|
export default function ResourcesTable({
|
||||||
resources,
|
resources,
|
||||||
internalResources,
|
internalResources,
|
||||||
@@ -158,6 +224,7 @@ export default function ResourcesTable({
|
|||||||
|
|
||||||
const api = createApiClient({ env });
|
const api = createApiClient({ env });
|
||||||
|
|
||||||
|
|
||||||
const [proxyPageSize, setProxyPageSize] = useState<number>(() =>
|
const [proxyPageSize, setProxyPageSize] = useState<number>(() =>
|
||||||
getStoredPageSize('proxy-resources', 20)
|
getStoredPageSize('proxy-resources', 20)
|
||||||
);
|
);
|
||||||
@@ -179,6 +246,10 @@ export default function ResourcesTable({
|
|||||||
const [proxySorting, setProxySorting] = useState<SortingState>(
|
const [proxySorting, setProxySorting] = useState<SortingState>(
|
||||||
defaultSort ? [defaultSort] : []
|
defaultSort ? [defaultSort] : []
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const [proxyColumnVisibility, setProxyColumnVisibility] = useState<VisibilityState>({});
|
||||||
|
const [internalColumnVisibility, setInternalColumnVisibility] = useState<VisibilityState>({});
|
||||||
|
|
||||||
const [proxyColumnFilters, setProxyColumnFilters] =
|
const [proxyColumnFilters, setProxyColumnFilters] =
|
||||||
useState<ColumnFiltersState>([]);
|
useState<ColumnFiltersState>([]);
|
||||||
const [proxyGlobalFilter, setProxyGlobalFilter] = useState<any>([]);
|
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>[] = [
|
const proxyColumns: ColumnDef<ResourceRow>[] = [
|
||||||
{
|
{
|
||||||
accessorKey: "name",
|
accessorKey: "name",
|
||||||
@@ -390,6 +531,33 @@ export default function ResourcesTable({
|
|||||||
return <span>{resourceRow.http ? (resourceRow.ssl ? "HTTPS" : "HTTP") : resourceRow.protocol.toUpperCase()}</span>;
|
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",
|
accessorKey: "domain",
|
||||||
header: t("access"),
|
header: t("access"),
|
||||||
@@ -647,6 +815,7 @@ export default function ResourcesTable({
|
|||||||
onColumnFiltersChange: setProxyColumnFilters,
|
onColumnFiltersChange: setProxyColumnFilters,
|
||||||
getFilteredRowModel: getFilteredRowModel(),
|
getFilteredRowModel: getFilteredRowModel(),
|
||||||
onGlobalFilterChange: setProxyGlobalFilter,
|
onGlobalFilterChange: setProxyGlobalFilter,
|
||||||
|
onColumnVisibilityChange: setProxyColumnVisibility,
|
||||||
initialState: {
|
initialState: {
|
||||||
pagination: {
|
pagination: {
|
||||||
pageSize: proxyPageSize,
|
pageSize: proxyPageSize,
|
||||||
@@ -656,7 +825,8 @@ export default function ResourcesTable({
|
|||||||
state: {
|
state: {
|
||||||
sorting: proxySorting,
|
sorting: proxySorting,
|
||||||
columnFilters: proxyColumnFilters,
|
columnFilters: proxyColumnFilters,
|
||||||
globalFilter: proxyGlobalFilter
|
globalFilter: proxyGlobalFilter,
|
||||||
|
columnVisibility: proxyColumnVisibility
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -670,6 +840,7 @@ export default function ResourcesTable({
|
|||||||
onColumnFiltersChange: setInternalColumnFilters,
|
onColumnFiltersChange: setInternalColumnFilters,
|
||||||
getFilteredRowModel: getFilteredRowModel(),
|
getFilteredRowModel: getFilteredRowModel(),
|
||||||
onGlobalFilterChange: setInternalGlobalFilter,
|
onGlobalFilterChange: setInternalGlobalFilter,
|
||||||
|
onColumnVisibilityChange: setInternalColumnVisibility,
|
||||||
initialState: {
|
initialState: {
|
||||||
pagination: {
|
pagination: {
|
||||||
pageSize: internalPageSize,
|
pageSize: internalPageSize,
|
||||||
@@ -679,7 +850,8 @@ export default function ResourcesTable({
|
|||||||
state: {
|
state: {
|
||||||
sorting: internalSorting,
|
sorting: internalSorting,
|
||||||
columnFilters: internalColumnFilters,
|
columnFilters: internalColumnFilters,
|
||||||
globalFilter: internalGlobalFilter
|
globalFilter: internalGlobalFilter,
|
||||||
|
columnVisibility: internalColumnVisibility
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user