Merge branch 'dev' of github.com:fosrl/pangolin into dev

This commit is contained in:
Owen
2025-08-04 20:02:08 -07:00
51 changed files with 1691 additions and 237 deletions

View File

@@ -38,3 +38,25 @@ updates:
directory: "/" directory: "/"
schedule: schedule:
interval: "weekly" interval: "weekly"
- package-ecosystem: "gomod"
directory: "/install"
schedule:
interval: "daily"
groups:
dev-patch-updates:
dependency-type: "development"
update-types:
- "patch"
dev-minor-updates:
dependency-type: "development"
update-types:
- "minor"
prod-patch-updates:
dependency-type: "production"
update-types:
- "patch"
prod-minor-updates:
dependency-type: "production"
update-types:
- "minor"

View File

@@ -30,7 +30,7 @@ jobs:
- name: Install Go - name: Install Go
uses: actions/setup-go@v5 uses: actions/setup-go@v5
with: with:
go-version: 1.23.0 go-version: 1.24
- name: Update version in package.json - name: Update version in package.json
run: | run: |

View File

@@ -4,7 +4,7 @@ Contributions are welcome!
Please see the contribution and local development guide on the docs page before getting started: Please see the contribution and local development guide on the docs page before getting started:
https://docs.fossorial.io/development https://docs.digpangolin.com/development/contributing
### Licensing Considerations ### Licensing Considerations

View File

