diff --git a/install/Makefile b/install/Makefile index 9bde02cf..644e4a35 100644 --- a/install/Makefile +++ b/install/Makefile @@ -21,4 +21,4 @@ update-versions: echo "Updated main.go with latest versions" put-back: - mv main.go.bak main.go \ No newline at end of file + mv main.go.bak main.go diff --git a/install/config/config.yml b/install/config/config.yml index 9b19febb..74b3d009 100644 --- a/install/config/config.yml +++ b/install/config/config.yml @@ -4,7 +4,6 @@ app: dashboard_url: "https://{{.DashboardDomain}}" log_level: "info" - save_logs: false domains: domain1: @@ -12,44 +11,21 @@ domains: cert_resolver: "letsencrypt" server: - external_port: 3000 - internal_port: 3001 - next_port: 3002 - internal_hostname: "pangolin" - session_cookie_name: "p_session_token" - resource_access_token_param: "p_token" - resource_access_token_headers: - id: "P-Access-Token-Id" - token: "P-Access-Token" - resource_session_request_param: "p_session_request" - secret: {{.Secret}} + secret: "{{.Secret}}" cors: origins: ["https://{{.DashboardDomain}}"] methods: ["GET", "POST", "PUT", "DELETE", "PATCH"] allowed_headers: ["X-CSRF-Token", "Content-Type"] credentials: false -traefik: - cert_resolver: "letsencrypt" - http_entrypoint: "web" - https_entrypoint: "websecure" - gerbil: start_port: 51820 base_endpoint: "{{.DashboardDomain}}" - use_subdomain: false - block_size: 24 - site_block_size: 30 - subnet_group: 100.89.137.0/20 orgs: block_size: 24 subnet_group: 100.89.138.0/20 -rate_limits: - global: - window_minutes: 1 - max_requests: 500 {{if .EnableEmail}} email: smtp_host: "{{.EmailSMTPHost}}" @@ -61,7 +37,7 @@ email: flags: require_email_verification: {{.EnableEmail}} - disable_signup_without_invite: {{.DisableSignupWithoutInvite}} - disable_user_create_org: {{.DisableUserCreateOrg}} + disable_signup_without_invite: true + disable_user_create_org: false allow_raw_resources: true allow_base_domain_resources: true diff --git a/install/main.go b/install/main.go index 5af3cbf7..38aa6f63 100644 --- a/install/main.go +++ b/install/main.go @@ -39,8 +39,6 @@ type Config struct { BaseDomain string DashboardDomain string LetsEncryptEmail string - DisableSignupWithoutInvite bool - DisableUserCreateOrg bool EnableEmail bool EmailSMTPHost string EmailSMTPPort int @@ -72,15 +70,15 @@ func main() { } var config Config - + // check if there is already a config file if _, err := os.Stat("config/config.yml"); err != nil { config = collectUserInput(reader) - + loadVersions(&config) config.DoCrowdsecInstall = false config.Secret = generateRandomSecretKey() - + if err := createConfigFiles(config); err != nil { fmt.Printf("Error creating config files: %v\n", err) os.Exit(1) @@ -234,14 +232,9 @@ func collectUserInput(reader *bufio.Reader) Config { config.LetsEncryptEmail = readString(reader, "Enter email for Let's Encrypt certificates", "") config.InstallGerbil = readBool(reader, "Do you want to use Gerbil to allow tunneled connections", true) - // Security settings - fmt.Println("\n=== Security Settings ===") - config.DisableSignupWithoutInvite = readBool(reader, "Disable signup without invite", true) - config.DisableUserCreateOrg = readBool(reader, "Disable users from creating organizations", false) - // Email configuration fmt.Println("\n=== Email Configuration ===") - config.EnableEmail = readBool(reader, "Enable email functionality", false) + config.EnableEmail = readBool(reader, "Enable email functionality (SMTP)", false) if config.EnableEmail { config.EmailSMTPHost = readString(reader, "Enter SMTP host", "") @@ -353,7 +346,7 @@ func installDocker() error { return fmt.Errorf("failed to detect Linux distribution: %v", err) } osRelease := string(output) - + // Detect system architecture archCmd := exec.Command("uname", "-m") archOutput, err := archCmd.Output() @@ -361,7 +354,7 @@ func installDocker() error { return fmt.Errorf("failed to detect system architecture: %v", err) } arch := strings.TrimSpace(string(archOutput)) - + // Map architecture to Docker's architecture naming var dockerArch string switch arch { @@ -403,7 +396,7 @@ func installDocker() error { fedoraVersion = v } } - + // Use appropriate DNF syntax based on version var repoCmd string if fedoraVersion >= 41 { @@ -413,7 +406,7 @@ func installDocker() error { // DNF 4 syntax for Fedora < 41 repoCmd = "dnf config-manager --add-repo https://download.docker.com/linux/fedora/docker-ce.repo" } - + installCmd = exec.Command("bash", "-c", fmt.Sprintf(` dnf -y install dnf-plugins-core && %s && @@ -442,7 +435,7 @@ func installDocker() error { default: return fmt.Errorf("unsupported Linux distribution") } - + installCmd.Stdout = os.Stdout installCmd.Stderr = os.Stderr return installCmd.Run() @@ -527,7 +520,7 @@ func executeDockerComposeCommandWithArgs(args ...string) error { return fmt.Errorf("neither 'docker compose' nor 'docker-compose' command is available") } } - + if useNewStyle { cmd = exec.Command("docker", append([]string{"compose"}, args...)...) } else { @@ -563,7 +556,7 @@ func startContainers() error { // stopContainers stops the containers using the appropriate command. func stopContainers() error { fmt.Println("Stopping containers...") - + if err := executeDockerComposeCommandWithArgs("-f", "docker-compose.yml", "down"); err != nil { return fmt.Errorf("failed to stop containers: %v", err) } @@ -574,7 +567,7 @@ func stopContainers() error { // restartContainer restarts a specific container using the appropriate command. func restartContainer(container string) error { fmt.Println("Restarting containers...") - + if err := executeDockerComposeCommandWithArgs("-f", "docker-compose.yml", "restart", container); err != nil { return fmt.Errorf("failed to stop the container \"%s\": %v", container, err) } diff --git a/messages/de-DE.json b/messages/de-DE.json index 1dd4d7ae..09276f72 100644 --- a/messages/de-DE.json +++ b/messages/de-DE.json @@ -37,7 +37,7 @@ "name": "Name", "online": "Online", "offline": "Offline", - "site": "Site", + "site": "Seite", "dataIn": "Daten eingehend", "dataOut": "Daten ausgehend", "connectionType": "Verbindungstyp", @@ -114,7 +114,7 @@ "shareTokenDescription": "Ihr Zugriffstoken kann auf zwei Arten übergeben werden: als Abfrageparameter oder in den Anfrage-Headern. Diese müssen vom Client auf jeder Anfrage für authentifizierten Zugriff weitergegeben werden.", "accessToken": "Zugangs-Token", "usageExamples": "Nutzungsbeispiele", - "tokenId": "Token ID", + "tokenId": "Token-ID", "requestHeades": "Anfrage-Header", "queryParameter": "Abfrageparameter", "importantNote": "Wichtige Notiz", @@ -177,8 +177,8 @@ "subdomnainDescription": "Die Subdomäne, auf die Ihre Ressource zugegriffen werden soll.", "resourceRawSettings": "TCP/UDP Einstellungen", "resourceRawSettingsDescription": "Konfigurieren Sie den Zugriff auf Ihre Ressource über TCP/UDP", - "protocol": "Protocol", - "protocolSelect": "Select a protocol", + "protocol": "Protokoll", + "protocolSelect": "Wählen Sie ein Protokoll", "resourcePortNumber": "Portnummer", "resourcePortNumberDescription": "Die externe Portnummer für Proxy-Anfragen.", "cancel": "Abbrechen", @@ -300,7 +300,7 @@ "userMessageConfirm": "Um zu bestätigen, geben Sie bitte den Namen des Benutzers unten ein.", "userQuestionRemove": "Sind Sie sicher, dass Sie {selectedUser} dauerhaft vom Server löschen möchten?", "licenseKey": "Lizenzschlüssel", - "valid": "Valid", + "valid": "Gültig", "numberOfSites": "Anzahl der Sites", "licenseKeySearch": "Lizenzschlüssel suchen...", "licenseKeyAdd": "Lizenzschlüssel hinzufügen", @@ -700,7 +700,7 @@ "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", "sitesNotFound": "Keine Sites gefunden.", - "pangolinServerAdmin": "Server Admin - Pangolin", + "pangolinServerAdmin": "Server-Admin - Pangolin", "licenseTierProfessional": "Professional Lizenz", "licenseTierEnterprise": "Enterprise Lizenz", "licenseTierCommercial": "Gewerbliche Lizenz", @@ -805,7 +805,7 @@ "redirectUrl": "Weiterleitungs-URL", "redirectUrlAbout": "Über die Weiterleitungs-URL", "redirectUrlAboutDescription": "Dies ist die URL, zu der Benutzer nach der Authentifizierung weitergeleitet werden. Sie müssen diese URL in den Einstellungen Ihres Identitätsanbieters konfigurieren.", - "pangolinAuth": "Auth - Pangolin", + "pangolinAuth": "Authentifizierung - Pangolin", "verificationCodeLengthRequirements": "Ihr Verifizierungscode muss 8 Zeichen lang sein.", "errorOccurred": "Ein Fehler ist aufgetreten", "emailErrorVerify": "E-Mail konnte nicht verifiziert werden:", @@ -1104,7 +1104,7 @@ "containerLabels": "Etiketten", "containerLabelsCount": "{count} Label{s,plural,one{} other{s}}", "containerLabelsTitle": "Container-Labels", - "containerLabelEmpty": "", + "containerLabelEmpty": "", "containerPorts": "Häfen", "containerPortsMore": "+{count} mehr", "containerActions": "Aktionen", @@ -1127,6 +1127,10 @@ "noContainersFoundMatching": "Keine Container gefunden mit \"{filter}\".", "light": "hell", "dark": "dunkel", - "system": "system", - "theme": "Design" -} \ No newline at end of file + "system": "System", + "theme": "Design", + "initialSetupTitle": "Initial Einrichtung des Servers", + "initialSetupDescription": "Erstellen Sie das initiale Server-Admin-Konto. Es kann nur einen Server-Admin geben. Sie können diese Anmeldedaten später immer ändern.", + "createAdminAccount": "Admin-Konto erstellen", + "setupErrorCreateAdmin": "Beim Erstellen des Server-Admin-Kontos ist ein Fehler aufgetreten." +} diff --git a/messages/es-ES.json b/messages/es-ES.json index 23ae5310..60856fe8 100644 --- a/messages/es-ES.json +++ b/messages/es-ES.json @@ -114,7 +114,7 @@ "shareTokenDescription": "Su token de acceso puede ser pasado de dos maneras: como parámetro de consulta o en las cabeceras de solicitud. Estos deben ser pasados del cliente en cada solicitud de acceso autenticado.", "accessToken": "Token de acceso", "usageExamples": "Ejemplos de uso", - "tokenId": "Token ID", + "tokenId": "ID de token", "requestHeades": "Solicitar cabeceras", "queryParameter": "Parámetro de consulta", "importantNote": "Nota Importante", @@ -177,8 +177,8 @@ "subdomnainDescription": "El subdominio al que su recurso será accesible.", "resourceRawSettings": "Configuración TCP/UDP", "resourceRawSettingsDescription": "Configurar cómo se accederá a su recurso a través de TCP/UDP", - "protocol": "Protocol", - "protocolSelect": "Select a protocol", + "protocol": "Protocolo", + "protocolSelect": "Seleccionar un protocolo", "resourcePortNumber": "Número de puerto", "resourcePortNumberDescription": "El número de puerto externo a las solicitudes de proxy.", "cancel": "Cancelar", @@ -300,7 +300,7 @@ "userMessageConfirm": "Para confirmar, por favor escriba el nombre del usuario a continuación.", "userQuestionRemove": "¿Está seguro que desea eliminar permanentemente {selectedUser} del servidor?", "licenseKey": "Clave de licencia", - "valid": "Valid", + "valid": "Válido", "numberOfSites": "Número de sitios", "licenseKeySearch": "Buscar claves de licencia...", "licenseKeyAdd": "Añadir clave de licencia", @@ -373,7 +373,7 @@ "inviteSent": "Se ha enviado una nueva invitación a {email}.", "inviteSentEmail": "Enviar notificación por correo electrónico al usuario", "inviteGenerate": "Se ha generado una nueva invitación para {email}.", - "inviteDuplicateError": "Duplicate Invite", + "inviteDuplicateError": "Invitación duplicada", "inviteDuplicateErrorDescription": "Ya existe una invitación para este usuario.", "inviteRateLimitError": "Límite de tasa excedido", "inviteRateLimitErrorDescription": "Has superado el límite de 3 regeneraciones por hora. Inténtalo de nuevo más tarde.", @@ -463,9 +463,9 @@ "targetErrorFetchDescription": "Se ha producido un error al recuperar los objetivos", "siteErrorFetch": "No se pudo obtener el recurso", "siteErrorFetchDescription": "Se ha producido un error al recuperar el recurso", - "targetErrorDuplicate": "Duplicate target", + "targetErrorDuplicate": "Objetivo duplicado", "targetErrorDuplicateDescription": "Ya existe un objetivo con estos ajustes", - "targetWireGuardErrorInvalidIp": "Invalid target IP", + "targetWireGuardErrorInvalidIp": "IP de destino no válida", "targetWireGuardErrorInvalidIpDescription": "La IP de destino debe estar dentro de la subred del sitio", "targetsUpdated": "Objetivos actualizados", "targetsUpdatedDescription": "Objetivos y ajustes actualizados correctamente", @@ -479,9 +479,9 @@ "proxyUpdatedDescription": "La configuración del proxy se ha actualizado correctamente", "proxyErrorUpdate": "Error al actualizar la configuración del proxy", "proxyErrorUpdateDescription": "Se ha producido un error al actualizar la configuración del proxy", - "targetAddr": "IP / Hostname", + "targetAddr": "IP / Nombre del host", "targetPort": "Puerto", - "targetProtocol": "Protocol", + "targetProtocol": "Protocolo", "targetTlsSettings": "Configuración de conexión segura", "targetTlsSettingsDescription": "Configurar ajustes SSL/TLS para su recurso", "targetTlsSettingsAdvanced": "Ajustes avanzados de TLS", @@ -493,7 +493,7 @@ "targetStickySessions": "Activar Sesiones Pegadas", "targetStickySessionsDescription": "Mantener conexiones en el mismo objetivo de backend para toda su sesión.", "methodSelect": "Seleccionar método", - "targetSubmit": "Add Target", + "targetSubmit": "Añadir destino", "targetNoOne": "No hay objetivos. Agregue un objetivo usando el formulario.", "targetNoOneDescription": "Si se añade más de un objetivo anterior se activará el balance de carga.", "targetsSubmit": "Guardar objetivos", @@ -585,8 +585,8 @@ "unknownCommand": "Comando desconocido", "newtErrorFetchReleases": "No se pudo obtener la información del lanzamiento: {err}", "newtErrorFetchLatest": "Error obteniendo la última versión: {err}", - "newtEndpoint": "Newt Endpoint", - "newtId": "Newt ID", + "newtEndpoint": "Punto final de Newt", + "newtId": "ID de Newt", "newtSecretKey": "Clave secreta de Newt", "architecture": "Arquitectura", "sites": "Sitios", @@ -867,7 +867,7 @@ "passwordReset": "Restablecer contraseña", "passwordResetDescription": "Siga los pasos para restablecer su contraseña", "passwordResetSent": "Enviaremos un código para restablecer la contraseña a esta dirección de correo electrónico.", - "passwordResetCode": "Reset Code", + "passwordResetCode": "Código de restablecimiento", "passwordResetCodeDescription": "Revisa tu correo electrónico para ver el código de restablecimiento.", "passwordNew": "Nueva contraseña", "passwordNewConfirm": "Confirmar nueva contraseña", @@ -895,7 +895,7 @@ "inviteErrorExpired": "La invitación puede haber caducado", "inviteErrorRevoked": "La invitación podría haber sido revocada", "inviteErrorTypo": "Puede haber un error en el enlace de invitación", - "pangolinSetup": "Setup - Pangolin", + "pangolinSetup": "Configuración - Pangolin", "orgNameRequired": "El nombre de la organización es obligatorio", "orgIdRequired": "El ID de la organización es obligatorio", "orgErrorCreate": "Se ha producido un error al crear el org", @@ -923,7 +923,7 @@ "tagWarnDuplicate": "Etiqueta {tagText} duplicada no añadida", "supportKeyInvalid": "Clave inválida", "supportKeyInvalidDescription": "Tu clave de seguidor no es válida.", - "supportKeyValid": "Valid Key", + "supportKeyValid": "Clave válida", "supportKeyValidDescription": "Su clave de seguidor ha sido validada. ¡Gracias por su apoyo!", "supportKeyErrorValidationDescription": "Error al validar la clave de seguidor.", "supportKey": "¡Apoya el Desarrollo y Adopte un Pangolin!", @@ -945,7 +945,7 @@ "supportKeyHideSevenDays": "Ocultar durante 7 días", "supportKeyEnter": "Introduzca Clave de Soporter", "supportKeyEnterDescription": "Conoce a tu propia mascota Pangolin!", - "githubUsername": "GitHub Username", + "githubUsername": "Nombre de usuario de GitHub", "supportKeyInput": "Clave de apoyo", "supportKeyBuy": "Comprar Clave de Apoyo", "logoutError": "Error al cerrar sesión", @@ -979,11 +979,11 @@ "actionSetResourcePincode": "Establecer Pincode del recurso", "actionSetResourceEmailWhitelist": "Establecer lista blanca de correo de recursos", "actionGetResourceEmailWhitelist": "Obtener correo electrónico de recursos", - "actionCreateTarget": "Create Target", + "actionCreateTarget": "Crear destino", "actionDeleteTarget": "Eliminar destino", "actionGetTarget": "Obtener objetivo", "actionListTargets": "Lista de objetivos", - "actionUpdateTarget": "Update Target", + "actionUpdateTarget": "Actualizar destino", "actionCreateRole": "Crear rol", "actionDeleteRole": "Eliminar rol", "actionGetRole": "Obtener rol", @@ -1031,7 +1031,7 @@ "otpAuthSubmit": "Enviar código", "idpContinue": "O continuar con", "otpAuthBack": "Volver a iniciar sesión", - "navbar": "Navigation Menu", + "navbar": "Menú de navegación", "navbarDescription": "Menú de navegación principal para la aplicación", "navbarDocsLink": "Documentación", "commercialEdition": "Edición Comercial", @@ -1100,11 +1100,11 @@ "containerImage": "Imagen", "containerState": "Estado", "containerNetworks": "Redes", - "containerHostnameIp": "Hostname/IP", + "containerHostnameIp": "Nombre del host/IP", "containerLabels": "Etiquetas", "containerLabelsCount": "{count} etiqueta{s,plural,one{} other{s}}", "containerLabelsTitle": "Etiquetas de contenedor", - "containerLabelEmpty": "", + "containerLabelEmpty": "", "containerPorts": "Puertos", "containerPortsMore": "+{count} más", "containerActions": "Acciones", @@ -1128,5 +1128,9 @@ "light": "claro", "dark": "oscuro", "system": "sistema", - "theme": "Tema" -} \ No newline at end of file + "theme": "Tema", + "initialSetupTitle": "Configuración inicial del servidor", + "initialSetupDescription": "Cree la cuenta de administrador del servidor inicial. Solo puede existir un administrador del servidor. Siempre puede cambiar estas credenciales más tarde.", + "createAdminAccount": "Crear cuenta de administrador", + "setupErrorCreateAdmin": "Se produjo un error al crear la cuenta de administrador del servidor." +} diff --git a/messages/fr-FR.json b/messages/fr-FR.json index e1a7dbd9..a7a237bf 100644 --- a/messages/fr-FR.json +++ b/messages/fr-FR.json @@ -114,7 +114,7 @@ "shareTokenDescription": "Votre jeton d'accès peut être passé de deux façons : en tant que paramètre de requête ou dans les en-têtes de la requête. Elles doivent être transmises par le client à chaque demande d'accès authentifié.", "accessToken": "Jeton d'accès", "usageExamples": "Exemples d'utilisation", - "tokenId": "Token ID", + "tokenId": "ID du jeton", "requestHeades": "En-têtes de la requête", "queryParameter": "Paramètre de requête", "importantNote": "Note importante", @@ -177,8 +177,8 @@ "subdomnainDescription": "Le sous-domaine où votre ressource sera accessible.", "resourceRawSettings": "Paramètres TCP/UDP", "resourceRawSettingsDescription": "Configurer comment votre ressource sera accédée via TCP/UDP", - "protocol": "Protocol", - "protocolSelect": "Select a protocol", + "protocol": "Protocole", + "protocolSelect": "Sélectionner un protocole", "resourcePortNumber": "Numéro de port", "resourcePortNumberDescription": "Le numéro de port externe pour les requêtes de proxy.", "cancel": "Abandonner", @@ -300,7 +300,7 @@ "userMessageConfirm": "Pour confirmer, veuillez saisir le nom de l'utilisateur ci-dessous.", "userQuestionRemove": "Êtes-vous sûr de vouloir supprimer définitivement {selectedUser} du serveur?", "licenseKey": "Clé de licence", - "valid": "Valid", + "valid": "Valide", "numberOfSites": "Nombre de sites", "licenseKeySearch": "Rechercher des clés de licence...", "licenseKeyAdd": "Ajouter une clé de licence", @@ -1100,11 +1100,11 @@ "containerImage": "Image", "containerState": "État", "containerNetworks": "Réseaux", - "containerHostnameIp": "Hostname/IP", + "containerHostnameIp": "Nom d'hôte/IP", "containerLabels": "Étiquettes", - "containerLabelsCount": "{count} label{s,plural,one{} other{s}}", + "containerLabelsCount": "{count} étiquette{s,plural,one{} other{s}}", "containerLabelsTitle": "Étiquettes de conteneur", - "containerLabelEmpty": "", + "containerLabelEmpty": "", "containerPorts": "Ports", "containerPortsMore": "+{count} de plus", "containerActions": "Actions", @@ -1128,5 +1128,9 @@ "light": "clair", "dark": "sombre", "system": "système", - "theme": "Thème" -} \ No newline at end of file + "theme": "Thème", + "initialSetupTitle": "Configuration initiale du serveur", + "initialSetupDescription": "Créer le compte administrateur du serveur initial. Un seul administrateur serveur peut exister. Vous pouvez toujours changer ces informations d'identification plus tard.", + "createAdminAccount": "Créer un compte administrateur", + "setupErrorCreateAdmin": "Une erreur s'est produite lors de la création du compte administrateur du serveur." +} diff --git a/messages/it-IT.json b/messages/it-IT.json index 92ea2ee3..cfe983d2 100644 --- a/messages/it-IT.json +++ b/messages/it-IT.json @@ -73,7 +73,7 @@ "searchSitesProgress": "Cerca siti...", "siteAdd": "Aggiungi Sito", "siteInstallNewt": "Installa Newt", - "siteInstallNewtDescription": "Get Newt running on your system", + "siteInstallNewtDescription": "Esegui Newt sul tuo sistema", "WgConfiguration": "Configurazione WireGuard", "WgConfigurationDescription": "Usa la seguente configurazione per connetterti alla tua rete", "operatingSystem": "Sistema Operativo", @@ -114,7 +114,7 @@ "shareTokenDescription": "Il token di accesso può essere passato in due modi: come parametro di interrogazione o nelle intestazioni della richiesta. Questi devono essere passati dal client su ogni richiesta di accesso autenticato.", "accessToken": "Token Di Accesso", "usageExamples": "Esempi Di Utilizzo", - "tokenId": "Token ID", + "tokenId": "ID del Token", "requestHeades": "Richiedi Intestazioni", "queryParameter": "Parametro Query", "importantNote": "Nota Importante", @@ -177,8 +177,8 @@ "subdomnainDescription": "Il sottodominio in cui la tua risorsa sarà accessibile.", "resourceRawSettings": "Impostazioni TCP/UDP", "resourceRawSettingsDescription": "Configura come accedere alla tua risorsa tramite TCP/UDP", - "protocol": "Protocol", - "protocolSelect": "Select a protocol", + "protocol": "Protocollo", + "protocolSelect": "Seleziona un protocollo", "resourcePortNumber": "Numero Porta", "resourcePortNumberDescription": "Il numero di porta esterna per le richieste di proxy.", "cancel": "Annulla", @@ -230,7 +230,7 @@ "accessUsersSearch": "Cerca utenti...", "accessUserCreate": "Crea Utente", "accessUserRemove": "Rimuovi Utente", - "username": "Username", + "username": "Nome utente", "identityProvider": "Provider Di Identità", "role": "Ruolo", "nameRequired": "Il nome è obbligatorio", @@ -300,7 +300,7 @@ "userMessageConfirm": "Per confermare, digita il nome dell'utente qui sotto.", "userQuestionRemove": "Sei sicuro di voler eliminare definitivamente {selectedUser} dal server?", "licenseKey": "Chiave Di Licenza", - "valid": "Valid", + "valid": "Valido", "numberOfSites": "Numero di siti", "licenseKeySearch": "Cerca chiavi di licenza...", "licenseKeyAdd": "Aggiungi Chiave Di Licenza", @@ -316,7 +316,7 @@ "licenseErrorKeyActivate": "Attivazione della chiave di licenza non riuscita", "licenseErrorKeyActivateDescription": "Si è verificato un errore nell'attivazione della chiave di licenza.", "licenseAbout": "Informazioni Su Licenze", - "communityEdition": "Community Edition", + "communityEdition": "Edizione Community", "licenseAboutDescription": "Questo è per gli utenti aziendali e aziendali che utilizzano Pangolin in un ambiente commerciale. Se stai usando Pangolin per uso personale, puoi ignorare questa sezione.", "licenseKeyActivated": "Chiave di licenza attivata", "licenseKeyActivatedDescription": "La chiave di licenza è stata attivata correttamente.", @@ -339,7 +339,7 @@ "licenseHost": "Licenza Host", "licenseHostDescription": "Gestisci la chiave di licenza principale per l'host.", "licensedNot": "Non Licenziato", - "hostId": "Host ID", + "hostId": "ID Host", "licenseReckeckAll": "Ricontrolla Tutte Le Tasti", "licenseSiteUsage": "Utilizzo Siti", "licenseSiteUsageDecsription": "Visualizza il numero di siti che utilizzano questa licenza.", @@ -1104,7 +1104,7 @@ "containerLabels": "Etichette", "containerLabelsCount": "{count} etichetta{s,plural,one{} other{s}}", "containerLabelsTitle": "Etichette Del Contenitore", - "containerLabelEmpty": "", + "containerLabelEmpty": "", "containerPorts": "Porte", "containerPortsMore": "+{count} in più", "containerActions": "Azioni", @@ -1128,5 +1128,9 @@ "light": "chiaro", "dark": "scuro", "system": "sistema", - "theme": "Tema" -} \ No newline at end of file + "theme": "Tema", + "initialSetupTitle": "Impostazione Iniziale del Server", + "initialSetupDescription": "Crea l'account amministratore del server iniziale. Può esistere solo un amministratore del server. È sempre possibile modificare queste credenziali in seguito.", + "createAdminAccount": "Crea Account Admin", + "setupErrorCreateAdmin": "Si è verificato un errore durante la creazione dell'account amministratore del server." +} diff --git a/messages/nl-NL.json b/messages/nl-NL.json index e656544b..7e625b00 100644 --- a/messages/nl-NL.json +++ b/messages/nl-NL.json @@ -12,7 +12,7 @@ "componentsErrorNoMember": "U bent momenteel geen lid van een organisatie.", "welcome": "Welkom bij Pangolin", "componentsCreateOrg": "Maak een Organisatie", - "componentsMember": "You're a member of {count, plural, =0 {no organization} =1 {one organization} other {# organizations}}.", + "componentsMember": "Je bent lid van {count, plural, =0 {geen organisatie} =1 {één organisatie} other {# organisaties}}.", "componentsInvalidKey": "Ongeldige of verlopen licentiesleutels gedetecteerd. Volg de licentievoorwaarden om alle functies te blijven gebruiken.", "dismiss": "Uitschakelen", "componentsLicenseViolation": "Licentie overtreding: Deze server gebruikt {usedSites} sites die de gelicentieerde limiet van {maxSites} sites overschrijden. Volg de licentievoorwaarden om door te gaan met het gebruik van alle functies.", @@ -92,7 +92,7 @@ "siteSetting": "{siteName} instellingen", "siteNewtTunnel": "Nieuwstunnel (Aanbevolen)", "siteNewtTunnelDescription": "Gemakkelijkste manier om een ingangspunt in uw netwerk te maken. Geen extra opzet.", - "siteWg": "Basic WireGuard", + "siteWg": "Basis WireGuard", "siteWgDescription": "Gebruik een WireGuard client om een tunnel te bouwen. Handmatige NAT installatie vereist.", "siteLocalDescription": "Alleen lokale bronnen. Geen tunneling.", "siteSeeAll": "Alle werkruimtes bekijken", @@ -116,7 +116,7 @@ "usageExamples": "Voorbeelden van gebruik", "tokenId": "Token ID", "requestHeades": "Aanvraag van headers", - "queryParameter": "Query Parameter", + "queryParameter": "Queryparameter", "importantNote": "Belangrijke opmerking", "shareImportantDescription": "Om veiligheidsredenen wordt het gebruik van headers aanbevolen over queryparameters indien mogelijk, omdat query parameters kunnen worden aangemeld in serverlogboeken of browsergeschiedenis.", "token": "Token", @@ -178,7 +178,7 @@ "resourceRawSettings": "TCP/UDP instellingen", "resourceRawSettingsDescription": "Stel in hoe je bron wordt benaderd via TCP/UDP", "protocol": "Protocol", - "protocolSelect": "Select a protocol", + "protocolSelect": "Selecteer een protocol", "resourcePortNumber": "Nummer van poort", "resourcePortNumberDescription": "Het externe poortnummer naar proxyverzoeken.", "cancel": "annuleren", @@ -300,7 +300,7 @@ "userMessageConfirm": "Typ de naam van de gebruiker hieronder om te bevestigen.", "userQuestionRemove": "Weet je zeker dat je {selectedUser} permanent van de server wilt verwijderen?", "licenseKey": "Licentie sleutel", - "valid": "Valid", + "valid": "Geldig", "numberOfSites": "Aantal sites", "licenseKeySearch": "Licentiesleutels zoeken...", "licenseKeyAdd": "Licentiesleutel toevoegen", @@ -339,7 +339,7 @@ "licenseHost": "Host Licentie", "licenseHostDescription": "Beheer de belangrijkste licentiesleutel voor de host.", "licensedNot": "Niet gelicentieerd", - "hostId": "Host ID", + "hostId": "Host-ID", "licenseReckeckAll": "Alle sleutels opnieuw selecteren", "licenseSiteUsage": "Websites gebruik", "licenseSiteUsageDecsription": "Bekijk het aantal sites dat deze licentie gebruikt.", @@ -373,7 +373,7 @@ "inviteSent": "Een nieuwe uitnodiging is verstuurd naar {email}.", "inviteSentEmail": "Stuur e-mail notificatie naar de gebruiker", "inviteGenerate": "Er is een nieuwe uitnodiging aangemaakt voor {email}.", - "inviteDuplicateError": "Duplicate Invite", + "inviteDuplicateError": "Dubbele uitnodiging", "inviteDuplicateErrorDescription": "Er bestaat al een uitnodiging voor deze gebruiker.", "inviteRateLimitError": "Tarief limiet overschreden", "inviteRateLimitErrorDescription": "U hebt de limiet van 3 regeneratie per uur overschreden. Probeer het later opnieuw.", @@ -436,7 +436,7 @@ "accessRoleSelect": "Selecteer rol", "inviteEmailSentDescription": "Een e-mail is verstuurd naar de gebruiker met de link hieronder. Ze moeten toegang krijgen tot de link om de uitnodiging te accepteren.", "inviteSentDescription": "De gebruiker is uitgenodigd. Ze moeten toegang krijgen tot de link hieronder om de uitnodiging te accepteren.", - "inviteExpiresIn": "The invite will expire in {days, plural, =1 {# day} other {# days}}.", + "inviteExpiresIn": "De uitnodiging vervalt in {days, plural, =1 {# dag} other {# dagen}}.", "idpTitle": "Identiteit Provider", "idpSelect": "Identiteitsprovider voor de externe gebruiker selecteren", "idpNotConfigured": "Er zijn geen identiteitsproviders geconfigureerd. Configureer een identiteitsprovider voordat u externe gebruikers aanmaakt.", @@ -463,9 +463,9 @@ "targetErrorFetchDescription": "Er is een fout opgetreden bij het ophalen van de objecten", "siteErrorFetch": "Mislukt om resource op te halen", "siteErrorFetchDescription": "Er is een fout opgetreden tijdens het ophalen van het document", - "targetErrorDuplicate": "Duplicate target", + "targetErrorDuplicate": "Dubbel doelwit", "targetErrorDuplicateDescription": "Een doel met deze instellingen bestaat al", - "targetWireGuardErrorInvalidIp": "Invalid target IP", + "targetWireGuardErrorInvalidIp": "Ongeldig doel-IP", "targetWireGuardErrorInvalidIpDescription": "Doel IP moet binnen de site subnet zijn", "targetsUpdated": "Doelstellingen bijgewerkt", "targetsUpdatedDescription": "Doelstellingen en instellingen succesvol bijgewerkt", @@ -479,7 +479,7 @@ "proxyUpdatedDescription": "Uw proxyinstellingen zijn succesvol bijgewerkt", "proxyErrorUpdate": "Bijwerken van proxy-instellingen mislukt", "proxyErrorUpdateDescription": "Fout opgetreden tijdens het bijwerken van de proxy-instellingen", - "targetAddr": "IP / Hostname", + "targetAddr": "IP / Hostnaam", "targetPort": "Poort", "targetProtocol": "Protocol", "targetTlsSettings": "HTTPS & TLS instellingen", @@ -493,7 +493,7 @@ "targetStickySessions": "Sticky sessies inschakelen", "targetStickySessionsDescription": "Behoud verbindingen op hetzelfde backend doel voor hun hele sessie.", "methodSelect": "Selecteer methode", - "targetSubmit": "Add Target", + "targetSubmit": "Doelwit toevoegen", "targetNoOne": "Geen doelwitten. Voeg een doel toe via het formulier.", "targetNoOneDescription": "Het toevoegen van meer dan één doel hierboven zal de load balancering mogelijk maken.", "targetsSubmit": "Doelstellingen opslaan", @@ -585,8 +585,8 @@ "unknownCommand": "Onbekende opdracht", "newtErrorFetchReleases": "Kan release-informatie niet ophalen: {err}", "newtErrorFetchLatest": "Fout bij ophalen van laatste release: {err}", - "newtEndpoint": "Newt Endpoint", - "newtId": "Newt ID", + "newtEndpoint": "Newt Eindoordeel", + "newtId": "Newt-ID", "newtSecretKey": "Nieuwe geheime sleutel", "architecture": "Architectuur", "sites": "Werkruimtes", @@ -639,7 +639,7 @@ "resourcePasswordSetupDescription": "Het wachtwoord voor de bron is succesvol ingesteld", "resourcePasswordSetupTitle": "Wachtwoord instellen", "resourcePasswordSetupTitleDescription": "Stel een wachtwoord in om deze bron te beschermen", - "resourcePincode": "PIN Code", + "resourcePincode": "Pincode", "resourcePincodeSubmit": "PIN-Code bescherming inschakelen", "resourcePincodeProtection": "PIN Code bescherming {status}", "resourcePincodeRemove": "Pijncode van resource verwijderd", @@ -700,7 +700,7 @@ "accessRoleRequiredRemove": "Voordat u deze rol verwijdert, selecteer een nieuwe rol om bestaande leden aan te dragen.", "manage": "Beheren", "sitesNotFound": "Geen sites gevonden.", - "pangolinServerAdmin": "Server Admin - Pangolin", + "pangolinServerAdmin": "Serverbeheer - Pangolin", "licenseTierProfessional": "Professionele licentie", "licenseTierEnterprise": "Enterprise Licentie", "licenseTierCommercial": "Commerciële licentie", @@ -745,7 +745,7 @@ "idpOidcConfigureDescription": "Configureer de eindpunten van de OAuth2/OIDC provider en referenties", "idpClientId": "Klant ID", "idpClientIdDescription": "De OAuth2-client-ID van uw identiteitsprovider", - "idpClientSecret": "Client Secret", + "idpClientSecret": "Clientgeheim", "idpClientSecretDescription": "Het OAuth2-clientgeheim van je identiteitsprovider", "idpAuthUrl": "URL autorisatie", "idpAuthUrlDescription": "De URL voor autorisatie OAuth2", @@ -826,7 +826,7 @@ "inviteAlready": "Het lijkt erop dat je bent uitgenodigd!", "inviteAlreadyDescription": "Om de uitnodiging te accepteren, moet je inloggen of een account aanmaken.", "signupQuestion": "Heeft u al een account?", - "login": "Log in", + "login": "Inloggen", "resourceNotFound": "Bron niet gevonden", "resourceNotFoundDescription": "De bron die u probeert te benaderen bestaat niet.", "pincodeRequirementsLength": "Pincode moet precies 6 cijfers zijn", @@ -867,11 +867,11 @@ "passwordReset": "Wachtwoord opnieuw instellen", "passwordResetDescription": "Volg de stappen om uw wachtwoord opnieuw in te stellen", "passwordResetSent": "We sturen een wachtwoord reset code naar dit e-mailadres.", - "passwordResetCode": "Reset Code", + "passwordResetCode": "Resetcode", "passwordResetCodeDescription": "Controleer je e-mail voor de reset code.", "passwordNew": "Nieuw wachtwoord", "passwordNewConfirm": "Bevestig nieuw wachtwoord", - "pincodeAuth": "Authenticator Code", + "pincodeAuth": "Authenticatiecode", "pincodeSubmit2": "Code indienen", "passwordResetSubmit": "Opnieuw instellen aanvragen", "passwordBack": "Terug naar wachtwoord", @@ -923,7 +923,7 @@ "tagWarnDuplicate": "Dubbele tag {tagText} niet toegevoegd", "supportKeyInvalid": "Ongeldige sleutel", "supportKeyInvalidDescription": "Je supporter sleutel is ongeldig.", - "supportKeyValid": "Valid Key", + "supportKeyValid": "Geldige sleutel", "supportKeyValidDescription": "Uw supporter sleutel is gevalideerd. Bedankt voor uw steun!", "supportKeyErrorValidationDescription": "Niet gelukt om de supportersleutel te valideren.", "supportKey": "Ondersteun ontwikkeling en Adopt een Pangolin!", @@ -945,7 +945,7 @@ "supportKeyHideSevenDays": "Verbergen voor 7 dagen", "supportKeyEnter": "Voer de supportersleutel in", "supportKeyEnterDescription": "Ontmoet je eigen huisdier Pangolin!", - "githubUsername": "GitHub Username", + "githubUsername": "GitHub-gebruikersnaam", "supportKeyInput": "Supporter Sleutel", "supportKeyBuy": "Koop Supportersleutel", "logoutError": "Fout bij uitloggen", @@ -979,11 +979,11 @@ "actionSetResourcePincode": "Stel Resource Pincode in", "actionSetResourceEmailWhitelist": "Stel Resource e-mail whitelist in", "actionGetResourceEmailWhitelist": "Verkrijg Resource E-mail Whitelist", - "actionCreateTarget": "Create Target", + "actionCreateTarget": "Doelwit aanmaken", "actionDeleteTarget": "Verwijder doel", "actionGetTarget": "Verkrijg Doel", "actionListTargets": "Doelstellingen weergeven", - "actionUpdateTarget": "Update Target", + "actionUpdateTarget": "Doelwit bijwerken", "actionCreateRole": "Rol aanmaken", "actionDeleteRole": "Verwijder rol", "actionGetRole": "Krijg Rol", @@ -1018,7 +1018,7 @@ "actionCreateIdpOrg": "Maak IDP Org Policy", "actionDeleteIdpOrg": "Verwijder IDP Org Beleid", "actionListIdpOrgs": "Toon IDP Orgs", - "actionUpdateIdpOrg": "Update IDP Org", + "actionUpdateIdpOrg": "IDP-org bijwerken", "noneSelected": "Niet geselecteerd", "orgNotFound2": "Geen organisaties gevonden.", "searchProgress": "Zoeken...", @@ -1031,7 +1031,7 @@ "otpAuthSubmit": "Code indienen", "idpContinue": "Of ga verder met", "otpAuthBack": "Terug naar inloggen", - "navbar": "Navigation Menu", + "navbar": "Navigatiemenu", "navbarDescription": "Hoofd navigatie menu voor de applicatie", "navbarDocsLink": "Documentatie", "commercialEdition": "Commerciële editie", @@ -1042,7 +1042,7 @@ "otpSetup": "Tweestapsverificatie inschakelen", "otpSetupDescription": "Beveilig je account met een extra beveiligingslaag", "otpSetupScanQr": "Scan deze QR-code met je authenticator-app of voer de geheime sleutel handmatig in:", - "otpSetupSecretCode": "Authenticator Code", + "otpSetupSecretCode": "Authenticatiecode", "otpSetupSuccess": "Tweestapsverificatie ingeschakeld", "otpSetupSuccessStoreBackupCodes": "Uw account is nu veiliger. Vergeet niet uw back-upcodes op te slaan.", "otpErrorDisable": "Kan 2FA niet uitschakelen", @@ -1100,11 +1100,11 @@ "containerImage": "Afbeelding", "containerState": "Provincie", "containerNetworks": "Netwerken", - "containerHostnameIp": "Hostname/IP", + "containerHostnameIp": "Hostnaam/IP", "containerLabels": "Labels", "containerLabelsCount": "{count} label{s,plural,one{} other{s}}", "containerLabelsTitle": "Container labels", - "containerLabelEmpty": "", + "containerLabelEmpty": "", "containerPorts": "Poorten", "containerPortsMore": "+{count} meer", "containerActions": "acties", @@ -1128,5 +1128,9 @@ "light": "licht", "dark": "donker", "system": "systeem", - "theme": "Thema" -} \ No newline at end of file + "theme": "Thema", + "initialSetupTitle": "Initiële serverconfiguratie", + "initialSetupDescription": "Maak het eerste serverbeheeraccount aan. Er kan slechts één serverbeheerder bestaan. U kunt deze inloggegevens later altijd wijzigen.", + "createAdminAccount": "Maak een beheeraccount aan", + "setupErrorCreateAdmin": "Er is een fout opgetreden bij het maken van het serverbeheerdersaccount." +} diff --git a/messages/pl-PL.json b/messages/pl-PL.json index fcbe1aa6..25cf2e6a 100644 --- a/messages/pl-PL.json +++ b/messages/pl-PL.json @@ -114,7 +114,7 @@ "shareTokenDescription": "Twój token dostępu może być przekazywany na dwa sposoby: jako parametr zapytania lub w nagłówkach żądania. Muszą być przekazywane z klienta na każde żądanie uwierzytelnionego dostępu.", "accessToken": "Token dostępu", "usageExamples": "Przykłady użycia", - "tokenId": "Token ID", + "tokenId": "Identyfikator tokena", "requestHeades": "Nagłówki żądania", "queryParameter": "Parametr zapytania", "importantNote": "Ważna uwaga", @@ -177,8 +177,8 @@ "subdomnainDescription": "Poddomena, w której twój zasób będzie dostępny.", "resourceRawSettings": "Ustawienia TCP/UDP", "resourceRawSettingsDescription": "Skonfiguruj jak twój zasób będzie dostępny przez TCP/UDP", - "protocol": "Protocol", - "protocolSelect": "Select a protocol", + "protocol": "Protokół", + "protocolSelect": "Wybierz protokół", "resourcePortNumber": "Numer portu", "resourcePortNumberDescription": "Numer portu zewnętrznego do żądań proxy.", "cancel": "Anuluj", @@ -196,7 +196,7 @@ "disabled": "Wyłączone", "general": "Ogólny", "generalSettings": "Ustawienia ogólne", - "proxy": "Proxy", + "proxy": "Serwer pośredniczący", "rules": "Regulamin", "resourceSettingDescription": "Skonfiguruj ustawienia zasobu", "resourceSetting": "Ustawienia {resourceName}", @@ -300,7 +300,7 @@ "userMessageConfirm": "Aby potwierdzić, wpisz nazwę użytkownika poniżej.", "userQuestionRemove": "Czy na pewno chcesz trwale usunąć {selectedUser} z serwera?", "licenseKey": "Klucz licencyjny", - "valid": "Valid", + "valid": "Prawidłowy", "numberOfSites": "Liczba witryn", "licenseKeySearch": "Szukaj kluczy licencyjnych...", "licenseKeyAdd": "Dodaj klucz licencyjny", @@ -1100,7 +1100,7 @@ "containerImage": "Obraz", "containerState": "Stan", "containerNetworks": "Sieci", - "containerHostnameIp": "Hostname/IP", + "containerHostnameIp": "Nazwa hosta/IP", "containerLabels": "Etykiety", "containerLabelsCount": "{count} etykieta{s,plural,one{} other{s}}", "containerLabelsTitle": "Etykiety kontenera", @@ -1127,6 +1127,10 @@ "noContainersFoundMatching": "Nie znaleziono kontenerów pasujących do \"{filter}\".", "light": "jasny", "dark": "ciemny", - "system": "system", - "theme": "Motyw" -} \ No newline at end of file + "system": "System", + "theme": "Motyw", + "initialSetupTitle": "Wstępna konfiguracja serwera", + "initialSetupDescription": "Utwórz początkowe konto administratora serwera. Może istnieć tylko jeden administrator serwera. Zawsze można zmienić te dane uwierzytelniające.", + "createAdminAccount": "Utwórz konto administratora", + "setupErrorCreateAdmin": "Wystąpił błąd podczas tworzenia konta administratora serwera." +} diff --git a/messages/pt-PT.json b/messages/pt-PT.json index 15089ce7..69e650d5 100644 --- a/messages/pt-PT.json +++ b/messages/pt-PT.json @@ -114,7 +114,7 @@ "shareTokenDescription": "Seu token de acesso pode ser passado de duas maneiras: como um parâmetro de consulta ou nos cabeçalhos da solicitação. Estes devem ser passados do cliente em todas as solicitações para acesso autenticado.", "accessToken": "Token de acesso", "usageExamples": "Exemplos de uso", - "tokenId": "Token ID", + "tokenId": "ID do Token", "requestHeades": "Cabeçalhos de solicitação", "queryParameter": "Parâmetro de consulta", "importantNote": "Nota importante", @@ -177,8 +177,8 @@ "subdomnainDescription": "O subdomínio onde seu recurso estará acessível.", "resourceRawSettings": "Configurações TCP/UDP", "resourceRawSettingsDescription": "Configure como seu recurso será acessado sobre TCP/UDP", - "protocol": "Protocol", - "protocolSelect": "Select a protocol", + "protocol": "Protocolo", + "protocolSelect": "Selecione um protocolo", "resourcePortNumber": "Número da Porta", "resourcePortNumberDescription": "O número da porta externa para requisições de proxy.", "cancel": "cancelar", @@ -300,7 +300,7 @@ "userMessageConfirm": "Para confirmar, por favor digite o nome do usuário abaixo.", "userQuestionRemove": "Tem certeza que deseja excluir o {selectedUser} permanentemente do servidor?", "licenseKey": "Chave de Licença", - "valid": "Valid", + "valid": "Válido", "numberOfSites": "Número de sites", "licenseKeySearch": "Pesquisar chaves da licença...", "licenseKeyAdd": "Adicionar chave de licença", @@ -1102,9 +1102,9 @@ "containerNetworks": "Redes", "containerHostnameIp": "Hostname/IP", "containerLabels": "Marcadores", - "containerLabelsCount": "{count} label{s,plural,one{} other{s}}", + "containerLabelsCount": "{count} rótulo{s,plural,one{} other{s}}", "containerLabelsTitle": "Etiquetas do Contêiner", - "containerLabelEmpty": "", + "containerLabelEmpty": "", "containerPorts": "Portas", "containerPortsMore": "+ Mais{count}", "containerActions": "Ações.", @@ -1114,7 +1114,7 @@ "showStoppedContainers": "Mostrar contêineres parados", "noContainersFound": "Nenhum contêiner encontrado. Certifique-se de que os contêineres Docker estão em execução.", "searchContainersPlaceholder": "Pesquisar entre os contêineres {count}...", - "searchResultsCount": "{count} result{s,plural,one{} other{s}}", + "searchResultsCount": "{count} resultado{s,plural,one{} other{s}}", "filters": "Filtros", "filterOptions": "Opções de Filtro", "filterPorts": "Portas", @@ -1128,5 +1128,9 @@ "light": "claro", "dark": "escuro", "system": "sistema", - "theme": "Tema" -} \ No newline at end of file + "theme": "Tema", + "initialSetupTitle": "Configuração Inicial do Servidor", + "initialSetupDescription": "Crie a conta de administrador inicial do servidor. Apenas um administrador do servidor pode existir. Você sempre pode alterar essas credenciais posteriormente.", + "createAdminAccount": "Criar Conta de Administrador", + "setupErrorCreateAdmin": "Ocorreu um erro ao criar a conta de administrador do servidor." +} diff --git a/messages/tr-TR.json b/messages/tr-TR.json index c9da93af..ad6a0fe3 100644 --- a/messages/tr-TR.json +++ b/messages/tr-TR.json @@ -1128,5 +1128,9 @@ "light": "açık", "dark": "koyu", "system": "sistem", - "theme": "Tema" -} \ No newline at end of file + "theme": "Tema", + "initialSetupTitle": "İlk Sunucu Kurulumu", + "initialSetupDescription": "İlk sunucu yönetici hesabını oluşturun. Yalnızca bir sunucu yöneticisi olabilir. Bu kimlik bilgilerini daha sonra her zaman değiştirebilirsiniz.", + "createAdminAccount": "Yönetici Hesabı Oluştur", + "setupErrorCreateAdmin": "Sunucu yönetici hesabı oluşturulurken bir hata oluştu." +} diff --git a/messages/zh-CN.json b/messages/zh-CN.json index 13cdf22f..676d5f56 100644 --- a/messages/zh-CN.json +++ b/messages/zh-CN.json @@ -12,7 +12,7 @@ "componentsErrorNoMember": "您目前不是任何组织的成员。", "welcome": "欢迎使用 Pangolin", "componentsCreateOrg": "创建组织", - "componentsMember": "You're a member of {count, plural, =0 {no organization} =1 {one organization} other {# organizations}}.", + "componentsMember": "您属于 {count, plural, =0 {无组织} =1 {一个组织} other {# 个组织}}。", "componentsInvalidKey": "检测到无效或过期的许可证密钥。按照许可证条款操作以继续使用所有功能。", "dismiss": "忽略", "componentsLicenseViolation": "许可证超限:该服务器使用了 {usedSites} 个站点,已超过授权的 {maxSites} 个。请遵守许可证条款以继续使用全部功能。", @@ -66,8 +66,8 @@ "siteLoadWGConfig": "正在载入 WireGuard 配置...", "siteDocker": "扩展 Docker 部署详细信息", "toggle": "切换", - "dockerCompose": "Docker Compose", - "dockerRun": "Docker Run", + "dockerCompose": "Docker 配置", + "dockerRun": "停靠栏", "siteLearnLocal": "本地站点不需要隧道连接,点击了解更多", "siteConfirmCopy": "我已经复制了配置信息", "searchSitesProgress": "搜索站点...", @@ -331,7 +331,7 @@ "fossorialLicense": "查看Fossorial Commercial License和订阅条款", "licenseMessageRemove": "这将删除许可证密钥和它授予的所有相关权限。", "licenseMessageConfirm": "要确认,请在下面输入许可证密钥。", - "licenseQuestionRemove": "Are you sure you want to delete the license key {selectedKey} ?", + "licenseQuestionRemove": "您确定要删除 {selectedKey} 的邀请吗?", "licenseKeyDelete": "删除许可证密钥", "licenseKeyDeleteConfirm": "确认删除许可证密钥", "licenseTitle": "管理许可证状态", @@ -465,7 +465,7 @@ "siteErrorFetchDescription": "获取资源时出错", "targetErrorDuplicate": "重复的目标", "targetErrorDuplicateDescription": "具有这些设置的目标已存在", - "targetWireGuardErrorInvalidIp": "Invalid target IP", + "targetWireGuardErrorInvalidIp": "无效的目标IP", "targetWireGuardErrorInvalidIpDescription": "目标IP必须在站点子网内", "targetsUpdated": "目标已更新", "targetsUpdatedDescription": "目标和设置更新成功", @@ -691,7 +691,7 @@ "accessRoleErrorRemove": "删除角色失败", "accessRoleErrorRemoveDescription": "删除角色时出错。", "accessRoleName": "角色名称", - "accessRoleQuestionRemove": "You're about to delete the {name} role. You cannot undo this action.", + "accessRoleQuestionRemove": "您即将删除 {name} 角色。 此操作无法撤销。", "accessRoleRemove": "删除角色", "accessRoleRemoveDescription": "从组织中删除角色", "accessRoleRemoveSubmit": "删除角色", @@ -822,7 +822,7 @@ "emailVerifyResend": "没有收到代码?点击此处重新发送", "passwordNotMatch": "密码不匹配", "signupError": "注册时出错", - "pangolinLogoAlt": "Pangolin Logo", + "pangolinLogoAlt": "Pangolin 标志", "inviteAlready": "看起来您已被邀请!", "inviteAlreadyDescription": "要接受邀请,您必须登录或创建一个帐户。", "signupQuestion": "已经有一个帐户?", @@ -881,7 +881,7 @@ "idpOidcTokenValidating": "正在验证 OIDC 令牌", "idpOidcTokenResponse": "验证 OIDC 令牌响应", "idpErrorOidcTokenValidating": "验证 OIDC 令牌出错", - "idpConnectingTo": "Connecting to {name}", + "idpConnectingTo": "连接到{name}", "idpConnectingToDescription": "正在验证您的身份", "idpConnectingToProcess": "正在连接...", "idpConnectingToFinished": "已连接", @@ -895,7 +895,7 @@ "inviteErrorExpired": "邀请可能已过期", "inviteErrorRevoked": "邀请可能已被吊销了", "inviteErrorTypo": "邀请链接中可能有一个类型", - "pangolinSetup": "Setup - Pangolin", + "pangolinSetup": "认证 - Pangolin", "orgNameRequired": "组织名称是必需的", "orgIdRequired": "组织ID是必需的", "orgErrorCreate": "创建组织时出错", @@ -1094,7 +1094,7 @@ "enableDockerSocketDescription": "启用 Docker Socket 发现以填充容器信息。必须向 Newt 提供 Socket 路径。", "enableDockerSocketLink": "了解更多", "viewDockerContainers": "查看停靠容器", - "containersIn": "Containers in {siteName}", + "containersIn": "{siteName} 中的容器", "selectContainerDescription": "选择任何容器作为目标的主机名。点击端口使用端口。", "containerName": "名称", "containerImage": "图片", @@ -1128,5 +1128,9 @@ "light": "浅色", "dark": "深色", "system": "系统", - "theme": "主题" -} \ No newline at end of file + "theme": "主题", + "initialSetupTitle": "初始服务器设置", + "initialSetupDescription": "创建初始服务器管理员帐户。 只能存在一个服务器管理员。 您可以随时更改这些凭据。", + "createAdminAccount": "创建管理员帐户", + "setupErrorCreateAdmin": "创建服务器管理员帐户时出错。" +} diff --git a/server/db/pg/driver.ts b/server/db/pg/driver.ts index 6208e4d1..116e4610 100644 --- a/server/db/pg/driver.ts +++ b/server/db/pg/driver.ts @@ -5,6 +5,12 @@ import { withReplicas } from "drizzle-orm/pg-core"; function createDb() { const config = readConfigFile(); + if (!config.postgres) { + throw new Error( + "Postgres configuration is missing in the configuration file." + ); + } + const connectionString = config.postgres?.connection_string; const replicaConnections = config.postgres?.replicas || []; diff --git a/server/db/sqlite/driver.ts b/server/db/sqlite/driver.ts index a83ca7dd..9a12b43d 100644 --- a/server/db/sqlite/driver.ts +++ b/server/db/sqlite/driver.ts @@ -5,7 +5,6 @@ import path from "path"; import fs from "fs/promises"; import { APP_PATH } from "@server/lib/consts"; import { existsSync, mkdirSync } from "fs"; -import { readConfigFile } from "@server/lib/readConfigFile"; export const location = path.join(APP_PATH, "db", "db.sqlite"); export const exists = await checkFileExists(location); @@ -13,8 +12,6 @@ export const exists = await checkFileExists(location); bootstrapVolume(); function createDb() { - const config = readConfigFile(); - const sqlite = new Database(location); return DrizzleSqlite(sqlite, { schema }); } diff --git a/server/lib/config.ts b/server/lib/config.ts index f6599e49..b133db5b 100644 --- a/server/lib/config.ts +++ b/server/lib/config.ts @@ -5,6 +5,7 @@ import { SupporterKey, supporterKey } from "@server/db"; import { eq } from "drizzle-orm"; import { license } from "@server/license/license"; import { configSchema, readConfigFile } from "./readConfigFile"; +import { fromError } from "zod-validation-error"; export class Config { private rawConfig!: z.infer; @@ -20,7 +21,35 @@ export class Config { } public load() { - const parsedConfig = readConfigFile(); + const environment = readConfigFile(); + + const { + data: parsedConfig, + success, + error + } = configSchema.safeParse(environment); + + if (!success) { + const errors = fromError(error); + 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 ( + // @ts-ignore + parsedConfig.users || + process.env.USERS_SERVERADMIN_EMAIL || + process.env.USERS_SERVERADMIN_PASSWORD + ) { + console.log( + "WARNING: Your admin credentials are still in the config file or environment variables. This method of setting admin credentials is no longer supported. It is recommended to remove them." + ); + } process.env.APP_VERSION = APP_VERSION; diff --git a/server/lib/consts.ts b/server/lib/consts.ts index 1dc46d2b..843ce54f 100644 --- a/server/lib/consts.ts +++ b/server/lib/consts.ts @@ -2,7 +2,7 @@ import path from "path"; import { fileURLToPath } from "url"; // This is a placeholder value replaced by the build process -export const APP_VERSION = "1.5.1"; +export const APP_VERSION = "1.6.0"; export const __FILENAME = fileURLToPath(import.meta.url); export const __DIRNAME = path.dirname(__FILENAME); diff --git a/server/lib/readConfigFile.ts b/server/lib/readConfigFile.ts index ed57508a..421a7028 100644 --- a/server/lib/readConfigFile.ts +++ b/server/lib/readConfigFile.ts @@ -295,24 +295,11 @@ export function readConfigFile() { environment = loadConfig(configFilePath2); } - if (process.env.APP_BASE_DOMAIN) { - console.log( - "You're using deprecated environment variables. Transition to the configuration file. https://docs.fossorial.io/" - ); - } - if (!environment) { throw new Error( "No configuration file found. Please create one. https://docs.fossorial.io/" ); } - const parsedConfig = configSchema.safeParse(environment); - - if (!parsedConfig.success) { - const errors = fromError(parsedConfig.error); - throw new Error(`Invalid configuration file: ${errors}`); - } - - return parsedConfig.data; + return environment; } diff --git a/server/middlewares/rateLimit.ts b/server/middlewares/rateLimit.ts index 9b40e242..19eac8bb 100644 --- a/server/middlewares/rateLimit.ts +++ b/server/middlewares/rateLimit.ts @@ -1,4 +1,4 @@ -import { rateLimit } from "express-rate-limit"; +import { MemoryStore, rateLimit, Store } from "express-rate-limit"; import createHttpError from "http-errors"; import { NextFunction, Request, Response } from "express"; import logger from "@server/logger"; @@ -6,7 +6,15 @@ import HttpCode from "@server/types/HttpCode"; import config from "@server/lib/config"; import { RedisStore } from "rate-limit-redis"; import redisManager from "@server/db/redis"; -import { Command as RedisCommand } from "ioredis"; + +export let rateLimitStore: Store = new MemoryStore(); +if (config.getRawConfig().flags?.enable_redis) { + const client = redisManager.client!; + rateLimitStore = new RedisStore({ + sendCommand: async (command: string, ...args: string[]) => + (await client.call(command, args)) as any + }); +} export function rateLimitMiddleware({ windowMin, @@ -19,11 +27,8 @@ export function rateLimitMiddleware({ type: "IP_ONLY" | "IP_AND_PATH"; skipCondition?: (req: Request, res: Response) => boolean; }) { - const enableRedis = config.getRawConfig().flags?.enable_redis; - - let opts; if (type === "IP_AND_PATH") { - opts = { + return rateLimit({ windowMs: windowMin * 60 * 1000, max, skip: skipCondition, @@ -38,10 +43,11 @@ export function rateLimitMiddleware({ return next( createHttpError(HttpCode.TOO_MANY_REQUESTS, message) ); - } - } as any; + }, + store: rateLimitStore + }); } else { - opts = { + return rateLimit({ windowMs: windowMin * 60 * 1000, max, skip: skipCondition, @@ -52,18 +58,8 @@ export function rateLimitMiddleware({ createHttpError(HttpCode.TOO_MANY_REQUESTS, message) ); } - }; - } - - if (enableRedis) { - const client = redisManager.client!; - opts.store = new RedisStore({ - sendCommand: async (command: string, ...args: string[]) => - (await client.call(command, args)) as any }); } - - return rateLimit(opts); } export default rateLimitMiddleware; diff --git a/server/routers/external.ts b/server/routers/external.ts index a5e577ee..939743ce 100644 --- a/server/routers/external.ts +++ b/server/routers/external.ts @@ -32,6 +32,7 @@ import { verifyIsLoggedInUser, verifyClientAccess, verifyApiKeyAccess, + rateLimitStore, } from "@server/middlewares"; import { verifyUserHasAction } from "../middlewares/verifyUserHasAction"; import { ActionsEnum } from "@server/auth/actions"; @@ -782,7 +783,8 @@ authRouter.post( handler: (req, res, next) => { const message = `You can only request an email verification code ${3} times every ${15} minutes. Please try again later.`; return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message)); - } + }, + store: rateLimitStore }), auth.requestEmailVerificationCode ); @@ -802,7 +804,8 @@ authRouter.post( handler: (req, res, next) => { const message = `You can only request a password reset ${3} times every ${15} minutes. Please try again later.`; return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message)); - } + }, + store: rateLimitStore }), auth.requestPasswordReset ); @@ -821,7 +824,8 @@ authRouter.post( handler: (req, res, next) => { const message = `You can only request an email OTP ${10} times every ${15} minutes. Please try again later.`; return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message)); - } + }, + store: rateLimitStore }), resource.authWithWhitelist ); diff --git a/src/app/page.tsx b/src/app/page.tsx index 2d2d6894..14738ac7 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -33,7 +33,6 @@ export default async function Page(props: { >(`/auth/initial-setup-complete`, await authCookieHeader()); const complete = setupRes.data.data.complete; if (!complete) { - console.log("compelte", complete); redirect("/auth/initial-setup"); } diff --git a/src/app/setup/page.tsx b/src/app/setup/page.tsx index a51ed8e1..d6f45bbb 100644 --- a/src/app/setup/page.tsx +++ b/src/app/setup/page.tsx @@ -308,13 +308,13 @@ export default function StepperForm() { )} /> - {orgIdTaken && ( + {orgIdTaken && !orgCreated ? ( {t('setupErrorIdentifier')} - )} + ) : null} {error && (