mirror of
https://github.com/fosrl/pangolin.git
synced 2026-09-03 09:49:06 +02:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d2936cd10b | |||
| 2f013335f9 | |||
| b4f6ae74d7 | |||
| f1711ee0b0 |
+1
-9
@@ -1176,10 +1176,6 @@
|
||||
"idpJmespathAboutDescriptionLink": "Научете повече за JMESPath",
|
||||
"idpJmespathLabel": "Идентификатор на пътя",
|
||||
"idpJmespathLabelDescription": "Пътят към идентификатора на потребителя в ID токена",
|
||||
"idpIdentifierChangeTitle": "Identifier Path Change Warning",
|
||||
"idpIdentifierChangeDescription": "You are about to change the identifier path. This will affect how existing users are mapped. Users who previously signed in through this identity provider may no longer be recognized as the same users.",
|
||||
"idpIdentifierChangeConfirmMessage": "I confirm",
|
||||
"idpIdentifierChangeWarningText": "This will affect how existing users are mapped",
|
||||
"idpJmespathEmailPathOptional": "Път за имейл (по избор)",
|
||||
"idpJmespathEmailPathOptionalDescription": "Пътят до имейла на потребителя в ID токена",
|
||||
"idpJmespathNamePathOptional": "Път (по избор) на име",
|
||||
@@ -1577,8 +1573,6 @@
|
||||
"search": "Търси…",
|
||||
"searchPlaceholder": "Търсене...",
|
||||
"emptySearchOptions": "Няма намерени опции",
|
||||
"ipFilterSearchPlaceholder": "Enter an IP address…",
|
||||
"ipFilterEmptyMessage": "Enter an IP address to filter by",
|
||||
"create": "Създаване",
|
||||
"orgs": "Организации",
|
||||
"loginError": "Възникна неочаквана грешка. Моля, опитайте отново.",
|
||||
@@ -2602,7 +2596,6 @@
|
||||
"createDomainType": "Тип:",
|
||||
"createDomainName": "Име:",
|
||||
"createDomainValue": "Стойност:",
|
||||
"multiSelectFilterCount": "{count} selected",
|
||||
"createDomainCnameRecords": "CNAME записи",
|
||||
"createDomainARecords": "A записи",
|
||||
"createDomainRecordNumber": "Запис {number}",
|
||||
@@ -2717,7 +2710,6 @@
|
||||
"healthScheme": "Метод",
|
||||
"healthSelectScheme": "Избор на метод",
|
||||
"healthCheckPortInvalid": "Портът трябва да бъде между 1 и 65535",
|
||||
"healthCheckHostnameInvalid": "Hostname must not contain whitespace",
|
||||
"healthCheckPath": "Път",
|
||||
"healthHostname": "IP / Хост",
|
||||
"healthPort": "Порт",
|
||||
@@ -3001,7 +2993,7 @@
|
||||
"remoteExitNodeNetworkingSubnetsPlaceholder": "Добавете CIDR диапазон (напр. 10.0.0.0/8)",
|
||||
"remoteExitNodeNetworkingSubnetsLoadError": "Неуспешно зареждане на подмрежи",
|
||||
"remoteExitNodeNetworkingLabelsTitle": "Етикети за Предпочитания",
|
||||
"remoteExitNodeNetworkingLabelsDescription": "Sites with these labels will prefer to connect through this remote exit node.",
|
||||
"remoteExitNodeNetworkingLabelsDescription": "Сайтове с тези етикети ще бъдат принудени да се свържат чрез този отдалечен край.",
|
||||
"remoteExitNodeNetworkingLabelsButtonText": "Изберете етикети...",
|
||||
"remoteExitNodeNetworkingLabelsSearchPlaceholder": "Търсене на етикети...",
|
||||
"remoteExitNodeNetworkingLabelsLoadError": "Неуспешно зареждане на етикети",
|
||||
|
||||
+1
-9
@@ -1176,10 +1176,6 @@
|
||||
"idpJmespathAboutDescriptionLink": "Další informace o cestě JMESPath",
|
||||
"idpJmespathLabel": "Cesta identifikátoru",
|
||||
"idpJmespathLabelDescription": "Cesta k identifikátoru uživatele v tokenu ID",
|
||||
"idpIdentifierChangeTitle": "Identifier Path Change Warning",
|
||||
"idpIdentifierChangeDescription": "You are about to change the identifier path. This will affect how existing users are mapped. Users who previously signed in through this identity provider may no longer be recognized as the same users.",
|
||||
"idpIdentifierChangeConfirmMessage": "I confirm",
|
||||
"idpIdentifierChangeWarningText": "This will affect how existing users are mapped",
|
||||
"idpJmespathEmailPathOptional": "Cesta e-mailu (volitelné)",
|
||||
"idpJmespathEmailPathOptionalDescription": "Cesta k e-mailu uživatele v ID tokenu",
|
||||
"idpJmespathNamePathOptional": "Cesta k názvu (volitelné)",
|
||||
@@ -1577,8 +1573,6 @@
|
||||
"search": "Vyhledávání…",
|
||||
"searchPlaceholder": "Hledat...",
|
||||
"emptySearchOptions": "Nebyly nalezeny žádné možnosti",
|
||||
"ipFilterSearchPlaceholder": "Enter an IP address…",
|
||||
"ipFilterEmptyMessage": "Enter an IP address to filter by",
|
||||
"create": "Vytvořit",
|
||||
"orgs": "Organizace",
|
||||
"loginError": "Došlo k neočekávané chybě. Zkuste to prosím znovu.",
|
||||
@@ -2602,7 +2596,6 @@
|
||||
"createDomainType": "Typ:",
|
||||
"createDomainName": "Jméno:",
|
||||
"createDomainValue": "Hodnota:",
|
||||
"multiSelectFilterCount": "{count} selected",
|
||||
"createDomainCnameRecords": "Záznamy CNAME",
|
||||
"createDomainARecords": "Záznamy",
|
||||
"createDomainRecordNumber": "Nahrát {number}",
|
||||
@@ -2717,7 +2710,6 @@
|
||||
"healthScheme": "Způsob",
|
||||
"healthSelectScheme": "Vybrat metodu",
|
||||
"healthCheckPortInvalid": "Port musí být mezi 1 a 65535",
|
||||
"healthCheckHostnameInvalid": "Hostname must not contain whitespace",
|
||||
"healthCheckPath": "Cesta",
|
||||
"healthHostname": "IP / Hostitel",
|
||||
"healthPort": "Přístav",
|
||||
@@ -3001,7 +2993,7 @@
|
||||
"remoteExitNodeNetworkingSubnetsPlaceholder": "Přidejte rozsah CIDR (např. 10.0.0.0/8)",
|
||||
"remoteExitNodeNetworkingSubnetsLoadError": "Nepodařilo se načíst podsítě",
|
||||
"remoteExitNodeNetworkingLabelsTitle": "Názvy preferencí",
|
||||
"remoteExitNodeNetworkingLabelsDescription": "Sites with these labels will prefer to connect through this remote exit node.",
|
||||
"remoteExitNodeNetworkingLabelsDescription": "Weby s těmito názvy budou nuceny připojit se tímto vzdáleným výstupním uzlem.",
|
||||
"remoteExitNodeNetworkingLabelsButtonText": "Vyberte názvy...",
|
||||
"remoteExitNodeNetworkingLabelsSearchPlaceholder": "Hledat názvy...",
|
||||
"remoteExitNodeNetworkingLabelsLoadError": "Nepodařilo se načíst názvy",
|
||||
|
||||
+1
-9
@@ -1176,10 +1176,6 @@
|
||||
"idpJmespathAboutDescriptionLink": "Læs mere om JMESPath",
|
||||
"idpJmespathLabel": "Identifikatorsti",
|
||||
"idpJmespathLabelDescription": "Stien til brugeridentifikatoren i ID-tokenet",
|
||||
"idpIdentifierChangeTitle": "Identifier Path Change Warning",
|
||||
"idpIdentifierChangeDescription": "You are about to change the identifier path. This will affect how existing users are mapped. Users who previously signed in through this identity provider may no longer be recognized as the same users.",
|
||||
"idpIdentifierChangeConfirmMessage": "I confirm",
|
||||
"idpIdentifierChangeWarningText": "This will affect how existing users are mapped",
|
||||
"idpJmespathEmailPathOptional": "E-mailsti (Valgfrit)",
|
||||
"idpJmespathEmailPathOptionalDescription": "Stien til brugerens e-mailadresse i ID-tokenet",
|
||||
"idpJmespathNamePathOptional": "Navn Sti (Valgfrit)",
|
||||
@@ -1577,8 +1573,6 @@
|
||||
"search": "Søg…",
|
||||
"searchPlaceholder": "Søg...",
|
||||
"emptySearchOptions": "Ingen valg fundet",
|
||||
"ipFilterSearchPlaceholder": "Enter an IP address…",
|
||||
"ipFilterEmptyMessage": "Enter an IP address to filter by",
|
||||
"create": "Opret",
|
||||
"orgs": "Organisationer",
|
||||
"loginError": "Der opstod en uventet fejl. Prøv venligst igen.",
|
||||
@@ -2602,7 +2596,6 @@
|
||||
"createDomainType": "Type:",
|
||||
"createDomainName": "Navn:",
|
||||
"createDomainValue": "Værdi:",
|
||||
"multiSelectFilterCount": "{count} selected",
|
||||
"createDomainCnameRecords": "CNAME-poster",
|
||||
"createDomainARecords": "A-poster",
|
||||
"createDomainRecordNumber": "Post {number}",
|
||||
@@ -2717,7 +2710,6 @@
|
||||
"healthScheme": "Metode",
|
||||
"healthSelectScheme": "Vælg metode",
|
||||
"healthCheckPortInvalid": "Porten skal være mellem 1 og 65535",
|
||||
"healthCheckHostnameInvalid": "Hostname must not contain whitespace",
|
||||
"healthCheckPath": "Sti",
|
||||
"healthHostname": "IP / Vært",
|
||||
"healthPort": "Port",
|
||||
@@ -3001,7 +2993,7 @@
|
||||
"remoteExitNodeNetworkingSubnetsPlaceholder": "Tilføj et CIDR-område (f.eks. 10.0.0.0/8)",
|
||||
"remoteExitNodeNetworkingSubnetsLoadError": "Kunne ikke indlæse subnets",
|
||||
"remoteExitNodeNetworkingLabelsTitle": "Præference Etiketter",
|
||||
"remoteExitNodeNetworkingLabelsDescription": "Sites with these labels will prefer to connect through this remote exit node.",
|
||||
"remoteExitNodeNetworkingLabelsDescription": "Sites med disse etiketter vil blive tvunget til at oprette forbindelse gennem denne fjerne exit-node.",
|
||||
"remoteExitNodeNetworkingLabelsButtonText": "Vælg etiketter...",
|
||||
"remoteExitNodeNetworkingLabelsSearchPlaceholder": "Søg efter etiketter...",
|
||||
"remoteExitNodeNetworkingLabelsLoadError": "Kunne ikke indlæse etiketter",
|
||||
|
||||
+1
-9
@@ -1176,10 +1176,6 @@
|
||||
"idpJmespathAboutDescriptionLink": "Mehr über JMESPath erfahren",
|
||||
"idpJmespathLabel": "Identifikationspfad",
|
||||
"idpJmespathLabelDescription": "Der JMESPath zum Benutzeridentifikator im ID-Token",
|
||||
"idpIdentifierChangeTitle": "Identifier Path Change Warning",
|
||||
"idpIdentifierChangeDescription": "You are about to change the identifier path. This will affect how existing users are mapped. Users who previously signed in through this identity provider may no longer be recognized as the same users.",
|
||||
"idpIdentifierChangeConfirmMessage": "I confirm",
|
||||
"idpIdentifierChangeWarningText": "This will affect how existing users are mapped",
|
||||
"idpJmespathEmailPathOptional": "E-Mail-Pfad (Optional)",
|
||||
"idpJmespathEmailPathOptionalDescription": "Der JMESPath zur E-Mail-Adresse des Benutzers im ID-Token",
|
||||
"idpJmespathNamePathOptional": "Namenspfad (Optional)",
|
||||
@@ -1577,8 +1573,6 @@
|
||||
"search": "Suche…",
|
||||
"searchPlaceholder": "Suche...",
|
||||
"emptySearchOptions": "Keine Optionen gefunden",
|
||||
"ipFilterSearchPlaceholder": "Enter an IP address…",
|
||||
"ipFilterEmptyMessage": "Enter an IP address to filter by",
|
||||
"create": "Erstellen",
|
||||
"orgs": "Organisationen",
|
||||
"loginError": "Ein unerwarteter Fehler ist aufgetreten. Bitte versuchen Sie es erneut.",
|
||||
@@ -2602,7 +2596,6 @@
|
||||
"createDomainType": "Typ:",
|
||||
"createDomainName": "Name:",
|
||||
"createDomainValue": "Wert:",
|
||||
"multiSelectFilterCount": "{count} selected",
|
||||
"createDomainCnameRecords": "CNAME-Einträge",
|
||||
"createDomainARecords": "A-Aufzeichnungen",
|
||||
"createDomainRecordNumber": "Eintrag {number}",
|
||||
@@ -2717,7 +2710,6 @@
|
||||
"healthScheme": "Methode",
|
||||
"healthSelectScheme": "Methode auswählen",
|
||||
"healthCheckPortInvalid": "Der Port muss zwischen 1 und 65535 liegen",
|
||||
"healthCheckHostnameInvalid": "Hostname must not contain whitespace",
|
||||
"healthCheckPath": "Pfad",
|
||||
"healthHostname": "IP / Host",
|
||||
"healthPort": "Port",
|
||||
@@ -3001,7 +2993,7 @@
|
||||
"remoteExitNodeNetworkingSubnetsPlaceholder": "Fügen Sie einen CIDR-Bereich hinzu (z.B. 10.0.0.0/8)",
|
||||
"remoteExitNodeNetworkingSubnetsLoadError": "Fehler beim Laden der Subnetze",
|
||||
"remoteExitNodeNetworkingLabelsTitle": "Präferenzetiketten",
|
||||
"remoteExitNodeNetworkingLabelsDescription": "Sites with these labels will prefer to connect through this remote exit node.",
|
||||
"remoteExitNodeNetworkingLabelsDescription": "Standorte mit diesen Etiketten werden gezwungen, über diesen Remote Exit Node zu verbinden.",
|
||||
"remoteExitNodeNetworkingLabelsButtonText": "Etiketten auswählen...",
|
||||
"remoteExitNodeNetworkingLabelsSearchPlaceholder": "Etiketten suchen...",
|
||||
"remoteExitNodeNetworkingLabelsLoadError": "Fehler beim Laden der Etiketten",
|
||||
|
||||
+14
-1
@@ -1176,6 +1176,10 @@
|
||||
"idpJmespathAboutDescriptionLink": "Learn more about JMESPath",
|
||||
"idpJmespathLabel": "Identifier Path",
|
||||
"idpJmespathLabelDescription": "The path to the user identifier in the ID token",
|
||||
"idpIdentifierChangeTitle": "Identifier Path Change Warning",
|
||||
"idpIdentifierChangeDescription": "You are about to change the identifier path. This will affect how existing users are mapped. Users who previously signed in through this identity provider may no longer be recognized as the same users.",
|
||||
"idpIdentifierChangeConfirmMessage": "I confirm",
|
||||
"idpIdentifierChangeWarningText": "This will affect how existing users are mapped",
|
||||
"idpJmespathEmailPathOptional": "Email Path (Optional)",
|
||||
"idpJmespathEmailPathOptionalDescription": "The path to the user's email in the ID token",
|
||||
"idpJmespathNamePathOptional": "Name Path (Optional)",
|
||||
@@ -1420,6 +1424,15 @@
|
||||
"logoutError": "Error logging out",
|
||||
"signingAs": "Signed in as",
|
||||
"serverAdmin": "Server Admin",
|
||||
"promoteServerAdmin": "Promote to Server admin",
|
||||
"promoteServerAdminTitle": "Promote to Server Admin",
|
||||
"promoteServerAdminQuestion": "Are you sure you want to promote {selectedUser} to server admin?",
|
||||
"promoteServerAdminMessage": "Server admins have full access to every organization, user, and setting on this instance.",
|
||||
"promoteServerAdminWarning": "You cannot demote a server admin from this page.",
|
||||
"promoteServerAdminConfirm": "Promote to server admin",
|
||||
"promoteServerAdminSuccess": "User promoted",
|
||||
"promoteServerAdminSuccessDescription": "{selectedUser} is now a server admin.",
|
||||
"promoteServerAdminError": "Failed to promote user",
|
||||
"managedSelfhosted": "Managed Self-Hosted",
|
||||
"otpEnable": "Enable Two-factor",
|
||||
"otpDisable": "Disable Two-factor",
|
||||
@@ -2996,7 +3009,7 @@
|
||||
"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.",
|
||||
"remoteExitNodeNetworkingLabelsDescription": "Sites with these labels will prefer to connect through this remote exit node.",
|
||||
"remoteExitNodeNetworkingLabelsButtonText": "Select labels...",
|
||||
"remoteExitNodeNetworkingLabelsSearchPlaceholder": "Search labels...",
|
||||
"remoteExitNodeNetworkingLabelsLoadError": "Failed to load labels",
|
||||
|
||||
+1
-9
@@ -1176,10 +1176,6 @@
|
||||
"idpJmespathAboutDescriptionLink": "Más información sobre JMESPath",
|
||||
"idpJmespathLabel": "Ruta del identificador",
|
||||
"idpJmespathLabelDescription": "La ruta al identificador de usuario en el token de ID",
|
||||
"idpIdentifierChangeTitle": "Identifier Path Change Warning",
|
||||
"idpIdentifierChangeDescription": "You are about to change the identifier path. This will affect how existing users are mapped. Users who previously signed in through this identity provider may no longer be recognized as the same users.",
|
||||
"idpIdentifierChangeConfirmMessage": "I confirm",
|
||||
"idpIdentifierChangeWarningText": "This will affect how existing users are mapped",
|
||||
"idpJmespathEmailPathOptional": "Ruta de correo (opcional)",
|
||||
"idpJmespathEmailPathOptionalDescription": "La ruta al correo electrónico del usuario en el token de ID",
|
||||
"idpJmespathNamePathOptional": "Ruta del nombre (opcional)",
|
||||
@@ -1577,8 +1573,6 @@
|
||||
"search": "Buscar…",
|
||||
"searchPlaceholder": "Buscar...",
|
||||
"emptySearchOptions": "No se encontraron opciones",
|
||||
"ipFilterSearchPlaceholder": "Enter an IP address…",
|
||||
"ipFilterEmptyMessage": "Enter an IP address to filter by",
|
||||
"create": "Crear",
|
||||
"orgs": "Organizaciones",
|
||||
"loginError": "Ocurrió un error inesperado. Por favor, inténtelo de nuevo.",
|
||||
@@ -2602,7 +2596,6 @@
|
||||
"createDomainType": "Tipo:",
|
||||
"createDomainName": "Nombre:",
|
||||
"createDomainValue": "Valor:",
|
||||
"multiSelectFilterCount": "{count} selected",
|
||||
"createDomainCnameRecords": "Registros CNAME",
|
||||
"createDomainARecords": "Registros A",
|
||||
"createDomainRecordNumber": "Registro {number}",
|
||||
@@ -2717,7 +2710,6 @@
|
||||
"healthScheme": "Método",
|
||||
"healthSelectScheme": "Seleccionar método",
|
||||
"healthCheckPortInvalid": "El puerto debe estar entre 1 y 65535",
|
||||
"healthCheckHostnameInvalid": "Hostname must not contain whitespace",
|
||||
"healthCheckPath": "Ruta",
|
||||
"healthHostname": "IP / Nombre del host",
|
||||
"healthPort": "Puerto",
|
||||
@@ -3001,7 +2993,7 @@
|
||||
"remoteExitNodeNetworkingSubnetsPlaceholder": "Añadir un rango CIDR (e.g. 10.0.0.0/8)",
|
||||
"remoteExitNodeNetworkingSubnetsLoadError": "Error al cargar las subredes",
|
||||
"remoteExitNodeNetworkingLabelsTitle": "Etiquetas de Preferencias",
|
||||
"remoteExitNodeNetworkingLabelsDescription": "Sites with these labels will prefer to connect through this remote exit node.",
|
||||
"remoteExitNodeNetworkingLabelsDescription": "Los sitios con estas etiquetas se verán obligados a conectarse a través de este nodo de salida remoto.",
|
||||
"remoteExitNodeNetworkingLabelsButtonText": "Seleccionar etiquetas...",
|
||||
"remoteExitNodeNetworkingLabelsSearchPlaceholder": "Buscar etiquetas...",
|
||||
"remoteExitNodeNetworkingLabelsLoadError": "Error al cargar las etiquetas",
|
||||
|
||||
+1
-9
@@ -1176,10 +1176,6 @@
|
||||
"idpJmespathAboutDescriptionLink": "En savoir plus sur JMESPath",
|
||||
"idpJmespathLabel": "Chemin d'identification",
|
||||
"idpJmespathLabelDescription": "Le JMESPath vers l'identifiant de l'utilisateur dans le jeton ID",
|
||||
"idpIdentifierChangeTitle": "Identifier Path Change Warning",
|
||||
"idpIdentifierChangeDescription": "You are about to change the identifier path. This will affect how existing users are mapped. Users who previously signed in through this identity provider may no longer be recognized as the same users.",
|
||||
"idpIdentifierChangeConfirmMessage": "I confirm",
|
||||
"idpIdentifierChangeWarningText": "This will affect how existing users are mapped",
|
||||
"idpJmespathEmailPathOptional": "Chemin de l'email (Optionnel)",
|
||||
"idpJmespathEmailPathOptionalDescription": "Le JMESPath vers l'email de l'utilisateur dans le jeton ID",
|
||||
"idpJmespathNamePathOptional": "Chemin du nom (Optionnel)",
|
||||
@@ -1577,8 +1573,6 @@
|
||||
"search": "Rechercher…",
|
||||
"searchPlaceholder": "Recherche...",
|
||||
"emptySearchOptions": "Aucune option trouvée",
|
||||
"ipFilterSearchPlaceholder": "Enter an IP address…",
|
||||
"ipFilterEmptyMessage": "Enter an IP address to filter by",
|
||||
"create": "Créer",
|
||||
"orgs": "Organisations",
|
||||
"loginError": "Une erreur inattendue s'est produite. Veuillez réessayer.",
|
||||
@@ -2602,7 +2596,6 @@
|
||||
"createDomainType": "Type :",
|
||||
"createDomainName": "Nom :",
|
||||
"createDomainValue": "Valeur :",
|
||||
"multiSelectFilterCount": "{count} selected",
|
||||
"createDomainCnameRecords": "Enregistrements CNAME",
|
||||
"createDomainARecords": "Enregistrements A",
|
||||
"createDomainRecordNumber": "Enregistrement {number}",
|
||||
@@ -2717,7 +2710,6 @@
|
||||
"healthScheme": "Méthode",
|
||||
"healthSelectScheme": "Sélectionnez la méthode",
|
||||
"healthCheckPortInvalid": "Le port doit être compris entre 1 et 65535",
|
||||
"healthCheckHostnameInvalid": "Hostname must not contain whitespace",
|
||||
"healthCheckPath": "Chemin d'accès",
|
||||
"healthHostname": "IP / Hôte",
|
||||
"healthPort": "Port",
|
||||
@@ -3001,7 +2993,7 @@
|
||||
"remoteExitNodeNetworkingSubnetsPlaceholder": "Ajouter une plage CIDR (par exemple 10.0.0.0/8)",
|
||||
"remoteExitNodeNetworkingSubnetsLoadError": "Échec du chargement des sous-réseaux",
|
||||
"remoteExitNodeNetworkingLabelsTitle": "Étiquettes de préférences",
|
||||
"remoteExitNodeNetworkingLabelsDescription": "Sites with these labels will prefer to connect through this remote exit node.",
|
||||
"remoteExitNodeNetworkingLabelsDescription": "Les sites avec ces étiquettes devront se connecter via ce nœud de sortie distant.",
|
||||
"remoteExitNodeNetworkingLabelsButtonText": "Sélectionner des étiquettes...",
|
||||
"remoteExitNodeNetworkingLabelsSearchPlaceholder": "Chercher des étiquettes...",
|
||||
"remoteExitNodeNetworkingLabelsLoadError": "Échec du chargement des étiquettes",
|
||||
|
||||
+1
-9
@@ -1176,10 +1176,6 @@
|
||||
"idpJmespathAboutDescriptionLink": "Scopri di più su JMESPath",
|
||||
"idpJmespathLabel": "Percorso Identificativo",
|
||||
"idpJmespathLabelDescription": "Il JMESPath per l'identificatore dell'utente nel token ID",
|
||||
"idpIdentifierChangeTitle": "Identifier Path Change Warning",
|
||||
"idpIdentifierChangeDescription": "You are about to change the identifier path. This will affect how existing users are mapped. Users who previously signed in through this identity provider may no longer be recognized as the same users.",
|
||||
"idpIdentifierChangeConfirmMessage": "I confirm",
|
||||
"idpIdentifierChangeWarningText": "This will affect how existing users are mapped",
|
||||
"idpJmespathEmailPathOptional": "Percorso Email (Opzionale)",
|
||||
"idpJmespathEmailPathOptionalDescription": "Il JMESPath per l'email dell'utente nel token ID",
|
||||
"idpJmespathNamePathOptional": "Percorso Nome (Opzionale)",
|
||||
@@ -1577,8 +1573,6 @@
|
||||
"search": "Cerca…",
|
||||
"searchPlaceholder": "Cerca...",
|
||||
"emptySearchOptions": "Nessuna opzione trovata",
|
||||
"ipFilterSearchPlaceholder": "Enter an IP address…",
|
||||
"ipFilterEmptyMessage": "Enter an IP address to filter by",
|
||||
"create": "Crea",
|
||||
"orgs": "Organizzazioni",
|
||||
"loginError": "Si è verificato un errore imprevisto. Riprova.",
|
||||
@@ -2602,7 +2596,6 @@
|
||||
"createDomainType": "Tipo:",
|
||||
"createDomainName": "Nome:",
|
||||
"createDomainValue": "Valore:",
|
||||
"multiSelectFilterCount": "{count} selected",
|
||||
"createDomainCnameRecords": "Record CNAME",
|
||||
"createDomainARecords": "Record A",
|
||||
"createDomainRecordNumber": "Record {number}",
|
||||
@@ -2717,7 +2710,6 @@
|
||||
"healthScheme": "Metodo",
|
||||
"healthSelectScheme": "Seleziona Metodo",
|
||||
"healthCheckPortInvalid": "La porta deve essere compresa tra 1 e 65535",
|
||||
"healthCheckHostnameInvalid": "Hostname must not contain whitespace",
|
||||
"healthCheckPath": "Percorso",
|
||||
"healthHostname": "IP / Nome host",
|
||||
"healthPort": "Porta",
|
||||
@@ -3001,7 +2993,7 @@
|
||||
"remoteExitNodeNetworkingSubnetsPlaceholder": "Aggiungi un intervallo CIDR (ad esempio 10.0.0.0/8)",
|
||||
"remoteExitNodeNetworkingSubnetsLoadError": "Caricamento sottoreti fallito",
|
||||
"remoteExitNodeNetworkingLabelsTitle": "Etichette Preferenze",
|
||||
"remoteExitNodeNetworkingLabelsDescription": "Sites with these labels will prefer to connect through this remote exit node.",
|
||||
"remoteExitNodeNetworkingLabelsDescription": "I siti con queste etichette saranno collegati attraverso questo nodo di uscita remoto.",
|
||||
"remoteExitNodeNetworkingLabelsButtonText": "Seleziona etichette...",
|
||||
"remoteExitNodeNetworkingLabelsSearchPlaceholder": "Cerca etichette...",
|
||||
"remoteExitNodeNetworkingLabelsLoadError": "Caricamento etichette fallito",
|
||||
|
||||
+1
-9
@@ -1176,10 +1176,6 @@
|
||||
"idpJmespathAboutDescriptionLink": "JMESPath에 대해 더 알아보기",
|
||||
"idpJmespathLabel": "식별자 경로",
|
||||
"idpJmespathLabelDescription": "ID 토큰에서 사용자 식별자에 대한 경로",
|
||||
"idpIdentifierChangeTitle": "Identifier Path Change Warning",
|
||||
"idpIdentifierChangeDescription": "You are about to change the identifier path. This will affect how existing users are mapped. Users who previously signed in through this identity provider may no longer be recognized as the same users.",
|
||||
"idpIdentifierChangeConfirmMessage": "I confirm",
|
||||
"idpIdentifierChangeWarningText": "This will affect how existing users are mapped",
|
||||
"idpJmespathEmailPathOptional": "이메일 경로 (선택 사항)",
|
||||
"idpJmespathEmailPathOptionalDescription": "ID 토큰에서 사용자의 이메일 경로",
|
||||
"idpJmespathNamePathOptional": "이름 경로 (선택 사항)",
|
||||
@@ -1577,8 +1573,6 @@
|
||||
"search": "검색…",
|
||||
"searchPlaceholder": "검색...",
|
||||
"emptySearchOptions": "옵션이 없습니다",
|
||||
"ipFilterSearchPlaceholder": "Enter an IP address…",
|
||||
"ipFilterEmptyMessage": "Enter an IP address to filter by",
|
||||
"create": "생성",
|
||||
"orgs": "조직",
|
||||
"loginError": "예기치 않은 오류가 발생했습니다. 다시 시도해주세요.",
|
||||
@@ -2602,7 +2596,6 @@
|
||||
"createDomainType": "유형:",
|
||||
"createDomainName": "이름:",
|
||||
"createDomainValue": "값:",
|
||||
"multiSelectFilterCount": "{count} selected",
|
||||
"createDomainCnameRecords": "CNAME 레코드",
|
||||
"createDomainARecords": "A 레코드",
|
||||
"createDomainRecordNumber": "레코드 {number}",
|
||||
@@ -2717,7 +2710,6 @@
|
||||
"healthScheme": "방법",
|
||||
"healthSelectScheme": "방법 선택",
|
||||
"healthCheckPortInvalid": "포트는 1에서 65535 사이여야 합니다",
|
||||
"healthCheckHostnameInvalid": "Hostname must not contain whitespace",
|
||||
"healthCheckPath": "경로",
|
||||
"healthHostname": "IP / 호스트",
|
||||
"healthPort": "포트",
|
||||
@@ -3001,7 +2993,7 @@
|
||||
"remoteExitNodeNetworkingSubnetsPlaceholder": "CIDR 범위 추가 (예: 10.0.0.0/8)",
|
||||
"remoteExitNodeNetworkingSubnetsLoadError": "서브넷 로드 실패",
|
||||
"remoteExitNodeNetworkingLabelsTitle": "우선순위 레이블",
|
||||
"remoteExitNodeNetworkingLabelsDescription": "Sites with these labels will prefer to connect through this remote exit node.",
|
||||
"remoteExitNodeNetworkingLabelsDescription": "이 레이블이 있는 사이트는 이 원격 출구 노드를 통해 연결됩니다.",
|
||||
"remoteExitNodeNetworkingLabelsButtonText": "레이블 선택...",
|
||||
"remoteExitNodeNetworkingLabelsSearchPlaceholder": "레이블 검색...",
|
||||
"remoteExitNodeNetworkingLabelsLoadError": "레이블 로드 실패",
|
||||
|
||||
+1
-9
@@ -1176,10 +1176,6 @@
|
||||
"idpJmespathAboutDescriptionLink": "Lær mer om JMESPath",
|
||||
"idpJmespathLabel": "Identifikatorsti",
|
||||
"idpJmespathLabelDescription": "Stien til brukeridentifikatoren i ID-tokenet",
|
||||
"idpIdentifierChangeTitle": "Identifier Path Change Warning",
|
||||
"idpIdentifierChangeDescription": "You are about to change the identifier path. This will affect how existing users are mapped. Users who previously signed in through this identity provider may no longer be recognized as the same users.",
|
||||
"idpIdentifierChangeConfirmMessage": "I confirm",
|
||||
"idpIdentifierChangeWarningText": "This will affect how existing users are mapped",
|
||||
"idpJmespathEmailPathOptional": "E-poststi (Valgfritt)",
|
||||
"idpJmespathEmailPathOptionalDescription": "Stien til brukerens e-postadresse i ID-tokenet",
|
||||
"idpJmespathNamePathOptional": "Navn Sti (Valgfritt)",
|
||||
@@ -1577,8 +1573,6 @@
|
||||
"search": "Søk…",
|
||||
"searchPlaceholder": "Søk...",
|
||||
"emptySearchOptions": "Ingen valg funnet",
|
||||
"ipFilterSearchPlaceholder": "Enter an IP address…",
|
||||
"ipFilterEmptyMessage": "Enter an IP address to filter by",
|
||||
"create": "Opprett",
|
||||
"orgs": "Organisasjoner",
|
||||
"loginError": "En uventet feil oppstod. Vennligst prøv igjen.",
|
||||
@@ -2602,7 +2596,6 @@
|
||||
"createDomainType": "Type:",
|
||||
"createDomainName": "Navn:",
|
||||
"createDomainValue": "Verdi:",
|
||||
"multiSelectFilterCount": "{count} selected",
|
||||
"createDomainCnameRecords": "CNAME-oppføringer",
|
||||
"createDomainARecords": "A-oppføringer",
|
||||
"createDomainRecordNumber": "Oppføring {number}",
|
||||
@@ -2717,7 +2710,6 @@
|
||||
"healthScheme": "Metode",
|
||||
"healthSelectScheme": "Velg metode",
|
||||
"healthCheckPortInvalid": "Porten må være mellom 1 og 65535",
|
||||
"healthCheckHostnameInvalid": "Hostname must not contain whitespace",
|
||||
"healthCheckPath": "Sti",
|
||||
"healthHostname": "IP / Vert",
|
||||
"healthPort": "Port",
|
||||
@@ -3001,7 +2993,7 @@
|
||||
"remoteExitNodeNetworkingSubnetsPlaceholder": "Legg til et CIDR-område (f.eks. 10.0.0.0/8)",
|
||||
"remoteExitNodeNetworkingSubnetsLoadError": "Feil ved lasting av subnett",
|
||||
"remoteExitNodeNetworkingLabelsTitle": "Preferanseetiketter",
|
||||
"remoteExitNodeNetworkingLabelsDescription": "Sites with these labels will prefer to connect through this remote exit node.",
|
||||
"remoteExitNodeNetworkingLabelsDescription": "Områder med disse etikettene vil bli tvunget til å koble gjennom denne fjerne utgangsnoden.",
|
||||
"remoteExitNodeNetworkingLabelsButtonText": "Velg etiketter...",
|
||||
"remoteExitNodeNetworkingLabelsSearchPlaceholder": "Søk etiketter...",
|
||||
"remoteExitNodeNetworkingLabelsLoadError": "Feil ved lasting av etiketter",
|
||||
|
||||
+1
-9
@@ -1176,10 +1176,6 @@
|
||||
"idpJmespathAboutDescriptionLink": "Meer informatie over JMESPath",
|
||||
"idpJmespathLabel": "ID pad",
|
||||
"idpJmespathLabelDescription": "Het pad naar het gebruiker-id in het ID-token",
|
||||
"idpIdentifierChangeTitle": "Identifier Path Change Warning",
|
||||
"idpIdentifierChangeDescription": "You are about to change the identifier path. This will affect how existing users are mapped. Users who previously signed in through this identity provider may no longer be recognized as the same users.",
|
||||
"idpIdentifierChangeConfirmMessage": "I confirm",
|
||||
"idpIdentifierChangeWarningText": "This will affect how existing users are mapped",
|
||||
"idpJmespathEmailPathOptional": "E-mail pad (optioneel)",
|
||||
"idpJmespathEmailPathOptionalDescription": "Het pad naar het e-mailadres van de gebruiker in het ID-token",
|
||||
"idpJmespathNamePathOptional": "Naam pad (optioneel)",
|
||||
@@ -1577,8 +1573,6 @@
|
||||
"search": "Zoeken…",
|
||||
"searchPlaceholder": "Zoeken...",
|
||||
"emptySearchOptions": "Geen opties gevonden",
|
||||
"ipFilterSearchPlaceholder": "Enter an IP address…",
|
||||
"ipFilterEmptyMessage": "Enter an IP address to filter by",
|
||||
"create": "Aanmaken",
|
||||
"orgs": "Organisaties",
|
||||
"loginError": "Er is een onverwachte fout opgetreden. Probeer het opnieuw.",
|
||||
@@ -2602,7 +2596,6 @@
|
||||
"createDomainType": "Type:",
|
||||
"createDomainName": "Naam:",
|
||||
"createDomainValue": "Waarde:",
|
||||
"multiSelectFilterCount": "{count} selected",
|
||||
"createDomainCnameRecords": "CNAME-records",
|
||||
"createDomainARecords": "A Records",
|
||||
"createDomainRecordNumber": "Record {number}",
|
||||
@@ -2717,7 +2710,6 @@
|
||||
"healthScheme": "Methode",
|
||||
"healthSelectScheme": "Selecteer methode",
|
||||
"healthCheckPortInvalid": "Poort moet tussen 1 en 65535 zijn",
|
||||
"healthCheckHostnameInvalid": "Hostname must not contain whitespace",
|
||||
"healthCheckPath": "Pad",
|
||||
"healthHostname": "IP / Hostnaam",
|
||||
"healthPort": "Poort",
|
||||
@@ -3001,7 +2993,7 @@
|
||||
"remoteExitNodeNetworkingSubnetsPlaceholder": "Voeg een CIDR-bereik toe (bijv. 10.0.0.0/8)",
|
||||
"remoteExitNodeNetworkingSubnetsLoadError": "Kon subnets niet laden",
|
||||
"remoteExitNodeNetworkingLabelsTitle": "Voorkeurslabels",
|
||||
"remoteExitNodeNetworkingLabelsDescription": "Sites with these labels will prefer to connect through this remote exit node.",
|
||||
"remoteExitNodeNetworkingLabelsDescription": "Sites met deze labels worden verplicht om verbinding te maken via dit externe exit-knooppunt.",
|
||||
"remoteExitNodeNetworkingLabelsButtonText": "Selecteer labels...",
|
||||
"remoteExitNodeNetworkingLabelsSearchPlaceholder": "Labels zoeken...",
|
||||
"remoteExitNodeNetworkingLabelsLoadError": "Kon labels niet laden",
|
||||
|
||||
+1
-9
@@ -1176,10 +1176,6 @@
|
||||
"idpJmespathAboutDescriptionLink": "Dowiedz się więcej o JMESPath",
|
||||
"idpJmespathLabel": "Ścieżka identyfikatora",
|
||||
"idpJmespathLabelDescription": "JMESPath do identyfikatora użytkownika w tokenie ID",
|
||||
"idpIdentifierChangeTitle": "Identifier Path Change Warning",
|
||||
"idpIdentifierChangeDescription": "You are about to change the identifier path. This will affect how existing users are mapped. Users who previously signed in through this identity provider may no longer be recognized as the same users.",
|
||||
"idpIdentifierChangeConfirmMessage": "I confirm",
|
||||
"idpIdentifierChangeWarningText": "This will affect how existing users are mapped",
|
||||
"idpJmespathEmailPathOptional": "Ścieżka email (Opcjonalnie)",
|
||||
"idpJmespathEmailPathOptionalDescription": "JMESPath do emaila użytkownika w tokenie ID",
|
||||
"idpJmespathNamePathOptional": "Ścieżka nazwy (Opcjonalnie)",
|
||||
@@ -1577,8 +1573,6 @@
|
||||
"search": "Szukaj…",
|
||||
"searchPlaceholder": "Szukaj...",
|
||||
"emptySearchOptions": "Nie znaleziono opcji",
|
||||
"ipFilterSearchPlaceholder": "Enter an IP address…",
|
||||
"ipFilterEmptyMessage": "Enter an IP address to filter by",
|
||||
"create": "Utwórz",
|
||||
"orgs": "Organizacje",
|
||||
"loginError": "Wystąpił nieoczekiwany błąd. Spróbuj ponownie.",
|
||||
@@ -2602,7 +2596,6 @@
|
||||
"createDomainType": "Typ:",
|
||||
"createDomainName": "Nazwa:",
|
||||
"createDomainValue": "Wartość:",
|
||||
"multiSelectFilterCount": "{count} selected",
|
||||
"createDomainCnameRecords": "Rekordy CNAME",
|
||||
"createDomainARecords": "Rekordy A",
|
||||
"createDomainRecordNumber": "Rekord {number}",
|
||||
@@ -2717,7 +2710,6 @@
|
||||
"healthScheme": "Metoda",
|
||||
"healthSelectScheme": "Wybierz metodę",
|
||||
"healthCheckPortInvalid": "Port musi być pomiędzy 1 a 65535",
|
||||
"healthCheckHostnameInvalid": "Hostname must not contain whitespace",
|
||||
"healthCheckPath": "Ścieżka",
|
||||
"healthHostname": "IP / Nazwa hosta",
|
||||
"healthPort": "Port",
|
||||
@@ -3001,7 +2993,7 @@
|
||||
"remoteExitNodeNetworkingSubnetsPlaceholder": "Dodaj zakres CIDR (np. 10.0.0.0/8)",
|
||||
"remoteExitNodeNetworkingSubnetsLoadError": "Nie udało się załadować podsieci",
|
||||
"remoteExitNodeNetworkingLabelsTitle": "Etykiety preferencji",
|
||||
"remoteExitNodeNetworkingLabelsDescription": "Sites with these labels will prefer to connect through this remote exit node.",
|
||||
"remoteExitNodeNetworkingLabelsDescription": "Strony z tymi etykietami będą zmuszone do połączenia się przez ten zdalny węzeł wyjściowy.",
|
||||
"remoteExitNodeNetworkingLabelsButtonText": "Wybierz etykiety...",
|
||||
"remoteExitNodeNetworkingLabelsSearchPlaceholder": "Szukaj etykiet...",
|
||||
"remoteExitNodeNetworkingLabelsLoadError": "Nie udało się załadować etykiet",
|
||||
|
||||
+1
-9
@@ -1176,10 +1176,6 @@
|
||||
"idpJmespathAboutDescriptionLink": "Saiba mais sobre JMESPath",
|
||||
"idpJmespathLabel": "Caminho do Identificador",
|
||||
"idpJmespathLabelDescription": "O JMESPath para o identificador do utilizador no token ID",
|
||||
"idpIdentifierChangeTitle": "Identifier Path Change Warning",
|
||||
"idpIdentifierChangeDescription": "You are about to change the identifier path. This will affect how existing users are mapped. Users who previously signed in through this identity provider may no longer be recognized as the same users.",
|
||||
"idpIdentifierChangeConfirmMessage": "I confirm",
|
||||
"idpIdentifierChangeWarningText": "This will affect how existing users are mapped",
|
||||
"idpJmespathEmailPathOptional": "Caminho do Email (Opcional)",
|
||||
"idpJmespathEmailPathOptionalDescription": "O JMESPath para o email do utilizador no token ID",
|
||||
"idpJmespathNamePathOptional": "Caminho do Nome (Opcional)",
|
||||
@@ -1577,8 +1573,6 @@
|
||||
"search": "Pesquisar…",
|
||||
"searchPlaceholder": "Buscar...",
|
||||
"emptySearchOptions": "Nenhuma opção encontrada",
|
||||
"ipFilterSearchPlaceholder": "Enter an IP address…",
|
||||
"ipFilterEmptyMessage": "Enter an IP address to filter by",
|
||||
"create": "Criar",
|
||||
"orgs": "Organizações",
|
||||
"loginError": "Ocorreu um erro inesperado. Por favor, tente novamente.",
|
||||
@@ -2602,7 +2596,6 @@
|
||||
"createDomainType": "Tipo:",
|
||||
"createDomainName": "Nome:",
|
||||
"createDomainValue": "Valor:",
|
||||
"multiSelectFilterCount": "{count} selected",
|
||||
"createDomainCnameRecords": "Registros CNAME",
|
||||
"createDomainARecords": "Registros A",
|
||||
"createDomainRecordNumber": "Registrar {number}",
|
||||
@@ -2717,7 +2710,6 @@
|
||||
"healthScheme": "Método",
|
||||
"healthSelectScheme": "Selecione o Método",
|
||||
"healthCheckPortInvalid": "A porta deve estar entre 1 e 65535",
|
||||
"healthCheckHostnameInvalid": "Hostname must not contain whitespace",
|
||||
"healthCheckPath": "Caminho",
|
||||
"healthHostname": "IP / Nome do Host",
|
||||
"healthPort": "Porta",
|
||||
@@ -3001,7 +2993,7 @@
|
||||
"remoteExitNodeNetworkingSubnetsPlaceholder": "Adicione um intervalo de CIDR (por exemplo, 10.0.0.0/8)",
|
||||
"remoteExitNodeNetworkingSubnetsLoadError": "Falha ao carregar sub-redes",
|
||||
"remoteExitNodeNetworkingLabelsTitle": "Etiquetas de Preferência",
|
||||
"remoteExitNodeNetworkingLabelsDescription": "Sites with these labels will prefer to connect through this remote exit node.",
|
||||
"remoteExitNodeNetworkingLabelsDescription": "Os sites com essas etiquetas serão forçados a se conectar através deste nó de saída remoto.",
|
||||
"remoteExitNodeNetworkingLabelsButtonText": "Selecionar etiquetas...",
|
||||
"remoteExitNodeNetworkingLabelsSearchPlaceholder": "Pesquisar etiquetas...",
|
||||
"remoteExitNodeNetworkingLabelsLoadError": "Falha ao carregar etiquetas",
|
||||
|
||||
+1
-9
@@ -1176,10 +1176,6 @@
|
||||
"idpJmespathAboutDescriptionLink": "Узнать больше о JMESPath",
|
||||
"idpJmespathLabel": "Путь идентификатора",
|
||||
"idpJmespathLabelDescription": "Путь к идентификатору пользователя в ID токене",
|
||||
"idpIdentifierChangeTitle": "Identifier Path Change Warning",
|
||||
"idpIdentifierChangeDescription": "You are about to change the identifier path. This will affect how existing users are mapped. Users who previously signed in through this identity provider may no longer be recognized as the same users.",
|
||||
"idpIdentifierChangeConfirmMessage": "I confirm",
|
||||
"idpIdentifierChangeWarningText": "This will affect how existing users are mapped",
|
||||
"idpJmespathEmailPathOptional": "Путь к email (необязательно)",
|
||||
"idpJmespathEmailPathOptionalDescription": "Путь к email пользователя в ID токене",
|
||||
"idpJmespathNamePathOptional": "Путь к имени (необязательно)",
|
||||
@@ -1577,8 +1573,6 @@
|
||||
"search": "Поиск…",
|
||||
"searchPlaceholder": "Поиск...",
|
||||
"emptySearchOptions": "Опции не найдены",
|
||||
"ipFilterSearchPlaceholder": "Enter an IP address…",
|
||||
"ipFilterEmptyMessage": "Enter an IP address to filter by",
|
||||
"create": "Создать",
|
||||
"orgs": "Организации",
|
||||
"loginError": "Произошла непредвиденная ошибка. Пожалуйста, попробуйте еще раз.",
|
||||
@@ -2602,7 +2596,6 @@
|
||||
"createDomainType": "Тип:",
|
||||
"createDomainName": "Имя:",
|
||||
"createDomainValue": "Значение:",
|
||||
"multiSelectFilterCount": "{count} selected",
|
||||
"createDomainCnameRecords": "CNAME Записи",
|
||||
"createDomainARecords": "A Записи",
|
||||
"createDomainRecordNumber": "Запись {number}",
|
||||
@@ -2717,7 +2710,6 @@
|
||||
"healthScheme": "Метод",
|
||||
"healthSelectScheme": "Выберите метод",
|
||||
"healthCheckPortInvalid": "Порт должен быть в диапазоне от 1 до 65535",
|
||||
"healthCheckHostnameInvalid": "Hostname must not contain whitespace",
|
||||
"healthCheckPath": "Путь",
|
||||
"healthHostname": "IP / хост",
|
||||
"healthPort": "Порт",
|
||||
@@ -3001,7 +2993,7 @@
|
||||
"remoteExitNodeNetworkingSubnetsPlaceholder": "Добавить диапазон CIDR (например, 10.0.0.0/8)",
|
||||
"remoteExitNodeNetworkingSubnetsLoadError": "Не удалось загрузить подсети",
|
||||
"remoteExitNodeNetworkingLabelsTitle": "Этикетки предпочтений",
|
||||
"remoteExitNodeNetworkingLabelsDescription": "Sites with these labels will prefer to connect through this remote exit node.",
|
||||
"remoteExitNodeNetworkingLabelsDescription": "Сайты с этими метками будут обязаны подключаться через этот удаленный узел выхода.",
|
||||
"remoteExitNodeNetworkingLabelsButtonText": "Выберите метки...",
|
||||
"remoteExitNodeNetworkingLabelsSearchPlaceholder": "Поиск меток...",
|
||||
"remoteExitNodeNetworkingLabelsLoadError": "Не удалось загрузить метки",
|
||||
|
||||
+1
-9
@@ -1176,10 +1176,6 @@
|
||||
"idpJmespathAboutDescriptionLink": "JMESPath hakkında daha fazla bilgi edinin",
|
||||
"idpJmespathLabel": "Tanımlayıcı Yolu",
|
||||
"idpJmespathLabelDescription": "The JMESPath to the user identifier in the ID token",
|
||||
"idpIdentifierChangeTitle": "Identifier Path Change Warning",
|
||||
"idpIdentifierChangeDescription": "You are about to change the identifier path. This will affect how existing users are mapped. Users who previously signed in through this identity provider may no longer be recognized as the same users.",
|
||||
"idpIdentifierChangeConfirmMessage": "I confirm",
|
||||
"idpIdentifierChangeWarningText": "This will affect how existing users are mapped",
|
||||
"idpJmespathEmailPathOptional": "E-posta Yolu (İsteğe Bağlı)",
|
||||
"idpJmespathEmailPathOptionalDescription": "The JMESPath to the user's email in the ID token",
|
||||
"idpJmespathNamePathOptional": "Ad Yolu (İsteğe Bağlı)",
|
||||
@@ -1577,8 +1573,6 @@
|
||||
"search": "Ara…",
|
||||
"searchPlaceholder": "Ara...",
|
||||
"emptySearchOptions": "Seçenek bulunamadı",
|
||||
"ipFilterSearchPlaceholder": "Enter an IP address…",
|
||||
"ipFilterEmptyMessage": "Enter an IP address to filter by",
|
||||
"create": "Oluştur",
|
||||
"orgs": "Organizasyonlar",
|
||||
"loginError": "Beklenmeyen bir hata oluştu. Lütfen tekrar deneyin.",
|
||||
@@ -2602,7 +2596,6 @@
|
||||
"createDomainType": "Tür:",
|
||||
"createDomainName": "Ad:",
|
||||
"createDomainValue": "Değer:",
|
||||
"multiSelectFilterCount": "{count} selected",
|
||||
"createDomainCnameRecords": "CNAME Kayıtları",
|
||||
"createDomainARecords": "A Kayıtları",
|
||||
"createDomainRecordNumber": "Kayıt {number}",
|
||||
@@ -2717,7 +2710,6 @@
|
||||
"healthScheme": "Yöntem",
|
||||
"healthSelectScheme": "Yöntem Seç",
|
||||
"healthCheckPortInvalid": "Bağlantı noktası 1 ile 65535 arasında olmalıdır",
|
||||
"healthCheckHostnameInvalid": "Hostname must not contain whitespace",
|
||||
"healthCheckPath": "Yol",
|
||||
"healthHostname": "IP / Hostname",
|
||||
"healthPort": "Bağlantı Noktası",
|
||||
@@ -3001,7 +2993,7 @@
|
||||
"remoteExitNodeNetworkingSubnetsPlaceholder": "Bir CIDR aralığı ekle (örneğin, 10.0.0.0/8)",
|
||||
"remoteExitNodeNetworkingSubnetsLoadError": "Alt ağlar yüklenemedi",
|
||||
"remoteExitNodeNetworkingLabelsTitle": "Tercih Etiketleri",
|
||||
"remoteExitNodeNetworkingLabelsDescription": "Sites with these labels will prefer to connect through this remote exit node.",
|
||||
"remoteExitNodeNetworkingLabelsDescription": "Bu etiketlere sahip siteler, bu uzak çıkış düğümü üzerinden bağlantı kurmaya zorlanacaktır.",
|
||||
"remoteExitNodeNetworkingLabelsButtonText": "Etiketleri seç...",
|
||||
"remoteExitNodeNetworkingLabelsSearchPlaceholder": "Etiketleri ara...",
|
||||
"remoteExitNodeNetworkingLabelsLoadError": "Etiketler yüklenemedi",
|
||||
|
||||
+1
-9
@@ -1176,10 +1176,6 @@
|
||||
"idpJmespathAboutDescriptionLink": "了解更多 JMESPath 信息",
|
||||
"idpJmespathLabel": "标识符路径",
|
||||
"idpJmespathLabelDescription": "ID 令牌中用户标识符的路径",
|
||||
"idpIdentifierChangeTitle": "Identifier Path Change Warning",
|
||||
"idpIdentifierChangeDescription": "You are about to change the identifier path. This will affect how existing users are mapped. Users who previously signed in through this identity provider may no longer be recognized as the same users.",
|
||||
"idpIdentifierChangeConfirmMessage": "I confirm",
|
||||
"idpIdentifierChangeWarningText": "This will affect how existing users are mapped",
|
||||
"idpJmespathEmailPathOptional": "邮箱路径(可选)",
|
||||
"idpJmespathEmailPathOptionalDescription": "ID 令牌中用户邮箱的路径",
|
||||
"idpJmespathNamePathOptional": "用户名路径(可选)",
|
||||
@@ -1577,8 +1573,6 @@
|
||||
"search": "搜索…",
|
||||
"searchPlaceholder": "搜索...",
|
||||
"emptySearchOptions": "未找到选项",
|
||||
"ipFilterSearchPlaceholder": "Enter an IP address…",
|
||||
"ipFilterEmptyMessage": "Enter an IP address to filter by",
|
||||
"create": "创建",
|
||||
"orgs": "组织",
|
||||
"loginError": "发生意外错误。请重试。",
|
||||
@@ -2602,7 +2596,6 @@
|
||||
"createDomainType": "类型:",
|
||||
"createDomainName": "名称:",
|
||||
"createDomainValue": "值:",
|
||||
"multiSelectFilterCount": "{count} selected",
|
||||
"createDomainCnameRecords": "CNAME 记录",
|
||||
"createDomainARecords": "A记录",
|
||||
"createDomainRecordNumber": "记录 {number}",
|
||||
@@ -2717,7 +2710,6 @@
|
||||
"healthScheme": "方法",
|
||||
"healthSelectScheme": "选择方法",
|
||||
"healthCheckPortInvalid": "端口必须在 1 和 65535 之间",
|
||||
"healthCheckHostnameInvalid": "Hostname must not contain whitespace",
|
||||
"healthCheckPath": "路径",
|
||||
"healthHostname": "IP / 主机",
|
||||
"healthPort": "端口",
|
||||
@@ -3001,7 +2993,7 @@
|
||||
"remoteExitNodeNetworkingSubnetsPlaceholder": "添加CIDR范围(例如10.0.0.0/8)",
|
||||
"remoteExitNodeNetworkingSubnetsLoadError": "无法加载子网",
|
||||
"remoteExitNodeNetworkingLabelsTitle": "首选标签",
|
||||
"remoteExitNodeNetworkingLabelsDescription": "Sites with these labels will prefer to connect through this remote exit node.",
|
||||
"remoteExitNodeNetworkingLabelsDescription": "拥有这些标签的站点将强制通过此远程出口节点连接。",
|
||||
"remoteExitNodeNetworkingLabelsButtonText": "选择标签……",
|
||||
"remoteExitNodeNetworkingLabelsSearchPlaceholder": "搜索标签……",
|
||||
"remoteExitNodeNetworkingLabelsLoadError": "无法加载标签",
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
import { and, asc, eq, or } from "drizzle-orm";
|
||||
import { Transaction, User, userOrgs, users } from "@server/db";
|
||||
|
||||
export async function findOrgUserByIdentifier(
|
||||
trx: Transaction,
|
||||
orgId: string,
|
||||
identifier: string
|
||||
): Promise<User | null> {
|
||||
const [match] = await trx
|
||||
.select()
|
||||
.from(users)
|
||||
.innerJoin(userOrgs, eq(users.userId, userOrgs.userId))
|
||||
.where(
|
||||
and(
|
||||
or(eq(users.username, identifier), eq(users.email, identifier)),
|
||||
eq(userOrgs.orgId, orgId)
|
||||
)
|
||||
)
|
||||
.orderBy(asc(users.dateCreated), asc(users.userId))
|
||||
.limit(1);
|
||||
|
||||
return match?.user ?? null;
|
||||
}
|
||||
|
||||
export async function resolveOrgUserIds(
|
||||
trx: Transaction,
|
||||
orgId: string,
|
||||
identifiers: string[]
|
||||
): Promise<string[]> {
|
||||
const userIds = new Set<string>();
|
||||
for (const identifier of identifiers) {
|
||||
const user = await findOrgUserByIdentifier(trx, orgId, identifier);
|
||||
if (user) {
|
||||
userIds.add(user.userId);
|
||||
}
|
||||
}
|
||||
return [...userIds];
|
||||
}
|
||||
@@ -11,15 +11,14 @@ import {
|
||||
siteNetworks,
|
||||
siteResources,
|
||||
Transaction,
|
||||
userOrgs,
|
||||
users,
|
||||
userSiteResources,
|
||||
networks
|
||||
} from "@server/db";
|
||||
import { sites } from "@server/db";
|
||||
import { eq, and, ne, inArray, or, isNotNull } from "drizzle-orm";
|
||||
import { eq, and, ne, inArray, isNotNull } from "drizzle-orm";
|
||||
import { Config } from "./types";
|
||||
import { getOrCreateLabelIds, syncSiteResourceLabels } from "./labels";
|
||||
import { resolveOrgUserIds } from "./findOrgUser";
|
||||
import logger from "@server/logger";
|
||||
import { defaultRoleAllowedActions } from "@server/routers/role/createRole";
|
||||
import { getNextAvailableAliasAddress } from "../ip";
|
||||
@@ -389,29 +388,23 @@ export async function updatePrivateResources(
|
||||
.where(eq(userSiteResources.siteResourceId, siteResourceId));
|
||||
|
||||
if (resourceData.users.length > 0) {
|
||||
// get userIds from username
|
||||
const usersToUpdate = await trx
|
||||
.select()
|
||||
.from(users)
|
||||
.innerJoin(userOrgs, eq(users.userId, userOrgs.userId))
|
||||
.where(
|
||||
and(
|
||||
or(
|
||||
inArray(users.username, resourceData.users),
|
||||
inArray(users.email, resourceData.users)
|
||||
),
|
||||
eq(userOrgs.orgId, orgId)
|
||||
)
|
||||
const userIds = await resolveOrgUserIds(
|
||||
trx,
|
||||
orgId,
|
||||
resourceData.users
|
||||
);
|
||||
|
||||
const userIds = usersToUpdate.map((user) => user.user.userId);
|
||||
|
||||
if (userIds.length > 0) {
|
||||
await trx
|
||||
.insert(userSiteResources)
|
||||
.values(
|
||||
userIds.map((userId) => ({ userId, siteResourceId }))
|
||||
userIds.map((userId) => ({
|
||||
userId,
|
||||
siteResourceId
|
||||
}))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Get all admin role IDs for this org to exclude from deletion
|
||||
const adminRoles = await trx
|
||||
@@ -721,29 +714,23 @@ export async function updatePrivateResources(
|
||||
}
|
||||
|
||||
if (resourceData.users.length > 0) {
|
||||
// get userIds from username
|
||||
const usersToUpdate = await trx
|
||||
.select()
|
||||
.from(users)
|
||||
.innerJoin(userOrgs, eq(users.userId, userOrgs.userId))
|
||||
.where(
|
||||
and(
|
||||
or(
|
||||
inArray(users.username, resourceData.users),
|
||||
inArray(users.email, resourceData.users)
|
||||
),
|
||||
eq(userOrgs.orgId, orgId)
|
||||
)
|
||||
const userIds = await resolveOrgUserIds(
|
||||
trx,
|
||||
orgId,
|
||||
resourceData.users
|
||||
);
|
||||
|
||||
const userIds = usersToUpdate.map((user) => user.user.userId);
|
||||
|
||||
if (userIds.length > 0) {
|
||||
await trx
|
||||
.insert(userSiteResources)
|
||||
.values(
|
||||
userIds.map((userId) => ({ userId, siteResourceId }))
|
||||
userIds.map((userId) => ({
|
||||
userId,
|
||||
siteResourceId
|
||||
}))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (resourceData.machines.length > 0) {
|
||||
// get clientIds from niceIds
|
||||
|
||||
@@ -46,11 +46,12 @@ import { encrypt } from "@server/lib/crypto";
|
||||
import logger from "@server/logger";
|
||||
import { defaultRoleAllowedActions } from "@server/routers/role/createRole";
|
||||
import { pickPort } from "@server/routers/target/helpers";
|
||||
import { and, asc, eq, isNotNull, ne, or } from "drizzle-orm";
|
||||
import { and, asc, eq, isNotNull, ne } from "drizzle-orm";
|
||||
import { tierMatrix } from "../billing/tierMatrix";
|
||||
import { isValidCIDR, isValidIP, isValidUrlGlobPattern } from "../validators";
|
||||
import { Config, isTargetsOnlyResource, TargetData } from "./types";
|
||||
import { getOrCreateLabelIds, syncResourceLabels } from "./labels";
|
||||
import { findOrgUserByIdentifier } from "./findOrgUser";
|
||||
import { LimitId } from "../billing";
|
||||
import { usageService } from "../billing/usageService";
|
||||
import { syncInferenceAiConfig } from "./aiProviders";
|
||||
@@ -1563,29 +1564,19 @@ async function syncUserResources(
|
||||
.where(eq(userResources.resourceId, resourceId));
|
||||
|
||||
for (const username of ssoUsers) {
|
||||
const [user] = await trx
|
||||
.select()
|
||||
.from(users)
|
||||
.innerJoin(userOrgs, eq(users.userId, userOrgs.userId))
|
||||
.where(
|
||||
and(
|
||||
or(eq(users.username, username), eq(users.email, username)),
|
||||
eq(userOrgs.orgId, orgId)
|
||||
)
|
||||
)
|
||||
.limit(1);
|
||||
const user = await findOrgUserByIdentifier(trx, orgId, username);
|
||||
|
||||
if (!user) {
|
||||
throw new Error(`User not found: ${username} in org ${orgId}`);
|
||||
}
|
||||
|
||||
const existingUserResource = existingUserResources.find(
|
||||
(rr) => rr.userId === user.user.userId
|
||||
(rr) => rr.userId === user.userId
|
||||
);
|
||||
|
||||
if (!existingUserResource) {
|
||||
await trx.insert(userResources).values({
|
||||
userId: user.user.userId,
|
||||
userId: user.userId,
|
||||
resourceId: resourceId
|
||||
});
|
||||
}
|
||||
@@ -1955,29 +1946,19 @@ async function syncUserPolicies(
|
||||
.where(eq(userPolicies.resourcePolicyId, policyId));
|
||||
|
||||
for (const username of ssoUsers) {
|
||||
const [user] = await trx
|
||||
.select()
|
||||
.from(users)
|
||||
.innerJoin(userOrgs, eq(users.userId, userOrgs.userId))
|
||||
.where(
|
||||
and(
|
||||
or(eq(users.username, username), eq(users.email, username)),
|
||||
eq(userOrgs.orgId, orgId)
|
||||
)
|
||||
)
|
||||
.limit(1);
|
||||
const user = await findOrgUserByIdentifier(trx, orgId, username);
|
||||
|
||||
if (!user) {
|
||||
throw new Error(`User not found: ${username} in org ${orgId}`);
|
||||
}
|
||||
|
||||
const existingUserPolicy = existingUserPoliciesList.find(
|
||||
(up) => up.userId === user.user.userId
|
||||
(up) => up.userId === user.userId
|
||||
);
|
||||
|
||||
if (!existingUserPolicy) {
|
||||
await trx.insert(userPolicies).values({
|
||||
userId: user.user.userId,
|
||||
userId: user.userId,
|
||||
resourcePolicyId: policyId
|
||||
});
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
userPolicies,
|
||||
users
|
||||
} from "@server/db";
|
||||
import { eq, and, or } from "drizzle-orm";
|
||||
import { eq, and } from "drizzle-orm";
|
||||
import { Config, ResourcePolicyData } from "./types";
|
||||
import logger from "@server/logger";
|
||||
import { getUniqueResourcePolicyName } from "@server/db/names";
|
||||
@@ -22,6 +22,7 @@ import { idpExistsForOrg } from "@server/lib/idp/idpExistsForOrg";
|
||||
import { isValidCIDR, isValidIP, isValidUrlGlobPattern } from "../validators";
|
||||
import { isLicensedOrSubscribed } from "#dynamic/lib/isLicencedOrSubscribed";
|
||||
import { tierMatrix } from "../billing/tierMatrix";
|
||||
import { findOrgUserByIdentifier } from "./findOrgUser";
|
||||
|
||||
export type ResourcePoliciesResults = {
|
||||
resourcePolicyId: number;
|
||||
@@ -466,17 +467,7 @@ async function syncUserPolicies(
|
||||
.where(eq(userPolicies.resourcePolicyId, policyId));
|
||||
|
||||
for (const username of ssoUsers) {
|
||||
const [user] = await trx
|
||||
.select()
|
||||
.from(users)
|
||||
.innerJoin(userOrgs, eq(users.userId, userOrgs.userId))
|
||||
.where(
|
||||
and(
|
||||
or(eq(users.username, username), eq(users.email, username)),
|
||||
eq(userOrgs.orgId, orgId)
|
||||
)
|
||||
)
|
||||
.limit(1);
|
||||
const user = await findOrgUserByIdentifier(trx, orgId, username);
|
||||
|
||||
if (!user) {
|
||||
logger.warn(
|
||||
@@ -486,12 +477,12 @@ async function syncUserPolicies(
|
||||
}
|
||||
|
||||
const alreadyExists = existingUserPolicies.some(
|
||||
(up) => up.userId === user.user.userId
|
||||
(up) => up.userId === user.userId
|
||||
);
|
||||
|
||||
if (!alreadyExists) {
|
||||
await trx.insert(userPolicies).values({
|
||||
userId: user.user.userId,
|
||||
userId: user.userId,
|
||||
resourcePolicyId: policyId
|
||||
});
|
||||
}
|
||||
@@ -536,17 +527,7 @@ async function addUserPolicies(
|
||||
trx: Transaction
|
||||
) {
|
||||
for (const username of ssoUsers) {
|
||||
const [user] = await trx
|
||||
.select()
|
||||
.from(users)
|
||||
.innerJoin(userOrgs, eq(users.userId, userOrgs.userId))
|
||||
.where(
|
||||
and(
|
||||
or(eq(users.username, username), eq(users.email, username)),
|
||||
eq(userOrgs.orgId, orgId)
|
||||
)
|
||||
)
|
||||
.limit(1);
|
||||
const user = await findOrgUserByIdentifier(trx, orgId, username);
|
||||
|
||||
if (!user) {
|
||||
logger.warn(
|
||||
@@ -556,7 +537,7 @@ async function addUserPolicies(
|
||||
}
|
||||
|
||||
await trx.insert(userPolicies).values({
|
||||
userId: user.user.userId,
|
||||
userId: user.userId,
|
||||
resourcePolicyId: policyId
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1378,6 +1378,12 @@ if (build !== "saas") {
|
||||
user.adminGeneratePasswordResetCode
|
||||
);
|
||||
|
||||
authenticated.post(
|
||||
"/user/:userId/promote-server-admin",
|
||||
verifyUserIsServerAdmin,
|
||||
user.adminPromoteServerAdmin
|
||||
);
|
||||
|
||||
authenticated.delete(
|
||||
"/user/:userId",
|
||||
verifyUserIsServerAdmin,
|
||||
|
||||
@@ -4,7 +4,7 @@ import { db, idp, users } from "@server/db";
|
||||
import response from "@server/lib/response";
|
||||
import HttpCode from "@server/types/HttpCode";
|
||||
import createHttpError from "http-errors";
|
||||
import { and, asc, desc, eq, like, or, sql } from "drizzle-orm";
|
||||
import { and, asc, desc, eq, like, or, sql, type SQL } from "drizzle-orm";
|
||||
import logger from "@server/logger";
|
||||
import { fromZodError } from "zod-validation-error";
|
||||
import { OpenAPITags, registry } from "@server/openApi";
|
||||
@@ -196,7 +196,7 @@ export async function adminListUsers(
|
||||
}
|
||||
}
|
||||
|
||||
const conditions = [eq(users.serverAdmin, false)];
|
||||
const conditions: Array<SQL<unknown> | undefined> = [];
|
||||
|
||||
if (query) {
|
||||
const q = "%" + query.toLowerCase() + "%";
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
import { Request, Response, NextFunction } from "express";
|
||||
import { z } from "zod";
|
||||
import { db, users } from "@server/db";
|
||||
import { eq } from "drizzle-orm";
|
||||
import response from "@server/lib/response";
|
||||
import HttpCode from "@server/types/HttpCode";
|
||||
import createHttpError from "http-errors";
|
||||
import logger from "@server/logger";
|
||||
import { fromError } from "zod-validation-error";
|
||||
import { OpenAPITags, registry } from "@server/openApi";
|
||||
import { createApiResponseSchema } from "@server/lib/openapi/createApiResponseSchema";
|
||||
|
||||
const promoteServerAdminParamsSchema = z.strictObject({
|
||||
userId: z.string()
|
||||
});
|
||||
|
||||
export type AdminPromoteServerAdminResponse = {
|
||||
userId: string;
|
||||
serverAdmin: boolean;
|
||||
};
|
||||
|
||||
const AdminPromoteServerAdminResponseDataSchema = z.object({
|
||||
userId: z.string(),
|
||||
serverAdmin: z.boolean()
|
||||
});
|
||||
|
||||
registry.registerPath({
|
||||
method: "post",
|
||||
path: "/user/{userId}/promote-server-admin",
|
||||
description: "Promote a user to server admin (server admin).",
|
||||
tags: [OpenAPITags.User],
|
||||
request: {
|
||||
params: promoteServerAdminParamsSchema
|
||||
},
|
||||
responses: {
|
||||
200: {
|
||||
description: "Successful response",
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: createApiResponseSchema(
|
||||
AdminPromoteServerAdminResponseDataSchema
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export async function adminPromoteServerAdmin(
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
): Promise<any> {
|
||||
try {
|
||||
const parsedParams = promoteServerAdminParamsSchema.safeParse(
|
||||
req.params
|
||||
);
|
||||
if (!parsedParams.success) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
fromError(parsedParams.error).toString()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const { userId } = parsedParams.data;
|
||||
|
||||
const [existingUser] = await db
|
||||
.select({
|
||||
userId: users.userId,
|
||||
serverAdmin: users.serverAdmin
|
||||
})
|
||||
.from(users)
|
||||
.where(eq(users.userId, userId))
|
||||
.limit(1);
|
||||
|
||||
if (!existingUser) {
|
||||
return next(createHttpError(HttpCode.NOT_FOUND, "User not found"));
|
||||
}
|
||||
|
||||
if (existingUser.serverAdmin) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
"User is already a server admin"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
logger.info(
|
||||
`Promoting user ${userId} to server admin (by ${req.user?.userId})`
|
||||
);
|
||||
|
||||
await db
|
||||
.update(users)
|
||||
.set({ serverAdmin: true })
|
||||
.where(eq(users.userId, userId));
|
||||
|
||||
return response<AdminPromoteServerAdminResponse>(res, {
|
||||
data: {
|
||||
userId: existingUser.userId,
|
||||
serverAdmin: true
|
||||
},
|
||||
success: true,
|
||||
error: false,
|
||||
message: "User promoted to server admin successfully",
|
||||
status: HttpCode.OK
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error(error);
|
||||
return next(
|
||||
createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred")
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ export * from "./adminListUsers";
|
||||
export * from "./adminRemoveUser";
|
||||
export * from "./adminGetUser";
|
||||
export * from "./adminGeneratePasswordResetCode";
|
||||
export * from "./adminPromoteServerAdmin";
|
||||
export * from "./listInvitations";
|
||||
export * from "./removeInvitation";
|
||||
export * from "./createOrgUser";
|
||||
|
||||
@@ -46,6 +46,7 @@ import { AxiosResponse } from "axios";
|
||||
import { ListRolesResponse } from "@server/routers/role";
|
||||
import AutoProvisionConfigWidget from "@app/components/AutoProvisionConfigWidget";
|
||||
import IdpAutoProvisionUsersDescription from "@app/components/IdpAutoProvisionUsersDescription";
|
||||
import IdpIdentifierChangeDialog from "@app/components/IdpIdentifierChangeDialog";
|
||||
import { PaidFeaturesAlert } from "@app/components/PaidFeaturesAlert";
|
||||
import { tierMatrix } from "@server/lib/billing/tierMatrix";
|
||||
import {
|
||||
@@ -75,6 +76,12 @@ export default function GeneralPage() {
|
||||
>([createMappingBuilderRule()]);
|
||||
const [rawRoleExpression, setRawRoleExpression] = useState("");
|
||||
const [variant, setVariant] = useState<"oidc" | "google" | "azure">("oidc");
|
||||
const [originalIdentifierPath, setOriginalIdentifierPath] = useState("");
|
||||
const [identifierConfirmOpen, setIdentifierConfirmOpen] = useState(false);
|
||||
const [pendingPayload, setPendingPayload] = useState<Record<
|
||||
string,
|
||||
unknown
|
||||
> | null>(null);
|
||||
|
||||
const dashboardRedirectUrl = `${env.app.dashboardUrl}/auth/idp/${idpId}/oidc/callback`;
|
||||
const [redirectUrl, setRedirectUrl] = useState(
|
||||
@@ -184,6 +191,9 @@ export default function GeneralPage() {
|
||||
const data = res.data.data;
|
||||
const roleMapping = data.idpOrg.roleMapping;
|
||||
const idpVariant = data.idpOidcConfig?.variant || "oidc";
|
||||
setOriginalIdentifierPath(
|
||||
data.idpOidcConfig?.identifierPath ?? "sub"
|
||||
);
|
||||
setRedirectUrl(res.data.data.redirectUrl);
|
||||
|
||||
// Set the variant
|
||||
@@ -378,18 +388,56 @@ export default function GeneralPage() {
|
||||
};
|
||||
}
|
||||
|
||||
const res = await api.post(
|
||||
`/org/${orgId}/idp/${idpId}/oidc`,
|
||||
payload
|
||||
);
|
||||
const nextIdentifierPath =
|
||||
variant === "oidc"
|
||||
? (data as OidcFormValues).identifierPath
|
||||
: undefined;
|
||||
|
||||
if (
|
||||
typeof nextIdentifierPath === "string" &&
|
||||
nextIdentifierPath !== originalIdentifierPath
|
||||
) {
|
||||
setPendingPayload(payload);
|
||||
setIdentifierConfirmOpen(true);
|
||||
return;
|
||||
}
|
||||
|
||||
await persistIdp(payload);
|
||||
} catch (e) {
|
||||
toast({
|
||||
title: t("error"),
|
||||
description: formatAxiosError(e),
|
||||
variant: "destructive"
|
||||
});
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
async function persistIdp(payload: Record<string, unknown>) {
|
||||
const res = await api.post(`/org/${orgId}/idp/${idpId}/oidc`, payload);
|
||||
|
||||
if (res.status === 200) {
|
||||
if (typeof payload.identifierPath === "string") {
|
||||
setOriginalIdentifierPath(payload.identifierPath);
|
||||
}
|
||||
toast({
|
||||
title: t("success"),
|
||||
description: t("idpUpdatedDescription")
|
||||
});
|
||||
router.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
async function confirmIdentifierChange() {
|
||||
if (!pendingPayload) {
|
||||
return;
|
||||
}
|
||||
|
||||
setLoading(true);
|
||||
try {
|
||||
await persistIdp(pendingPayload);
|
||||
setPendingPayload(null);
|
||||
} catch (e) {
|
||||
toast({
|
||||
title: t("error"),
|
||||
@@ -407,6 +455,16 @@ export default function GeneralPage() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<IdpIdentifierChangeDialog
|
||||
open={identifierConfirmOpen}
|
||||
setOpen={(open) => {
|
||||
setIdentifierConfirmOpen(open);
|
||||
if (!open) {
|
||||
setPendingPayload(null);
|
||||
}
|
||||
}}
|
||||
onConfirm={confirmIdentifierChange}
|
||||
/>
|
||||
<SettingsContainer>
|
||||
<SettingsSection>
|
||||
<SettingsSectionHeader>
|
||||
|
||||
@@ -41,6 +41,7 @@ import {
|
||||
} from "@app/components/InfoSection";
|
||||
import CopyToClipboard from "@app/components/CopyToClipboard";
|
||||
import IdpTypeBadge from "@app/components/IdpTypeBadge";
|
||||
import IdpIdentifierChangeDialog from "@app/components/IdpIdentifierChangeDialog";
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
export default function GeneralPage() {
|
||||
@@ -51,6 +52,12 @@ export default function GeneralPage() {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [initialLoading, setInitialLoading] = useState(true);
|
||||
const [variant, setVariant] = useState<"oidc" | "google" | "azure">("oidc");
|
||||
const [originalIdentifierPath, setOriginalIdentifierPath] = useState("");
|
||||
const [identifierConfirmOpen, setIdentifierConfirmOpen] = useState(false);
|
||||
const [pendingPayload, setPendingPayload] = useState<Record<
|
||||
string,
|
||||
unknown
|
||||
> | null>(null);
|
||||
|
||||
const redirectUrl = `${env.app.dashboardUrl}/auth/idp/${idpId}/oidc/callback`;
|
||||
const t = useTranslations();
|
||||
@@ -141,6 +148,9 @@ export default function GeneralPage() {
|
||||
| "google"
|
||||
| "azure") || "oidc";
|
||||
setVariant(idpVariant);
|
||||
setOriginalIdentifierPath(
|
||||
data.idpOidcConfig?.identifierPath ?? "sub"
|
||||
);
|
||||
|
||||
let tenantId = "";
|
||||
if (idpVariant === "azure" && data.idpOidcConfig?.authUrl) {
|
||||
@@ -258,15 +268,56 @@ export default function GeneralPage() {
|
||||
};
|
||||
}
|
||||
|
||||
const nextIdentifierPath =
|
||||
variant === "oidc"
|
||||
? (data as OidcFormValues).identifierPath
|
||||
: undefined;
|
||||
|
||||
if (
|
||||
typeof nextIdentifierPath === "string" &&
|
||||
nextIdentifierPath !== originalIdentifierPath
|
||||
) {
|
||||
setPendingPayload(payload);
|
||||
setIdentifierConfirmOpen(true);
|
||||
return;
|
||||
}
|
||||
|
||||
await persistIdp(payload);
|
||||
} catch (e) {
|
||||
toast({
|
||||
title: t("error"),
|
||||
description: formatAxiosError(e),
|
||||
variant: "destructive"
|
||||
});
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
async function persistIdp(payload: Record<string, unknown>) {
|
||||
const res = await api.post(`/idp/${idpId}/oidc`, payload);
|
||||
|
||||
if (res.status === 200) {
|
||||
if (typeof payload.identifierPath === "string") {
|
||||
setOriginalIdentifierPath(payload.identifierPath);
|
||||
}
|
||||
toast({
|
||||
title: t("success"),
|
||||
description: t("idpUpdatedDescription")
|
||||
});
|
||||
router.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
async function confirmIdentifierChange() {
|
||||
if (!pendingPayload) {
|
||||
return;
|
||||
}
|
||||
|
||||
setLoading(true);
|
||||
try {
|
||||
await persistIdp(pendingPayload);
|
||||
setPendingPayload(null);
|
||||
} catch (e) {
|
||||
toast({
|
||||
title: t("error"),
|
||||
@@ -284,6 +335,16 @@ export default function GeneralPage() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<IdpIdentifierChangeDialog
|
||||
open={identifierConfirmOpen}
|
||||
setOpen={(open) => {
|
||||
setIdentifierConfirmOpen(open);
|
||||
if (!open) {
|
||||
setPendingPayload(null);
|
||||
}
|
||||
}}
|
||||
onConfirm={confirmIdentifierChange}
|
||||
/>
|
||||
<SettingsContainer>
|
||||
<SettingsSection>
|
||||
<SettingsSectionHeader>
|
||||
|
||||
@@ -81,6 +81,9 @@ export default async function UsersPage(props: AdminUsersPageProps) {
|
||||
};
|
||||
});
|
||||
|
||||
console.log({
|
||||
userRows
|
||||
});
|
||||
return (
|
||||
<>
|
||||
<SettingsSectionTitle
|
||||
|
||||
@@ -19,7 +19,8 @@ import {
|
||||
ArrowRight,
|
||||
ArrowUp10Icon,
|
||||
ChevronsUpDownIcon,
|
||||
MoreHorizontal
|
||||
MoreHorizontal,
|
||||
ShieldUserIcon
|
||||
} from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useRouter } from "next/navigation";
|
||||
@@ -43,6 +44,14 @@ import {
|
||||
CredenzaClose
|
||||
} from "@app/components/Credenza";
|
||||
import CopyToClipboard from "@app/components/CopyToClipboard";
|
||||
import { Badge } from "./ui/badge";
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipProvider,
|
||||
TooltipTrigger
|
||||
} from "./ui/tooltip";
|
||||
import { useUserContext } from "@app/hooks/useUserContext";
|
||||
|
||||
export type GlobalUserRow = {
|
||||
id: string;
|
||||
@@ -90,6 +99,9 @@ export default function UsersTable({
|
||||
const [passwordResetCodeData, setPasswordResetCodeData] =
|
||||
useState<AdminGeneratePasswordResetCodeResponse | null>(null);
|
||||
const [isGeneratingCode, setIsGeneratingCode] = useState(false);
|
||||
const [isPromoteModalOpen, setIsPromoteModalOpen] = useState(false);
|
||||
const [promoting, setPromoting] = useState<GlobalUserRow | null>(null);
|
||||
const user = useUserContext();
|
||||
|
||||
const [isRefreshing, startTransition] = useTransition();
|
||||
const {
|
||||
@@ -184,6 +196,37 @@ export default function UsersTable({
|
||||
}
|
||||
};
|
||||
|
||||
const promoteToServerAdmin = async (user: GlobalUserRow) => {
|
||||
try {
|
||||
await api.post(`/user/${user.id}/promote-server-admin`);
|
||||
|
||||
toast({
|
||||
title: t("promoteServerAdminSuccess"),
|
||||
description: t("promoteServerAdminSuccessDescription", {
|
||||
selectedUser: getUserDisplayName({
|
||||
email: user.email,
|
||||
name: user.name,
|
||||
username: user.username
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
startTransition(() => {
|
||||
router.refresh();
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(t("promoteServerAdminError"), e);
|
||||
toast({
|
||||
variant: "destructive",
|
||||
title: t("promoteServerAdminError"),
|
||||
description: formatAxiosError(e, t("promoteServerAdminError"))
|
||||
});
|
||||
} finally {
|
||||
setIsPromoteModalOpen(false);
|
||||
setPromoting(null);
|
||||
}
|
||||
};
|
||||
|
||||
function toggleSort(column: string) {
|
||||
const newSearch = getNextSortOrder(column, searchParams);
|
||||
filter({
|
||||
@@ -235,7 +278,35 @@ export default function UsersTable({
|
||||
<Icon className="ml-2 h-4 w-4" />
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
},
|
||||
cell: ({ row }) => (
|
||||
<span className="inline-flex gap-1 items-center">
|
||||
{row.original.username}{" "}
|
||||
{row.original.id === user.user.userId && (
|
||||
<>
|
||||
<span className="text-muted-foreground">
|
||||
·
|
||||
</span>{" "}
|
||||
<span className="text-primary">you</span>
|
||||
</>
|
||||
)}
|
||||
{row.original.serverAdmin && (
|
||||
<>
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<ShieldUserIcon className="text-primary size-5 flex-none" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
{t("serverAdmin")}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
{/* <Badge>{t("serverAdmin")}</Badge> */}
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
)
|
||||
},
|
||||
{
|
||||
accessorKey: "email",
|
||||
@@ -369,11 +440,22 @@ export default function UsersTable({
|
||||
{t("generatePasswordResetCode")}
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
{!r.serverAdmin && (
|
||||
<DropdownMenuItem
|
||||
onClick={() => {
|
||||
setPromoting(r);
|
||||
setIsPromoteModalOpen(true);
|
||||
}}
|
||||
>
|
||||
{t("promoteServerAdmin")}
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
<DropdownMenuItem
|
||||
onClick={() => {
|
||||
setSelected(r);
|
||||
setIsDeleteModalOpen(true);
|
||||
}}
|
||||
className="text-red-400"
|
||||
>
|
||||
{t("delete")}
|
||||
</DropdownMenuItem>
|
||||
@@ -435,6 +517,42 @@ export default function UsersTable({
|
||||
/>
|
||||
)}
|
||||
|
||||
{promoting && (
|
||||
<ConfirmDeleteDialog
|
||||
open={isPromoteModalOpen}
|
||||
setOpen={(val) => {
|
||||
setIsPromoteModalOpen(val);
|
||||
if (!val) {
|
||||
setPromoting(null);
|
||||
}
|
||||
}}
|
||||
dialog={
|
||||
<div className="space-y-2">
|
||||
<p>
|
||||
{t("promoteServerAdminQuestion", {
|
||||
selectedUser: getUserDisplayName({
|
||||
email: promoting.email,
|
||||
name: promoting.name,
|
||||
username: promoting.username
|
||||
})
|
||||
})}
|
||||
</p>
|
||||
|
||||
<p>{t("promoteServerAdminMessage")}</p>
|
||||
</div>
|
||||
}
|
||||
buttonText={t("promoteServerAdminConfirm")}
|
||||
onConfirm={async () => promoteToServerAdmin(promoting)}
|
||||
string={getUserDisplayName({
|
||||
email: promoting.email,
|
||||
name: promoting.name,
|
||||
username: promoting.username
|
||||
})}
|
||||
warningText={t("promoteServerAdminWarning")}
|
||||
title={t("promoteServerAdminTitle")}
|
||||
/>
|
||||
)}
|
||||
|
||||
<ControlledDataTable
|
||||
columns={columns}
|
||||
rows={users}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
"use client";
|
||||
|
||||
import ConfirmDeleteDialog from "@app/components/ConfirmDeleteDialog";
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
type IdpIdentifierChangeDialogProps = {
|
||||
open: boolean;
|
||||
setOpen: (open: boolean) => void;
|
||||
onConfirm: () => Promise<void>;
|
||||
};
|
||||
|
||||
export default function IdpIdentifierChangeDialog({
|
||||
open,
|
||||
setOpen,
|
||||
onConfirm
|
||||
}: IdpIdentifierChangeDialogProps) {
|
||||
const t = useTranslations();
|
||||
|
||||
return (
|
||||
<ConfirmDeleteDialog
|
||||
open={open}
|
||||
setOpen={setOpen}
|
||||
dialog={
|
||||
<div className="space-y-2">
|
||||
<p>{t("idpIdentifierChangeDescription")}</p>
|
||||
</div>
|
||||
}
|
||||
buttonText={t("saveGeneralSettings")}
|
||||
onConfirm={onConfirm}
|
||||
string={t("idpIdentifierChangeConfirmMessage")}
|
||||
title={t("idpIdentifierChangeTitle")}
|
||||
warningText={t("idpIdentifierChangeWarningText")}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -155,13 +155,13 @@ export function CommandPalette({ orgId, orgs, navItems }: CommandPaletteProps) {
|
||||
}
|
||||
/>
|
||||
<CommandList className="max-h-118 min-h-0 h-(--cmdk-list-height) scroll-pb-4 scroll-pt-2 transition-[height] duration-250 ease-in-out">
|
||||
<CommandEmpty>{t("commandPaletteNoResults")}</CommandEmpty>
|
||||
|
||||
<CommandGroup
|
||||
heading={t("commandActionModeInfo")}
|
||||
className="[&_[cmdk-group-heading]]:text-sm"
|
||||
/>
|
||||
|
||||
<CommandEmpty>{t("commandPaletteNoResults")}</CommandEmpty>
|
||||
|
||||
{!isActionMode &&
|
||||
navigationGroups.map((group, groupIndex) => (
|
||||
<React.Fragment key={group.heading}>
|
||||
|
||||
Reference in New Issue
Block a user