@@ -20,7 +20,7 @@ _Pangolin tunnels your services to the internet so you can access anything from
Website Website
</a> </a>
<span> | </span> <span> | </span>
<a href="https://docs.fossorial.io/Getting%20Started/quick-install"> <a href="https://docs.digpangolin.com/self-host/quick-install">
Install Guide Install Guide
</a> </a>
<span> | </span> <span> | </span>
@@ -104,7 +104,7 @@ Pangolin is a self-hosted tunneled reverse proxy server with identity and access
### Fully Self Hosted ### Fully Self Hosted
Host the full application on your own server or on the cloud with a VPS. Take a look at the [documentation](https://docs.fossorial.io/Getting%20Started/quick-install) to get started. Host the full application on your own server or on the cloud with a VPS. Take a look at the [documentation](https://docs.digpangolin.com/self-host/quick-install) to get started.
> Many of our users have had a great experience with [RackNerd](https://my.racknerd.com/aff.php?aff=13788). Depending on promotions, you can get a [**VPS with 1 vCPU, 1GB RAM, and ~20GB SSD for just around $12/year**](https://my.racknerd.com/aff.php?aff=13788&pid=912). That's a great deal! > Many of our users have had a great experience with [RackNerd](https://my.racknerd.com/aff.php?aff=13788). Depending on promotions, you can get a [**VPS with 1 vCPU, 1GB RAM, and ~20GB SSD for just around $12/year**](https://my.racknerd.com/aff.php?aff=13788&pid=912). That's a great deal!

View File

@@ -1,5 +1,5 @@
# To see all available options, please visit the docs: # To see all available options, please visit the docs:
# https://docs.fossorial.io/Pangolin/Configuration/config # https://docs.digpangolin.com/self-host/advanced/config-file
app: app:
dashboard_url: "http://localhost:3002" dashboard_url: "http://localhost:3002"

View File

@@ -36,7 +36,7 @@ services:
- 80:80 # Port for traefik because of the network_mode - 80:80 # Port for traefik because of the network_mode
traefik: traefik:
image: traefik:v3.4.0 image: traefik:v3.5
container_name: traefik container_name: traefik
restart: unless-stopped restart: unless-stopped
network_mode: service:gerbil # Ports appear on the gerbil service network_mode: service:gerbil # Ports appear on the gerbil service

View File

@@ -1,5 +1,5 @@
# To see all available options, please visit the docs: # To see all available options, please visit the docs:
# https://docs.fossorial.io/Pangolin/Configuration/config # https://docs.digpangolin.com/self-host/dns-and-networking
app: app:
dashboard_url: "https://{{.DashboardDomain}}" dashboard_url: "https://{{.DashboardDomain}}"

View File

@@ -16,7 +16,7 @@ experimental:
version: "{{.BadgerVersion}}" version: "{{.BadgerVersion}}"
crowdsec: # CrowdSec plugin configuration added crowdsec: # CrowdSec plugin configuration added
moduleName: "github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin" moduleName: "github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
version: "v1.4.2" version: "v1.4.4"
log: log:
level: "INFO" level: "INFO"

View File

@@ -36,7 +36,7 @@ services:
- 80:80 # Port for traefik because of the network_mode - 80:80 # Port for traefik because of the network_mode
{{end}} {{end}}
traefik: traefik:
image: docker.io/traefik:v3.4.1 image: docker.io/traefik:v3.5
container_name: traefik container_name: traefik
restart: unless-stopped restart: unless-stopped
{{if .InstallGerbil}} {{if .InstallGerbil}}

View File

@@ -1,10 +1,10 @@
module installer module installer
go 1.23.0 go 1.24
require ( require (
golang.org/x/term v0.28.0 golang.org/x/term v0.33.0
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
) )
require golang.org/x/sys v0.29.0 // indirect require golang.org/x/sys v0.34.0 // indirect

View File

@@ -1,7 +1,7 @@
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg= golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg=
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

View File

@@ -70,7 +70,7 @@ func main() {
fmt.Println("- Open TCP ports 80 and 443 and UDP ports 51820 and 21820 on your VPS and firewall.") fmt.Println("- Open TCP ports 80 and 443 and UDP ports 51820 and 21820 on your VPS and firewall.")
fmt.Println("- Point your domain to the VPS IP with A records.") fmt.Println("- Point your domain to the VPS IP with A records.")
fmt.Println("") fmt.Println("")
fmt.Println("http://docs.fossorial.io/Getting%20Started/dns-networking") fmt.Println("https://docs.digpangolin.com/self-host/dns-and-networking")
fmt.Println("") fmt.Println("")
fmt.Println("Lets get started!") fmt.Println("Lets get started!")
fmt.Println("") fmt.Println("")

1327
messages/bg-BG.json Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1022,6 +1022,11 @@
"actionDeleteIdpOrg": "Delete IDP Org Policy", "actionDeleteIdpOrg": "Delete IDP Org Policy",
"actionListIdpOrgs": "List IDP Orgs", "actionListIdpOrgs": "List IDP Orgs",
"actionUpdateIdpOrg": "Update IDP Org", "actionUpdateIdpOrg": "Update IDP Org",
"actionCreateClient": "Create Client",
"actionDeleteClient": "Delete Client",
"actionUpdateClient": "Update Client",
"actionListClients": "List Clients",
"actionGetClient": "Get Client",
"noneSelected": "None selected", "noneSelected": "None selected",
"orgNotFound2": "No organizations found.", "orgNotFound2": "No organizations found.",
"searchProgress": "Search...", "searchProgress": "Search...",

View File

@@ -1,5 +1,5 @@
{ {
"setupCreate": "Erstelle eine Organisation, Site und Ressourcen", "setupCreate": "Erstelle eine Organisation, einen Standort und Ressourcen",
"setupNewOrg": "Neue Organisation", "setupNewOrg": "Neue Organisation",
"setupCreateOrg": "Organisation erstellen", "setupCreateOrg": "Organisation erstellen",
"setupCreateResources": "Ressource erstellen", "setupCreateResources": "Ressource erstellen",
@@ -16,7 +16,7 @@
"componentsMember": "Du bist Mitglied von {count, plural, =0 {keiner Organisation} one {einer Organisation} other {# Organisationen}}.", "componentsMember": "Du bist Mitglied von {count, plural, =0 {keiner Organisation} one {einer Organisation} other {# Organisationen}}.",
"componentsInvalidKey": "Ungültige oder abgelaufene Lizenzschlüssel erkannt. Beachte die Lizenzbedingungen, um alle Funktionen weiterhin zu nutzen.", "componentsInvalidKey": "Ungültige oder abgelaufene Lizenzschlüssel erkannt. Beachte die Lizenzbedingungen, um alle Funktionen weiterhin zu nutzen.",
"dismiss": "Verwerfen", "dismiss": "Verwerfen",
"componentsLicenseViolation": "Lizenzverstoß: Dieser Server benutzt {usedSites} Sites, die das Lizenzlimit der {maxSites} Sites überschreiten. Beachte die Lizenzbedingungen, um alle Funktionen weiterhin zu nutzen.", "componentsLicenseViolation": "Lizenzverstoß: Dieser Server benutzt {usedSites} Standorte, was das Lizenzlimit von {maxSites} Standorten überschreitet. Beachte die Lizenzbedingungen, um alle Funktionen weiterhin zu nutzen.",
"componentsSupporterMessage": "Vielen Dank für die Unterstützung von Pangolin als {tier}!", "componentsSupporterMessage": "Vielen Dank für die Unterstützung von Pangolin als {tier}!",
"inviteErrorNotValid": "Es tut uns leid, aber es sieht so aus, als wäre die Einladung, auf die du zugreifen möchtest, entweder nicht angenommen worden oder nicht mehr gültig.", "inviteErrorNotValid": "Es tut uns leid, aber es sieht so aus, als wäre die Einladung, auf die du zugreifen möchtest, entweder nicht angenommen worden oder nicht mehr gültig.",
"inviteErrorUser": "Es tut uns leid, aber es scheint, als sei die Einladung, auf die du zugreifen möchtest, nicht für diesen Benutzer bestimmt.", "inviteErrorUser": "Es tut uns leid, aber es scheint, als sei die Einladung, auf die du zugreifen möchtest, nicht für diesen Benutzer bestimmt.",
@@ -38,25 +38,25 @@
"name": "Name", "name": "Name",
"online": "Online", "online": "Online",
"offline": "Offline", "offline": "Offline",
"site": "Seite", "site": "Standort",
"dataIn": "Daten eingehend", "dataIn": "Daten eingehend",
"dataOut": "Daten ausgehend", "dataOut": "Daten ausgehend",
"connectionType": "Verbindungstyp", "connectionType": "Verbindungstyp",
"tunnelType": "Tunneltyp", "tunnelType": "Tunneltyp",
"local": "Lokal", "local": "Lokal",
"edit": "Bearbeiten", "edit": "Bearbeiten",
"siteConfirmDelete": "Site löschen bestätigen", "siteConfirmDelete": "Standort löschen bestätigen",
"siteDelete": "Site löschen", "siteDelete": "Standort löschen",
"siteMessageRemove": "Sobald diese Seite entfernt ist, wird sie nicht mehr zugänglich sein. Alle Ressourcen und Ziele, die mit der Site verbunden sind, werden ebenfalls entfernt.", "siteMessageRemove": "Sobald dieser Standort entfernt ist, wird er nicht mehr zugänglich sein. Alle Ressourcen und Ziele, die mit diesem Standort verbunden sind, werden ebenfalls entfernt.",
"siteMessageConfirm": "Um zu bestätigen, gib den Namen der Site ein.", "siteMessageConfirm": "Um zu bestätigen, gib den Namen des Standortes unten ein.",
"siteQuestionRemove": "Bist du sicher, dass Sie die Site {selectedSite} aus der Organisation entfernt werden soll?", "siteQuestionRemove": "Bist du sicher, dass der Standort {selectedSite} aus der Organisation entfernt werden soll?",
"siteManageSites": "Sites verwalten", "siteManageSites": "Standorte verwalten",
"siteDescription": "Verbindung zum Netzwerk durch sichere Tunnel erlauben", "siteDescription": "Verbindung zum Netzwerk durch sichere Tunnel erlauben",
"siteCreate": "Site erstellen", "siteCreate": "Standort erstellen",
"siteCreateDescription2": "Folge den nachfolgenden Schritten, um eine neue Site zu erstellen und zu verbinden", "siteCreateDescription2": "Folge den nachfolgenden Schritten, um einen neuen Standort zu erstellen und zu verbinden",
"siteCreateDescription": "Erstelle eine neue Site, um Ressourcen zu verbinden", "siteCreateDescription": "Erstelle einen neuen Standort, um Ressourcen zu verbinden",
"close": "Schließen", "close": "Schließen",
"siteErrorCreate": "Fehler beim Erstellen der Site", "siteErrorCreate": "Fehler beim Erstellen des Standortes",
"siteErrorCreateKeyPair": "Schlüsselpaar oder Standardwerte nicht gefunden", "siteErrorCreateKeyPair": "Schlüsselpaar oder Standardwerte nicht gefunden",
"siteErrorCreateDefaults": "Standardwerte der Site nicht gefunden", "siteErrorCreateDefaults": "Standardwerte der Site nicht gefunden",
"method": "Methode", "method": "Methode",
@@ -70,8 +70,8 @@
"dockerRun": "Docker Run", "dockerRun": "Docker Run",
"siteLearnLocal": "Mehr Infos zu lokalen Sites", "siteLearnLocal": "Mehr Infos zu lokalen Sites",
"siteConfirmCopy": "Ich habe die Konfiguration kopiert", "siteConfirmCopy": "Ich habe die Konfiguration kopiert",
"searchSitesProgress": "Sites durchsuchen...", "searchSitesProgress": "Standorte durchsuchen...",
"siteAdd": "Site hinzufügen", "siteAdd": "Standort hinzufügen",
"siteInstallNewt": "Newt installieren", "siteInstallNewt": "Newt installieren",
"siteInstallNewtDescription": "Installiere Newt auf deinem System.", "siteInstallNewtDescription": "Installiere Newt auf deinem System.",
"WgConfiguration": "WireGuard Konfiguration", "WgConfiguration": "WireGuard Konfiguration",
@@ -82,26 +82,26 @@
"siteNewtDescription": "Nutze Newt für die beste Benutzererfahrung. Newt verwendet WireGuard as Basis und erlaubt Ihnen, Ihre privaten Ressourcen über ihre LAN-Adresse in Ihrem privaten Netzwerk aus dem Pangolin-Dashboard heraus zu adressieren.", "siteNewtDescription": "Nutze Newt für die beste Benutzererfahrung. Newt verwendet WireGuard as Basis und erlaubt Ihnen, Ihre privaten Ressourcen über ihre LAN-Adresse in Ihrem privaten Netzwerk aus dem Pangolin-Dashboard heraus zu adressieren.",
"siteRunsInDocker": "Läuft in Docker", "siteRunsInDocker": "Läuft in Docker",
"siteRunsInShell": "Läuft in der Konsole auf macOS, Linux und Windows", "siteRunsInShell": "Läuft in der Konsole auf macOS, Linux und Windows",
"siteErrorDelete": "Fehler beim Löschen der Site", "siteErrorDelete": "Fehler beim Löschen des Standortes",
"siteErrorUpdate": "Fehler beim Aktualisieren der Site", "siteErrorUpdate": "Fehler beim Aktualisieren des Standortes",
"siteErrorUpdateDescription": "Beim Aktualisieren der Site ist ein Fehler aufgetreten.", "siteErrorUpdateDescription": "Beim Aktualisieren des Standortes ist ein Fehler aufgetreten.",
"siteUpdated": "Site aktualisiert", "siteUpdated": "Standort aktualisiert",
"siteUpdatedDescription": "Die Site wurde aktualisiert.", "siteUpdatedDescription": "Der Standort wurde aktualisiert.",
"siteGeneralDescription": "Allgemeine Einstellungen für diese Site konfigurieren", "siteGeneralDescription": "Allgemeine Einstellungen für diesen Standort konfigurieren",
"siteSettingDescription": "Konfigurieren der Site Einstellungen", "siteSettingDescription": "Konfigurieren der Standort Einstellungen",
"siteSetting": "{siteName} Einstellungen", "siteSetting": "{siteName} Einstellungen",
"siteNewtTunnel": "Newt-Tunnel (empfohlen)", "siteNewtTunnel": "Newt-Tunnel (empfohlen)",
"siteNewtTunnelDescription": "Einfachster Weg, einen Zugriffspunkt zu deinem Netzwerk zu erstellen. Keine zusätzliche Einrichtung erforderlich.", "siteNewtTunnelDescription": "Einfachster Weg, einen Zugriffspunkt zu deinem Netzwerk zu erstellen. Keine zusätzliche Einrichtung erforderlich.",
"siteWg": "Einfacher WireGuard Tunnel", "siteWg": "Einfacher WireGuard Tunnel",
"siteWgDescription": "Verwende jeden WireGuard-Client, um einen Tunnel einzurichten. Manuelles NAT-Setup erforderlich.", "siteWgDescription": "Verwende jeden WireGuard-Client, um einen Tunnel einzurichten. Manuelles NAT-Setup erforderlich.",
"siteLocalDescription": "Nur lokale Ressourcen. Kein Tunneling.", "siteLocalDescription": "Nur lokale Ressourcen. Kein Tunneling.",
"siteSeeAll": "Alle Sites anzeigen", "siteSeeAll": "Alle Standorte anzeigen",
"siteTunnelDescription": "Lege fest, wie du dich mit deiner Site verbinden möchtest", "siteTunnelDescription": "Lege fest, wie du dich mit deinem Standort verbinden möchtest",
"siteNewtCredentials": "Neue Newt Zugangsdaten", "siteNewtCredentials": "Neue Newt Zugangsdaten",
"siteNewtCredentialsDescription": "So wird sich Newt mit dem Server authentifizieren", "siteNewtCredentialsDescription": "So wird sich Newt mit dem Server authentifizieren",
"siteCredentialsSave": "Ihre Zugangsdaten speichern", "siteCredentialsSave": "Ihre Zugangsdaten speichern",
"siteCredentialsSaveDescription": "Du kannst das nur einmal sehen. Stelle sicher, dass du es an einen sicheren Ort kopierst.", "siteCredentialsSaveDescription": "Du kannst das nur einmal sehen. Stelle sicher, dass du es an einen sicheren Ort kopierst.",
"siteInfo": "Site-Informationen", "siteInfo": "Standort-Informationen",
"status": "Status", "status": "Status",
"shareTitle": "Links zum Teilen verwalten", "shareTitle": "Links zum Teilen verwalten",
"shareDescription": "Erstellen Sie teilbare Links, um temporären oder permanenten Zugriff auf Ihre Ressourcen zu gewähren", "shareDescription": "Erstellen Sie teilbare Links, um temporären oder permanenten Zugriff auf Ihre Ressourcen zu gewähren",
@@ -163,10 +163,10 @@
"resourceSeeAll": "Alle Ressourcen anzeigen", "resourceSeeAll": "Alle Ressourcen anzeigen",
"resourceInfo": "Ressourcen-Informationen", "resourceInfo": "Ressourcen-Informationen",
"resourceNameDescription": "Dies ist der Anzeigename für die Ressource.", "resourceNameDescription": "Dies ist der Anzeigename für die Ressource.",
"siteSelect": "Site auswählen", "siteSelect": "Standort auswählen",
"siteSearch": "Website durchsuchen", "siteSearch": "Standorte durchsuchen",
"siteNotFound": "Keine Site gefunden.", "siteNotFound": "Keinen Standort gefunden.",
"siteSelectionDescription": "Diese Seite wird die Verbindung zu der Ressource herstellen.", "siteSelectionDescription": "Dieser Standort wird die Verbindung zu der Ressource herstellen.",
"resourceType": "Ressourcentyp", "resourceType": "Ressourcentyp",
"resourceTypeDescription": "Legen Sie fest, wie Sie auf Ihre Ressource zugreifen möchten", "resourceTypeDescription": "Legen Sie fest, wie Sie auf Ihre Ressource zugreifen möchten",
"resourceHTTPSSettings": "HTTPS-Einstellungen", "resourceHTTPSSettings": "HTTPS-Einstellungen",
@@ -302,7 +302,7 @@
"userQuestionRemove": "Sind Sie sicher, dass Sie {selectedUser} dauerhaft vom Server löschen möchten?", "userQuestionRemove": "Sind Sie sicher, dass Sie {selectedUser} dauerhaft vom Server löschen möchten?",
"licenseKey": "Lizenzschlüssel", "licenseKey": "Lizenzschlüssel",
"valid": "Gültig", "valid": "Gültig",
"numberOfSites": "Anzahl der Sites", "numberOfSites": "Anzahl der Standorte",
"licenseKeySearch": "Lizenzschlüssel suchen...", "licenseKeySearch": "Lizenzschlüssel suchen...",
"licenseKeyAdd": "Lizenzschlüssel hinzufügen", "licenseKeyAdd": "Lizenzschlüssel hinzufügen",
"type": "Typ", "type": "Typ",
@@ -342,16 +342,16 @@
"licensedNot": "Nicht lizenziert", "licensedNot": "Nicht lizenziert",
"hostId": "Host-ID", "hostId": "Host-ID",
"licenseReckeckAll": "Überprüfe alle Schlüssel", "licenseReckeckAll": "Überprüfe alle Schlüssel",
"licenseSiteUsage": "Website-Nutzung", "licenseSiteUsage": "Standort-Nutzung",
"licenseSiteUsageDecsription": "Sehen Sie sich die Anzahl der Sites an, die diese Lizenz verwenden.", "licenseSiteUsageDecsription": "Sehen Sie sich die Anzahl der Standorte an, die diese Lizenz verwenden.",
"licenseNoSiteLimit": "Die Anzahl der Sites, die einen nicht lizenzierten Host verwenden, ist unbegrenzt.", "licenseNoSiteLimit": "Die Anzahl der Standorte, die einen nicht lizenzierten Host verwenden, ist unbegrenzt.",
"licensePurchase": "Lizenz kaufen", "licensePurchase": "Lizenz kaufen",
"licensePurchaseSites": "Zusätzliche Seiten kaufen", "licensePurchaseSites": "Zusätzliche Standorte kaufen\n",
"licenseSitesUsedMax": "{usedSites} der {maxSites} Seiten verwendet", "licenseSitesUsedMax": "{usedSites} von {maxSites} Standorten verwendet",
"licenseSitesUsed": "{count, plural, =0 {# Seiten} one {# Seite} other {# Seiten}} im System.", "licenseSitesUsed": "{count, plural, =0 {# Standorte} one {# Standort} other {# Standorte}} im System.",
"licensePurchaseDescription": "Wähle aus, für wieviele Seiten du möchtest {selectedMode, select, license {kaufe eine Lizenz. Du kannst später immer weitere Seiten hinzufügen.} other {Füge zu deiner bestehenden Lizenz hinzu.}}", "licensePurchaseDescription": "Wähle aus, für wieviele Seiten du möchtest {selectedMode, select, license {kaufe eine Lizenz. Du kannst später immer weitere Seiten hinzufügen.} other {Füge zu deiner bestehenden Lizenz hinzu.}}",
"licenseFee": "Lizenzgebühr", "licenseFee": "Lizenzgebühr",
"licensePriceSite": "Preis pro Seite", "licensePriceSite": "Preis pro Standort",
"total": "Gesamt", "total": "Gesamt",
"licenseContinuePayment": "Weiter zur Zahlung", "licenseContinuePayment": "Weiter zur Zahlung",
"pricingPage": "Preisseite", "pricingPage": "Preisseite",
@@ -467,7 +467,7 @@
"targetErrorDuplicate": "Doppeltes Ziel", "targetErrorDuplicate": "Doppeltes Ziel",
"targetErrorDuplicateDescription": "Ein Ziel mit diesen Einstellungen existiert bereits", "targetErrorDuplicateDescription": "Ein Ziel mit diesen Einstellungen existiert bereits",
"targetWireGuardErrorInvalidIp": "Ungültige Ziel-IP", "targetWireGuardErrorInvalidIp": "Ungültige Ziel-IP",
"targetWireGuardErrorInvalidIpDescription": "Die Ziel-IP muss innerhalb des Site-Subnets liegen", "targetWireGuardErrorInvalidIpDescription": "Die Ziel-IP muss innerhalb des Standort-Subnets liegen",
"targetsUpdated": "Ziele aktualisiert", "targetsUpdated": "Ziele aktualisiert",
"targetsUpdatedDescription": "Ziele und Einstellungen erfolgreich aktualisiert", "targetsUpdatedDescription": "Ziele und Einstellungen erfolgreich aktualisiert",
"targetsErrorUpdate": "Fehler beim Aktualisieren der Ziele", "targetsErrorUpdate": "Fehler beim Aktualisieren der Ziele",
@@ -558,8 +558,8 @@
"resourceErrorCreateDescription": "Beim Erstellen der Ressource ist ein Fehler aufgetreten", "resourceErrorCreateDescription": "Beim Erstellen der Ressource ist ein Fehler aufgetreten",
"resourceErrorCreateMessage": "Fehler beim Erstellen der Ressource:", "resourceErrorCreateMessage": "Fehler beim Erstellen der Ressource:",
"resourceErrorCreateMessageDescription": "Ein unerwarteter Fehler ist aufgetreten", "resourceErrorCreateMessageDescription": "Ein unerwarteter Fehler ist aufgetreten",
"sitesErrorFetch": "Fehler beim Abrufen der Sites", "sitesErrorFetch": "Fehler beim Abrufen der Standorte",
"sitesErrorFetchDescription": "Beim Abrufen der Sites ist ein Fehler aufgetreten", "sitesErrorFetchDescription": "Beim Abrufen der Standorte ist ein Fehler aufgetreten",
"domainsErrorFetch": "Fehler beim Abrufen der Domains", "domainsErrorFetch": "Fehler beim Abrufen der Domains",
"domainsErrorFetchDescription": "Beim Abrufen der Domains ist ein Fehler aufgetreten", "domainsErrorFetchDescription": "Beim Abrufen der Domains ist ein Fehler aufgetreten",
"none": "Keine", "none": "Keine",
@@ -677,10 +677,10 @@
"resourceGeneralDescription": "Konfigurieren Sie die allgemeinen Einstellungen für diese Ressource", "resourceGeneralDescription": "Konfigurieren Sie die allgemeinen Einstellungen für diese Ressource",
"resourceEnable": "Ressource aktivieren", "resourceEnable": "Ressource aktivieren",
"resourceTransfer": "Ressource übertragen", "resourceTransfer": "Ressource übertragen",
"resourceTransferDescription": "Diese Ressource auf eine andere Site übertragen", "resourceTransferDescription": "Diese Ressource auf einen anderen Standort übertragen",
"resourceTransferSubmit": "Ressource übertragen", "resourceTransferSubmit": "Ressource übertragen",
"siteDestination": "Zielsite", "siteDestination": "Zielort",
"searchSites": "Sites durchsuchen", "searchSites": "Standorte durchsuchen",
"accessRoleCreate": "Rolle erstellen", "accessRoleCreate": "Rolle erstellen",
"accessRoleCreateDescription": "Erstellen Sie eine neue Rolle, um Benutzer zu gruppieren und ihre Berechtigungen zu verwalten.", "accessRoleCreateDescription": "Erstellen Sie eine neue Rolle, um Benutzer zu gruppieren und ihre Berechtigungen zu verwalten.",
"accessRoleCreateSubmit": "Rolle erstellen", "accessRoleCreateSubmit": "Rolle erstellen",
@@ -700,7 +700,7 @@
"accessRoleRemovedDescription": "Die Rolle wurde erfolgreich entfernt.", "accessRoleRemovedDescription": "Die Rolle wurde erfolgreich entfernt.",
"accessRoleRequiredRemove": "Bevor Sie diese Rolle löschen, wählen Sie bitte eine neue Rolle aus, zu der die bestehenden Mitglieder übertragen werden sollen.", "accessRoleRequiredRemove": "Bevor Sie diese Rolle löschen, wählen Sie bitte eine neue Rolle aus, zu der die bestehenden Mitglieder übertragen werden sollen.",
"manage": "Verwalten", "manage": "Verwalten",
"sitesNotFound": "Keine Sites gefunden.", "sitesNotFound": "Keine Standorte gefunden.",
"pangolinServerAdmin": "Server-Admin - Pangolin", "pangolinServerAdmin": "Server-Admin - Pangolin",
"licenseTierProfessional": "Professional Lizenz", "licenseTierProfessional": "Professional Lizenz",
"licenseTierEnterprise": "Enterprise Lizenz", "licenseTierEnterprise": "Enterprise Lizenz",
@@ -708,10 +708,10 @@
"licensed": "Lizenziert", "licensed": "Lizenziert",
"yes": "Ja", "yes": "Ja",
"no": "Nein", "no": "Nein",
"sitesAdditional": "Zusätzliche Sites", "sitesAdditional": "Zusätzliche Standorte",
"licenseKeys": "Lizenzschlüssel", "licenseKeys": "Lizenzschlüssel",
"sitestCountDecrease": "Anzahl der Sites verringern", "sitestCountDecrease": "Anzahl der Standorte verringern",
"sitestCountIncrease": "Anzahl der Sites erhöhen", "sitestCountIncrease": "Anzahl der Standorte erhöhen",
"idpManage": "Identitätsanbieter verwalten", "idpManage": "Identitätsanbieter verwalten",
"idpManageDescription": "Identitätsanbieter im System anzeigen und verwalten", "idpManageDescription": "Identitätsanbieter im System anzeigen und verwalten",
"idpDeletedDescription": "Identitätsanbieter erfolgreich gelöscht", "idpDeletedDescription": "Identitätsanbieter erfolgreich gelöscht",
@@ -963,12 +963,12 @@
"actionGetUser": "Benutzer abrufen", "actionGetUser": "Benutzer abrufen",
"actionGetOrgUser": "Organisationsbenutzer abrufen", "actionGetOrgUser": "Organisationsbenutzer abrufen",
"actionListOrgDomains": "Organisationsdomänen auflisten", "actionListOrgDomains": "Organisationsdomänen auflisten",
"actionCreateSite": "Site erstellen", "actionCreateSite": "Standort erstellen",
"actionDeleteSite": "Site löschen", "actionDeleteSite": "Standort löschen",
"actionGetSite": "Site abrufen", "actionGetSite": "Standort abrufen",
"actionListSites": "Sites auflisten", "actionListSites": "Standorte auflisten",
"actionUpdateSite": "Site aktualisieren", "actionUpdateSite": "Standorte aktualisieren",
"actionListSiteRoles": "Erlaubte Site-Rollen auflisten", "actionListSiteRoles": "Erlaubte Standort-Rollen auflisten",
"actionCreateResource": "Ressource erstellen", "actionCreateResource": "Ressource erstellen",
"actionDeleteResource": "Ressource löschen", "actionDeleteResource": "Ressource löschen",
"actionGetResource": "Ressource abrufen", "actionGetResource": "Ressource abrufen",
@@ -1022,6 +1022,11 @@
"actionDeleteIdpOrg": "IDP-Organisationsrichtlinie löschen", "actionDeleteIdpOrg": "IDP-Organisationsrichtlinie löschen",
"actionListIdpOrgs": "IDP-Organisationen auflisten", "actionListIdpOrgs": "IDP-Organisationen auflisten",
"actionUpdateIdpOrg": "IDP-Organisation aktualisieren", "actionUpdateIdpOrg": "IDP-Organisation aktualisieren",
"actionCreateClient": "Kunde erstellen",
"actionDeleteClient": "Kunde löschen",
"actionUpdateClient": "Kunde aktualisieren",
"actionListClients": "Kunden auflisten",
"actionGetClient": "Kunde holen",
"noneSelected": "Keine ausgewählt", "noneSelected": "Keine ausgewählt",
"orgNotFound2": "Keine Organisationen gefunden.", "orgNotFound2": "Keine Organisationen gefunden.",
"searchProgress": "Suche...", "searchProgress": "Suche...",
@@ -1073,7 +1078,7 @@
"language": "Sprache", "language": "Sprache",
"verificationCodeRequired": "Code ist erforderlich", "verificationCodeRequired": "Code ist erforderlich",
"userErrorNoUpdate": "Kein Benutzer zum Aktualisieren", "userErrorNoUpdate": "Kein Benutzer zum Aktualisieren",
"siteErrorNoUpdate": "Keine Site zum Aktualisieren", "siteErrorNoUpdate": "Keine Standorte zum Aktualisieren",
"resourceErrorNoUpdate": "Keine Ressource zum Aktualisieren", "resourceErrorNoUpdate": "Keine Ressource zum Aktualisieren",
"authErrorNoUpdate": "Keine Auth-Informationen zum Aktualisieren", "authErrorNoUpdate": "Keine Auth-Informationen zum Aktualisieren",
"orgErrorNoUpdate": "Keine Organisation zum Aktualisieren", "orgErrorNoUpdate": "Keine Organisation zum Aktualisieren",
@@ -1081,7 +1086,7 @@
"apiKeysErrorNoUpdate": "Kein API-Schlüssel zum Aktualisieren", "apiKeysErrorNoUpdate": "Kein API-Schlüssel zum Aktualisieren",
"sidebarOverview": "Übersicht", "sidebarOverview": "Übersicht",
"sidebarHome": "Zuhause", "sidebarHome": "Zuhause",
"sidebarSites": "Seiten", "sidebarSites": "Standorte",
"sidebarResources": "Ressourcen", "sidebarResources": "Ressourcen",
"sidebarAccessControl": "Zugriffskontrolle", "sidebarAccessControl": "Zugriffskontrolle",
"sidebarUsers": "Benutzer", "sidebarUsers": "Benutzer",
@@ -1280,21 +1285,21 @@
"and": "und", "and": "und",
"privacyPolicy": "Datenschutzrichtlinie" "privacyPolicy": "Datenschutzrichtlinie"
}, },
"siteRequired": "Site ist erforderlich.", "siteRequired": "Standort ist erforderlich.",
"olmTunnel": "Olm Tunnel", "olmTunnel": "Olm Tunnel",
"olmTunnelDescription": "Nutzen Sie Olm für die Kundenverbindung", "olmTunnelDescription": "Nutzen Sie Olm für die Kundenverbindung",
"errorCreatingClient": "Fehler beim Erstellen des Clients", "errorCreatingClient": "Fehler beim Erstellen des Clients",
"clientDefaultsNotFound": "Kundenvorgaben nicht gefunden", "clientDefaultsNotFound": "Kundenvorgaben nicht gefunden",
"createClient": "Client erstellen", "createClient": "Client erstellen",
"createClientDescription": "Erstellen Sie einen neuen Client für die Verbindung zu Ihren Sites.", "createClientDescription": "Erstellen Sie einen neuen Client für die Verbindung zu Ihren Standorten.",
"seeAllClients": "Alle Clients anzeigen", "seeAllClients": "Alle Clients anzeigen",
"clientInformation": "Kundeninformationen", "clientInformation": "Kundeninformationen",
"clientNamePlaceholder": "Kundenname", "clientNamePlaceholder": "Kundenname",
"address": "Adresse", "address": "Adresse",
"subnetPlaceholder": "Subnetz", "subnetPlaceholder": "Subnetz",
"addressDescription": "Die Adresse, die dieser Client für die Verbindung verwenden wird.", "addressDescription": "Die Adresse, die dieser Client für die Verbindung verwenden wird.",
"selectSites": "Sites auswählen", "selectSites": "Standorte auswählen",
"sitesDescription": "Der Client wird zu den ausgewählten Sites eine Verbindung haben.", "sitesDescription": "Der Client wird zu den ausgewählten Standorten eine Verbindung haben.",
"clientInstallOlm": "Olm installieren", "clientInstallOlm": "Olm installieren",
"clientInstallOlmDescription": "Olm auf Ihrem System zum Laufen bringen", "clientInstallOlmDescription": "Olm auf Ihrem System zum Laufen bringen",
"clientOlmCredentials": "Olm-Zugangsdaten", "clientOlmCredentials": "Olm-Zugangsdaten",
@@ -1309,13 +1314,13 @@
"clientUpdatedDescription": "Der Client wurde aktualisiert.", "clientUpdatedDescription": "Der Client wurde aktualisiert.",
"clientUpdateFailed": "Fehler beim Aktualisieren des Clients", "clientUpdateFailed": "Fehler beim Aktualisieren des Clients",
"clientUpdateError": "Beim Aktualisieren des Clients ist ein Fehler aufgetreten.", "clientUpdateError": "Beim Aktualisieren des Clients ist ein Fehler aufgetreten.",
"sitesFetchFailed": "Fehler beim Abrufen von Sites", "sitesFetchFailed": "Fehler beim Abrufen von Standorten",
"sitesFetchError": "Beim Abrufen von Sites ist ein Fehler aufgetreten.", "sitesFetchError": "Beim Abrufen von Standorten ist ein Fehler aufgetreten.",
"olmErrorFetchReleases": "Beim Abrufen von Olm-Veröffentlichungen ist ein Fehler aufgetreten.", "olmErrorFetchReleases": "Beim Abrufen von Olm-Veröffentlichungen ist ein Fehler aufgetreten.",
"olmErrorFetchLatest": "Beim Abrufen der neuesten Olm-Veröffentlichung ist ein Fehler aufgetreten.", "olmErrorFetchLatest": "Beim Abrufen der neuesten Olm-Veröffentlichung ist ein Fehler aufgetreten.",
"remoteSubnets": "Remote-Subnetze", "remoteSubnets": "Remote-Subnetze",
"enterCidrRange": "Geben Sie den CIDR-Bereich ein", "enterCidrRange": "Geben Sie den CIDR-Bereich ein",
"remoteSubnetsDescription": "Fügen Sie CIDR-Bereiche hinzu, die aus der Ferne auf diese Site zugreifen können. Verwenden Sie das Format wie 10.0.0.0/24 oder 192.168.1.0/24.", "remoteSubnetsDescription": "Fügen Sie CIDR-Bereiche hinzu, die aus der Ferne auf diesen Standort zugreifen können. Verwenden Sie das Format wie 10.0.0.0/24 oder 192.168.1.0/24.",
"resourceEnableProxy": "Öffentlichen Proxy aktivieren", "resourceEnableProxy": "Öffentlichen Proxy aktivieren",
"resourceEnableProxyDescription": "Ermöglichen Sie öffentliches Proxieren zu dieser Ressource. Dies ermöglicht den Zugriff auf die Ressource von außerhalb des Netzwerks durch die Cloud über einen offenen Port. Erfordert Traefik-Config.", "resourceEnableProxyDescription": "Ermöglichen Sie öffentliches Proxieren zu dieser Ressource. Dies ermöglicht den Zugriff auf die Ressource von außerhalb des Netzwerks durch die Cloud über einen offenen Port. Erfordert Traefik-Config.",
"externalProxyEnabled": "Externer Proxy aktiviert" "externalProxyEnabled": "Externer Proxy aktiviert"

View File

@@ -1022,6 +1022,11 @@
"actionDeleteIdpOrg": "Eliminar política de IDP Org", "actionDeleteIdpOrg": "Eliminar política de IDP Org",
"actionListIdpOrgs": "Listar Orgs IDP", "actionListIdpOrgs": "Listar Orgs IDP",
"actionUpdateIdpOrg": "Actualizar IDP Org", "actionUpdateIdpOrg": "Actualizar IDP Org",
"actionCreateClient": "Crear cliente",
"actionDeleteClient": "Eliminar cliente",
"actionUpdateClient": "Actualizar cliente",
"actionListClients": "Listar clientes",
"actionGetClient": "Obtener cliente",
"noneSelected": "Ninguno seleccionado", "noneSelected": "Ninguno seleccionado",
"orgNotFound2": "No se encontraron organizaciones.", "orgNotFound2": "No se encontraron organizaciones.",
"searchProgress": "Buscar...", "searchProgress": "Buscar...",

View File

@@ -1022,6 +1022,11 @@
"actionDeleteIdpOrg": "Supprimer une politique d'organisation IDP", "actionDeleteIdpOrg": "Supprimer une politique d'organisation IDP",
"actionListIdpOrgs": "Lister les organisations IDP", "actionListIdpOrgs": "Lister les organisations IDP",
"actionUpdateIdpOrg": "Mettre à jour une organisation IDP", "actionUpdateIdpOrg": "Mettre à jour une organisation IDP",
"actionCreateClient": "Créer un client",
"actionDeleteClient": "Supprimer le client",
"actionUpdateClient": "Mettre à jour le client",
"actionListClients": "Liste des clients",
"actionGetClient": "Obtenir le client",
"noneSelected": "Aucune sélection", "noneSelected": "Aucune sélection",
"orgNotFound2": "Aucune organisation trouvée.", "orgNotFound2": "Aucune organisation trouvée.",
"searchProgress": "Rechercher...", "searchProgress": "Rechercher...",

View File

@@ -1022,6 +1022,11 @@
"actionDeleteIdpOrg": "Elimina Politica Org IDP", "actionDeleteIdpOrg": "Elimina Politica Org IDP",
"actionListIdpOrgs": "Elenca Org IDP", "actionListIdpOrgs": "Elenca Org IDP",
"actionUpdateIdpOrg": "Aggiorna Org IDP", "actionUpdateIdpOrg": "Aggiorna Org IDP",
"actionCreateClient": "Crea Client",
"actionDeleteClient": "Elimina Client",
"actionUpdateClient": "Aggiorna Client",
"actionListClients": "Elenco Clienti",
"actionGetClient": "Ottieni Client",
"noneSelected": "Nessuna selezione", "noneSelected": "Nessuna selezione",
"orgNotFound2": "Nessuna organizzazione trovata.", "orgNotFound2": "Nessuna organizzazione trovata.",
"searchProgress": "Ricerca...", "searchProgress": "Ricerca...",

View File

@@ -1022,6 +1022,11 @@
"actionDeleteIdpOrg": "IDP 조직 정책 삭제", "actionDeleteIdpOrg": "IDP 조직 정책 삭제",
"actionListIdpOrgs": "IDP 조직 목록", "actionListIdpOrgs": "IDP 조직 목록",
"actionUpdateIdpOrg": "IDP 조직 업데이트", "actionUpdateIdpOrg": "IDP 조직 업데이트",
"actionCreateClient": "Create Client",
"actionDeleteClient": "Delete Client",
"actionUpdateClient": "Update Client",
"actionListClients": "List Clients",
"actionGetClient": "Get Client",
"noneSelected": "선택된 항목 없음", "noneSelected": "선택된 항목 없음",
"orgNotFound2": "조직이 없습니다.", "orgNotFound2": "조직이 없습니다.",
"searchProgress": "검색...", "searchProgress": "검색...",

View File

@@ -1022,6 +1022,11 @@
"actionDeleteIdpOrg": "Verwijder IDP Org Beleid", "actionDeleteIdpOrg": "Verwijder IDP Org Beleid",
"actionListIdpOrgs": "Toon IDP Orgs", "actionListIdpOrgs": "Toon IDP Orgs",
"actionUpdateIdpOrg": "IDP-org bijwerken", "actionUpdateIdpOrg": "IDP-org bijwerken",
"actionCreateClient": "Client aanmaken",
"actionDeleteClient": "Verwijder klant",
"actionUpdateClient": "Klant bijwerken",
"actionListClients": "Lijst klanten",
"actionGetClient": "Client ophalen",
"noneSelected": "Niet geselecteerd", "noneSelected": "Niet geselecteerd",
"orgNotFound2": "Geen organisaties gevonden.", "orgNotFound2": "Geen organisaties gevonden.",
"searchProgress": "Zoeken...", "searchProgress": "Zoeken...",

View File

@@ -1022,6 +1022,11 @@
"actionDeleteIdpOrg": "Usuń politykę organizacji IDP", "actionDeleteIdpOrg": "Usuń politykę organizacji IDP",
"actionListIdpOrgs": "Lista organizacji IDP", "actionListIdpOrgs": "Lista organizacji IDP",
"actionUpdateIdpOrg": "Aktualizuj organizację IDP", "actionUpdateIdpOrg": "Aktualizuj organizację IDP",
"actionCreateClient": "Utwórz klienta",
"actionDeleteClient": "Usuń klienta",
"actionUpdateClient": "Aktualizuj klienta",
"actionListClients": "Lista klientów",
"actionGetClient": "Pobierz klienta",
"noneSelected": "Nie wybrano", "noneSelected": "Nie wybrano",
"orgNotFound2": "Nie znaleziono organizacji.", "orgNotFound2": "Nie znaleziono organizacji.",
"searchProgress": "Szukaj...", "searchProgress": "Szukaj...",

View File

@@ -1022,6 +1022,11 @@
"actionDeleteIdpOrg": "Eliminar Política de Organização IDP", "actionDeleteIdpOrg": "Eliminar Política de Organização IDP",
"actionListIdpOrgs": "Listar Organizações IDP", "actionListIdpOrgs": "Listar Organizações IDP",
"actionUpdateIdpOrg": "Atualizar Organização IDP", "actionUpdateIdpOrg": "Atualizar Organização IDP",
"actionCreateClient": "Criar Cliente",
"actionDeleteClient": "Excluir Cliente",
"actionUpdateClient": "Atualizar Cliente",
"actionListClients": "Listar Clientes",
"actionGetClient": "Obter Cliente",
"noneSelected": "Nenhum selecionado", "noneSelected": "Nenhum selecionado",
"orgNotFound2": "Nenhuma organização encontrada.", "orgNotFound2": "Nenhuma organização encontrada.",
"searchProgress": "Pesquisar...", "searchProgress": "Pesquisar...",

View File

@@ -925,74 +925,74 @@
"supportKeyInvalid": "Недействительный ключ", "supportKeyInvalid": "Недействительный ключ",
"supportKeyInvalidDescription": "Ваш ключ поддержки недействителен.", "supportKeyInvalidDescription": "Ваш ключ поддержки недействителен.",
"supportKeyValid": "Действительный ключ", "supportKeyValid": "Действительный ключ",
"supportKeyValidDescription": "Your supporter key has been validated. Thank you for your support!", "supportKeyValidDescription": "Ваш ключ поддержки был проверен. Спасибо за поддержку!",
"supportKeyErrorValidationDescription": "Failed to validate supporter key.", "supportKeyErrorValidationDescription": "Не удалось проверить ключ поддержки.",
"supportKey": "Support Development and Adopt a Pangolin!", "supportKey": "Поддержите разработку и усыновите Панголина!",
"supportKeyDescription": "Приобретите ключ поддержки, чтобы помочь нам продолжать разработку Pangolin для сообщества. Ваш вклад позволяет нам уделять больше времени поддержке и добавлению новых функций в приложение для всех. Мы никогда не будем использовать это для платного доступа к функциям. Это отдельно от любой коммерческой версии.", "supportKeyDescription": "Приобретите ключ поддержки, чтобы помочь нам продолжать разработку Pangolin для сообщества. Ваш вклад позволяет нам уделять больше времени поддержке и добавлению новых функций в приложение для всех. Мы никогда не будем использовать это для платного доступа к функциям. Это отдельно от любой коммерческой версии.",
"supportKeyPet": "You will also get to adopt and meet your very own pet Pangolin!", "supportKeyPet": "Вы также сможете усыновить и встретить вашего собственного питомца Панголина!",
"supportKeyPurchase": "Payments are processed via GitHub. Afterward, you can retrieve your key on", "supportKeyPurchase": "Платежи обрабатываются через GitHub. После этого вы сможете получить свой ключ на",
"supportKeyPurchaseLink": "our website", "supportKeyPurchaseLink": "нашем сайте",
"supportKeyPurchase2": "and redeem it here.", "supportKeyPurchase2": "и активировать его здесь.",
"supportKeyLearnMore": "Learn more.", "supportKeyLearnMore": "Узнать больше.",
"supportKeyOptions": "Please select the option that best suits you.", "supportKeyOptions": "Пожалуйста, выберите подходящий вам вариант.",
"supportKetOptionFull": "Full Supporter", "supportKetOptionFull": "Полная поддержка",
"forWholeServer": "For the whole server", "forWholeServer": "За весь сервер",
"lifetimePurchase": "Lifetime purchase", "lifetimePurchase": "Пожизненная покупка",
"supporterStatus": "Supporter status", "supporterStatus": "Статус поддержки",
"buy": "Buy", "buy": "Купить",
"supportKeyOptionLimited": "Limited Supporter", "supportKeyOptionLimited": "Лимитированная поддержка",
"forFiveUsers": "For 5 or less users", "forFiveUsers": "За 5 или меньше пользователей",
"supportKeyRedeem": "Redeem Supporter Key", "supportKeyRedeem": "Использовать ключ Поддержки",
"supportKeyHideSevenDays": "Hide for 7 days", "supportKeyHideSevenDays": "Скрыть на 7 дней",
"supportKeyEnter": "Enter Supporter Key", "supportKeyEnter": "Введите ключ поддержки",
"supportKeyEnterDescription": "Meet your very own pet Pangolin!", "supportKeyEnterDescription": "Встречайте своего питомца Панголина!",
"githubUsername": "GitHub Username", "githubUsername": "Имя пользователя Github",
"supportKeyInput": "Supporter Key", "supportKeyInput": "Ключ поддержки",
"supportKeyBuy": "Buy Supporter Key", "supportKeyBuy": "Ключ поддержки",
"logoutError": "Error logging out", "logoutError": "Ошибка при выходе",
"signingAs": "Signed in as", "signingAs": "Вы вошли как",
"serverAdmin": "Server Admin", "serverAdmin": "Администратор сервера",
"otpEnable": "Enable Two-factor", "otpEnable": "Включить Двухфакторную Аутентификацию",
"otpDisable": "Disable Two-factor", "otpDisable": "Отключить двухфакторную аутентификацию",
"logout": "Log Out", "logout": "Выйти",
"licenseTierProfessionalRequired": "Professional Edition Required", "licenseTierProfessionalRequired": "Требуется профессиональная версия",
"licenseTierProfessionalRequiredDescription": "Эта функция доступна только в профессиональной версии.", "licenseTierProfessionalRequiredDescription": "Эта функция доступна только в профессиональной версии.",
"actionGetOrg": "Get Organization", "actionGetOrg": "Получить организацию",
"actionUpdateOrg": "Update Organization", "actionUpdateOrg": "Обновить организацию",
"actionUpdateUser": "Update User", "actionUpdateUser": "Обновить пользователя",
"actionGetUser": "Get User", "actionGetUser": "Получить пользователя",
"actionGetOrgUser": "Get Organization User", "actionGetOrgUser": "Получить пользователя организации",
"actionListOrgDomains": "List Organization Domains", "actionListOrgDomains": "Список доменов организации",
"actionCreateSite": "Create Site", "actionCreateSite": "Создать сайт",
"actionDeleteSite": "Delete Site", "actionDeleteSite": "Удалить сайт",
"actionGetSite": "Get Site", "actionGetSite": "Получить сайт",
"actionListSites": "List Sites", "actionListSites": "Список сайтов",
"actionUpdateSite": "Update Site", "actionUpdateSite": "Обновить сайт",
"actionListSiteRoles": "List Allowed Site Roles", "actionListSiteRoles": "Список разрешенных ролей сайта",
"actionCreateResource": "Create Resource", "actionCreateResource": "Создать ресурс",
"actionDeleteResource": "Delete Resource", "actionDeleteResource": "Удалить ресурс",
"actionGetResource": "Get Resource", "actionGetResource": "Получить ресурсы",
"actionListResource": "List Resources", "actionListResource": "Список ресурсов",
"actionUpdateResource": "Update Resource", "actionUpdateResource": "Обновить ресурс",
"actionListResourceUsers": "List Resource Users", "actionListResourceUsers": "Список пользователей ресурсов",
"actionSetResourceUsers": "Set Resource Users", "actionSetResourceUsers": "Список пользователей ресурсов",
"actionSetAllowedResourceRoles": "Set Allowed Resource Roles", "actionSetAllowedResourceRoles": "Набор разрешенных ролей ресурсов",
"actionListAllowedResourceRoles": "List Allowed Resource Roles", "actionListAllowedResourceRoles": "Список разрешенных ролей сайта",
"actionSetResourcePassword": "Set Resource Password", "actionSetResourcePassword": "Задать пароль ресурса",
"actionSetResourcePincode": "Set Resource Pincode", "actionSetResourcePincode": "Установить ПИН-код ресурса",
"actionSetResourceEmailWhitelist": "Set Resource Email Whitelist", "actionSetResourceEmailWhitelist": "Set Resource Email Whitelist",
"actionGetResourceEmailWhitelist": "Get Resource Email Whitelist", "actionGetResourceEmailWhitelist": "Get Resource Email Whitelist",
"actionCreateTarget": "Create Target", "actionCreateTarget": "Создать цель",
"actionDeleteTarget": "Delete Target", "actionDeleteTarget": "Удалить цель",
"actionGetTarget": "Get Target", "actionGetTarget": "Получить цель",
"actionListTargets": "List Targets", "actionListTargets": "Список целей",
"actionUpdateTarget": "Update Target", "actionUpdateTarget": "Обновить цель",
"actionCreateRole": "Create Role", "actionCreateRole": "Создать роль",
"actionDeleteRole": "Delete Role", "actionDeleteRole": "Удалить роль",
"actionGetRole": "Get Role", "actionGetRole": "Получить Роль",
"actionListRole": "List Roles", "actionListRole": "Список ролей",
"actionUpdateRole": "Update Role", "actionUpdateRole": "Обновить роль",
"actionListAllowedRoleResources": "List Allowed Role Resources", "actionListAllowedRoleResources": "Список разрешенных ролей сайта",
"actionInviteUser": "Пригласить пользователя", "actionInviteUser": "Пригласить пользователя",
"actionRemoveUser": "Удалить пользователя", "actionRemoveUser": "Удалить пользователя",
"actionListUsers": "Список пользователей", "actionListUsers": "Список пользователей",
@@ -1022,6 +1022,11 @@
"actionDeleteIdpOrg": "Удалить политику IDP организации", "actionDeleteIdpOrg": "Удалить политику IDP организации",
"actionListIdpOrgs": "Список организаций IDP", "actionListIdpOrgs": "Список организаций IDP",
"actionUpdateIdpOrg": "Обновить организацию IDP", "actionUpdateIdpOrg": "Обновить организацию IDP",
"actionCreateClient": "Создать Клиента",
"actionDeleteClient": "Удалить Клиента",
"actionUpdateClient": "Обновить Клиента",
"actionListClients": "Список Клиентов",
"actionGetClient": "Получить Клиента",
"noneSelected": "Ничего не выбрано", "noneSelected": "Ничего не выбрано",
"orgNotFound2": "Организации не найдены.", "orgNotFound2": "Организации не найдены.",
"searchProgress": "Поиск...", "searchProgress": "Поиск...",
@@ -1093,8 +1098,8 @@
"sidebarAllUsers": "Все пользователи", "sidebarAllUsers": "Все пользователи",
"sidebarIdentityProviders": "Поставщики удостоверений", "sidebarIdentityProviders": "Поставщики удостоверений",
"sidebarLicense": "Лицензия", "sidebarLicense": "Лицензия",
"sidebarClients": "Clients (Beta)", "sidebarClients": "Клиенты (бета)",
"sidebarDomains": "Domains", "sidebarDomains": "Домены",
"enableDockerSocket": "Включить Docker Socket", "enableDockerSocket": "Включить Docker Socket",
"enableDockerSocketDescription": "Включить обнаружение Docker Socket для заполнения информации о контейнерах. Путь к сокету должен быть предоставлен Newt.", "enableDockerSocketDescription": "Включить обнаружение Docker Socket для заполнения информации о контейнерах. Путь к сокету должен быть предоставлен Newt.",
"enableDockerSocketLink": "Узнать больше", "enableDockerSocketLink": "Узнать больше",
@@ -1134,36 +1139,36 @@
"dark": "тёмная", "dark": "тёмная",
"system": "системная", "system": "системная",
"theme": "Тема", "theme": "Тема",
"subnetRequired": "Subnet is required", "subnetRequired": "Требуется подсеть",
"initialSetupTitle": "Начальная настройка сервера", "initialSetupTitle": "Начальная настройка сервера",
"initialSetupDescription": "Создайте первоначальную учётную запись администратора сервера. Может существовать только один администратор сервера. Вы всегда можете изменить эти учётные данные позже.", "initialSetupDescription": "Создайте первоначальную учётную запись администратора сервера. Может существовать только один администратор сервера. Вы всегда можете изменить эти учётные данные позже.",
"createAdminAccount": "Создать учётную запись администратора", "createAdminAccount": "Создать учётную запись администратора",
"setupErrorCreateAdmin": "Произошла ошибка при создании учётной записи администратора сервера.", "setupErrorCreateAdmin": "Произошла ошибка при создании учётной записи администратора сервера.",
"certificateStatus": "Certificate Status", "certificateStatus": "Статус сертификата",
"loading": "Loading", "loading": "Загрузка",
"restart": "Restart", "restart": "Перезагрузка",
"domains": "Domains", "domains": "Домены",
"domainsDescription": "Manage domains for your organization", "domainsDescription": "Управление доменами для вашей организации",
"domainsSearch": "Search domains...", "domainsSearch": "Поиск доменов...",
"domainAdd": "Add Domain", "domainAdd": "Добавить Домен",
"domainAddDescription": "Register a new domain with your organization", "domainAddDescription": "Зарегистрировать новый домен в вашей организации",
"domainCreate": "Create Domain", "domainCreate": "Создать Домен",
"domainCreatedDescription": "Domain created successfully", "domainCreatedDescription": "Домен успешно создан",
"domainDeletedDescription": "Domain deleted successfully", "domainDeletedDescription": "Домен успешно удален",
"domainQuestionRemove": "Are you sure you want to remove the domain {domain} from your account?", "domainQuestionRemove": "Вы уверены, что хотите удалить домен {domain} из вашего аккаунта?",
"domainMessageRemove": "Once removed, the domain will no longer be associated with your account.", "domainMessageRemove": "После удаления домен больше не будет связан с вашей учетной записью.",
"domainMessageConfirm": "To confirm, please type the domain name below.", "domainMessageConfirm": "Для подтверждения введите ниже имя домена.",
"domainConfirmDelete": "Confirm Delete Domain", "domainConfirmDelete": "Подтвердить удаление домена",
"domainDelete": "Delete Domain", "domainDelete": "Удалить Домен",
"domain": "Domain", "domain": "Домен",
"selectDomainTypeNsName": "Domain Delegation (NS)", "selectDomainTypeNsName": "Делегация домена (NS)",
"selectDomainTypeNsDescription": "This domain and all its subdomains. Use this when you want to control an entire domain zone.", "selectDomainTypeNsDescription": "Этот домен и все его субдомены. Используйте это, когда вы хотите управлять всей доменной зоной.",
"selectDomainTypeCnameName": "Single Domain (CNAME)", "selectDomainTypeCnameName": "Одиночный домен (CNAME)",
"selectDomainTypeCnameDescription": "Just this specific domain. Use this for individual subdomains or specific domain entries.", "selectDomainTypeCnameDescription": "Только этот конкретный домен. Используйте это для отдельных субдоменов или отдельных записей домена.",
"selectDomainTypeWildcardName": "Wildcard Domain", "selectDomainTypeWildcardName": "Wildcard Domain",
"selectDomainTypeWildcardDescription": "This domain and its subdomains.", "selectDomainTypeWildcardDescription": "Этот домен и его субдомены.",
"domainDelegation": "Single Domain", "domainDelegation": "Единый домен",
"selectType": "Select a type", "selectType": "Выберите тип",
"actions": "Actions", "actions": "Actions",
"refresh": "Refresh", "refresh": "Refresh",
"refreshError": "Failed to refresh data", "refreshError": "Failed to refresh data",
@@ -1268,19 +1273,19 @@
"createDomainARecords": "A Records", "createDomainARecords": "A Records",
"createDomainRecordNumber": "Record {number}", "createDomainRecordNumber": "Record {number}",
"createDomainTxtRecords": "TXT Records", "createDomainTxtRecords": "TXT Records",
"createDomainSaveTheseRecords": "Save These Records", "createDomainSaveTheseRecords": "Сохранить эти записи",
"createDomainSaveTheseRecordsDescription": "Make sure to save these DNS records as you will not see them again.", "createDomainSaveTheseRecordsDescription": "Обязательно сохраните эти DNS записи, так как вы их больше не увидите.",
"createDomainDnsPropagation": "DNS Propagation", "createDomainDnsPropagation": "Распространение DNS",
"createDomainDnsPropagationDescription": "DNS changes may take some time to propagate across the internet. This can take anywhere from a few minutes to 48 hours, depending on your DNS provider and TTL settings.", "createDomainDnsPropagationDescription": "Изменения DNS могут занять некоторое время для распространения через интернет. Это может занять от нескольких минут до 48 часов в зависимости от вашего DNS провайдера и настроек TTL.",
"resourcePortRequired": "Port number is required for non-HTTP resources", "resourcePortRequired": "Номер порта необходим для не-HTTP ресурсов",
"resourcePortNotAllowed": "Port number should not be set for HTTP resources", "resourcePortNotAllowed": "Номер порта не должен быть установлен для HTTP ресурсов",
"signUpTerms": { "signUpTerms": {
"IAgreeToThe": "I agree to the", "IAgreeToThe": "Я согласен с",
"termsOfService": "terms of service", "termsOfService": "условия использования",
"and": "and", "and": "и",
"privacyPolicy": "privacy policy" "privacyPolicy": "политика конфиденциальности"
}, },
"siteRequired": "Site is required.", "siteRequired": "Необходимо указать сайт.",
"olmTunnel": "Olm Tunnel", "olmTunnel": "Olm Tunnel",
"olmTunnelDescription": "Use Olm for client connectivity", "olmTunnelDescription": "Use Olm for client connectivity",
"errorCreatingClient": "Error creating client", "errorCreatingClient": "Error creating client",

View File

@@ -1022,6 +1022,11 @@
"actionDeleteIdpOrg": "Kimlik Sağlayıcı Organizasyon Politikasını Sil", "actionDeleteIdpOrg": "Kimlik Sağlayıcı Organizasyon Politikasını Sil",
"actionListIdpOrgs": "Kimlik Sağlayıcı Organizasyonları Listele", "actionListIdpOrgs": "Kimlik Sağlayıcı Organizasyonları Listele",
"actionUpdateIdpOrg": "Kimlik Sağlayıcı Organizasyonu Güncelle", "actionUpdateIdpOrg": "Kimlik Sağlayıcı Organizasyonu Güncelle",
"actionCreateClient": "Müşteri Oluştur",
"actionDeleteClient": "Müşteri Sil",
"actionUpdateClient": "Müşteri Güncelle",
"actionListClients": "Müşterileri Listele",
"actionGetClient": "Müşteriyi Al",
"noneSelected": "Hiçbiri seçili değil", "noneSelected": "Hiçbiri seçili değil",
"orgNotFound2": "Hiçbir organizasyon bulunamadı.", "orgNotFound2": "Hiçbir organizasyon bulunamadı.",
"searchProgress": "Ara...", "searchProgress": "Ara...",

View File

@@ -1022,6 +1022,11 @@
"actionDeleteIdpOrg": "删除 IDP组织策略", "actionDeleteIdpOrg": "删除 IDP组织策略",
"actionListIdpOrgs": "列出 IDP组织", "actionListIdpOrgs": "列出 IDP组织",
"actionUpdateIdpOrg": "更新 IDP组织", "actionUpdateIdpOrg": "更新 IDP组织",
"actionCreateClient": "创建客户端",
"actionDeleteClient": "删除客户端",
"actionUpdateClient": "更新客户端",
"actionListClients": "列出客户端",
"actionGetClient": "获取客户端",
"noneSelected": "未选择", "noneSelected": "未选择",
"orgNotFound2": "未找到组织。", "orgNotFound2": "未找到组织。",
"searchProgress": "搜索中...", "searchProgress": "搜索中...",

View File

@@ -88,7 +88,7 @@ export const WelcomeQuickStart = ({
To learn how to use Newt, including more To learn how to use Newt, including more
installation methods, visit the{" "} installation methods, visit the{" "}
<a <a
href="https://docs.fossorial.io" href="https://docs.digpangolin.com/manage/sites/install-site"
className="underline" className="underline"
> >
docs docs

View File

@@ -30,12 +30,6 @@ export class Config {
throw new Error(`Invalid configuration file: ${errors}`); throw new Error(`Invalid configuration file: ${errors}`);
} }
if (process.env.APP_BASE_DOMAIN) {
console.log(
"WARNING: You're using deprecated environment variables. Transition to the configuration file. https://docs.fossorial.io/"
);
}
if ( if (
// @ts-ignore // @ts-ignore
parsedConfig.users || parsedConfig.users ||

View File

@@ -287,7 +287,7 @@ export function readConfigFile() {
if (!environment) { if (!environment) {
throw new Error( throw new Error(
"No configuration file found. Please create one. https://docs.fossorial.io/" "No configuration file found. Please create one. https://docs.digpangolin.com/self-host/advanced/config-file"
); );
} }

View File

@@ -35,6 +35,11 @@ export async function verifyApiKeyApiKeyAccess(
); );
} }
if (callerApiKey.isRoot) {
// Root keys can access any key in any org
return next();
}
const [callerApiKeyOrg] = await db const [callerApiKeyOrg] = await db
.select() .select()
.from(apiKeyOrg) .from(apiKeyOrg)

View File

@@ -28,6 +28,11 @@ export async function verifyApiKeyClientAccess(
); );
} }
if (apiKey.isRoot) {
// Root keys can access any key in any org
return next();
}
const client = await db const client = await db
.select() .select()
.from(clients) .from(clients)

View File

@@ -27,6 +27,11 @@ export async function verifyApiKeyOrgAccess(
); );
} }
if (req.apiKey?.isRoot) {
// Root keys can access any key in any org
return next();
}
if (!req.apiKeyOrg) { if (!req.apiKeyOrg) {
const apiKeyOrgRes = await db const apiKeyOrgRes = await db
.select() .select()

View File

@@ -37,6 +37,11 @@ export async function verifyApiKeyResourceAccess(
); );
} }
if (apiKey.isRoot) {
// Root keys can access any key in any org
return next();
}
if (!resource.orgId) { if (!resource.orgId) {
return next( return next(
createHttpError( createHttpError(

View File

@@ -45,6 +45,11 @@ export async function verifyApiKeyRoleAccess(
); );
} }
if (apiKey.isRoot) {
// Root keys can access any key in any org
return next();
}
const orgIds = new Set(rolesData.map((role) => role.orgId)); const orgIds = new Set(rolesData.map((role) => role.orgId));
for (const role of rolesData) { for (const role of rolesData) {

View File

@@ -32,6 +32,11 @@ export async function verifyApiKeySetResourceUsers(
return next(createHttpError(HttpCode.BAD_REQUEST, "Invalid user IDs")); return next(createHttpError(HttpCode.BAD_REQUEST, "Invalid user IDs"));
} }
if (apiKey.isRoot) {
// Root keys can access any key in any org
return next();
}
if (userIds.length === 0) { if (userIds.length === 0) {
return next(); return next();
} }

View File

@@ -1,9 +1,6 @@
import { Request, Response, NextFunction } from "express"; import { Request, Response, NextFunction } from "express";
import { db } from "@server/db"; import { db } from "@server/db";
import { import { sites, apiKeyOrg } from "@server/db";
sites,
apiKeyOrg
} from "@server/db";
import { and, eq, or } from "drizzle-orm"; import { and, eq, or } from "drizzle-orm";
import createHttpError from "http-errors"; import createHttpError from "http-errors";
import HttpCode from "@server/types/HttpCode"; import HttpCode from "@server/types/HttpCode";
@@ -31,6 +28,11 @@ export async function verifyApiKeySiteAccess(
); );
} }
if (apiKey.isRoot) {
// Root keys can access any key in any org
return next();
}
const site = await db const site = await db
.select() .select()
.from(sites) .from(sites)

View File

@@ -66,6 +66,11 @@ export async function verifyApiKeyTargetAccess(
); );
} }
if (apiKey.isRoot) {
// Root keys can access any key in any org
return next();
}
if (!resource.orgId) { if (!resource.orgId) {
return next( return next(
createHttpError( createHttpError(

View File

@@ -27,6 +27,11 @@ export async function verifyApiKeyUserAccess(
); );
} }
if (apiKey.isRoot) {
// Root keys can access any key in any org
return next();
}
if (!req.apiKeyOrg || !req.apiKeyOrg.orgId) { if (!req.apiKeyOrg || !req.apiKeyOrg.orgId) {
return next( return next(
createHttpError( createHttpError(

View File

@@ -63,15 +63,6 @@ export async function createRootApiKey(
lastChars, lastChars,
isRoot: true isRoot: true
}); });
const allOrgs = await trx.select().from(orgs);
for (const org of allOrgs) {
await trx.insert(apiKeyOrg).values({
apiKeyId,
orgId: org.orgId
});
}
}); });
try { try {

View File

@@ -52,20 +52,26 @@ export async function exchangeSession(
try { try {
const { requestToken, host, requestIp } = parsedBody.data; const { requestToken, host, requestIp } = parsedBody.data;
let cleanHost = host;
// if the host ends with :port
if (cleanHost.match(/:[0-9]{1,5}$/)) {
let matched = ''+cleanHost.match(/:[0-9]{1,5}$/);
cleanHost = cleanHost.slice(0, -1*matched.length);
}
const clientIp = requestIp?.split(":")[0]; const clientIp = requestIp?.split(":")[0];
const [resource] = await db const [resource] = await db
.select() .select()
.from(resources) .from(resources)
.where(eq(resources.fullDomain, host)) .where(eq(resources.fullDomain, cleanHost))
.limit(1); .limit(1);
if (!resource) { if (!resource) {
return next( return next(
createHttpError( createHttpError(
HttpCode.NOT_FOUND, HttpCode.NOT_FOUND,
`Resource with host ${host} not found` `Resource with host ${cleanHost} not found`
) )
); );
} }

View File

@@ -121,11 +121,10 @@ export async function verifyResourceSession(
logger.debug("Client IP:", { clientIp }); logger.debug("Client IP:", { clientIp });
let cleanHost = host; let cleanHost = host;
// if the host ends with :443 or :80 remove it // if the host ends with :port, strip it
if (cleanHost.endsWith(":443")) { if (cleanHost.match(/:[0-9]{1,5}$/)) {
cleanHost = cleanHost.slice(0, -4); let matched = ''+cleanHost.match(/:[0-9]{1,5}$/);
} else if (cleanHost.endsWith(":80")) { cleanHost = cleanHost.slice(0, -1*matched.length);
cleanHost = cleanHost.slice(0, -3);
} }
const resourceCacheKey = `resource:${cleanHost}`; const resourceCacheKey = `resource:${cleanHost}`;

View File

@@ -234,18 +234,6 @@ export async function createOrg(
orgId orgId
})) }))
); );
const rootApiKeys = await trx
.select()
.from(apiKeys)
.where(eq(apiKeys.isRoot, true));
for (const apiKey of rootApiKeys) {
await trx.insert(apiKeyOrg).values({
apiKeyId: apiKey.apiKeyId,
orgId: newOrg[0].orgId
});
}
}); });
if (!org) { if (!org) {

View File

@@ -78,7 +78,7 @@ export default async function migration() {
fs.writeFileSync(filePath, updatedYaml, "utf8"); fs.writeFileSync(filePath, updatedYaml, "utf8");
} catch (e) { } catch (e) {
console.log( console.log(
`Failed to add resource_session_request_param to config. Please add it manually. https://docs.fossorial.io/Pangolin/Configuration/config` `Failed to add resource_session_request_param to config. Please add it manually. https://docs.digpangolin.com/self-host/advanced/config-file`
); );
trx.rollback(); trx.rollback();
return; return;

View File

@@ -63,7 +63,7 @@ export default async function migration() {
console.log(`Added new config option: resource_access_token_headers`); console.log(`Added new config option: resource_access_token_headers`);
} catch (e) { } catch (e) {
console.log( console.log(
`Unable to add new config option: resource_access_token_headers. Please add it manually. https://docs.fossorial.io/Pangolin/Configuration/config` `Unable to add new config option: resource_access_token_headers. Please add it manually. https://docs.digpangolin.com/self-host/advanced/config-file`
); );
console.error(e); console.error(e);
} }

View File

@@ -734,7 +734,7 @@ export default function Page() {
<Link <Link
className="text-sm text-primary flex items-center gap-1" className="text-sm text-primary flex items-center gap-1"
href="https://docs.fossorial.io/Pangolin/tcp-udp" href="https://docs.digpangolin.com/manage/resources/tcp-udp-resources"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
> >

View File

@@ -64,7 +64,7 @@ export const SitesSplashCard = () => {
<div className="mt-4"> <div className="mt-4">
<Link <Link
href="https://docs.fossorial.io/Newt/install" href="https://docs.digpangolin.com/manage/sites/install-site"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
> >

View File

@@ -208,7 +208,7 @@ export default function GeneralPage() {
"enableDockerSocketDescription" "enableDockerSocketDescription"
)}{" "} )}{" "}
<Link <Link
href="https://docs.fossorial.io/Newt/overview#docker-socket-integration" href="https://docs.digpangolin.com/manage/sites/configure-site#docker-socket-integration"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
className="text-primary hover:underline inline-flex items-center" className="text-primary hover:underline inline-flex items-center"

View File

@@ -326,7 +326,7 @@ export default function PoliciesPage() {
{/*TODO(vlalx): Validate replacing */} {/*TODO(vlalx): Validate replacing */}
{t('orgPoliciesAboutDescription')}{" "} {t('orgPoliciesAboutDescription')}{" "}
<Link <Link
href="https://docs.fossorial.io/Pangolin/Identity%20Providers/auto-provision" href="https://docs.digpangolin.com/manage/identity-providers/auto-provisioning"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
className="text-primary hover:underline" className="text-primary hover:underline"

View File

@@ -59,9 +59,14 @@ export default async function ResourceAuthPage(props: {
try { try {
const serverResourceHost = new URL(authInfo.url).host; const serverResourceHost = new URL(authInfo.url).host;
const redirectHost = new URL(searchParams.redirect).host; const redirectHost = new URL(searchParams.redirect).host;
const redirectPort = new URL(searchParams.redirect).port;
const serverResourceHostWithPort = `${serverResourceHost}:${redirectPort}`;
if (serverResourceHost === redirectHost) { if (serverResourceHost === redirectHost) {
redirectUrl = searchParams.redirect; redirectUrl = searchParams.redirect;
} else if ( serverResourceHostWithPort === redirectHost ) {
redirectUrl = searchParams.redirect;
} }
} catch (e) {} } catch (e) {}
} }

View File

@@ -126,7 +126,7 @@ export function LayoutSidebar({
</div> </div>
<div className="text-xs text-muted-foreground "> <div className="text-xs text-muted-foreground ">
<Link <Link
href="https://docs.fossorial.io/" href="https://docs.digpangolin.com/"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
className="flex items-center justify-center gap-1" className="flex items-center justify-center gap-1"

View File

@@ -219,7 +219,7 @@ export default function SupporterStatus({ isCollapsed = false }: SupporterStatus
</Link>{" "} </Link>{" "}
{t('supportKeyPurchase2')}{" "} {t('supportKeyPurchase2')}{" "}
<Link <Link
href="https://docs.fossorial.io/supporter-program" href="https://docs.digpangolin.com/self-host/supporter-program"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
className="underline" className="underline"

View File

@@ -1,16 +1,36 @@
'use server'; 'use server';
import {cookies} from 'next/headers'; import { cookies, headers } from 'next/headers';
import {Locale, defaultLocale} from '@/i18n/config'; import { Locale, defaultLocale, locales } from '@/i18n/config';
// In this example the locale is read from a cookie. You could alternatively // In this example the locale is read from a cookie. You could alternatively
// also read it from a database, backend service, or any other source. // also read it from a database, backend service, or any other source.
const COOKIE_NAME = 'NEXT_LOCALE'; const COOKIE_NAME = 'NEXT_LOCALE';
export async function getUserLocale() { export async function getUserLocale(): Promise<Locale> {
return (await cookies()).get(COOKIE_NAME)?.value || defaultLocale; const cookieLocale = (await cookies()).get(COOKIE_NAME)?.value;
if (cookieLocale && locales.includes(cookieLocale as Locale)) {
return cookieLocale as Locale;
}
const headerList = await headers();
const acceptLang = headerList.get('accept-language');
if (acceptLang) {
const browserLang = acceptLang.split(',')[0];
const matched = locales.find((locale) =>
browserLang.toLowerCase().startsWith(locale.split('-')[0].toLowerCase())
);
if (matched) {
return matched;
}
}
return defaultLocale;
} }
export async function setUserLocale(locale: Locale) { export async function setUserLocale(locale: Locale) {
(await cookies()).set(COOKIE_NAME, locale); (await cookies()).set(COOKIE_NAME, locale);
} }