mirror of
https://github.com/fosrl/pangolin.git
synced 2026-07-01 18:13:49 +00:00
Compare commits
3 Commits
crowdin_de
...
1.19.4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
784588cebc | ||
|
|
2e628fe0e4 | ||
|
|
7590e8d8a1 |
@@ -123,16 +123,6 @@
|
|||||||
"siteUpdated": "Сайтът е обновен",
|
"siteUpdated": "Сайтът е обновен",
|
||||||
"siteUpdatedDescription": "Сайтът е актуализиран.",
|
"siteUpdatedDescription": "Сайтът е актуализиран.",
|
||||||
"siteGeneralDescription": "Конфигурирайте общи настройки за този сайт",
|
"siteGeneralDescription": "Конфигурирайте общи настройки за този сайт",
|
||||||
"siteRestartTitle": "Restart Site",
|
|
||||||
"siteRestartDescription": "Restart the WireGuard tunnel for this site. This will briefly interrupt connectivity.",
|
|
||||||
"siteRestartBody": "Use this if the site tunnel is not functioning correctly and you want to force a reconnect without restarting the host.",
|
|
||||||
"siteRestartButton": "Restart Site",
|
|
||||||
"siteRestartDialogMessage": "Are you sure you want to restart the WireGuard tunnel for <b>{name}</b>? The site will briefly lose connectivity.",
|
|
||||||
"siteRestartWarning": "The site will briefly disconnect while the tunnel restarts.",
|
|
||||||
"siteRestarted": "Site restarted",
|
|
||||||
"siteRestartedDescription": "The WireGuard tunnel has been restarted.",
|
|
||||||
"siteErrorRestart": "Failed to restart site",
|
|
||||||
"siteErrorRestartDescription": "An error occurred while restarting the site.",
|
|
||||||
"siteSettingDescription": "Конфигурирайте настройките на сайта",
|
"siteSettingDescription": "Конфигурирайте настройките на сайта",
|
||||||
"siteResourcesTab": "Ресурси",
|
"siteResourcesTab": "Ресурси",
|
||||||
"siteResourcesNoneOnSite": "Този сайт все още няма публични или частни ресурси.",
|
"siteResourcesNoneOnSite": "Този сайт все още няма публични или частни ресурси.",
|
||||||
@@ -2388,21 +2378,6 @@
|
|||||||
"sidebarRemoteExitNodes": "Отдалечени възли",
|
"sidebarRemoteExitNodes": "Отдалечени възли",
|
||||||
"remoteExitNodeId": "ID.",
|
"remoteExitNodeId": "ID.",
|
||||||
"remoteExitNodeSecretKey": "Секретен ключ.",
|
"remoteExitNodeSecretKey": "Секретен ключ.",
|
||||||
"remoteExitNodeNetworkingTitle": "Network Settings",
|
|
||||||
"remoteExitNodeNetworkingDescription": "Configure how this remote exit node routes traffic and which sites prefer to connect through it. Advanced features to be used with backhaul networking configurations.",
|
|
||||||
"remoteExitNodeNetworkingSave": "Save Settings",
|
|
||||||
"remoteExitNodeNetworkingSaveSuccessTitle": "Network settings saved",
|
|
||||||
"remoteExitNodeNetworkingSaveSuccessDescription": "Network settings have been updated successfully.",
|
|
||||||
"remoteExitNodeNetworkingSaveError": "Failed to save network settings",
|
|
||||||
"remoteExitNodeNetworkingSubnetsTitle": "Remote Subnets",
|
|
||||||
"remoteExitNodeNetworkingSubnetsDescription": "Define the CIDR ranges that this remote exit node will route traffic to. Type a valid CIDR (e.g. <code>10.0.0.0/8</code>) and press Enter to add.",
|
|
||||||
"remoteExitNodeNetworkingSubnetsPlaceholder": "Add a CIDR range (e.g. 10.0.0.0/8)",
|
|
||||||
"remoteExitNodeNetworkingSubnetsLoadError": "Failed to load subnets",
|
|
||||||
"remoteExitNodeNetworkingLabelsTitle": "Preference Labels",
|
|
||||||
"remoteExitNodeNetworkingLabelsDescription": "Sites with these labels will be enforced to connect through this remote exit node.",
|
|
||||||
"remoteExitNodeNetworkingLabelsButtonText": "Select labels...",
|
|
||||||
"remoteExitNodeNetworkingLabelsSearchPlaceholder": "Search labels...",
|
|
||||||
"remoteExitNodeNetworkingLabelsLoadError": "Failed to load labels",
|
|
||||||
"remoteExitNodeCreate": {
|
"remoteExitNodeCreate": {
|
||||||
"title": "Създаване на отдалечен възел.",
|
"title": "Създаване на отдалечен възел.",
|
||||||
"description": "Създайте нов самохостнал отдалечен ретранслатор и прокси сървърен възел.",
|
"description": "Създайте нов самохостнал отдалечен ретранслатор и прокси сървърен възел.",
|
||||||
@@ -2581,7 +2556,6 @@
|
|||||||
"idpGoogleDescription": "Google OAuth2/OIDC доставчик",
|
"idpGoogleDescription": "Google OAuth2/OIDC доставчик",
|
||||||
"idpAzureDescription": "Microsoft Azure OAuth2/OIDC доставчик",
|
"idpAzureDescription": "Microsoft Azure OAuth2/OIDC доставчик",
|
||||||
"subnet": "Подмрежа",
|
"subnet": "Подмрежа",
|
||||||
"utilitySubnet": "Utility Subnet",
|
|
||||||
"subnetDescription": "Подмрежата за конфигурацията на мрежата на тази организация.",
|
"subnetDescription": "Подмрежата за конфигурацията на мрежата на тази организация.",
|
||||||
"customDomain": "Персонализиран домейн.",
|
"customDomain": "Персонализиран домейн.",
|
||||||
"authPage": "Страници за автентификация.",
|
"authPage": "Страници за автентификация.",
|
||||||
@@ -3602,8 +3576,7 @@
|
|||||||
"sshPrivateKeyPlaceholder": "-----НАЧАЛО НА OPENSSH ЧАСТЕН КЛЮЧ-----",
|
"sshPrivateKeyPlaceholder": "-----НАЧАЛО НА OPENSSH ЧАСТЕН КЛЮЧ-----",
|
||||||
"sshPrivateKeyRequired": "Изисква се частен ключ",
|
"sshPrivateKeyRequired": "Изисква се частен ключ",
|
||||||
"vncTitle": "VNC",
|
"vncTitle": "VNC",
|
||||||
"vncSignInDescription": "Enter your VNC credentials to connect",
|
"vncSignInDescription": "Въведете вашата VNC парола за свързване",
|
||||||
"vncUsernameOptional": "Username (optional)",
|
|
||||||
"vncPasswordOptional": "Парола (по избор)",
|
"vncPasswordOptional": "Парола (по избор)",
|
||||||
"vncNoResourceTarget": "Не е налична цел за ресурса",
|
"vncNoResourceTarget": "Не е налична цел за ресурса",
|
||||||
"vncFailedToLoadNovnc": "Неуспешно зареждане на noVNC",
|
"vncFailedToLoadNovnc": "Неуспешно зареждане на noVNC",
|
||||||
|
|||||||
@@ -123,16 +123,6 @@
|
|||||||
"siteUpdated": "Lokalita upravena",
|
"siteUpdated": "Lokalita upravena",
|
||||||
"siteUpdatedDescription": "Lokalita byla upravena.",
|
"siteUpdatedDescription": "Lokalita byla upravena.",
|
||||||
"siteGeneralDescription": "Upravte obecná nastavení pro tuto lokalitu",
|
"siteGeneralDescription": "Upravte obecná nastavení pro tuto lokalitu",
|
||||||
"siteRestartTitle": "Restart Site",
|
|
||||||
"siteRestartDescription": "Restart the WireGuard tunnel for this site. This will briefly interrupt connectivity.",
|
|
||||||
"siteRestartBody": "Use this if the site tunnel is not functioning correctly and you want to force a reconnect without restarting the host.",
|
|
||||||
"siteRestartButton": "Restart Site",
|
|
||||||
"siteRestartDialogMessage": "Are you sure you want to restart the WireGuard tunnel for <b>{name}</b>? The site will briefly lose connectivity.",
|
|
||||||
"siteRestartWarning": "The site will briefly disconnect while the tunnel restarts.",
|
|
||||||
"siteRestarted": "Site restarted",
|
|
||||||
"siteRestartedDescription": "The WireGuard tunnel has been restarted.",
|
|
||||||
"siteErrorRestart": "Failed to restart site",
|
|
||||||
"siteErrorRestartDescription": "An error occurred while restarting the site.",
|
|
||||||
"siteSettingDescription": "Konfigurace nastavení na webu",
|
"siteSettingDescription": "Konfigurace nastavení na webu",
|
||||||
"siteResourcesTab": "Zdroje",
|
"siteResourcesTab": "Zdroje",
|
||||||
"siteResourcesNoneOnSite": "Tento web zatím nemá veřejné ani soukromé zdroje.",
|
"siteResourcesNoneOnSite": "Tento web zatím nemá veřejné ani soukromé zdroje.",
|
||||||
@@ -2388,21 +2378,6 @@
|
|||||||
"sidebarRemoteExitNodes": "Vzdálené uzly",
|
"sidebarRemoteExitNodes": "Vzdálené uzly",
|
||||||
"remoteExitNodeId": "ID",
|
"remoteExitNodeId": "ID",
|
||||||
"remoteExitNodeSecretKey": "Tajný klíč",
|
"remoteExitNodeSecretKey": "Tajný klíč",
|
||||||
"remoteExitNodeNetworkingTitle": "Network Settings",
|
|
||||||
"remoteExitNodeNetworkingDescription": "Configure how this remote exit node routes traffic and which sites prefer to connect through it. Advanced features to be used with backhaul networking configurations.",
|
|
||||||
"remoteExitNodeNetworkingSave": "Save Settings",
|
|
||||||
"remoteExitNodeNetworkingSaveSuccessTitle": "Network settings saved",
|
|
||||||
"remoteExitNodeNetworkingSaveSuccessDescription": "Network settings have been updated successfully.",
|
|
||||||
"remoteExitNodeNetworkingSaveError": "Failed to save network settings",
|
|
||||||
"remoteExitNodeNetworkingSubnetsTitle": "Remote Subnets",
|
|
||||||
"remoteExitNodeNetworkingSubnetsDescription": "Define the CIDR ranges that this remote exit node will route traffic to. Type a valid CIDR (e.g. <code>10.0.0.0/8</code>) and press Enter to add.",
|
|
||||||
"remoteExitNodeNetworkingSubnetsPlaceholder": "Add a CIDR range (e.g. 10.0.0.0/8)",
|
|
||||||
"remoteExitNodeNetworkingSubnetsLoadError": "Failed to load subnets",
|
|
||||||
"remoteExitNodeNetworkingLabelsTitle": "Preference Labels",
|
|
||||||
"remoteExitNodeNetworkingLabelsDescription": "Sites with these labels will be enforced to connect through this remote exit node.",
|
|
||||||
"remoteExitNodeNetworkingLabelsButtonText": "Select labels...",
|
|
||||||
"remoteExitNodeNetworkingLabelsSearchPlaceholder": "Search labels...",
|
|
||||||
"remoteExitNodeNetworkingLabelsLoadError": "Failed to load labels",
|
|
||||||
"remoteExitNodeCreate": {
|
"remoteExitNodeCreate": {
|
||||||
"title": "Vytvořit vzdálený uzel",
|
"title": "Vytvořit vzdálený uzel",
|
||||||
"description": "Vytvořte nový vlastní hostovaný vzdálený relační a proxy server uzel",
|
"description": "Vytvořte nový vlastní hostovaný vzdálený relační a proxy server uzel",
|
||||||
@@ -2581,7 +2556,6 @@
|
|||||||
"idpGoogleDescription": "Poskytovatel Google OAuth2/OIDC",
|
"idpGoogleDescription": "Poskytovatel Google OAuth2/OIDC",
|
||||||
"idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider",
|
"idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider",
|
||||||
"subnet": "Podsíť",
|
"subnet": "Podsíť",
|
||||||
"utilitySubnet": "Utility Subnet",
|
|
||||||
"subnetDescription": "Podsíť pro konfiguraci sítě této organizace.",
|
"subnetDescription": "Podsíť pro konfiguraci sítě této organizace.",
|
||||||
"customDomain": "Vlastní doména",
|
"customDomain": "Vlastní doména",
|
||||||
"authPage": "Autentizační stránky",
|
"authPage": "Autentizační stránky",
|
||||||
@@ -3602,8 +3576,7 @@
|
|||||||
"sshPrivateKeyPlaceholder": "-----ZAČÁTEK SOUKROMÉHO KLÍČE OPENSSH-----",
|
"sshPrivateKeyPlaceholder": "-----ZAČÁTEK SOUKROMÉHO KLÍČE OPENSSH-----",
|
||||||
"sshPrivateKeyRequired": "Je vyžadován soukromý klíč",
|
"sshPrivateKeyRequired": "Je vyžadován soukromý klíč",
|
||||||
"vncTitle": "VNC",
|
"vncTitle": "VNC",
|
||||||
"vncSignInDescription": "Enter your VNC credentials to connect",
|
"vncSignInDescription": "Zadejte své heslo VNC pro připojení",
|
||||||
"vncUsernameOptional": "Username (optional)",
|
|
||||||
"vncPasswordOptional": "Heslo (nepovinné)",
|
"vncPasswordOptional": "Heslo (nepovinné)",
|
||||||
"vncNoResourceTarget": "Není k dispozici žádný cíl zdroje",
|
"vncNoResourceTarget": "Není k dispozici žádný cíl zdroje",
|
||||||
"vncFailedToLoadNovnc": "Nepodařilo se načíst noVNC",
|
"vncFailedToLoadNovnc": "Nepodařilo se načíst noVNC",
|
||||||
|
|||||||
@@ -123,16 +123,6 @@
|
|||||||
"siteUpdated": "Site opdateret",
|
"siteUpdated": "Site opdateret",
|
||||||
"siteUpdatedDescription": "Sitet er blevet opdateret.",
|
"siteUpdatedDescription": "Sitet er blevet opdateret.",
|
||||||
"siteGeneralDescription": "Konfigurer de generelle indstillinger for dette site",
|
"siteGeneralDescription": "Konfigurer de generelle indstillinger for dette site",
|
||||||
"siteRestartTitle": "Restart Site",
|
|
||||||
"siteRestartDescription": "Restart the WireGuard tunnel for this site. This will briefly interrupt connectivity.",
|
|
||||||
"siteRestartBody": "Use this if the site tunnel is not functioning correctly and you want to force a reconnect without restarting the host.",
|
|
||||||
"siteRestartButton": "Restart Site",
|
|
||||||
"siteRestartDialogMessage": "Are you sure you want to restart the WireGuard tunnel for <b>{name}</b>? The site will briefly lose connectivity.",
|
|
||||||
"siteRestartWarning": "The site will briefly disconnect while the tunnel restarts.",
|
|
||||||
"siteRestarted": "Site restarted",
|
|
||||||
"siteRestartedDescription": "The WireGuard tunnel has been restarted.",
|
|
||||||
"siteErrorRestart": "Failed to restart site",
|
|
||||||
"siteErrorRestartDescription": "An error occurred while restarting the site.",
|
|
||||||
"siteSettingDescription": "Konfigurer indstillingerne for sitet",
|
"siteSettingDescription": "Konfigurer indstillingerne for sitet",
|
||||||
"siteResourcesTab": "Ressourcer",
|
"siteResourcesTab": "Ressourcer",
|
||||||
"siteResourcesNoneOnSite": "Dette site har endnu ingen offentlige eller private ressourcer.",
|
"siteResourcesNoneOnSite": "Dette site har endnu ingen offentlige eller private ressourcer.",
|
||||||
@@ -2388,21 +2378,6 @@
|
|||||||
"sidebarRemoteExitNodes": "Eksterne noder",
|
"sidebarRemoteExitNodes": "Eksterne noder",
|
||||||
"remoteExitNodeId": "ID",
|
"remoteExitNodeId": "ID",
|
||||||
"remoteExitNodeSecretKey": "Sikkerhedsnøgle",
|
"remoteExitNodeSecretKey": "Sikkerhedsnøgle",
|
||||||
"remoteExitNodeNetworkingTitle": "Network Settings",
|
|
||||||
"remoteExitNodeNetworkingDescription": "Configure how this remote exit node routes traffic and which sites prefer to connect through it. Advanced features to be used with backhaul networking configurations.",
|
|
||||||
"remoteExitNodeNetworkingSave": "Save Settings",
|
|
||||||
"remoteExitNodeNetworkingSaveSuccessTitle": "Network settings saved",
|
|
||||||
"remoteExitNodeNetworkingSaveSuccessDescription": "Network settings have been updated successfully.",
|
|
||||||
"remoteExitNodeNetworkingSaveError": "Failed to save network settings",
|
|
||||||
"remoteExitNodeNetworkingSubnetsTitle": "Remote Subnets",
|
|
||||||
"remoteExitNodeNetworkingSubnetsDescription": "Define the CIDR ranges that this remote exit node will route traffic to. Type a valid CIDR (e.g. <code>10.0.0.0/8</code>) and press Enter to add.",
|
|
||||||
"remoteExitNodeNetworkingSubnetsPlaceholder": "Add a CIDR range (e.g. 10.0.0.0/8)",
|
|
||||||
"remoteExitNodeNetworkingSubnetsLoadError": "Failed to load subnets",
|
|
||||||
"remoteExitNodeNetworkingLabelsTitle": "Preference Labels",
|
|
||||||
"remoteExitNodeNetworkingLabelsDescription": "Sites with these labels will be enforced to connect through this remote exit node.",
|
|
||||||
"remoteExitNodeNetworkingLabelsButtonText": "Select labels...",
|
|
||||||
"remoteExitNodeNetworkingLabelsSearchPlaceholder": "Search labels...",
|
|
||||||
"remoteExitNodeNetworkingLabelsLoadError": "Failed to load labels",
|
|
||||||
"remoteExitNodeCreate": {
|
"remoteExitNodeCreate": {
|
||||||
"title": "Opret ekstern node",
|
"title": "Opret ekstern node",
|
||||||
"description": "Opret en ny selvhostet ekstern relay- og proxyservernode",
|
"description": "Opret en ny selvhostet ekstern relay- og proxyservernode",
|
||||||
@@ -2581,7 +2556,6 @@
|
|||||||
"idpGoogleDescription": "Google OAuth2/OIDC udbyder",
|
"idpGoogleDescription": "Google OAuth2/OIDC udbyder",
|
||||||
"idpAzureDescription": "Microsoft Azure OAuth2/OIDC leverandør",
|
"idpAzureDescription": "Microsoft Azure OAuth2/OIDC leverandør",
|
||||||
"subnet": "Subnet",
|
"subnet": "Subnet",
|
||||||
"utilitySubnet": "Utility Subnet",
|
|
||||||
"subnetDescription": "Subnettet for denne organisations netværkskonfiguration.",
|
"subnetDescription": "Subnettet for denne organisations netværkskonfiguration.",
|
||||||
"customDomain": "Brugerdefineret domæne",
|
"customDomain": "Brugerdefineret domæne",
|
||||||
"authPage": "Autentiseringssider",
|
"authPage": "Autentiseringssider",
|
||||||
@@ -3602,8 +3576,7 @@
|
|||||||
"sshPrivateKeyPlaceholder": "-----BEGYNN OPENSSH PRIVAT NØGLE-----",
|
"sshPrivateKeyPlaceholder": "-----BEGYNN OPENSSH PRIVAT NØGLE-----",
|
||||||
"sshPrivateKeyRequired": "Privat nøgle er påkrævet",
|
"sshPrivateKeyRequired": "Privat nøgle er påkrævet",
|
||||||
"vncTitle": "VNC",
|
"vncTitle": "VNC",
|
||||||
"vncSignInDescription": "Enter your VNC credentials to connect",
|
"vncSignInDescription": "Indtast VNC-adgangskoden for at oprette forbindelse til",
|
||||||
"vncUsernameOptional": "Username (optional)",
|
|
||||||
"vncPasswordOptional": "Adgangskode (valgfrit)",
|
"vncPasswordOptional": "Adgangskode (valgfrit)",
|
||||||
"vncNoResourceTarget": "Intet ressourcemål tilgængeligt",
|
"vncNoResourceTarget": "Intet ressourcemål tilgængeligt",
|
||||||
"vncFailedToLoadNovnc": "Kunne ikke indlæse noVNC",
|
"vncFailedToLoadNovnc": "Kunne ikke indlæse noVNC",
|
||||||
|
|||||||
@@ -123,16 +123,6 @@
|
|||||||
"siteUpdated": "Standort aktualisiert",
|
"siteUpdated": "Standort aktualisiert",
|
||||||
"siteUpdatedDescription": "Der Standort wurde aktualisiert.",
|
"siteUpdatedDescription": "Der Standort wurde aktualisiert.",
|
||||||
"siteGeneralDescription": "Allgemeine Einstellungen für diesen Standort konfigurieren",
|
"siteGeneralDescription": "Allgemeine Einstellungen für diesen Standort konfigurieren",
|
||||||
"siteRestartTitle": "Restart Site",
|
|
||||||
"siteRestartDescription": "Restart the WireGuard tunnel for this site. This will briefly interrupt connectivity.",
|
|
||||||
"siteRestartBody": "Use this if the site tunnel is not functioning correctly and you want to force a reconnect without restarting the host.",
|
|
||||||
"siteRestartButton": "Restart Site",
|
|
||||||
"siteRestartDialogMessage": "Are you sure you want to restart the WireGuard tunnel for <b>{name}</b>? The site will briefly lose connectivity.",
|
|
||||||
"siteRestartWarning": "The site will briefly disconnect while the tunnel restarts.",
|
|
||||||
"siteRestarted": "Site restarted",
|
|
||||||
"siteRestartedDescription": "The WireGuard tunnel has been restarted.",
|
|
||||||
"siteErrorRestart": "Failed to restart site",
|
|
||||||
"siteErrorRestartDescription": "An error occurred while restarting the site.",
|
|
||||||
"siteSettingDescription": "Standorteinstellungen konfigurieren",
|
"siteSettingDescription": "Standorteinstellungen konfigurieren",
|
||||||
"siteResourcesTab": "Ressourcen",
|
"siteResourcesTab": "Ressourcen",
|
||||||
"siteResourcesNoneOnSite": "Dieser Standort hat noch keine öffentlichen oder privaten Ressourcen",
|
"siteResourcesNoneOnSite": "Dieser Standort hat noch keine öffentlichen oder privaten Ressourcen",
|
||||||
@@ -2388,21 +2378,6 @@
|
|||||||
"sidebarRemoteExitNodes": "Entfernte Knoten",
|
"sidebarRemoteExitNodes": "Entfernte Knoten",
|
||||||
"remoteExitNodeId": "ID",
|
"remoteExitNodeId": "ID",
|
||||||
"remoteExitNodeSecretKey": "Geheimnis",
|
"remoteExitNodeSecretKey": "Geheimnis",
|
||||||
"remoteExitNodeNetworkingTitle": "Network Settings",
|
|
||||||
"remoteExitNodeNetworkingDescription": "Configure how this remote exit node routes traffic and which sites prefer to connect through it. Advanced features to be used with backhaul networking configurations.",
|
|
||||||
"remoteExitNodeNetworkingSave": "Save Settings",
|
|
||||||
"remoteExitNodeNetworkingSaveSuccessTitle": "Network settings saved",
|
|
||||||
"remoteExitNodeNetworkingSaveSuccessDescription": "Network settings have been updated successfully.",
|
|
||||||
"remoteExitNodeNetworkingSaveError": "Failed to save network settings",
|
|
||||||
"remoteExitNodeNetworkingSubnetsTitle": "Remote Subnets",
|
|
||||||
"remoteExitNodeNetworkingSubnetsDescription": "Define the CIDR ranges that this remote exit node will route traffic to. Type a valid CIDR (e.g. <code>10.0.0.0/8</code>) and press Enter to add.",
|
|
||||||
"remoteExitNodeNetworkingSubnetsPlaceholder": "Add a CIDR range (e.g. 10.0.0.0/8)",
|
|
||||||
"remoteExitNodeNetworkingSubnetsLoadError": "Failed to load subnets",
|
|
||||||
"remoteExitNodeNetworkingLabelsTitle": "Preference Labels",
|
|
||||||
"remoteExitNodeNetworkingLabelsDescription": "Sites with these labels will be enforced to connect through this remote exit node.",
|
|
||||||
"remoteExitNodeNetworkingLabelsButtonText": "Select labels...",
|
|
||||||
"remoteExitNodeNetworkingLabelsSearchPlaceholder": "Search labels...",
|
|
||||||
"remoteExitNodeNetworkingLabelsLoadError": "Failed to load labels",
|
|
||||||
"remoteExitNodeCreate": {
|
"remoteExitNodeCreate": {
|
||||||
"title": "Erstelle Remote Node",
|
"title": "Erstelle Remote Node",
|
||||||
"description": "Erstelle einen neues selbst gehostetes Relay und ihre Proxyserver Nodes",
|
"description": "Erstelle einen neues selbst gehostetes Relay und ihre Proxyserver Nodes",
|
||||||
@@ -2581,7 +2556,6 @@
|
|||||||
"idpGoogleDescription": "Google OAuth2/OIDC Provider",
|
"idpGoogleDescription": "Google OAuth2/OIDC Provider",
|
||||||
"idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider",
|
"idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider",
|
||||||
"subnet": "Subnetz",
|
"subnet": "Subnetz",
|
||||||
"utilitySubnet": "Utility Subnet",
|
|
||||||
"subnetDescription": "Das Subnetz für die Netzwerkkonfiguration dieser Organisation.",
|
"subnetDescription": "Das Subnetz für die Netzwerkkonfiguration dieser Organisation.",
|
||||||
"customDomain": "Eigene Domain",
|
"customDomain": "Eigene Domain",
|
||||||
"authPage": "Authentifizierungs-Seiten",
|
"authPage": "Authentifizierungs-Seiten",
|
||||||
@@ -3602,8 +3576,7 @@
|
|||||||
"sshPrivateKeyPlaceholder": "-----BEGIN OPENSSH PRIVATE KEY-----",
|
"sshPrivateKeyPlaceholder": "-----BEGIN OPENSSH PRIVATE KEY-----",
|
||||||
"sshPrivateKeyRequired": "Privater Schlüssel ist erforderlich",
|
"sshPrivateKeyRequired": "Privater Schlüssel ist erforderlich",
|
||||||
"vncTitle": "VNC",
|
"vncTitle": "VNC",
|
||||||
"vncSignInDescription": "Enter your VNC credentials to connect",
|
"vncSignInDescription": "Geben Sie Ihr VNC-Passwort ein, um sich zu verbinden",
|
||||||
"vncUsernameOptional": "Username (optional)",
|
|
||||||
"vncPasswordOptional": "Passwort (optional)",
|
"vncPasswordOptional": "Passwort (optional)",
|
||||||
"vncNoResourceTarget": "Kein Ressourcen-Ziel verfügbar",
|
"vncNoResourceTarget": "Kein Ressourcen-Ziel verfügbar",
|
||||||
"vncFailedToLoadNovnc": "Fehler beim Laden von noVNC",
|
"vncFailedToLoadNovnc": "Fehler beim Laden von noVNC",
|
||||||
|
|||||||
@@ -123,16 +123,6 @@
|
|||||||
"siteUpdated": "Sitio actualizado",
|
"siteUpdated": "Sitio actualizado",
|
||||||
"siteUpdatedDescription": "El sitio ha sido actualizado.",
|
"siteUpdatedDescription": "El sitio ha sido actualizado.",
|
||||||
"siteGeneralDescription": "Configurar la configuración general de este sitio",
|
"siteGeneralDescription": "Configurar la configuración general de este sitio",
|
||||||
"siteRestartTitle": "Restart Site",
|
|
||||||
"siteRestartDescription": "Restart the WireGuard tunnel for this site. This will briefly interrupt connectivity.",
|
|
||||||
"siteRestartBody": "Use this if the site tunnel is not functioning correctly and you want to force a reconnect without restarting the host.",
|
|
||||||
"siteRestartButton": "Restart Site",
|
|
||||||
"siteRestartDialogMessage": "Are you sure you want to restart the WireGuard tunnel for <b>{name}</b>? The site will briefly lose connectivity.",
|
|
||||||
"siteRestartWarning": "The site will briefly disconnect while the tunnel restarts.",
|
|
||||||
"siteRestarted": "Site restarted",
|
|
||||||
"siteRestartedDescription": "The WireGuard tunnel has been restarted.",
|
|
||||||
"siteErrorRestart": "Failed to restart site",
|
|
||||||
"siteErrorRestartDescription": "An error occurred while restarting the site.",
|
|
||||||
"siteSettingDescription": "Configurar los ajustes en el sitio",
|
"siteSettingDescription": "Configurar los ajustes en el sitio",
|
||||||
"siteResourcesTab": "Recursos",
|
"siteResourcesTab": "Recursos",
|
||||||
"siteResourcesNoneOnSite": "Este sitio aún no tiene recursos públicos o privados.",
|
"siteResourcesNoneOnSite": "Este sitio aún no tiene recursos públicos o privados.",
|
||||||
@@ -2388,21 +2378,6 @@
|
|||||||
"sidebarRemoteExitNodes": "Nodos remotos",
|
"sidebarRemoteExitNodes": "Nodos remotos",
|
||||||
"remoteExitNodeId": "ID",
|
"remoteExitNodeId": "ID",
|
||||||
"remoteExitNodeSecretKey": "Secreto",
|
"remoteExitNodeSecretKey": "Secreto",
|
||||||
"remoteExitNodeNetworkingTitle": "Network Settings",
|
|
||||||
"remoteExitNodeNetworkingDescription": "Configure how this remote exit node routes traffic and which sites prefer to connect through it. Advanced features to be used with backhaul networking configurations.",
|
|
||||||
"remoteExitNodeNetworkingSave": "Save Settings",
|
|
||||||
"remoteExitNodeNetworkingSaveSuccessTitle": "Network settings saved",
|
|
||||||
"remoteExitNodeNetworkingSaveSuccessDescription": "Network settings have been updated successfully.",
|
|
||||||
"remoteExitNodeNetworkingSaveError": "Failed to save network settings",
|
|
||||||
"remoteExitNodeNetworkingSubnetsTitle": "Remote Subnets",
|
|
||||||
"remoteExitNodeNetworkingSubnetsDescription": "Define the CIDR ranges that this remote exit node will route traffic to. Type a valid CIDR (e.g. <code>10.0.0.0/8</code>) and press Enter to add.",
|
|
||||||
"remoteExitNodeNetworkingSubnetsPlaceholder": "Add a CIDR range (e.g. 10.0.0.0/8)",
|
|
||||||
"remoteExitNodeNetworkingSubnetsLoadError": "Failed to load subnets",
|
|
||||||
"remoteExitNodeNetworkingLabelsTitle": "Preference Labels",
|
|
||||||
"remoteExitNodeNetworkingLabelsDescription": "Sites with these labels will be enforced to connect through this remote exit node.",
|
|
||||||
"remoteExitNodeNetworkingLabelsButtonText": "Select labels...",
|
|
||||||
"remoteExitNodeNetworkingLabelsSearchPlaceholder": "Search labels...",
|
|
||||||
"remoteExitNodeNetworkingLabelsLoadError": "Failed to load labels",
|
|
||||||
"remoteExitNodeCreate": {
|
"remoteExitNodeCreate": {
|
||||||
"title": "Crear nodo remoto",
|
"title": "Crear nodo remoto",
|
||||||
"description": "Crea un nuevo nodo de retransmisión y proxy server autogestionado",
|
"description": "Crea un nuevo nodo de retransmisión y proxy server autogestionado",
|
||||||
@@ -2581,7 +2556,6 @@
|
|||||||
"idpGoogleDescription": "Proveedor OAuth2/OIDC de Google",
|
"idpGoogleDescription": "Proveedor OAuth2/OIDC de Google",
|
||||||
"idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider",
|
"idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider",
|
||||||
"subnet": "Subred",
|
"subnet": "Subred",
|
||||||
"utilitySubnet": "Utility Subnet",
|
|
||||||
"subnetDescription": "La subred para la configuración de red de esta organización.",
|
"subnetDescription": "La subred para la configuración de red de esta organización.",
|
||||||
"customDomain": "Dominio personalizado",
|
"customDomain": "Dominio personalizado",
|
||||||
"authPage": "Páginas de autenticación",
|
"authPage": "Páginas de autenticación",
|
||||||
@@ -3602,8 +3576,7 @@
|
|||||||
"sshPrivateKeyPlaceholder": "-----COMIENZO DE LA CLAVE PRIVADA OPENSSH-----",
|
"sshPrivateKeyPlaceholder": "-----COMIENZO DE LA CLAVE PRIVADA OPENSSH-----",
|
||||||
"sshPrivateKeyRequired": "Se requiere clave privada",
|
"sshPrivateKeyRequired": "Se requiere clave privada",
|
||||||
"vncTitle": "VNC",
|
"vncTitle": "VNC",
|
||||||
"vncSignInDescription": "Enter your VNC credentials to connect",
|
"vncSignInDescription": "Introduce tu contraseña VNC para conectar",
|
||||||
"vncUsernameOptional": "Username (optional)",
|
|
||||||
"vncPasswordOptional": "Contraseña (opcional)",
|
"vncPasswordOptional": "Contraseña (opcional)",
|
||||||
"vncNoResourceTarget": "No hay objetivo de recurso disponible",
|
"vncNoResourceTarget": "No hay objetivo de recurso disponible",
|
||||||
"vncFailedToLoadNovnc": "Error al cargar noVNC",
|
"vncFailedToLoadNovnc": "Error al cargar noVNC",
|
||||||
|
|||||||
@@ -123,16 +123,6 @@
|
|||||||
"siteUpdated": "Nœud mis à jour",
|
"siteUpdated": "Nœud mis à jour",
|
||||||
"siteUpdatedDescription": "Le nœud a été mis à jour.",
|
"siteUpdatedDescription": "Le nœud a été mis à jour.",
|
||||||
"siteGeneralDescription": "Configurer les paramètres par défaut de ce nœud",
|
"siteGeneralDescription": "Configurer les paramètres par défaut de ce nœud",
|
||||||
"siteRestartTitle": "Restart Site",
|
|
||||||
"siteRestartDescription": "Restart the WireGuard tunnel for this site. This will briefly interrupt connectivity.",
|
|
||||||
"siteRestartBody": "Use this if the site tunnel is not functioning correctly and you want to force a reconnect without restarting the host.",
|
|
||||||
"siteRestartButton": "Restart Site",
|
|
||||||
"siteRestartDialogMessage": "Are you sure you want to restart the WireGuard tunnel for <b>{name}</b>? The site will briefly lose connectivity.",
|
|
||||||
"siteRestartWarning": "The site will briefly disconnect while the tunnel restarts.",
|
|
||||||
"siteRestarted": "Site restarted",
|
|
||||||
"siteRestartedDescription": "The WireGuard tunnel has been restarted.",
|
|
||||||
"siteErrorRestart": "Failed to restart site",
|
|
||||||
"siteErrorRestartDescription": "An error occurred while restarting the site.",
|
|
||||||
"siteSettingDescription": "Configurer les paramètres du site",
|
"siteSettingDescription": "Configurer les paramètres du site",
|
||||||
"siteResourcesTab": "Ressources",
|
"siteResourcesTab": "Ressources",
|
||||||
"siteResourcesNoneOnSite": "Ce site n'a pas encore de ressources publiques ou privées.",
|
"siteResourcesNoneOnSite": "Ce site n'a pas encore de ressources publiques ou privées.",
|
||||||
@@ -2388,21 +2378,6 @@
|
|||||||
"sidebarRemoteExitNodes": "Nœuds distants",
|
"sidebarRemoteExitNodes": "Nœuds distants",
|
||||||
"remoteExitNodeId": "ID",
|
"remoteExitNodeId": "ID",
|
||||||
"remoteExitNodeSecretKey": "Clé secrète",
|
"remoteExitNodeSecretKey": "Clé secrète",
|
||||||
"remoteExitNodeNetworkingTitle": "Network Settings",
|
|
||||||
"remoteExitNodeNetworkingDescription": "Configure how this remote exit node routes traffic and which sites prefer to connect through it. Advanced features to be used with backhaul networking configurations.",
|
|
||||||
"remoteExitNodeNetworkingSave": "Save Settings",
|
|
||||||
"remoteExitNodeNetworkingSaveSuccessTitle": "Network settings saved",
|
|
||||||
"remoteExitNodeNetworkingSaveSuccessDescription": "Network settings have been updated successfully.",
|
|
||||||
"remoteExitNodeNetworkingSaveError": "Failed to save network settings",
|
|
||||||
"remoteExitNodeNetworkingSubnetsTitle": "Remote Subnets",
|
|
||||||
"remoteExitNodeNetworkingSubnetsDescription": "Define the CIDR ranges that this remote exit node will route traffic to. Type a valid CIDR (e.g. <code>10.0.0.0/8</code>) and press Enter to add.",
|
|
||||||
"remoteExitNodeNetworkingSubnetsPlaceholder": "Add a CIDR range (e.g. 10.0.0.0/8)",
|
|
||||||
"remoteExitNodeNetworkingSubnetsLoadError": "Failed to load subnets",
|
|
||||||
"remoteExitNodeNetworkingLabelsTitle": "Preference Labels",
|
|
||||||
"remoteExitNodeNetworkingLabelsDescription": "Sites with these labels will be enforced to connect through this remote exit node.",
|
|
||||||
"remoteExitNodeNetworkingLabelsButtonText": "Select labels...",
|
|
||||||
"remoteExitNodeNetworkingLabelsSearchPlaceholder": "Search labels...",
|
|
||||||
"remoteExitNodeNetworkingLabelsLoadError": "Failed to load labels",
|
|
||||||
"remoteExitNodeCreate": {
|
"remoteExitNodeCreate": {
|
||||||
"title": "Créer un nœud distant",
|
"title": "Créer un nœud distant",
|
||||||
"description": "Créez un nouveau nœud de relais et de serveur proxy distant auto-hébergé",
|
"description": "Créez un nouveau nœud de relais et de serveur proxy distant auto-hébergé",
|
||||||
@@ -2581,7 +2556,6 @@
|
|||||||
"idpGoogleDescription": "Fournisseur Google OAuth2/OIDC",
|
"idpGoogleDescription": "Fournisseur Google OAuth2/OIDC",
|
||||||
"idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider",
|
"idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider",
|
||||||
"subnet": "Sous-réseau",
|
"subnet": "Sous-réseau",
|
||||||
"utilitySubnet": "Utility Subnet",
|
|
||||||
"subnetDescription": "Le sous-réseau de la configuration réseau de cette organisation.",
|
"subnetDescription": "Le sous-réseau de la configuration réseau de cette organisation.",
|
||||||
"customDomain": "Domaine personnalisé",
|
"customDomain": "Domaine personnalisé",
|
||||||
"authPage": "Pages d'authentification",
|
"authPage": "Pages d'authentification",
|
||||||
@@ -3602,8 +3576,7 @@
|
|||||||
"sshPrivateKeyPlaceholder": "-----BEGIN OPENSSH PRIVATE KEY-----",
|
"sshPrivateKeyPlaceholder": "-----BEGIN OPENSSH PRIVATE KEY-----",
|
||||||
"sshPrivateKeyRequired": "Une clé privée est requise",
|
"sshPrivateKeyRequired": "Une clé privée est requise",
|
||||||
"vncTitle": "VNC",
|
"vncTitle": "VNC",
|
||||||
"vncSignInDescription": "Enter your VNC credentials to connect",
|
"vncSignInDescription": "Entrez votre mot de passe VNC pour vous connecter",
|
||||||
"vncUsernameOptional": "Username (optional)",
|
|
||||||
"vncPasswordOptional": "Mot de passe (facultatif)",
|
"vncPasswordOptional": "Mot de passe (facultatif)",
|
||||||
"vncNoResourceTarget": "Aucune cible de ressource disponible",
|
"vncNoResourceTarget": "Aucune cible de ressource disponible",
|
||||||
"vncFailedToLoadNovnc": "Échec du chargement de noVNC",
|
"vncFailedToLoadNovnc": "Échec du chargement de noVNC",
|
||||||
|
|||||||
@@ -123,16 +123,6 @@
|
|||||||
"siteUpdated": "Sito aggiornato",
|
"siteUpdated": "Sito aggiornato",
|
||||||
"siteUpdatedDescription": "Il sito è stato aggiornato.",
|
"siteUpdatedDescription": "Il sito è stato aggiornato.",
|
||||||
"siteGeneralDescription": "Configura le impostazioni generali per questo sito",
|
"siteGeneralDescription": "Configura le impostazioni generali per questo sito",
|
||||||
"siteRestartTitle": "Restart Site",
|
|
||||||
"siteRestartDescription": "Restart the WireGuard tunnel for this site. This will briefly interrupt connectivity.",
|
|
||||||
"siteRestartBody": "Use this if the site tunnel is not functioning correctly and you want to force a reconnect without restarting the host.",
|
|
||||||
"siteRestartButton": "Restart Site",
|
|
||||||
"siteRestartDialogMessage": "Are you sure you want to restart the WireGuard tunnel for <b>{name}</b>? The site will briefly lose connectivity.",
|
|
||||||
"siteRestartWarning": "The site will briefly disconnect while the tunnel restarts.",
|
|
||||||
"siteRestarted": "Site restarted",
|
|
||||||
"siteRestartedDescription": "The WireGuard tunnel has been restarted.",
|
|
||||||
"siteErrorRestart": "Failed to restart site",
|
|
||||||
"siteErrorRestartDescription": "An error occurred while restarting the site.",
|
|
||||||
"siteSettingDescription": "Configura le impostazioni del sito",
|
"siteSettingDescription": "Configura le impostazioni del sito",
|
||||||
"siteResourcesTab": "Risorse",
|
"siteResourcesTab": "Risorse",
|
||||||
"siteResourcesNoneOnSite": "Questo sito non ha ancora risorse pubbliche o private.",
|
"siteResourcesNoneOnSite": "Questo sito non ha ancora risorse pubbliche o private.",
|
||||||
@@ -2388,21 +2378,6 @@
|
|||||||
"sidebarRemoteExitNodes": "Nodi Remoti",
|
"sidebarRemoteExitNodes": "Nodi Remoti",
|
||||||
"remoteExitNodeId": "ID",
|
"remoteExitNodeId": "ID",
|
||||||
"remoteExitNodeSecretKey": "Segreto",
|
"remoteExitNodeSecretKey": "Segreto",
|
||||||
"remoteExitNodeNetworkingTitle": "Network Settings",
|
|
||||||
"remoteExitNodeNetworkingDescription": "Configure how this remote exit node routes traffic and which sites prefer to connect through it. Advanced features to be used with backhaul networking configurations.",
|
|
||||||
"remoteExitNodeNetworkingSave": "Save Settings",
|
|
||||||
"remoteExitNodeNetworkingSaveSuccessTitle": "Network settings saved",
|
|
||||||
"remoteExitNodeNetworkingSaveSuccessDescription": "Network settings have been updated successfully.",
|
|
||||||
"remoteExitNodeNetworkingSaveError": "Failed to save network settings",
|
|
||||||
"remoteExitNodeNetworkingSubnetsTitle": "Remote Subnets",
|
|
||||||
"remoteExitNodeNetworkingSubnetsDescription": "Define the CIDR ranges that this remote exit node will route traffic to. Type a valid CIDR (e.g. <code>10.0.0.0/8</code>) and press Enter to add.",
|
|
||||||
"remoteExitNodeNetworkingSubnetsPlaceholder": "Add a CIDR range (e.g. 10.0.0.0/8)",
|
|
||||||
"remoteExitNodeNetworkingSubnetsLoadError": "Failed to load subnets",
|
|
||||||
"remoteExitNodeNetworkingLabelsTitle": "Preference Labels",
|
|
||||||
"remoteExitNodeNetworkingLabelsDescription": "Sites with these labels will be enforced to connect through this remote exit node.",
|
|
||||||
"remoteExitNodeNetworkingLabelsButtonText": "Select labels...",
|
|
||||||
"remoteExitNodeNetworkingLabelsSearchPlaceholder": "Search labels...",
|
|
||||||
"remoteExitNodeNetworkingLabelsLoadError": "Failed to load labels",
|
|
||||||
"remoteExitNodeCreate": {
|
"remoteExitNodeCreate": {
|
||||||
"title": "Crea Nodo Remoto",
|
"title": "Crea Nodo Remoto",
|
||||||
"description": "Crea un nuovo nodo server proxy e relay remoto ospitato in proprio",
|
"description": "Crea un nuovo nodo server proxy e relay remoto ospitato in proprio",
|
||||||
@@ -2581,7 +2556,6 @@
|
|||||||
"idpGoogleDescription": "Google OAuth2/OIDC provider",
|
"idpGoogleDescription": "Google OAuth2/OIDC provider",
|
||||||
"idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider",
|
"idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider",
|
||||||
"subnet": "Sottorete",
|
"subnet": "Sottorete",
|
||||||
"utilitySubnet": "Utility Subnet",
|
|
||||||
"subnetDescription": "La sottorete per la configurazione di rete di questa organizzazione.",
|
"subnetDescription": "La sottorete per la configurazione di rete di questa organizzazione.",
|
||||||
"customDomain": "Dominio Personalizzato",
|
"customDomain": "Dominio Personalizzato",
|
||||||
"authPage": "Pagine di Autenticazione",
|
"authPage": "Pagine di Autenticazione",
|
||||||
@@ -3602,8 +3576,7 @@
|
|||||||
"sshPrivateKeyPlaceholder": "-----BEGIN OPENSSH PRIVATE KEY-----",
|
"sshPrivateKeyPlaceholder": "-----BEGIN OPENSSH PRIVATE KEY-----",
|
||||||
"sshPrivateKeyRequired": "È richiesta una chiave privata",
|
"sshPrivateKeyRequired": "È richiesta una chiave privata",
|
||||||
"vncTitle": "VNC",
|
"vncTitle": "VNC",
|
||||||
"vncSignInDescription": "Enter your VNC credentials to connect",
|
"vncSignInDescription": "Inserisci la tua password VNC per connetterti",
|
||||||
"vncUsernameOptional": "Username (optional)",
|
|
||||||
"vncPasswordOptional": "Password (opzionale)",
|
"vncPasswordOptional": "Password (opzionale)",
|
||||||
"vncNoResourceTarget": "Nessun bersaglio di risorsa disponibile",
|
"vncNoResourceTarget": "Nessun bersaglio di risorsa disponibile",
|
||||||
"vncFailedToLoadNovnc": "Impossibile caricare noVNC",
|
"vncFailedToLoadNovnc": "Impossibile caricare noVNC",
|
||||||
|
|||||||
@@ -123,16 +123,6 @@
|
|||||||
"siteUpdated": "사이트가 업데이트되었습니다",
|
"siteUpdated": "사이트가 업데이트되었습니다",
|
||||||
"siteUpdatedDescription": "사이트가 업데이트되었습니다.",
|
"siteUpdatedDescription": "사이트가 업데이트되었습니다.",
|
||||||
"siteGeneralDescription": "이 사이트에 대한 일반 설정을 구성하세요.",
|
"siteGeneralDescription": "이 사이트에 대한 일반 설정을 구성하세요.",
|
||||||
"siteRestartTitle": "Restart Site",
|
|
||||||
"siteRestartDescription": "Restart the WireGuard tunnel for this site. This will briefly interrupt connectivity.",
|
|
||||||
"siteRestartBody": "Use this if the site tunnel is not functioning correctly and you want to force a reconnect without restarting the host.",
|
|
||||||
"siteRestartButton": "Restart Site",
|
|
||||||
"siteRestartDialogMessage": "Are you sure you want to restart the WireGuard tunnel for <b>{name}</b>? The site will briefly lose connectivity.",
|
|
||||||
"siteRestartWarning": "The site will briefly disconnect while the tunnel restarts.",
|
|
||||||
"siteRestarted": "Site restarted",
|
|
||||||
"siteRestartedDescription": "The WireGuard tunnel has been restarted.",
|
|
||||||
"siteErrorRestart": "Failed to restart site",
|
|
||||||
"siteErrorRestartDescription": "An error occurred while restarting the site.",
|
|
||||||
"siteSettingDescription": "사이트에서 설정을 구성하세요.",
|
"siteSettingDescription": "사이트에서 설정을 구성하세요.",
|
||||||
"siteResourcesTab": "리소스",
|
"siteResourcesTab": "리소스",
|
||||||
"siteResourcesNoneOnSite": "이 사이트에는 아직 공용 또는 개인 리소스가 없습니다.",
|
"siteResourcesNoneOnSite": "이 사이트에는 아직 공용 또는 개인 리소스가 없습니다.",
|
||||||
@@ -2388,21 +2378,6 @@
|
|||||||
"sidebarRemoteExitNodes": "원격 노드",
|
"sidebarRemoteExitNodes": "원격 노드",
|
||||||
"remoteExitNodeId": "ID",
|
"remoteExitNodeId": "ID",
|
||||||
"remoteExitNodeSecretKey": "비밀",
|
"remoteExitNodeSecretKey": "비밀",
|
||||||
"remoteExitNodeNetworkingTitle": "Network Settings",
|
|
||||||
"remoteExitNodeNetworkingDescription": "Configure how this remote exit node routes traffic and which sites prefer to connect through it. Advanced features to be used with backhaul networking configurations.",
|
|
||||||
"remoteExitNodeNetworkingSave": "Save Settings",
|
|
||||||
"remoteExitNodeNetworkingSaveSuccessTitle": "Network settings saved",
|
|
||||||
"remoteExitNodeNetworkingSaveSuccessDescription": "Network settings have been updated successfully.",
|
|
||||||
"remoteExitNodeNetworkingSaveError": "Failed to save network settings",
|
|
||||||
"remoteExitNodeNetworkingSubnetsTitle": "Remote Subnets",
|
|
||||||
"remoteExitNodeNetworkingSubnetsDescription": "Define the CIDR ranges that this remote exit node will route traffic to. Type a valid CIDR (e.g. <code>10.0.0.0/8</code>) and press Enter to add.",
|
|
||||||
"remoteExitNodeNetworkingSubnetsPlaceholder": "Add a CIDR range (e.g. 10.0.0.0/8)",
|
|
||||||
"remoteExitNodeNetworkingSubnetsLoadError": "Failed to load subnets",
|
|
||||||
"remoteExitNodeNetworkingLabelsTitle": "Preference Labels",
|
|
||||||
"remoteExitNodeNetworkingLabelsDescription": "Sites with these labels will be enforced to connect through this remote exit node.",
|
|
||||||
"remoteExitNodeNetworkingLabelsButtonText": "Select labels...",
|
|
||||||
"remoteExitNodeNetworkingLabelsSearchPlaceholder": "Search labels...",
|
|
||||||
"remoteExitNodeNetworkingLabelsLoadError": "Failed to load labels",
|
|
||||||
"remoteExitNodeCreate": {
|
"remoteExitNodeCreate": {
|
||||||
"title": "원격 노드 생성",
|
"title": "원격 노드 생성",
|
||||||
"description": "새로운 자체 호스팅 원격 중계 및 프록시 서버 노드를 생성하십시오.",
|
"description": "새로운 자체 호스팅 원격 중계 및 프록시 서버 노드를 생성하십시오.",
|
||||||
@@ -2581,7 +2556,6 @@
|
|||||||
"idpGoogleDescription": "Google OAuth2/OIDC 공급자",
|
"idpGoogleDescription": "Google OAuth2/OIDC 공급자",
|
||||||
"idpAzureDescription": "Microsoft Azure OAuth2/OIDC 공급자",
|
"idpAzureDescription": "Microsoft Azure OAuth2/OIDC 공급자",
|
||||||
"subnet": "서브넷",
|
"subnet": "서브넷",
|
||||||
"utilitySubnet": "Utility Subnet",
|
|
||||||
"subnetDescription": "이 조직의 네트워크 구성에 대한 서브넷입니다.",
|
"subnetDescription": "이 조직의 네트워크 구성에 대한 서브넷입니다.",
|
||||||
"customDomain": "사용자 정의 도메인",
|
"customDomain": "사용자 정의 도메인",
|
||||||
"authPage": "인증 페이지",
|
"authPage": "인증 페이지",
|
||||||
@@ -3602,8 +3576,7 @@
|
|||||||
"sshPrivateKeyPlaceholder": "-----BEGIN OPENSSH PRIVATE KEY-----",
|
"sshPrivateKeyPlaceholder": "-----BEGIN OPENSSH PRIVATE KEY-----",
|
||||||
"sshPrivateKeyRequired": "프라이빗 키가 필요합니다",
|
"sshPrivateKeyRequired": "프라이빗 키가 필요합니다",
|
||||||
"vncTitle": "VNC",
|
"vncTitle": "VNC",
|
||||||
"vncSignInDescription": "Enter your VNC credentials to connect",
|
"vncSignInDescription": "연결하려면 VNC 비밀번호를 입력하세요",
|
||||||
"vncUsernameOptional": "Username (optional)",
|
|
||||||
"vncPasswordOptional": "비밀번호 (선택 사항)",
|
"vncPasswordOptional": "비밀번호 (선택 사항)",
|
||||||
"vncNoResourceTarget": "사용할 수 있는 리소스 대상이 없습니다",
|
"vncNoResourceTarget": "사용할 수 있는 리소스 대상이 없습니다",
|
||||||
"vncFailedToLoadNovnc": "noVNC 로드를 실패했습니다",
|
"vncFailedToLoadNovnc": "noVNC 로드를 실패했습니다",
|
||||||
|
|||||||
@@ -123,16 +123,6 @@
|
|||||||
"siteUpdated": "Område oppdatert",
|
"siteUpdated": "Område oppdatert",
|
||||||
"siteUpdatedDescription": "Området har blitt oppdatert.",
|
"siteUpdatedDescription": "Området har blitt oppdatert.",
|
||||||
"siteGeneralDescription": "Konfigurer de generelle innstillingene for dette området",
|
"siteGeneralDescription": "Konfigurer de generelle innstillingene for dette området",
|
||||||
"siteRestartTitle": "Restart Site",
|
|
||||||
"siteRestartDescription": "Restart the WireGuard tunnel for this site. This will briefly interrupt connectivity.",
|
|
||||||
"siteRestartBody": "Use this if the site tunnel is not functioning correctly and you want to force a reconnect without restarting the host.",
|
|
||||||
"siteRestartButton": "Restart Site",
|
|
||||||
"siteRestartDialogMessage": "Are you sure you want to restart the WireGuard tunnel for <b>{name}</b>? The site will briefly lose connectivity.",
|
|
||||||
"siteRestartWarning": "The site will briefly disconnect while the tunnel restarts.",
|
|
||||||
"siteRestarted": "Site restarted",
|
|
||||||
"siteRestartedDescription": "The WireGuard tunnel has been restarted.",
|
|
||||||
"siteErrorRestart": "Failed to restart site",
|
|
||||||
"siteErrorRestartDescription": "An error occurred while restarting the site.",
|
|
||||||
"siteSettingDescription": "Konfigurere innstillingene på nettstedet",
|
"siteSettingDescription": "Konfigurere innstillingene på nettstedet",
|
||||||
"siteResourcesTab": "Ressurser",
|
"siteResourcesTab": "Ressurser",
|
||||||
"siteResourcesNoneOnSite": "Dette nettstedet har ingen offentlige eller private ressurser enda.",
|
"siteResourcesNoneOnSite": "Dette nettstedet har ingen offentlige eller private ressurser enda.",
|
||||||
@@ -2388,21 +2378,6 @@
|
|||||||
"sidebarRemoteExitNodes": "Eksterne Noder",
|
"sidebarRemoteExitNodes": "Eksterne Noder",
|
||||||
"remoteExitNodeId": "ID",
|
"remoteExitNodeId": "ID",
|
||||||
"remoteExitNodeSecretKey": "Sikkerhetsnøkkel",
|
"remoteExitNodeSecretKey": "Sikkerhetsnøkkel",
|
||||||
"remoteExitNodeNetworkingTitle": "Network Settings",
|
|
||||||
"remoteExitNodeNetworkingDescription": "Configure how this remote exit node routes traffic and which sites prefer to connect through it. Advanced features to be used with backhaul networking configurations.",
|
|
||||||
"remoteExitNodeNetworkingSave": "Save Settings",
|
|
||||||
"remoteExitNodeNetworkingSaveSuccessTitle": "Network settings saved",
|
|
||||||
"remoteExitNodeNetworkingSaveSuccessDescription": "Network settings have been updated successfully.",
|
|
||||||
"remoteExitNodeNetworkingSaveError": "Failed to save network settings",
|
|
||||||
"remoteExitNodeNetworkingSubnetsTitle": "Remote Subnets",
|
|
||||||
"remoteExitNodeNetworkingSubnetsDescription": "Define the CIDR ranges that this remote exit node will route traffic to. Type a valid CIDR (e.g. <code>10.0.0.0/8</code>) and press Enter to add.",
|
|
||||||
"remoteExitNodeNetworkingSubnetsPlaceholder": "Add a CIDR range (e.g. 10.0.0.0/8)",
|
|
||||||
"remoteExitNodeNetworkingSubnetsLoadError": "Failed to load subnets",
|
|
||||||
"remoteExitNodeNetworkingLabelsTitle": "Preference Labels",
|
|
||||||
"remoteExitNodeNetworkingLabelsDescription": "Sites with these labels will be enforced to connect through this remote exit node.",
|
|
||||||
"remoteExitNodeNetworkingLabelsButtonText": "Select labels...",
|
|
||||||
"remoteExitNodeNetworkingLabelsSearchPlaceholder": "Search labels...",
|
|
||||||
"remoteExitNodeNetworkingLabelsLoadError": "Failed to load labels",
|
|
||||||
"remoteExitNodeCreate": {
|
"remoteExitNodeCreate": {
|
||||||
"title": "Opprett ekstern node",
|
"title": "Opprett ekstern node",
|
||||||
"description": "Opprett en ny egendrift ekstern relé- og proxyservernode",
|
"description": "Opprett en ny egendrift ekstern relé- og proxyservernode",
|
||||||
@@ -2581,7 +2556,6 @@
|
|||||||
"idpGoogleDescription": "Google OAuth2/OIDC leverandør",
|
"idpGoogleDescription": "Google OAuth2/OIDC leverandør",
|
||||||
"idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider",
|
"idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider",
|
||||||
"subnet": "Subnett",
|
"subnet": "Subnett",
|
||||||
"utilitySubnet": "Utility Subnet",
|
|
||||||
"subnetDescription": "Undernettverket for denne organisasjonens nettverkskonfigurasjon.",
|
"subnetDescription": "Undernettverket for denne organisasjonens nettverkskonfigurasjon.",
|
||||||
"customDomain": "Egendefinert domene",
|
"customDomain": "Egendefinert domene",
|
||||||
"authPage": "Autentiseringssider",
|
"authPage": "Autentiseringssider",
|
||||||
@@ -3602,8 +3576,7 @@
|
|||||||
"sshPrivateKeyPlaceholder": "-----BEGYNN OPENSSH PRIVAT NØKKEL-----",
|
"sshPrivateKeyPlaceholder": "-----BEGYNN OPENSSH PRIVAT NØKKEL-----",
|
||||||
"sshPrivateKeyRequired": "Privat nøkkel er påkrevd",
|
"sshPrivateKeyRequired": "Privat nøkkel er påkrevd",
|
||||||
"vncTitle": "VNC",
|
"vncTitle": "VNC",
|
||||||
"vncSignInDescription": "Enter your VNC credentials to connect",
|
"vncSignInDescription": "Skriv inn VNC-passordet for å koble til",
|
||||||
"vncUsernameOptional": "Username (optional)",
|
|
||||||
"vncPasswordOptional": "Passord (valgfritt)",
|
"vncPasswordOptional": "Passord (valgfritt)",
|
||||||
"vncNoResourceTarget": "Ingen ressursemål tilgjengelig",
|
"vncNoResourceTarget": "Ingen ressursemål tilgjengelig",
|
||||||
"vncFailedToLoadNovnc": "Klarte ikke å laste noVNC",
|
"vncFailedToLoadNovnc": "Klarte ikke å laste noVNC",
|
||||||
|
|||||||
@@ -123,16 +123,6 @@
|
|||||||
"siteUpdated": "Site bijgewerkt",
|
"siteUpdated": "Site bijgewerkt",
|
||||||
"siteUpdatedDescription": "De site is bijgewerkt.",
|
"siteUpdatedDescription": "De site is bijgewerkt.",
|
||||||
"siteGeneralDescription": "Algemene instellingen voor deze site configureren",
|
"siteGeneralDescription": "Algemene instellingen voor deze site configureren",
|
||||||
"siteRestartTitle": "Restart Site",
|
|
||||||
"siteRestartDescription": "Restart the WireGuard tunnel for this site. This will briefly interrupt connectivity.",
|
|
||||||
"siteRestartBody": "Use this if the site tunnel is not functioning correctly and you want to force a reconnect without restarting the host.",
|
|
||||||
"siteRestartButton": "Restart Site",
|
|
||||||
"siteRestartDialogMessage": "Are you sure you want to restart the WireGuard tunnel for <b>{name}</b>? The site will briefly lose connectivity.",
|
|
||||||
"siteRestartWarning": "The site will briefly disconnect while the tunnel restarts.",
|
|
||||||
"siteRestarted": "Site restarted",
|
|
||||||
"siteRestartedDescription": "The WireGuard tunnel has been restarted.",
|
|
||||||
"siteErrorRestart": "Failed to restart site",
|
|
||||||
"siteErrorRestartDescription": "An error occurred while restarting the site.",
|
|
||||||
"siteSettingDescription": "Configureer de instellingen van de site",
|
"siteSettingDescription": "Configureer de instellingen van de site",
|
||||||
"siteResourcesTab": "Bronnen",
|
"siteResourcesTab": "Bronnen",
|
||||||
"siteResourcesNoneOnSite": "Deze site heeft nog geen openbare of privébronnen.",
|
"siteResourcesNoneOnSite": "Deze site heeft nog geen openbare of privébronnen.",
|
||||||
@@ -2388,21 +2378,6 @@
|
|||||||
"sidebarRemoteExitNodes": "Externe knooppunten",
|
"sidebarRemoteExitNodes": "Externe knooppunten",
|
||||||
"remoteExitNodeId": "ID",
|
"remoteExitNodeId": "ID",
|
||||||
"remoteExitNodeSecretKey": "Geheim",
|
"remoteExitNodeSecretKey": "Geheim",
|
||||||
"remoteExitNodeNetworkingTitle": "Network Settings",
|
|
||||||
"remoteExitNodeNetworkingDescription": "Configure how this remote exit node routes traffic and which sites prefer to connect through it. Advanced features to be used with backhaul networking configurations.",
|
|
||||||
"remoteExitNodeNetworkingSave": "Save Settings",
|
|
||||||
"remoteExitNodeNetworkingSaveSuccessTitle": "Network settings saved",
|
|
||||||
"remoteExitNodeNetworkingSaveSuccessDescription": "Network settings have been updated successfully.",
|
|
||||||
"remoteExitNodeNetworkingSaveError": "Failed to save network settings",
|
|
||||||
"remoteExitNodeNetworkingSubnetsTitle": "Remote Subnets",
|
|
||||||
"remoteExitNodeNetworkingSubnetsDescription": "Define the CIDR ranges that this remote exit node will route traffic to. Type a valid CIDR (e.g. <code>10.0.0.0/8</code>) and press Enter to add.",
|
|
||||||
"remoteExitNodeNetworkingSubnetsPlaceholder": "Add a CIDR range (e.g. 10.0.0.0/8)",
|
|
||||||
"remoteExitNodeNetworkingSubnetsLoadError": "Failed to load subnets",
|
|
||||||
"remoteExitNodeNetworkingLabelsTitle": "Preference Labels",
|
|
||||||
"remoteExitNodeNetworkingLabelsDescription": "Sites with these labels will be enforced to connect through this remote exit node.",
|
|
||||||
"remoteExitNodeNetworkingLabelsButtonText": "Select labels...",
|
|
||||||
"remoteExitNodeNetworkingLabelsSearchPlaceholder": "Search labels...",
|
|
||||||
"remoteExitNodeNetworkingLabelsLoadError": "Failed to load labels",
|
|
||||||
"remoteExitNodeCreate": {
|
"remoteExitNodeCreate": {
|
||||||
"title": "Externe knoop aanmaken",
|
"title": "Externe knoop aanmaken",
|
||||||
"description": "Maak een nieuwe zelf-gehoste externe relais- en proxyservermodule",
|
"description": "Maak een nieuwe zelf-gehoste externe relais- en proxyservermodule",
|
||||||
@@ -2581,7 +2556,6 @@
|
|||||||
"idpGoogleDescription": "Google OAuth2/OIDC provider",
|
"idpGoogleDescription": "Google OAuth2/OIDC provider",
|
||||||
"idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider",
|
"idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider",
|
||||||
"subnet": "Subnet",
|
"subnet": "Subnet",
|
||||||
"utilitySubnet": "Utility Subnet",
|
|
||||||
"subnetDescription": "Het subnet van de netwerkconfiguratie van deze organisatie.",
|
"subnetDescription": "Het subnet van de netwerkconfiguratie van deze organisatie.",
|
||||||
"customDomain": "Aangepast domein",
|
"customDomain": "Aangepast domein",
|
||||||
"authPage": "Authenticatiepagina's",
|
"authPage": "Authenticatiepagina's",
|
||||||
@@ -3602,8 +3576,7 @@
|
|||||||
"sshPrivateKeyPlaceholder": "-----BEGIN OPENSSH PRIVATE KEY-----",
|
"sshPrivateKeyPlaceholder": "-----BEGIN OPENSSH PRIVATE KEY-----",
|
||||||
"sshPrivateKeyRequired": "Privésleutel is vereist",
|
"sshPrivateKeyRequired": "Privésleutel is vereist",
|
||||||
"vncTitle": "VNC",
|
"vncTitle": "VNC",
|
||||||
"vncSignInDescription": "Enter your VNC credentials to connect",
|
"vncSignInDescription": "Voer uw VNC-wachtwoord in om verbinding te maken",
|
||||||
"vncUsernameOptional": "Username (optional)",
|
|
||||||
"vncPasswordOptional": "Wachtwoord (optioneel)",
|
"vncPasswordOptional": "Wachtwoord (optioneel)",
|
||||||
"vncNoResourceTarget": "Geen bron doelwit beschikbaar",
|
"vncNoResourceTarget": "Geen bron doelwit beschikbaar",
|
||||||
"vncFailedToLoadNovnc": "Laden van noVNC mislukt",
|
"vncFailedToLoadNovnc": "Laden van noVNC mislukt",
|
||||||
|
|||||||
@@ -123,16 +123,6 @@
|
|||||||
"siteUpdated": "Strona zaktualizowana",
|
"siteUpdated": "Strona zaktualizowana",
|
||||||
"siteUpdatedDescription": "Strona została zaktualizowana.",
|
"siteUpdatedDescription": "Strona została zaktualizowana.",
|
||||||
"siteGeneralDescription": "Skonfiguruj ustawienia ogólne dla tej witryny",
|
"siteGeneralDescription": "Skonfiguruj ustawienia ogólne dla tej witryny",
|
||||||
"siteRestartTitle": "Restart Site",
|
|
||||||
"siteRestartDescription": "Restart the WireGuard tunnel for this site. This will briefly interrupt connectivity.",
|
|
||||||
"siteRestartBody": "Use this if the site tunnel is not functioning correctly and you want to force a reconnect without restarting the host.",
|
|
||||||
"siteRestartButton": "Restart Site",
|
|
||||||
"siteRestartDialogMessage": "Are you sure you want to restart the WireGuard tunnel for <b>{name}</b>? The site will briefly lose connectivity.",
|
|
||||||
"siteRestartWarning": "The site will briefly disconnect while the tunnel restarts.",
|
|
||||||
"siteRestarted": "Site restarted",
|
|
||||||
"siteRestartedDescription": "The WireGuard tunnel has been restarted.",
|
|
||||||
"siteErrorRestart": "Failed to restart site",
|
|
||||||
"siteErrorRestartDescription": "An error occurred while restarting the site.",
|
|
||||||
"siteSettingDescription": "Skonfiguruj ustawienia na stronie",
|
"siteSettingDescription": "Skonfiguruj ustawienia na stronie",
|
||||||
"siteResourcesTab": "Zasoby",
|
"siteResourcesTab": "Zasoby",
|
||||||
"siteResourcesNoneOnSite": "Ta strona nie ma jeszcze żadnych zasobów publicznych ani prywatnych.",
|
"siteResourcesNoneOnSite": "Ta strona nie ma jeszcze żadnych zasobów publicznych ani prywatnych.",
|
||||||
@@ -2388,21 +2378,6 @@
|
|||||||
"sidebarRemoteExitNodes": "Zdalne węzły",
|
"sidebarRemoteExitNodes": "Zdalne węzły",
|
||||||
"remoteExitNodeId": "ID",
|
"remoteExitNodeId": "ID",
|
||||||
"remoteExitNodeSecretKey": "Sekret",
|
"remoteExitNodeSecretKey": "Sekret",
|
||||||
"remoteExitNodeNetworkingTitle": "Network Settings",
|
|
||||||
"remoteExitNodeNetworkingDescription": "Configure how this remote exit node routes traffic and which sites prefer to connect through it. Advanced features to be used with backhaul networking configurations.",
|
|
||||||
"remoteExitNodeNetworkingSave": "Save Settings",
|
|
||||||
"remoteExitNodeNetworkingSaveSuccessTitle": "Network settings saved",
|
|
||||||
"remoteExitNodeNetworkingSaveSuccessDescription": "Network settings have been updated successfully.",
|
|
||||||
"remoteExitNodeNetworkingSaveError": "Failed to save network settings",
|
|
||||||
"remoteExitNodeNetworkingSubnetsTitle": "Remote Subnets",
|
|
||||||
"remoteExitNodeNetworkingSubnetsDescription": "Define the CIDR ranges that this remote exit node will route traffic to. Type a valid CIDR (e.g. <code>10.0.0.0/8</code>) and press Enter to add.",
|
|
||||||
"remoteExitNodeNetworkingSubnetsPlaceholder": "Add a CIDR range (e.g. 10.0.0.0/8)",
|
|
||||||
"remoteExitNodeNetworkingSubnetsLoadError": "Failed to load subnets",
|
|
||||||
"remoteExitNodeNetworkingLabelsTitle": "Preference Labels",
|
|
||||||
"remoteExitNodeNetworkingLabelsDescription": "Sites with these labels will be enforced to connect through this remote exit node.",
|
|
||||||
"remoteExitNodeNetworkingLabelsButtonText": "Select labels...",
|
|
||||||
"remoteExitNodeNetworkingLabelsSearchPlaceholder": "Search labels...",
|
|
||||||
"remoteExitNodeNetworkingLabelsLoadError": "Failed to load labels",
|
|
||||||
"remoteExitNodeCreate": {
|
"remoteExitNodeCreate": {
|
||||||
"title": "Utwórz zdalny węzeł",
|
"title": "Utwórz zdalny węzeł",
|
||||||
"description": "Utwórz nowy, samodzielnie hostowany węzeł przekaźnika zdalnego i serwera proxy",
|
"description": "Utwórz nowy, samodzielnie hostowany węzeł przekaźnika zdalnego i serwera proxy",
|
||||||
@@ -2581,7 +2556,6 @@
|
|||||||
"idpGoogleDescription": "Dostawca Google OAuth2/OIDC",
|
"idpGoogleDescription": "Dostawca Google OAuth2/OIDC",
|
||||||
"idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider",
|
"idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider",
|
||||||
"subnet": "Podsieć",
|
"subnet": "Podsieć",
|
||||||
"utilitySubnet": "Utility Subnet",
|
|
||||||
"subnetDescription": "Podsieć dla konfiguracji sieci tej organizacji.",
|
"subnetDescription": "Podsieć dla konfiguracji sieci tej organizacji.",
|
||||||
"customDomain": "Niestandardowa domena",
|
"customDomain": "Niestandardowa domena",
|
||||||
"authPage": "Strony uwierzytelniania",
|
"authPage": "Strony uwierzytelniania",
|
||||||
@@ -3602,8 +3576,7 @@
|
|||||||
"sshPrivateKeyPlaceholder": "-----BEGIN OPENSSH PRIVATE KEY-----",
|
"sshPrivateKeyPlaceholder": "-----BEGIN OPENSSH PRIVATE KEY-----",
|
||||||
"sshPrivateKeyRequired": "Wymagany jest klucz prywatny",
|
"sshPrivateKeyRequired": "Wymagany jest klucz prywatny",
|
||||||
"vncTitle": "VNC",
|
"vncTitle": "VNC",
|
||||||
"vncSignInDescription": "Enter your VNC credentials to connect",
|
"vncSignInDescription": "Wprowadź hasło VNC, aby się połączyć",
|
||||||
"vncUsernameOptional": "Username (optional)",
|
|
||||||
"vncPasswordOptional": "Hasło (opcjonalne)",
|
"vncPasswordOptional": "Hasło (opcjonalne)",
|
||||||
"vncNoResourceTarget": "Brak dostępnego celu zasobu",
|
"vncNoResourceTarget": "Brak dostępnego celu zasobu",
|
||||||
"vncFailedToLoadNovnc": "Błąd ładowania noVNC",
|
"vncFailedToLoadNovnc": "Błąd ładowania noVNC",
|
||||||
|
|||||||
@@ -123,16 +123,6 @@
|
|||||||
"siteUpdated": "Site atualizado",
|
"siteUpdated": "Site atualizado",
|
||||||
"siteUpdatedDescription": "O site foi atualizado.",
|
"siteUpdatedDescription": "O site foi atualizado.",
|
||||||
"siteGeneralDescription": "Configurar as configurações gerais para este site",
|
"siteGeneralDescription": "Configurar as configurações gerais para este site",
|
||||||
"siteRestartTitle": "Restart Site",
|
|
||||||
"siteRestartDescription": "Restart the WireGuard tunnel for this site. This will briefly interrupt connectivity.",
|
|
||||||
"siteRestartBody": "Use this if the site tunnel is not functioning correctly and you want to force a reconnect without restarting the host.",
|
|
||||||
"siteRestartButton": "Restart Site",
|
|
||||||
"siteRestartDialogMessage": "Are you sure you want to restart the WireGuard tunnel for <b>{name}</b>? The site will briefly lose connectivity.",
|
|
||||||
"siteRestartWarning": "The site will briefly disconnect while the tunnel restarts.",
|
|
||||||
"siteRestarted": "Site restarted",
|
|
||||||
"siteRestartedDescription": "The WireGuard tunnel has been restarted.",
|
|
||||||
"siteErrorRestart": "Failed to restart site",
|
|
||||||
"siteErrorRestartDescription": "An error occurred while restarting the site.",
|
|
||||||
"siteSettingDescription": "Configurar as configurações no site",
|
"siteSettingDescription": "Configurar as configurações no site",
|
||||||
"siteResourcesTab": "Recursos",
|
"siteResourcesTab": "Recursos",
|
||||||
"siteResourcesNoneOnSite": "Este site ainda não possui recursos públicos ou privados.",
|
"siteResourcesNoneOnSite": "Este site ainda não possui recursos públicos ou privados.",
|
||||||
@@ -2388,21 +2378,6 @@
|
|||||||
"sidebarRemoteExitNodes": "Nós remotos",
|
"sidebarRemoteExitNodes": "Nós remotos",
|
||||||
"remoteExitNodeId": "ID",
|
"remoteExitNodeId": "ID",
|
||||||
"remoteExitNodeSecretKey": "Chave Secreta",
|
"remoteExitNodeSecretKey": "Chave Secreta",
|
||||||
"remoteExitNodeNetworkingTitle": "Network Settings",
|
|
||||||
"remoteExitNodeNetworkingDescription": "Configure how this remote exit node routes traffic and which sites prefer to connect through it. Advanced features to be used with backhaul networking configurations.",
|
|
||||||
"remoteExitNodeNetworkingSave": "Save Settings",
|
|
||||||
"remoteExitNodeNetworkingSaveSuccessTitle": "Network settings saved",
|
|
||||||
"remoteExitNodeNetworkingSaveSuccessDescription": "Network settings have been updated successfully.",
|
|
||||||
"remoteExitNodeNetworkingSaveError": "Failed to save network settings",
|
|
||||||
"remoteExitNodeNetworkingSubnetsTitle": "Remote Subnets",
|
|
||||||
"remoteExitNodeNetworkingSubnetsDescription": "Define the CIDR ranges that this remote exit node will route traffic to. Type a valid CIDR (e.g. <code>10.0.0.0/8</code>) and press Enter to add.",
|
|
||||||
"remoteExitNodeNetworkingSubnetsPlaceholder": "Add a CIDR range (e.g. 10.0.0.0/8)",
|
|
||||||
"remoteExitNodeNetworkingSubnetsLoadError": "Failed to load subnets",
|
|
||||||
"remoteExitNodeNetworkingLabelsTitle": "Preference Labels",
|
|
||||||
"remoteExitNodeNetworkingLabelsDescription": "Sites with these labels will be enforced to connect through this remote exit node.",
|
|
||||||
"remoteExitNodeNetworkingLabelsButtonText": "Select labels...",
|
|
||||||
"remoteExitNodeNetworkingLabelsSearchPlaceholder": "Search labels...",
|
|
||||||
"remoteExitNodeNetworkingLabelsLoadError": "Failed to load labels",
|
|
||||||
"remoteExitNodeCreate": {
|
"remoteExitNodeCreate": {
|
||||||
"title": "Criar Nó Remoto",
|
"title": "Criar Nó Remoto",
|
||||||
"description": "Crie um novo nó de retransmissão e proxy servidor auto-hospedado",
|
"description": "Crie um novo nó de retransmissão e proxy servidor auto-hospedado",
|
||||||
@@ -2581,7 +2556,6 @@
|
|||||||
"idpGoogleDescription": "Provedor Google OAuth2/OIDC",
|
"idpGoogleDescription": "Provedor Google OAuth2/OIDC",
|
||||||
"idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider",
|
"idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider",
|
||||||
"subnet": "Sub-rede",
|
"subnet": "Sub-rede",
|
||||||
"utilitySubnet": "Utility Subnet",
|
|
||||||
"subnetDescription": "A sub-rede para a configuração de rede dessa organização.",
|
"subnetDescription": "A sub-rede para a configuração de rede dessa organização.",
|
||||||
"customDomain": "Domínio Personalizado",
|
"customDomain": "Domínio Personalizado",
|
||||||
"authPage": "Páginas de Autenticação",
|
"authPage": "Páginas de Autenticação",
|
||||||
@@ -3602,8 +3576,7 @@
|
|||||||
"sshPrivateKeyPlaceholder": "-----BEGIN OPENSSH PRIVATE KEY-----",
|
"sshPrivateKeyPlaceholder": "-----BEGIN OPENSSH PRIVATE KEY-----",
|
||||||
"sshPrivateKeyRequired": "Chave privada é necessária",
|
"sshPrivateKeyRequired": "Chave privada é necessária",
|
||||||
"vncTitle": "VNC",
|
"vncTitle": "VNC",
|
||||||
"vncSignInDescription": "Enter your VNC credentials to connect",
|
"vncSignInDescription": "Digite sua senha VNC para conectar",
|
||||||
"vncUsernameOptional": "Username (optional)",
|
|
||||||
"vncPasswordOptional": "Senha (opcional)",
|
"vncPasswordOptional": "Senha (opcional)",
|
||||||
"vncNoResourceTarget": "Nenhum alvo de recurso disponível",
|
"vncNoResourceTarget": "Nenhum alvo de recurso disponível",
|
||||||
"vncFailedToLoadNovnc": "Falha ao carregar noVNC",
|
"vncFailedToLoadNovnc": "Falha ao carregar noVNC",
|
||||||
|
|||||||
@@ -123,16 +123,6 @@
|
|||||||
"siteUpdated": "Сайт обновлён",
|
"siteUpdated": "Сайт обновлён",
|
||||||
"siteUpdatedDescription": "Сайт был успешно обновлён.",
|
"siteUpdatedDescription": "Сайт был успешно обновлён.",
|
||||||
"siteGeneralDescription": "Настройте общие параметры для этого сайта",
|
"siteGeneralDescription": "Настройте общие параметры для этого сайта",
|
||||||
"siteRestartTitle": "Restart Site",
|
|
||||||
"siteRestartDescription": "Restart the WireGuard tunnel for this site. This will briefly interrupt connectivity.",
|
|
||||||
"siteRestartBody": "Use this if the site tunnel is not functioning correctly and you want to force a reconnect without restarting the host.",
|
|
||||||
"siteRestartButton": "Restart Site",
|
|
||||||
"siteRestartDialogMessage": "Are you sure you want to restart the WireGuard tunnel for <b>{name}</b>? The site will briefly lose connectivity.",
|
|
||||||
"siteRestartWarning": "The site will briefly disconnect while the tunnel restarts.",
|
|
||||||
"siteRestarted": "Site restarted",
|
|
||||||
"siteRestartedDescription": "The WireGuard tunnel has been restarted.",
|
|
||||||
"siteErrorRestart": "Failed to restart site",
|
|
||||||
"siteErrorRestartDescription": "An error occurred while restarting the site.",
|
|
||||||
"siteSettingDescription": "Настройка параметров на сайте",
|
"siteSettingDescription": "Настройка параметров на сайте",
|
||||||
"siteResourcesTab": "Ресурсы",
|
"siteResourcesTab": "Ресурсы",
|
||||||
"siteResourcesNoneOnSite": "На этом сайте пока нет публичных или частных ресурсов.",
|
"siteResourcesNoneOnSite": "На этом сайте пока нет публичных или частных ресурсов.",
|
||||||
@@ -2388,21 +2378,6 @@
|
|||||||
"sidebarRemoteExitNodes": "Удаленные узлы",
|
"sidebarRemoteExitNodes": "Удаленные узлы",
|
||||||
"remoteExitNodeId": "ID",
|
"remoteExitNodeId": "ID",
|
||||||
"remoteExitNodeSecretKey": "Секретный ключ",
|
"remoteExitNodeSecretKey": "Секретный ключ",
|
||||||
"remoteExitNodeNetworkingTitle": "Network Settings",
|
|
||||||
"remoteExitNodeNetworkingDescription": "Configure how this remote exit node routes traffic and which sites prefer to connect through it. Advanced features to be used with backhaul networking configurations.",
|
|
||||||
"remoteExitNodeNetworkingSave": "Save Settings",
|
|
||||||
"remoteExitNodeNetworkingSaveSuccessTitle": "Network settings saved",
|
|
||||||
"remoteExitNodeNetworkingSaveSuccessDescription": "Network settings have been updated successfully.",
|
|
||||||
"remoteExitNodeNetworkingSaveError": "Failed to save network settings",
|
|
||||||
"remoteExitNodeNetworkingSubnetsTitle": "Remote Subnets",
|
|
||||||
"remoteExitNodeNetworkingSubnetsDescription": "Define the CIDR ranges that this remote exit node will route traffic to. Type a valid CIDR (e.g. <code>10.0.0.0/8</code>) and press Enter to add.",
|
|
||||||
"remoteExitNodeNetworkingSubnetsPlaceholder": "Add a CIDR range (e.g. 10.0.0.0/8)",
|
|
||||||
"remoteExitNodeNetworkingSubnetsLoadError": "Failed to load subnets",
|
|
||||||
"remoteExitNodeNetworkingLabelsTitle": "Preference Labels",
|
|
||||||
"remoteExitNodeNetworkingLabelsDescription": "Sites with these labels will be enforced to connect through this remote exit node.",
|
|
||||||
"remoteExitNodeNetworkingLabelsButtonText": "Select labels...",
|
|
||||||
"remoteExitNodeNetworkingLabelsSearchPlaceholder": "Search labels...",
|
|
||||||
"remoteExitNodeNetworkingLabelsLoadError": "Failed to load labels",
|
|
||||||
"remoteExitNodeCreate": {
|
"remoteExitNodeCreate": {
|
||||||
"title": "Создать удалённый узел",
|
"title": "Создать удалённый узел",
|
||||||
"description": "Создайте новый самостоятельный удалённый ретранслятор и узел прокси-сервера",
|
"description": "Создайте новый самостоятельный удалённый ретранслятор и узел прокси-сервера",
|
||||||
@@ -2581,7 +2556,6 @@
|
|||||||
"idpGoogleDescription": "Google OAuth2/OIDC провайдер",
|
"idpGoogleDescription": "Google OAuth2/OIDC провайдер",
|
||||||
"idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider",
|
"idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider",
|
||||||
"subnet": "Подсеть",
|
"subnet": "Подсеть",
|
||||||
"utilitySubnet": "Utility Subnet",
|
|
||||||
"subnetDescription": "Подсеть для конфигурации сети этой организации.",
|
"subnetDescription": "Подсеть для конфигурации сети этой организации.",
|
||||||
"customDomain": "Пользовательский домен",
|
"customDomain": "Пользовательский домен",
|
||||||
"authPage": "Страницы аутентификации",
|
"authPage": "Страницы аутентификации",
|
||||||
@@ -3602,8 +3576,7 @@
|
|||||||
"sshPrivateKeyPlaceholder": "-----НАЧАЛО ЛИЧНОГО КЛЮЧА OPENSSH-----",
|
"sshPrivateKeyPlaceholder": "-----НАЧАЛО ЛИЧНОГО КЛЮЧА OPENSSH-----",
|
||||||
"sshPrivateKeyRequired": "Требуется личный ключ",
|
"sshPrivateKeyRequired": "Требуется личный ключ",
|
||||||
"vncTitle": "VNC",
|
"vncTitle": "VNC",
|
||||||
"vncSignInDescription": "Enter your VNC credentials to connect",
|
"vncSignInDescription": "Введите пароль VNC для подключения",
|
||||||
"vncUsernameOptional": "Username (optional)",
|
|
||||||
"vncPasswordOptional": "Пароль (необязательно)",
|
"vncPasswordOptional": "Пароль (необязательно)",
|
||||||
"vncNoResourceTarget": "Отсутствует целевой ресурс",
|
"vncNoResourceTarget": "Отсутствует целевой ресурс",
|
||||||
"vncFailedToLoadNovnc": "Не удалось загрузить noVNC",
|
"vncFailedToLoadNovnc": "Не удалось загрузить noVNC",
|
||||||
|
|||||||
@@ -123,16 +123,6 @@
|
|||||||
"siteUpdated": "Site güncellendi",
|
"siteUpdated": "Site güncellendi",
|
||||||
"siteUpdatedDescription": "Site güncellendi.",
|
"siteUpdatedDescription": "Site güncellendi.",
|
||||||
"siteGeneralDescription": "Bu site için genel ayarları yapılandırın",
|
"siteGeneralDescription": "Bu site için genel ayarları yapılandırın",
|
||||||
"siteRestartTitle": "Restart Site",
|
|
||||||
"siteRestartDescription": "Restart the WireGuard tunnel for this site. This will briefly interrupt connectivity.",
|
|
||||||
"siteRestartBody": "Use this if the site tunnel is not functioning correctly and you want to force a reconnect without restarting the host.",
|
|
||||||
"siteRestartButton": "Restart Site",
|
|
||||||
"siteRestartDialogMessage": "Are you sure you want to restart the WireGuard tunnel for <b>{name}</b>? The site will briefly lose connectivity.",
|
|
||||||
"siteRestartWarning": "The site will briefly disconnect while the tunnel restarts.",
|
|
||||||
"siteRestarted": "Site restarted",
|
|
||||||
"siteRestartedDescription": "The WireGuard tunnel has been restarted.",
|
|
||||||
"siteErrorRestart": "Failed to restart site",
|
|
||||||
"siteErrorRestartDescription": "An error occurred while restarting the site.",
|
|
||||||
"siteSettingDescription": "Sitenizdeki ayarları yapılandırın",
|
"siteSettingDescription": "Sitenizdeki ayarları yapılandırın",
|
||||||
"siteResourcesTab": "Kaynaklar",
|
"siteResourcesTab": "Kaynaklar",
|
||||||
"siteResourcesNoneOnSite": "Bu sitede henüz genel veya özel kaynak yok.",
|
"siteResourcesNoneOnSite": "Bu sitede henüz genel veya özel kaynak yok.",
|
||||||
@@ -2388,21 +2378,6 @@
|
|||||||
"sidebarRemoteExitNodes": "Uzak Düğümler",
|
"sidebarRemoteExitNodes": "Uzak Düğümler",
|
||||||
"remoteExitNodeId": "Kimlik",
|
"remoteExitNodeId": "Kimlik",
|
||||||
"remoteExitNodeSecretKey": "Gizli",
|
"remoteExitNodeSecretKey": "Gizli",
|
||||||
"remoteExitNodeNetworkingTitle": "Network Settings",
|
|
||||||
"remoteExitNodeNetworkingDescription": "Configure how this remote exit node routes traffic and which sites prefer to connect through it. Advanced features to be used with backhaul networking configurations.",
|
|
||||||
"remoteExitNodeNetworkingSave": "Save Settings",
|
|
||||||
"remoteExitNodeNetworkingSaveSuccessTitle": "Network settings saved",
|
|
||||||
"remoteExitNodeNetworkingSaveSuccessDescription": "Network settings have been updated successfully.",
|
|
||||||
"remoteExitNodeNetworkingSaveError": "Failed to save network settings",
|
|
||||||
"remoteExitNodeNetworkingSubnetsTitle": "Remote Subnets",
|
|
||||||
"remoteExitNodeNetworkingSubnetsDescription": "Define the CIDR ranges that this remote exit node will route traffic to. Type a valid CIDR (e.g. <code>10.0.0.0/8</code>) and press Enter to add.",
|
|
||||||
"remoteExitNodeNetworkingSubnetsPlaceholder": "Add a CIDR range (e.g. 10.0.0.0/8)",
|
|
||||||
"remoteExitNodeNetworkingSubnetsLoadError": "Failed to load subnets",
|
|
||||||
"remoteExitNodeNetworkingLabelsTitle": "Preference Labels",
|
|
||||||
"remoteExitNodeNetworkingLabelsDescription": "Sites with these labels will be enforced to connect through this remote exit node.",
|
|
||||||
"remoteExitNodeNetworkingLabelsButtonText": "Select labels...",
|
|
||||||
"remoteExitNodeNetworkingLabelsSearchPlaceholder": "Search labels...",
|
|
||||||
"remoteExitNodeNetworkingLabelsLoadError": "Failed to load labels",
|
|
||||||
"remoteExitNodeCreate": {
|
"remoteExitNodeCreate": {
|
||||||
"title": "Uzak Düğüm Oluştur",
|
"title": "Uzak Düğüm Oluştur",
|
||||||
"description": "Yeni bir kendine misafir uzaktan ileti ve ara sunucu düğümü oluşturun",
|
"description": "Yeni bir kendine misafir uzaktan ileti ve ara sunucu düğümü oluşturun",
|
||||||
@@ -2581,7 +2556,6 @@
|
|||||||
"idpGoogleDescription": "Google OAuth2/OIDC sağlayıcısı",
|
"idpGoogleDescription": "Google OAuth2/OIDC sağlayıcısı",
|
||||||
"idpAzureDescription": "Microsoft Azure OAuth2/OIDC sağlayıcısı",
|
"idpAzureDescription": "Microsoft Azure OAuth2/OIDC sağlayıcısı",
|
||||||
"subnet": "Alt ağ",
|
"subnet": "Alt ağ",
|
||||||
"utilitySubnet": "Utility Subnet",
|
|
||||||
"subnetDescription": "Bu organizasyonun ağ yapılandırması için alt ağ.",
|
"subnetDescription": "Bu organizasyonun ağ yapılandırması için alt ağ.",
|
||||||
"customDomain": "Özel Alan",
|
"customDomain": "Özel Alan",
|
||||||
"authPage": "Kimlik Sayfaları",
|
"authPage": "Kimlik Sayfaları",
|
||||||
@@ -3602,8 +3576,7 @@
|
|||||||
"sshPrivateKeyPlaceholder": "-----BAŞLANGIÇ OPENSSH ÖZEL ANAHTARI-----",
|
"sshPrivateKeyPlaceholder": "-----BAŞLANGIÇ OPENSSH ÖZEL ANAHTARI-----",
|
||||||
"sshPrivateKeyRequired": "Özel anahtar gereklidir",
|
"sshPrivateKeyRequired": "Özel anahtar gereklidir",
|
||||||
"vncTitle": "VNC",
|
"vncTitle": "VNC",
|
||||||
"vncSignInDescription": "Enter your VNC credentials to connect",
|
"vncSignInDescription": "Bağlanmak için VNC parolanızı girin",
|
||||||
"vncUsernameOptional": "Username (optional)",
|
|
||||||
"vncPasswordOptional": "Parola (isteğe bağlı)",
|
"vncPasswordOptional": "Parola (isteğe bağlı)",
|
||||||
"vncNoResourceTarget": "Kaynak hedefi mevcut değil",
|
"vncNoResourceTarget": "Kaynak hedefi mevcut değil",
|
||||||
"vncFailedToLoadNovnc": "NoVNC yüklenemedi",
|
"vncFailedToLoadNovnc": "NoVNC yüklenemedi",
|
||||||
|
|||||||
@@ -123,16 +123,6 @@
|
|||||||
"siteUpdated": "站点已更新",
|
"siteUpdated": "站点已更新",
|
||||||
"siteUpdatedDescription": "网站已更新。",
|
"siteUpdatedDescription": "网站已更新。",
|
||||||
"siteGeneralDescription": "配置此站点的常规设置",
|
"siteGeneralDescription": "配置此站点的常规设置",
|
||||||
"siteRestartTitle": "Restart Site",
|
|
||||||
"siteRestartDescription": "Restart the WireGuard tunnel for this site. This will briefly interrupt connectivity.",
|
|
||||||
"siteRestartBody": "Use this if the site tunnel is not functioning correctly and you want to force a reconnect without restarting the host.",
|
|
||||||
"siteRestartButton": "Restart Site",
|
|
||||||
"siteRestartDialogMessage": "Are you sure you want to restart the WireGuard tunnel for <b>{name}</b>? The site will briefly lose connectivity.",
|
|
||||||
"siteRestartWarning": "The site will briefly disconnect while the tunnel restarts.",
|
|
||||||
"siteRestarted": "Site restarted",
|
|
||||||
"siteRestartedDescription": "The WireGuard tunnel has been restarted.",
|
|
||||||
"siteErrorRestart": "Failed to restart site",
|
|
||||||
"siteErrorRestartDescription": "An error occurred while restarting the site.",
|
|
||||||
"siteSettingDescription": "配置站点设置",
|
"siteSettingDescription": "配置站点设置",
|
||||||
"siteResourcesTab": "资源",
|
"siteResourcesTab": "资源",
|
||||||
"siteResourcesNoneOnSite": "此站点尚无公开或私人资源。",
|
"siteResourcesNoneOnSite": "此站点尚无公开或私人资源。",
|
||||||
@@ -2388,21 +2378,6 @@
|
|||||||
"sidebarRemoteExitNodes": "远程节点",
|
"sidebarRemoteExitNodes": "远程节点",
|
||||||
"remoteExitNodeId": "ID",
|
"remoteExitNodeId": "ID",
|
||||||
"remoteExitNodeSecretKey": "密钥",
|
"remoteExitNodeSecretKey": "密钥",
|
||||||
"remoteExitNodeNetworkingTitle": "Network Settings",
|
|
||||||
"remoteExitNodeNetworkingDescription": "Configure how this remote exit node routes traffic and which sites prefer to connect through it. Advanced features to be used with backhaul networking configurations.",
|
|
||||||
"remoteExitNodeNetworkingSave": "Save Settings",
|
|
||||||
"remoteExitNodeNetworkingSaveSuccessTitle": "Network settings saved",
|
|
||||||
"remoteExitNodeNetworkingSaveSuccessDescription": "Network settings have been updated successfully.",
|
|
||||||
"remoteExitNodeNetworkingSaveError": "Failed to save network settings",
|
|
||||||
"remoteExitNodeNetworkingSubnetsTitle": "Remote Subnets",
|
|
||||||
"remoteExitNodeNetworkingSubnetsDescription": "Define the CIDR ranges that this remote exit node will route traffic to. Type a valid CIDR (e.g. <code>10.0.0.0/8</code>) and press Enter to add.",
|
|
||||||
"remoteExitNodeNetworkingSubnetsPlaceholder": "Add a CIDR range (e.g. 10.0.0.0/8)",
|
|
||||||
"remoteExitNodeNetworkingSubnetsLoadError": "Failed to load subnets",
|
|
||||||
"remoteExitNodeNetworkingLabelsTitle": "Preference Labels",
|
|
||||||
"remoteExitNodeNetworkingLabelsDescription": "Sites with these labels will be enforced to connect through this remote exit node.",
|
|
||||||
"remoteExitNodeNetworkingLabelsButtonText": "Select labels...",
|
|
||||||
"remoteExitNodeNetworkingLabelsSearchPlaceholder": "Search labels...",
|
|
||||||
"remoteExitNodeNetworkingLabelsLoadError": "Failed to load labels",
|
|
||||||
"remoteExitNodeCreate": {
|
"remoteExitNodeCreate": {
|
||||||
"title": "创建远程节点",
|
"title": "创建远程节点",
|
||||||
"description": "创建一个新的自托管远程中继和代理服务器节点",
|
"description": "创建一个新的自托管远程中继和代理服务器节点",
|
||||||
@@ -2581,7 +2556,6 @@
|
|||||||
"idpGoogleDescription": "Google OAuth2/OIDC 提供商",
|
"idpGoogleDescription": "Google OAuth2/OIDC 提供商",
|
||||||
"idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider",
|
"idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider",
|
||||||
"subnet": "子网",
|
"subnet": "子网",
|
||||||
"utilitySubnet": "Utility Subnet",
|
|
||||||
"subnetDescription": "此组织网络配置的子网。",
|
"subnetDescription": "此组织网络配置的子网。",
|
||||||
"customDomain": "自定义域",
|
"customDomain": "自定义域",
|
||||||
"authPage": "身份验证页面",
|
"authPage": "身份验证页面",
|
||||||
@@ -3602,8 +3576,7 @@
|
|||||||
"sshPrivateKeyPlaceholder": "-----BEGIN OPENSSH PRIVATE KEY-----",
|
"sshPrivateKeyPlaceholder": "-----BEGIN OPENSSH PRIVATE KEY-----",
|
||||||
"sshPrivateKeyRequired": "需要私钥",
|
"sshPrivateKeyRequired": "需要私钥",
|
||||||
"vncTitle": "VNC",
|
"vncTitle": "VNC",
|
||||||
"vncSignInDescription": "Enter your VNC credentials to connect",
|
"vncSignInDescription": "输入您的 VNC 密码以连接",
|
||||||
"vncUsernameOptional": "Username (optional)",
|
|
||||||
"vncPasswordOptional": "密码 (可选)",
|
"vncPasswordOptional": "密码 (可选)",
|
||||||
"vncNoResourceTarget": "没有可用的资源目标",
|
"vncNoResourceTarget": "没有可用的资源目标",
|
||||||
"vncFailedToLoadNovnc": "加载 noVNC 失败",
|
"vncFailedToLoadNovnc": "加载 noVNC 失败",
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ type ClientRow = typeof clients.$inferSelect;
|
|||||||
function runQueuedClientAssociationRebuilds(
|
function runQueuedClientAssociationRebuilds(
|
||||||
userId: string,
|
userId: string,
|
||||||
queuedClients: ClientRow[]
|
queuedClients: ClientRow[]
|
||||||
): void {
|
) {
|
||||||
if (queuedClients.length === 0) {
|
if (queuedClients.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -39,425 +39,403 @@ function runQueuedClientAssociationRebuilds(
|
|||||||
uniqueClientsById.set(client.clientId, client);
|
uniqueClientsById.set(client.clientId, client);
|
||||||
}
|
}
|
||||||
|
|
||||||
void (async () => {
|
for (const client of uniqueClientsById.values()) {
|
||||||
for (const client of uniqueClientsById.values()) {
|
rebuildClientAssociationsFromClient(client).catch((error) => {
|
||||||
try {
|
logger.error(
|
||||||
await rebuildClientAssociationsFromClient(client);
|
`Error rebuilding client associations for client ${client.clientId} (user ${userId}): ${String(
|
||||||
} catch (error) {
|
error
|
||||||
logger.error(
|
)}`
|
||||||
`Failed rebuilding associations for client ${client.clientId} (user ${userId}): ${String(error)}`
|
);
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
logger.debug(
|
logger.debug(
|
||||||
`Queued association rebuild completed for ${uniqueClientsById.size} client(s) (user ${userId})`
|
`Queued association rebuild completed for ${uniqueClientsById.size} client(s) (user ${userId})`
|
||||||
);
|
);
|
||||||
})();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function calculateUserClientsForOrgs(
|
export async function calculateUserClientsForOrgs(
|
||||||
userId: string
|
userId: string
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const trx = primaryDb;
|
const trx = primaryDb;
|
||||||
|
|
||||||
const queuedAssociationRebuilds: ClientRow[] = [];
|
const queuedAssociationRebuilds: ClientRow[] = [];
|
||||||
|
const orgCache = new Map<string, typeof orgs.$inferSelect | null>();
|
||||||
|
const adminRoleCache = new Map<string, typeof roles.$inferSelect | null>();
|
||||||
|
const exitNodesCache = new Map<
|
||||||
|
string,
|
||||||
|
Awaited<ReturnType<typeof listExitNodes>>
|
||||||
|
>();
|
||||||
|
const isOrgLicensedCache = new Map<string, boolean>();
|
||||||
|
const existingClientCache = new Map<
|
||||||
|
string,
|
||||||
|
typeof clients.$inferSelect | null
|
||||||
|
>();
|
||||||
|
const roleClientAccessCache = new Map<string, boolean>();
|
||||||
|
const userClientAccessCache = new Map<string, boolean>();
|
||||||
|
|
||||||
const execute = async (transaction: Transaction | typeof db) => {
|
const getOrgOlmKey = (orgId: string, olmId: string) => `${orgId}:${olmId}`;
|
||||||
const orgCache = new Map<string, typeof orgs.$inferSelect | null>();
|
const getRoleClientKey = (roleId: number, clientId: number) =>
|
||||||
const adminRoleCache = new Map<
|
`${roleId}:${clientId}`;
|
||||||
string,
|
const getUserClientKey = (cachedUserId: string, clientId: number) =>
|
||||||
typeof roles.$inferSelect | null
|
`${cachedUserId}:${clientId}`;
|
||||||
>();
|
|
||||||
const exitNodesCache = new Map<
|
|
||||||
string,
|
|
||||||
Awaited<ReturnType<typeof listExitNodes>>
|
|
||||||
>();
|
|
||||||
const isOrgLicensedCache = new Map<string, boolean>();
|
|
||||||
const existingClientCache = new Map<
|
|
||||||
string,
|
|
||||||
typeof clients.$inferSelect | null
|
|
||||||
>();
|
|
||||||
const roleClientAccessCache = new Map<string, boolean>();
|
|
||||||
const userClientAccessCache = new Map<string, boolean>();
|
|
||||||
|
|
||||||
const getOrgOlmKey = (orgId: string, olmId: string) =>
|
const getOrg = async (orgId: string) => {
|
||||||
`${orgId}:${olmId}`;
|
if (orgCache.has(orgId)) {
|
||||||
const getRoleClientKey = (roleId: number, clientId: number) =>
|
return orgCache.get(orgId) ?? null;
|
||||||
`${roleId}:${clientId}`;
|
|
||||||
const getUserClientKey = (cachedUserId: string, clientId: number) =>
|
|
||||||
`${cachedUserId}:${clientId}`;
|
|
||||||
|
|
||||||
const getOrg = async (orgId: string) => {
|
|
||||||
if (orgCache.has(orgId)) {
|
|
||||||
return orgCache.get(orgId) ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const [org] = await transaction
|
|
||||||
.select()
|
|
||||||
.from(orgs)
|
|
||||||
.where(eq(orgs.orgId, orgId));
|
|
||||||
orgCache.set(orgId, org ?? null);
|
|
||||||
|
|
||||||
return org ?? null;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getAdminRole = async (orgId: string) => {
|
|
||||||
if (adminRoleCache.has(orgId)) {
|
|
||||||
return adminRoleCache.get(orgId) ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const [adminRole] = await transaction
|
|
||||||
.select()
|
|
||||||
.from(roles)
|
|
||||||
.where(and(eq(roles.isAdmin, true), eq(roles.orgId, orgId)))
|
|
||||||
.limit(1);
|
|
||||||
adminRoleCache.set(orgId, adminRole ?? null);
|
|
||||||
|
|
||||||
return adminRole ?? null;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getExitNodes = async (orgId: string) => {
|
|
||||||
if (exitNodesCache.has(orgId)) {
|
|
||||||
return exitNodesCache.get(orgId)!;
|
|
||||||
}
|
|
||||||
|
|
||||||
const exitNodes = await listExitNodes(orgId);
|
|
||||||
exitNodesCache.set(orgId, exitNodes);
|
|
||||||
|
|
||||||
return exitNodes;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getIsOrgLicensed = async (orgId: string) => {
|
|
||||||
if (isOrgLicensedCache.has(orgId)) {
|
|
||||||
return isOrgLicensedCache.get(orgId)!;
|
|
||||||
}
|
|
||||||
|
|
||||||
const isOrgLicensed = await isLicensedOrSubscribed(
|
|
||||||
orgId,
|
|
||||||
tierMatrix.deviceApprovals
|
|
||||||
);
|
|
||||||
isOrgLicensedCache.set(orgId, isOrgLicensed);
|
|
||||||
|
|
||||||
return isOrgLicensed;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getExistingClient = async (orgId: string, olmId: string) => {
|
|
||||||
const key = getOrgOlmKey(orgId, olmId);
|
|
||||||
if (existingClientCache.has(key)) {
|
|
||||||
return existingClientCache.get(key) ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const [existingClient] = await transaction
|
|
||||||
.select()
|
|
||||||
.from(clients)
|
|
||||||
.where(
|
|
||||||
and(
|
|
||||||
eq(clients.userId, userId),
|
|
||||||
eq(clients.orgId, orgId),
|
|
||||||
eq(clients.olmId, olmId)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.limit(1);
|
|
||||||
|
|
||||||
existingClientCache.set(key, existingClient ?? null);
|
|
||||||
|
|
||||||
return existingClient ?? null;
|
|
||||||
};
|
|
||||||
|
|
||||||
const hasRoleClientAccess = async (
|
|
||||||
roleId: number,
|
|
||||||
clientId: number
|
|
||||||
) => {
|
|
||||||
const key = getRoleClientKey(roleId, clientId);
|
|
||||||
if (roleClientAccessCache.has(key)) {
|
|
||||||
return roleClientAccessCache.get(key)!;
|
|
||||||
}
|
|
||||||
|
|
||||||
const [existingRoleClient] = await transaction
|
|
||||||
.select()
|
|
||||||
.from(roleClients)
|
|
||||||
.where(
|
|
||||||
and(
|
|
||||||
eq(roleClients.roleId, roleId),
|
|
||||||
eq(roleClients.clientId, clientId)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.limit(1);
|
|
||||||
|
|
||||||
const hasAccess = Boolean(existingRoleClient);
|
|
||||||
roleClientAccessCache.set(key, hasAccess);
|
|
||||||
|
|
||||||
return hasAccess;
|
|
||||||
};
|
|
||||||
|
|
||||||
const hasUserClientAccess = async (
|
|
||||||
cachedUserId: string,
|
|
||||||
clientId: number
|
|
||||||
) => {
|
|
||||||
const key = getUserClientKey(cachedUserId, clientId);
|
|
||||||
if (userClientAccessCache.has(key)) {
|
|
||||||
return userClientAccessCache.get(key)!;
|
|
||||||
}
|
|
||||||
|
|
||||||
const [existingUserClient] = await transaction
|
|
||||||
.select()
|
|
||||||
.from(userClients)
|
|
||||||
.where(
|
|
||||||
and(
|
|
||||||
eq(userClients.userId, cachedUserId),
|
|
||||||
eq(userClients.clientId, clientId)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.limit(1);
|
|
||||||
|
|
||||||
const hasAccess = Boolean(existingUserClient);
|
|
||||||
userClientAccessCache.set(key, hasAccess);
|
|
||||||
|
|
||||||
return hasAccess;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Get all OLMs for this user
|
|
||||||
const userOlms = await transaction
|
|
||||||
.select()
|
|
||||||
.from(olms)
|
|
||||||
.where(eq(olms.userId, userId));
|
|
||||||
|
|
||||||
if (userOlms.length === 0) {
|
|
||||||
// No OLMs for this user, but we should still clean up any orphaned clients
|
|
||||||
await cleanupOrphanedClients(
|
|
||||||
userId,
|
|
||||||
transaction,
|
|
||||||
[],
|
|
||||||
queuedAssociationRebuilds
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get all user orgs with all roles (for org list and role-based logic)
|
const [org] = await trx
|
||||||
const userOrgRoleRows = await transaction
|
|
||||||
.select()
|
.select()
|
||||||
.from(userOrgs)
|
.from(orgs)
|
||||||
.innerJoin(
|
.where(eq(orgs.orgId, orgId));
|
||||||
userOrgRoles,
|
orgCache.set(orgId, org ?? null);
|
||||||
|
|
||||||
|
return org ?? null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getAdminRole = async (orgId: string) => {
|
||||||
|
if (adminRoleCache.has(orgId)) {
|
||||||
|
return adminRoleCache.get(orgId) ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [adminRole] = await trx
|
||||||
|
.select()
|
||||||
|
.from(roles)
|
||||||
|
.where(and(eq(roles.isAdmin, true), eq(roles.orgId, orgId)))
|
||||||
|
.limit(1);
|
||||||
|
adminRoleCache.set(orgId, adminRole ?? null);
|
||||||
|
|
||||||
|
return adminRole ?? null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getExitNodes = async (orgId: string) => {
|
||||||
|
if (exitNodesCache.has(orgId)) {
|
||||||
|
return exitNodesCache.get(orgId)!;
|
||||||
|
}
|
||||||
|
|
||||||
|
const exitNodes = await listExitNodes(orgId);
|
||||||
|
exitNodesCache.set(orgId, exitNodes);
|
||||||
|
|
||||||
|
return exitNodes;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getIsOrgLicensed = async (orgId: string) => {
|
||||||
|
if (isOrgLicensedCache.has(orgId)) {
|
||||||
|
return isOrgLicensedCache.get(orgId)!;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isOrgLicensed = await isLicensedOrSubscribed(
|
||||||
|
orgId,
|
||||||
|
tierMatrix.deviceApprovals
|
||||||
|
);
|
||||||
|
isOrgLicensedCache.set(orgId, isOrgLicensed);
|
||||||
|
|
||||||
|
return isOrgLicensed;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getExistingClient = async (orgId: string, olmId: string) => {
|
||||||
|
const key = getOrgOlmKey(orgId, olmId);
|
||||||
|
if (existingClientCache.has(key)) {
|
||||||
|
return existingClientCache.get(key) ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [existingClient] = await trx
|
||||||
|
.select()
|
||||||
|
.from(clients)
|
||||||
|
.where(
|
||||||
and(
|
and(
|
||||||
eq(userOrgs.userId, userOrgRoles.userId),
|
eq(clients.userId, userId),
|
||||||
eq(userOrgs.orgId, userOrgRoles.orgId)
|
eq(clients.orgId, orgId),
|
||||||
|
eq(clients.olmId, olmId)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.innerJoin(roles, eq(userOrgRoles.roleId, roles.roleId))
|
.limit(1);
|
||||||
.where(eq(userOrgs.userId, userId));
|
|
||||||
|
|
||||||
const userOrgIds = [
|
existingClientCache.set(key, existingClient ?? null);
|
||||||
...new Set(userOrgRoleRows.map((r) => r.userOrgs.orgId))
|
|
||||||
];
|
return existingClient ?? null;
|
||||||
const orgIdToRoleRows = new Map<
|
};
|
||||||
string,
|
|
||||||
(typeof userOrgRoleRows)[0][]
|
const hasRoleClientAccess = async (roleId: number, clientId: number) => {
|
||||||
>();
|
const key = getRoleClientKey(roleId, clientId);
|
||||||
for (const r of userOrgRoleRows) {
|
if (roleClientAccessCache.has(key)) {
|
||||||
const list = orgIdToRoleRows.get(r.userOrgs.orgId) ?? [];
|
return roleClientAccessCache.get(key)!;
|
||||||
list.push(r);
|
|
||||||
orgIdToRoleRows.set(r.userOrgs.orgId, list);
|
|
||||||
}
|
|
||||||
const orgRequiresDeviceApprovalRole = new Map<string, boolean>();
|
|
||||||
for (const [orgId, roleRowsForOrg] of orgIdToRoleRows.entries()) {
|
|
||||||
orgRequiresDeviceApprovalRole.set(
|
|
||||||
orgId,
|
|
||||||
roleRowsForOrg.some((r) => r.roles.requireDeviceApproval)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// For each OLM, ensure there's a client in each org the user is in
|
const [existingRoleClient] = await trx
|
||||||
for (const olm of userOlms) {
|
.select()
|
||||||
for (const orgId of orgIdToRoleRows.keys()) {
|
.from(roleClients)
|
||||||
const roleRowsForOrg = orgIdToRoleRows.get(orgId)!;
|
.where(
|
||||||
const userOrg = roleRowsForOrg[0].userOrgs;
|
and(
|
||||||
|
eq(roleClients.roleId, roleId),
|
||||||
|
eq(roleClients.clientId, clientId)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
const org = await getOrg(orgId);
|
const hasAccess = Boolean(existingRoleClient);
|
||||||
|
roleClientAccessCache.set(key, hasAccess);
|
||||||
|
|
||||||
if (!org) {
|
return hasAccess;
|
||||||
logger.warn(
|
};
|
||||||
`Skipping org ${orgId} for OLM ${olm.olmId} (user ${userId}): org not found`
|
|
||||||
);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!org.subnet) {
|
const hasUserClientAccess = async (
|
||||||
logger.warn(
|
cachedUserId: string,
|
||||||
`Skipping org ${orgId} for OLM ${olm.olmId} (user ${userId}): org has no subnet configured`
|
clientId: number
|
||||||
);
|
) => {
|
||||||
continue;
|
const key = getUserClientKey(cachedUserId, clientId);
|
||||||
}
|
if (userClientAccessCache.has(key)) {
|
||||||
|
return userClientAccessCache.get(key)!;
|
||||||
// Get admin role for this org (needed for access grants)
|
|
||||||
const adminRole = await getAdminRole(orgId);
|
|
||||||
|
|
||||||
if (!adminRole) {
|
|
||||||
logger.warn(
|
|
||||||
`Skipping org ${orgId} for OLM ${olm.olmId} (user ${userId}): no admin role found`
|
|
||||||
);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if a client already exists for this OLM+user+org combination
|
|
||||||
const existingClient = await getExistingClient(
|
|
||||||
orgId,
|
|
||||||
olm.olmId
|
|
||||||
);
|
|
||||||
|
|
||||||
if (existingClient) {
|
|
||||||
// Ensure admin role has access to the client
|
|
||||||
const hasRoleAccess = await hasRoleClientAccess(
|
|
||||||
adminRole.roleId,
|
|
||||||
existingClient.clientId
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!hasRoleAccess) {
|
|
||||||
await transaction.insert(roleClients).values({
|
|
||||||
roleId: adminRole.roleId,
|
|
||||||
clientId: existingClient.clientId
|
|
||||||
});
|
|
||||||
roleClientAccessCache.set(
|
|
||||||
getRoleClientKey(
|
|
||||||
adminRole.roleId,
|
|
||||||
existingClient.clientId
|
|
||||||
),
|
|
||||||
true
|
|
||||||
);
|
|
||||||
logger.debug(
|
|
||||||
`Granted admin role access to existing client ${existingClient.clientId} for OLM ${olm.olmId} in org ${orgId} (user ${userId})`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure user has access to the client
|
|
||||||
const hasUserAccess = await hasUserClientAccess(
|
|
||||||
userId,
|
|
||||||
existingClient.clientId
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!hasUserAccess) {
|
|
||||||
await transaction.insert(userClients).values({
|
|
||||||
userId,
|
|
||||||
clientId: existingClient.clientId
|
|
||||||
});
|
|
||||||
userClientAccessCache.set(
|
|
||||||
getUserClientKey(userId, existingClient.clientId),
|
|
||||||
true
|
|
||||||
);
|
|
||||||
logger.debug(
|
|
||||||
`Granted user access to existing client ${existingClient.clientId} for OLM ${olm.olmId} in org ${orgId} (user ${userId})`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.debug(
|
|
||||||
`Client already exists for OLM ${olm.olmId} in org ${orgId} (user ${userId}), skipping creation`
|
|
||||||
);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get exit nodes for this org
|
|
||||||
const exitNodesList = await getExitNodes(orgId);
|
|
||||||
|
|
||||||
if (exitNodesList.length === 0) {
|
|
||||||
logger.warn(
|
|
||||||
`Skipping org ${orgId} for OLM ${olm.olmId} (user ${userId}): no exit nodes found`
|
|
||||||
);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const randomExitNode =
|
|
||||||
exitNodesList[
|
|
||||||
Math.floor(Math.random() * exitNodesList.length)
|
|
||||||
];
|
|
||||||
|
|
||||||
// Get next available subnet
|
|
||||||
const { value: newSubnet, release: releaseSubnetLock } =
|
|
||||||
await getNextAvailableClientSubnet(orgId, transaction);
|
|
||||||
|
|
||||||
const subnet = newSubnet.split("/")[0];
|
|
||||||
const updatedSubnet = `${subnet}/${org.subnet.split("/")[1]}`;
|
|
||||||
|
|
||||||
const niceId = await getUniqueClientName(orgId);
|
|
||||||
|
|
||||||
const isOrgLicensed = await getIsOrgLicensed(userOrg.orgId);
|
|
||||||
const requireApproval =
|
|
||||||
build !== "oss" &&
|
|
||||||
isOrgLicensed &&
|
|
||||||
orgRequiresDeviceApprovalRole.get(orgId) === true;
|
|
||||||
|
|
||||||
const newClientData: InferInsertModel<typeof clients> = {
|
|
||||||
userId,
|
|
||||||
orgId: userOrg.orgId,
|
|
||||||
exitNodeId: randomExitNode.exitNodeId,
|
|
||||||
name: olm.name || "User Client",
|
|
||||||
subnet: updatedSubnet,
|
|
||||||
olmId: olm.olmId,
|
|
||||||
type: "olm",
|
|
||||||
niceId,
|
|
||||||
approvalState: requireApproval ? "pending" : null
|
|
||||||
};
|
|
||||||
|
|
||||||
// Create the client
|
|
||||||
const [newClient] = await transaction
|
|
||||||
.insert(clients)
|
|
||||||
.values(newClientData)
|
|
||||||
.returning();
|
|
||||||
await releaseSubnetLock();
|
|
||||||
existingClientCache.set(
|
|
||||||
getOrgOlmKey(orgId, olm.olmId),
|
|
||||||
newClient
|
|
||||||
);
|
|
||||||
|
|
||||||
// create approval request
|
|
||||||
if (requireApproval) {
|
|
||||||
await transaction
|
|
||||||
.insert(approvals)
|
|
||||||
.values({
|
|
||||||
timestamp: Math.floor(new Date().getTime() / 1000),
|
|
||||||
orgId: userOrg.orgId,
|
|
||||||
clientId: newClient.clientId,
|
|
||||||
userId,
|
|
||||||
type: "user_device"
|
|
||||||
})
|
|
||||||
.returning();
|
|
||||||
}
|
|
||||||
|
|
||||||
queuedAssociationRebuilds.push(newClient);
|
|
||||||
|
|
||||||
// Grant admin role access to the client
|
|
||||||
await transaction.insert(roleClients).values({
|
|
||||||
roleId: adminRole.roleId,
|
|
||||||
clientId: newClient.clientId
|
|
||||||
});
|
|
||||||
roleClientAccessCache.set(
|
|
||||||
getRoleClientKey(adminRole.roleId, newClient.clientId),
|
|
||||||
true
|
|
||||||
);
|
|
||||||
|
|
||||||
// Grant user access to the client
|
|
||||||
await transaction.insert(userClients).values({
|
|
||||||
userId,
|
|
||||||
clientId: newClient.clientId
|
|
||||||
});
|
|
||||||
userClientAccessCache.set(
|
|
||||||
getUserClientKey(userId, newClient.clientId),
|
|
||||||
true
|
|
||||||
);
|
|
||||||
|
|
||||||
logger.debug(
|
|
||||||
`Created client for OLM ${olm.olmId} in org ${orgId} (user ${userId}) with access granted to admin role and user`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clean up clients in orgs the user is no longer in
|
const [existingUserClient] = await trx
|
||||||
|
.select()
|
||||||
|
.from(userClients)
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(userClients.userId, cachedUserId),
|
||||||
|
eq(userClients.clientId, clientId)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
|
const hasAccess = Boolean(existingUserClient);
|
||||||
|
userClientAccessCache.set(key, hasAccess);
|
||||||
|
|
||||||
|
return hasAccess;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get all OLMs for this user
|
||||||
|
const userOlms = await trx
|
||||||
|
.select()
|
||||||
|
.from(olms)
|
||||||
|
.where(eq(olms.userId, userId));
|
||||||
|
|
||||||
|
if (userOlms.length === 0) {
|
||||||
|
// No OLMs for this user, but we should still clean up any orphaned clients
|
||||||
await cleanupOrphanedClients(
|
await cleanupOrphanedClients(
|
||||||
userId,
|
userId,
|
||||||
transaction,
|
trx,
|
||||||
userOrgIds,
|
[],
|
||||||
queuedAssociationRebuilds
|
queuedAssociationRebuilds
|
||||||
);
|
);
|
||||||
};
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all user orgs with all roles (for org list and role-based logic)
|
||||||
|
const userOrgRoleRows = await trx
|
||||||
|
.select()
|
||||||
|
.from(userOrgs)
|
||||||
|
.innerJoin(
|
||||||
|
userOrgRoles,
|
||||||
|
and(
|
||||||
|
eq(userOrgs.userId, userOrgRoles.userId),
|
||||||
|
eq(userOrgs.orgId, userOrgRoles.orgId)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.innerJoin(roles, eq(userOrgRoles.roleId, roles.roleId))
|
||||||
|
.where(eq(userOrgs.userId, userId));
|
||||||
|
|
||||||
|
const userOrgIds = [
|
||||||
|
...new Set(userOrgRoleRows.map((r) => r.userOrgs.orgId))
|
||||||
|
];
|
||||||
|
const orgIdToRoleRows = new Map<string, (typeof userOrgRoleRows)[0][]>();
|
||||||
|
for (const r of userOrgRoleRows) {
|
||||||
|
const list = orgIdToRoleRows.get(r.userOrgs.orgId) ?? [];
|
||||||
|
list.push(r);
|
||||||
|
orgIdToRoleRows.set(r.userOrgs.orgId, list);
|
||||||
|
}
|
||||||
|
const orgRequiresDeviceApprovalRole = new Map<string, boolean>();
|
||||||
|
for (const [orgId, roleRowsForOrg] of orgIdToRoleRows.entries()) {
|
||||||
|
orgRequiresDeviceApprovalRole.set(
|
||||||
|
orgId,
|
||||||
|
roleRowsForOrg.some((r) => r.roles.requireDeviceApproval)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// For each OLM, ensure there's a client in each org the user is in
|
||||||
|
for (const olm of userOlms) {
|
||||||
|
for (const orgId of orgIdToRoleRows.keys()) {
|
||||||
|
const roleRowsForOrg = orgIdToRoleRows.get(orgId)!;
|
||||||
|
const userOrg = roleRowsForOrg[0].userOrgs;
|
||||||
|
|
||||||
|
const org = await getOrg(orgId);
|
||||||
|
|
||||||
|
if (!org) {
|
||||||
|
logger.warn(
|
||||||
|
`Skipping org ${orgId} for OLM ${olm.olmId} (user ${userId}): org not found`
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!org.subnet) {
|
||||||
|
logger.warn(
|
||||||
|
`Skipping org ${orgId} for OLM ${olm.olmId} (user ${userId}): org has no subnet configured`
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get admin role for this org (needed for access grants)
|
||||||
|
const adminRole = await getAdminRole(orgId);
|
||||||
|
|
||||||
|
if (!adminRole) {
|
||||||
|
logger.warn(
|
||||||
|
`Skipping org ${orgId} for OLM ${olm.olmId} (user ${userId}): no admin role found`
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if a client already exists for this OLM+user+org combination
|
||||||
|
const existingClient = await getExistingClient(orgId, olm.olmId);
|
||||||
|
|
||||||
|
if (existingClient) {
|
||||||
|
// Ensure admin role has access to the client
|
||||||
|
const hasRoleAccess = await hasRoleClientAccess(
|
||||||
|
adminRole.roleId,
|
||||||
|
existingClient.clientId
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!hasRoleAccess) {
|
||||||
|
await trx.insert(roleClients).values({
|
||||||
|
roleId: adminRole.roleId,
|
||||||
|
clientId: existingClient.clientId
|
||||||
|
});
|
||||||
|
roleClientAccessCache.set(
|
||||||
|
getRoleClientKey(
|
||||||
|
adminRole.roleId,
|
||||||
|
existingClient.clientId
|
||||||
|
),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
logger.debug(
|
||||||
|
`Granted admin role access to existing client ${existingClient.clientId} for OLM ${olm.olmId} in org ${orgId} (user ${userId})`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure user has access to the client
|
||||||
|
const hasUserAccess = await hasUserClientAccess(
|
||||||
|
userId,
|
||||||
|
existingClient.clientId
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!hasUserAccess) {
|
||||||
|
await trx.insert(userClients).values({
|
||||||
|
userId,
|
||||||
|
clientId: existingClient.clientId
|
||||||
|
});
|
||||||
|
userClientAccessCache.set(
|
||||||
|
getUserClientKey(userId, existingClient.clientId),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
logger.debug(
|
||||||
|
`Granted user access to existing client ${existingClient.clientId} for OLM ${olm.olmId} in org ${orgId} (user ${userId})`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug(
|
||||||
|
`Client already exists for OLM ${olm.olmId} in org ${orgId} (user ${userId}), skipping creation`
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get exit nodes for this org
|
||||||
|
const exitNodesList = await getExitNodes(orgId);
|
||||||
|
|
||||||
|
if (exitNodesList.length === 0) {
|
||||||
|
logger.warn(
|
||||||
|
`Skipping org ${orgId} for OLM ${olm.olmId} (user ${userId}): no exit nodes found`
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const randomExitNode =
|
||||||
|
exitNodesList[Math.floor(Math.random() * exitNodesList.length)];
|
||||||
|
|
||||||
|
// Get next available subnet
|
||||||
|
const { value: newSubnet, release: releaseSubnetLock } =
|
||||||
|
await getNextAvailableClientSubnet(orgId, trx);
|
||||||
|
|
||||||
|
const subnet = newSubnet.split("/")[0];
|
||||||
|
const updatedSubnet = `${subnet}/${org.subnet.split("/")[1]}`;
|
||||||
|
|
||||||
|
const niceId = await getUniqueClientName(orgId);
|
||||||
|
|
||||||
|
const isOrgLicensed = await getIsOrgLicensed(userOrg.orgId);
|
||||||
|
const requireApproval =
|
||||||
|
build !== "oss" &&
|
||||||
|
isOrgLicensed &&
|
||||||
|
orgRequiresDeviceApprovalRole.get(orgId) === true;
|
||||||
|
|
||||||
|
const newClientData: InferInsertModel<typeof clients> = {
|
||||||
|
userId,
|
||||||
|
orgId: userOrg.orgId,
|
||||||
|
exitNodeId: randomExitNode.exitNodeId,
|
||||||
|
name: olm.name || "User Client",
|
||||||
|
subnet: updatedSubnet,
|
||||||
|
olmId: olm.olmId,
|
||||||
|
type: "olm",
|
||||||
|
niceId,
|
||||||
|
approvalState: requireApproval ? "pending" : null
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create the client
|
||||||
|
const [newClient] = await trx
|
||||||
|
.insert(clients)
|
||||||
|
.values(newClientData)
|
||||||
|
.returning();
|
||||||
|
await releaseSubnetLock();
|
||||||
|
existingClientCache.set(getOrgOlmKey(orgId, olm.olmId), newClient);
|
||||||
|
|
||||||
|
// create approval request
|
||||||
|
if (requireApproval) {
|
||||||
|
await trx
|
||||||
|
.insert(approvals)
|
||||||
|
.values({
|
||||||
|
timestamp: Math.floor(new Date().getTime() / 1000),
|
||||||
|
orgId: userOrg.orgId,
|
||||||
|
clientId: newClient.clientId,
|
||||||
|
userId,
|
||||||
|
type: "user_device"
|
||||||
|
})
|
||||||
|
.returning();
|
||||||
|
}
|
||||||
|
|
||||||
|
queuedAssociationRebuilds.push(newClient);
|
||||||
|
|
||||||
|
// Grant admin role access to the client
|
||||||
|
await trx.insert(roleClients).values({
|
||||||
|
roleId: adminRole.roleId,
|
||||||
|
clientId: newClient.clientId
|
||||||
|
});
|
||||||
|
roleClientAccessCache.set(
|
||||||
|
getRoleClientKey(adminRole.roleId, newClient.clientId),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
// Grant user access to the client
|
||||||
|
await trx.insert(userClients).values({
|
||||||
|
userId,
|
||||||
|
clientId: newClient.clientId
|
||||||
|
});
|
||||||
|
userClientAccessCache.set(
|
||||||
|
getUserClientKey(userId, newClient.clientId),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
logger.debug(
|
||||||
|
`Created client for OLM ${olm.olmId} in org ${orgId} (user ${userId}) with access granted to admin role and user`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up clients in orgs the user is no longer in
|
||||||
|
await cleanupOrphanedClients(
|
||||||
|
userId,
|
||||||
|
trx,
|
||||||
|
userOrgIds,
|
||||||
|
queuedAssociationRebuilds
|
||||||
|
);
|
||||||
|
|
||||||
runQueuedClientAssociationRebuilds(userId, queuedAssociationRebuilds);
|
runQueuedClientAssociationRebuilds(userId, queuedAssociationRebuilds);
|
||||||
}
|
}
|
||||||
@@ -496,7 +474,7 @@ async function cleanupOrphanedClients(
|
|||||||
)
|
)
|
||||||
.returning();
|
.returning();
|
||||||
|
|
||||||
// Queue deleted clients for post-transaction association cleanup.
|
// Queue deleted clients for post-trx association cleanup.
|
||||||
for (const deletedClient of deletedClients) {
|
for (const deletedClient of deletedClients) {
|
||||||
queuedAssociationRebuilds.push(deletedClient);
|
queuedAssociationRebuilds.push(deletedClient);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user