Compare commits

..

2 Commits

Author SHA1 Message Date
Philipinho 8b16ac4151 chore: add undici for oidc proxy support 2026-04-24 14:29:57 +01:00
Philipinho a573acedd0 fix: local storage, and package overrides 2026-04-22 14:13:25 +01:00
23 changed files with 499 additions and 631 deletions
+1 -1
View File
@@ -42,7 +42,7 @@
"mantine-form-zod-resolver": "^1.3.0", "mantine-form-zod-resolver": "^1.3.0",
"mermaid": "^11.13.0", "mermaid": "^11.13.0",
"mitt": "^3.0.1", "mitt": "^3.0.1",
"posthog-js": "1.363.1", "posthog-js": "1.370.0",
"react": "^18.3.1", "react": "^18.3.1",
"react-arborist": "3.4.0", "react-arborist": "3.4.0",
"react-clear-modal": "^2.0.18", "react-clear-modal": "^2.0.18",
@@ -391,7 +391,7 @@
"Write anything. Enter \"/\" for commands": "Schreiben Sie etwas. Geben Sie \"/\" für Befehle ein", "Write anything. Enter \"/\" for commands": "Schreiben Sie etwas. Geben Sie \"/\" für Befehle ein",
"Write...": "\"Schreiben...\"", "Write...": "\"Schreiben...\"",
"Column count": "Spaltenanzahl", "Column count": "Spaltenanzahl",
"{{count}} Columns": "{{count}} Spalten", "{{count}} Columns": "{count, plural, one {# Spalte} other {# Spalten}}",
"Equal columns": "Gleich breite Spalten", "Equal columns": "Gleich breite Spalten",
"Left sidebar": "Linke Seitenleiste", "Left sidebar": "Linke Seitenleiste",
"Right sidebar": "Rechte Seitenleiste", "Right sidebar": "Rechte Seitenleiste",
@@ -608,21 +608,25 @@
"Image exceeds 10MB limit.": "Bild überschreitet das Limit von 10 MB.", "Image exceeds 10MB limit.": "Bild überschreitet das Limit von 10 MB.",
"Image removed successfully": "Bild erfolgreich entfernt", "Image removed successfully": "Bild erfolgreich entfernt",
"API key": "API-Schlüssel", "API key": "API-Schlüssel",
"API key created successfully": "API-Schlüssel erfolgreich erstellt",
"API keys": "API-Schlüssel", "API keys": "API-Schlüssel",
"API management": "API-Verwaltung", "API management": "API-Verwaltung",
"Are you sure you want to revoke this API key": "Sind Sie sicher, dass Sie diesen API-Schlüssel widerrufen möchten?",
"Create API Key": "API-Schlüssel erstellen",
"Custom expiration date": "Benutzerdefiniertes Ablaufdatum", "Custom expiration date": "Benutzerdefiniertes Ablaufdatum",
"Enter a descriptive token name": "Geben Sie einen beschreibenden Token-Namen ein", "Enter a descriptive token name": "Geben Sie einen beschreibenden Token-Namen ein",
"Expiration": "Ablauf", "Expiration": "Ablauf",
"Expired": "Abgelaufen", "Expired": "Abgelaufen",
"Expires": "Läuft ab", "Expires": "Läuft ab",
"I've saved my API key": "Ich habe meinen API-Schlüssel gespeichert",
"Last use": "Zuletzt verwendet", "Last use": "Zuletzt verwendet",
"No API keys found": "Keine API-Schlüssel gefunden", "No API keys found": "Keine API-Schlüssel gefunden",
"No expiration": "Kein Ablauf", "No expiration": "Kein Ablauf",
"Revoke API key": "API-Schlüssel widerrufen",
"Revoked successfully": "Erfolgreich widerrufen", "Revoked successfully": "Erfolgreich widerrufen",
"Select expiration date": "Ablaufdatum wählen", "Select expiration date": "Ablaufdatum wählen",
"This action cannot be undone. Any applications using this API key will stop working.": "Diese Aktion kann nicht rückgängig gemacht werden. Alle Anwendungen, die diesen API-Schlüssel verwenden, werden nicht mehr funktionieren.", "This action cannot be undone. Any applications using this API key will stop working.": "Diese Aktion kann nicht rückgängig gemacht werden. Alle Anwendungen, die diesen API-Schlüssel verwenden, werden nicht mehr funktionieren.",
"Update": "Update", "Update API key": "API-Schlüssel aktualisieren",
"Update {{credential}}": "Update {{credential}}",
"Manage API keys for all users in the workspace": "Verwalten Sie API-Schlüssel für alle Benutzer im Arbeitsbereich", "Manage API keys for all users in the workspace": "Verwalten Sie API-Schlüssel für alle Benutzer im Arbeitsbereich",
"Restrict API key creation to admins": "API-Schlüsselerstellung auf Administratoren beschränken", "Restrict API key creation to admins": "API-Schlüsselerstellung auf Administratoren beschränken",
"Only admins and owners can create new API keys. Existing member keys will continue to work.": "Nur Administratoren und Eigentümer können neue API-Schlüssel erstellen. Bestehende Mitgliederschlüssel funktionieren weiterhin.", "Only admins and owners can create new API keys. Existing member keys will continue to work.": "Nur Administratoren und Eigentümer können neue API-Schlüssel erstellen. Bestehende Mitgliederschlüssel funktionieren weiterhin.",
@@ -876,29 +880,5 @@
"Try a different search term.": "Versuchen Sie einen anderen Suchbegriff.", "Try a different search term.": "Versuchen Sie einen anderen Suchbegriff.",
"Try again": "Erneut versuchen", "Try again": "Erneut versuchen",
"Untitled chat": "Chat ohne Titel", "Untitled chat": "Chat ohne Titel",
"What can I help you with?": "Womit kann ich Ihnen helfen?", "What can I help you with?": "Womit kann ich Ihnen helfen?"
"Are you sure you want to revoke this {{credential}}": "Are you sure you want to revoke this {{credential}}",
"Automatically provision users and groups from your identity provider via SCIM.": "Automatically provision users and groups from your identity provider via SCIM.",
"Configure your identity provider with this URL to provision users and groups.": "Configure your identity provider with this URL to provision users and groups.",
"Create {{credential}}": "Create {{credential}}",
"{{credential}} created": "{{credential}} created",
"{{credential}} created successfully": "{{credential}} created successfully",
"Created by": "Created by",
"Custom": "Custom",
"Enable SCIM": "Enable SCIM",
"Enter a descriptive name": "Enter a descriptive name",
"I've saved my {{credential}}": "I've saved my {{credential}}",
"Important": "Important",
"Make sure to copy your {{credential}} now. You won't be able to see it again!": "Make sure to copy your {{credential}} now. You won't be able to see it again!",
"Never": "Never",
"Revoke {{credential}}": "Revoke {{credential}}",
"SCIM endpoint URL": "SCIM endpoint URL",
"SCIM provisioning": "SCIM provisioning",
"SCIM takes precedence over SSO group sync while enabled.": "SCIM takes precedence over SSO group sync while enabled.",
"You have reached the maximum of {{max}} SCIM tokens. Delete an existing token to create a new one.": "You have reached the maximum of {{max}} SCIM tokens. Delete an existing token to create a new one.",
"SCIM token": "SCIM token",
"SCIM tokens": "SCIM tokens",
"This action cannot be undone. Your identity provider will stop syncing immediately.": "This action cannot be undone. Your identity provider will stop syncing immediately.",
"Toggle SCIM provisioning": "Toggle SCIM provisioning",
"Token": "Token"
} }
@@ -608,21 +608,25 @@
"Image exceeds 10MB limit.": "Image exceeds 10MB limit.", "Image exceeds 10MB limit.": "Image exceeds 10MB limit.",
"Image removed successfully": "Image removed successfully", "Image removed successfully": "Image removed successfully",
"API key": "API key", "API key": "API key",
"API key created successfully": "API key created successfully",
"API keys": "API keys", "API keys": "API keys",
"API management": "API management", "API management": "API management",
"Are you sure you want to revoke this API key": "Are you sure you want to revoke this API key",
"Create API Key": "Create API Key",
"Custom expiration date": "Custom expiration date", "Custom expiration date": "Custom expiration date",
"Enter a descriptive token name": "Enter a descriptive token name", "Enter a descriptive token name": "Enter a descriptive token name",
"Expiration": "Expiration", "Expiration": "Expiration",
"Expired": "Expired", "Expired": "Expired",
"Expires": "Expires", "Expires": "Expires",
"I've saved my API key": "I've saved my API key",
"Last use": "Last Used", "Last use": "Last Used",
"No API keys found": "No API keys found", "No API keys found": "No API keys found",
"No expiration": "No expiration", "No expiration": "No expiration",
"Revoke API key": "Revoke API key",
"Revoked successfully": "Revoked successfully", "Revoked successfully": "Revoked successfully",
"Select expiration date": "Select expiration date", "Select expiration date": "Select expiration date",
"This action cannot be undone. Any applications using this API key will stop working.": "This action cannot be undone. Any applications using this API key will stop working.", "This action cannot be undone. Any applications using this API key will stop working.": "This action cannot be undone. Any applications using this API key will stop working.",
"Update": "Update", "Update API key": "Update API key",
"Update {{credential}}": "Update {{credential}}",
"Manage API keys for all users in the workspace": "Manage API keys for all users in the workspace", "Manage API keys for all users in the workspace": "Manage API keys for all users in the workspace",
"Restrict API key creation to admins": "Restrict API key creation to admins", "Restrict API key creation to admins": "Restrict API key creation to admins",
"Only admins and owners can create new API keys. Existing member keys will continue to work.": "Only admins and owners can create new API keys. Existing member keys will continue to work.", "Only admins and owners can create new API keys. Existing member keys will continue to work.": "Only admins and owners can create new API keys. Existing member keys will continue to work.",
@@ -876,29 +880,5 @@
"Try a different search term.": "Try a different search term.", "Try a different search term.": "Try a different search term.",
"Try again": "Try again", "Try again": "Try again",
"Untitled chat": "Untitled chat", "Untitled chat": "Untitled chat",
"What can I help you with?": "What can I help you with?", "What can I help you with?": "What can I help you with?"
"Are you sure you want to revoke this {{credential}}": "Are you sure you want to revoke this {{credential}}",
"Automatically provision users and groups from your identity provider via SCIM.": "Automatically provision users and groups from your identity provider via SCIM.",
"Configure your identity provider with this URL to provision users and groups.": "Configure your identity provider with this URL to provision users and groups.",
"Create {{credential}}": "Create {{credential}}",
"{{credential}} created": "{{credential}} created",
"{{credential}} created successfully": "{{credential}} created successfully",
"Created by": "Created by",
"Custom": "Custom",
"Enable SCIM": "Enable SCIM",
"Enter a descriptive name": "Enter a descriptive name",
"I've saved my {{credential}}": "I've saved my {{credential}}",
"Important": "Important",
"Make sure to copy your {{credential}} now. You won't be able to see it again!": "Make sure to copy your {{credential}} now. You won't be able to see it again!",
"Never": "Never",
"Revoke {{credential}}": "Revoke {{credential}}",
"SCIM endpoint URL": "SCIM endpoint URL",
"SCIM provisioning": "SCIM provisioning",
"SCIM takes precedence over SSO group sync while enabled.": "SCIM takes precedence over SSO group sync while enabled.",
"You have reached the maximum of {{max}} SCIM tokens. Delete an existing token to create a new one.": "You have reached the maximum of {{max}} SCIM tokens. Delete an existing token to create a new one.",
"SCIM token": "SCIM token",
"SCIM tokens": "SCIM tokens",
"This action cannot be undone. Your identity provider will stop syncing immediately.": "This action cannot be undone. Your identity provider will stop syncing immediately.",
"Toggle SCIM provisioning": "Toggle SCIM provisioning",
"Token": "Token"
} }
@@ -608,21 +608,25 @@
"Image exceeds 10MB limit.": "La imagen excede del límite de 10 MB", "Image exceeds 10MB limit.": "La imagen excede del límite de 10 MB",
"Image removed successfully": "Imagen eliminada correctamente", "Image removed successfully": "Imagen eliminada correctamente",
"API key": "Clave API", "API key": "Clave API",
"API key created successfully": "Clave API creada correctamente",
"API keys": "Claves API", "API keys": "Claves API",
"API management": "Gestión de API", "API management": "Gestión de API",
"Are you sure you want to revoke this API key": "¿Está seguro de que desea revocar esta clave API? ",
"Create API Key": "Crear clave API",
"Custom expiration date": "Fecha de vencimiento personalizada", "Custom expiration date": "Fecha de vencimiento personalizada",
"Enter a descriptive token name": "Introduce un nombre descriptivo del token", "Enter a descriptive token name": "Introduce un nombre descriptivo del token",
"Expiration": "Vencimiento", "Expiration": "Vencimiento",
"Expired": "Vencido", "Expired": "Vencido",
"Expires": "Vence", "Expires": "Vence",
"I've saved my API key": "He guardado mi clave API",
"Last use": "Último uso", "Last use": "Último uso",
"No API keys found": "No se han encontrado claves API", "No API keys found": "No se han encontrado claves API",
"No expiration": "Sin vencimiento", "No expiration": "Sin vencimiento",
"Revoke API key": "Revocar clave API",
"Revoked successfully": "Revocada correctamente", "Revoked successfully": "Revocada correctamente",
"Select expiration date": "Seleccionar fecha de vencimiento", "Select expiration date": "Seleccionar fecha de vencimiento",
"This action cannot be undone. Any applications using this API key will stop working.": "Esta acción no se puede deshacer. Las aplicaciones que utilicen esta clave API dejarán de funcionar.", "This action cannot be undone. Any applications using this API key will stop working.": "Esta acción no se puede deshacer. Las aplicaciones que utilicen esta clave API dejarán de funcionar.",
"Update": "Update", "Update API key": "Actualizar clave API",
"Update {{credential}}": "Update {{credential}}",
"Manage API keys for all users in the workspace": "Gestionar claves API para todos los usuarios en el espacio de trabajo", "Manage API keys for all users in the workspace": "Gestionar claves API para todos los usuarios en el espacio de trabajo",
"Restrict API key creation to admins": "Restringir la creación de claves API a administradores", "Restrict API key creation to admins": "Restringir la creación de claves API a administradores",
"Only admins and owners can create new API keys. Existing member keys will continue to work.": "Solo los administradores y propietarios pueden crear nuevas claves API. Las claves de miembros existentes seguirán funcionando.", "Only admins and owners can create new API keys. Existing member keys will continue to work.": "Solo los administradores y propietarios pueden crear nuevas claves API. Las claves de miembros existentes seguirán funcionando.",
@@ -876,29 +880,5 @@
"Try a different search term.": "Prueba con otro término de búsqueda.", "Try a different search term.": "Prueba con otro término de búsqueda.",
"Try again": "Intentar de nuevo", "Try again": "Intentar de nuevo",
"Untitled chat": "Chat sin título", "Untitled chat": "Chat sin título",
"What can I help you with?": "¿En qué puedo ayudarte?", "What can I help you with?": "¿En qué puedo ayudarte?"
"Are you sure you want to revoke this {{credential}}": "Are you sure you want to revoke this {{credential}}",
"Automatically provision users and groups from your identity provider via SCIM.": "Automatically provision users and groups from your identity provider via SCIM.",
"Configure your identity provider with this URL to provision users and groups.": "Configure your identity provider with this URL to provision users and groups.",
"Create {{credential}}": "Create {{credential}}",
"{{credential}} created": "{{credential}} created",
"{{credential}} created successfully": "{{credential}} created successfully",
"Created by": "Created by",
"Custom": "Custom",
"Enable SCIM": "Enable SCIM",
"Enter a descriptive name": "Enter a descriptive name",
"I've saved my {{credential}}": "I've saved my {{credential}}",
"Important": "Important",
"Make sure to copy your {{credential}} now. You won't be able to see it again!": "Make sure to copy your {{credential}} now. You won't be able to see it again!",
"Never": "Never",
"Revoke {{credential}}": "Revoke {{credential}}",
"SCIM endpoint URL": "SCIM endpoint URL",
"SCIM provisioning": "SCIM provisioning",
"SCIM takes precedence over SSO group sync while enabled.": "SCIM takes precedence over SSO group sync while enabled.",
"You have reached the maximum of {{max}} SCIM tokens. Delete an existing token to create a new one.": "You have reached the maximum of {{max}} SCIM tokens. Delete an existing token to create a new one.",
"SCIM token": "SCIM token",
"SCIM tokens": "SCIM tokens",
"This action cannot be undone. Your identity provider will stop syncing immediately.": "This action cannot be undone. Your identity provider will stop syncing immediately.",
"Toggle SCIM provisioning": "Toggle SCIM provisioning",
"Token": "Token"
} }
@@ -608,21 +608,25 @@
"Image exceeds 10MB limit.": "L'image dépasse la limite de 10 Mo.", "Image exceeds 10MB limit.": "L'image dépasse la limite de 10 Mo.",
"Image removed successfully": "Image supprimée avec succès", "Image removed successfully": "Image supprimée avec succès",
"API key": "Clé API", "API key": "Clé API",
"API key created successfully": "Clé API créée avec succès",
"API keys": "Clés API", "API keys": "Clés API",
"API management": "Gestion des API", "API management": "Gestion des API",
"Are you sure you want to revoke this API key": "Êtes-vous sûr de vouloir révoquer cette clé API",
"Create API Key": "Créer une clé API",
"Custom expiration date": "Date d'expiration personnalisée", "Custom expiration date": "Date d'expiration personnalisée",
"Enter a descriptive token name": "Entrez un nom descriptif pour le jeton", "Enter a descriptive token name": "Entrez un nom descriptif pour le jeton",
"Expiration": "Expiration", "Expiration": "Expiration",
"Expired": "Expiré(e)", "Expired": "Expiré(e)",
"Expires": "Expire", "Expires": "Expire",
"I've saved my API key": "J'ai enregistré ma clé API",
"Last use": "Dernière utilisation", "Last use": "Dernière utilisation",
"No API keys found": "Aucune clé API trouvée", "No API keys found": "Aucune clé API trouvée",
"No expiration": "Pas d'expiration", "No expiration": "Pas d'expiration",
"Revoke API key": "Révoquer la clé API",
"Revoked successfully": "Révoqué(e) avec succès", "Revoked successfully": "Révoqué(e) avec succès",
"Select expiration date": "Sélectionnez la date d'expiration", "Select expiration date": "Sélectionnez la date d'expiration",
"This action cannot be undone. Any applications using this API key will stop working.": "Cette action ne peut pas être annulée. Toutes les applications utilisant cette clé API cesseront de fonctionner.", "This action cannot be undone. Any applications using this API key will stop working.": "Cette action ne peut pas être annulée. Toutes les applications utilisant cette clé API cesseront de fonctionner.",
"Update": "Update", "Update API key": "Mettre à jour la clé API",
"Update {{credential}}": "Update {{credential}}",
"Manage API keys for all users in the workspace": "Gérer les clés API pour tous les utilisateurs dans l'espace de travail", "Manage API keys for all users in the workspace": "Gérer les clés API pour tous les utilisateurs dans l'espace de travail",
"Restrict API key creation to admins": "Restreindre la création de clés API aux administrateurs", "Restrict API key creation to admins": "Restreindre la création de clés API aux administrateurs",
"Only admins and owners can create new API keys. Existing member keys will continue to work.": "Seuls les administrateurs et les propriétaires peuvent créer de nouvelles clés API. Les clés des membres existants continueront de fonctionner.", "Only admins and owners can create new API keys. Existing member keys will continue to work.": "Seuls les administrateurs et les propriétaires peuvent créer de nouvelles clés API. Les clés des membres existants continueront de fonctionner.",
@@ -876,29 +880,5 @@
"Try a different search term.": "Essayez un autre terme de recherche.", "Try a different search term.": "Essayez un autre terme de recherche.",
"Try again": "Réessayer", "Try again": "Réessayer",
"Untitled chat": "Discussion sans titre", "Untitled chat": "Discussion sans titre",
"What can I help you with?": "Que puis-je faire pour vous aider ?", "What can I help you with?": "Que puis-je faire pour vous aider ?"
"Are you sure you want to revoke this {{credential}}": "Are you sure you want to revoke this {{credential}}",
"Automatically provision users and groups from your identity provider via SCIM.": "Automatically provision users and groups from your identity provider via SCIM.",
"Configure your identity provider with this URL to provision users and groups.": "Configure your identity provider with this URL to provision users and groups.",
"Create {{credential}}": "Create {{credential}}",
"{{credential}} created": "{{credential}} created",
"{{credential}} created successfully": "{{credential}} created successfully",
"Created by": "Created by",
"Custom": "Custom",
"Enable SCIM": "Enable SCIM",
"Enter a descriptive name": "Enter a descriptive name",
"I've saved my {{credential}}": "I've saved my {{credential}}",
"Important": "Important",
"Make sure to copy your {{credential}} now. You won't be able to see it again!": "Make sure to copy your {{credential}} now. You won't be able to see it again!",
"Never": "Never",
"Revoke {{credential}}": "Revoke {{credential}}",
"SCIM endpoint URL": "SCIM endpoint URL",
"SCIM provisioning": "SCIM provisioning",
"SCIM takes precedence over SSO group sync while enabled.": "SCIM takes precedence over SSO group sync while enabled.",
"You have reached the maximum of {{max}} SCIM tokens. Delete an existing token to create a new one.": "You have reached the maximum of {{max}} SCIM tokens. Delete an existing token to create a new one.",
"SCIM token": "SCIM token",
"SCIM tokens": "SCIM tokens",
"This action cannot be undone. Your identity provider will stop syncing immediately.": "This action cannot be undone. Your identity provider will stop syncing immediately.",
"Toggle SCIM provisioning": "Toggle SCIM provisioning",
"Token": "Token"
} }
@@ -608,21 +608,25 @@
"Image exceeds 10MB limit.": "L'immagine supera il limite di 10MB.", "Image exceeds 10MB limit.": "L'immagine supera il limite di 10MB.",
"Image removed successfully": "Immagine rimossa con successo", "Image removed successfully": "Immagine rimossa con successo",
"API key": "Chiave API", "API key": "Chiave API",
"API key created successfully": "Chiave API creata con successo",
"API keys": "Chiavi API", "API keys": "Chiavi API",
"API management": "Gestione API", "API management": "Gestione API",
"Are you sure you want to revoke this API key": "Sei sicuro di voler revocare questa chiave API",
"Create API Key": "Crea Chiave API",
"Custom expiration date": "Data di scadenza personalizzata", "Custom expiration date": "Data di scadenza personalizzata",
"Enter a descriptive token name": "Inserisci un nome descrittivo del token", "Enter a descriptive token name": "Inserisci un nome descrittivo del token",
"Expiration": "Scadenza", "Expiration": "Scadenza",
"Expired": "Scaduto", "Expired": "Scaduto",
"Expires": "Scade", "Expires": "Scade",
"I've saved my API key": "Ho salvato la mia chiave API",
"Last use": "Ultimo utilizzo", "Last use": "Ultimo utilizzo",
"No API keys found": "Nessuna chiave API trovata", "No API keys found": "Nessuna chiave API trovata",
"No expiration": "Nessuna scadenza", "No expiration": "Nessuna scadenza",
"Revoke API key": "Revoca chiave API",
"Revoked successfully": "Revocata con successo", "Revoked successfully": "Revocata con successo",
"Select expiration date": "Seleziona la data di scadenza", "Select expiration date": "Seleziona la data di scadenza",
"This action cannot be undone. Any applications using this API key will stop working.": "Questa azione non può essere annullata. Qualsiasi applicazione che utilizza questa chiave API smetterà di funzionare.", "This action cannot be undone. Any applications using this API key will stop working.": "Questa azione non può essere annullata. Qualsiasi applicazione che utilizza questa chiave API smetterà di funzionare.",
"Update": "Update", "Update API key": "Aggiorna chiave API",
"Update {{credential}}": "Update {{credential}}",
"Manage API keys for all users in the workspace": "Gestisci le chiavi API per tutti gli utenti nell'area di lavoro", "Manage API keys for all users in the workspace": "Gestisci le chiavi API per tutti gli utenti nell'area di lavoro",
"Restrict API key creation to admins": "Limita la creazione delle chiavi API agli amministratori", "Restrict API key creation to admins": "Limita la creazione delle chiavi API agli amministratori",
"Only admins and owners can create new API keys. Existing member keys will continue to work.": "Solo gli amministratori e i proprietari possono creare nuove chiavi API. Le chiavi dei membri esistenti continueranno a funzionare.", "Only admins and owners can create new API keys. Existing member keys will continue to work.": "Solo gli amministratori e i proprietari possono creare nuove chiavi API. Le chiavi dei membri esistenti continueranno a funzionare.",
@@ -876,29 +880,5 @@
"Try a different search term.": "Prova un termine di ricerca diverso.", "Try a different search term.": "Prova un termine di ricerca diverso.",
"Try again": "Riprova", "Try again": "Riprova",
"Untitled chat": "Chat senza titolo", "Untitled chat": "Chat senza titolo",
"What can I help you with?": "Con cosa posso aiutarti?", "What can I help you with?": "Con cosa posso aiutarti?"
"Are you sure you want to revoke this {{credential}}": "Are you sure you want to revoke this {{credential}}",
"Automatically provision users and groups from your identity provider via SCIM.": "Automatically provision users and groups from your identity provider via SCIM.",
"Configure your identity provider with this URL to provision users and groups.": "Configure your identity provider with this URL to provision users and groups.",
"Create {{credential}}": "Create {{credential}}",
"{{credential}} created": "{{credential}} created",
"{{credential}} created successfully": "{{credential}} created successfully",
"Created by": "Created by",
"Custom": "Custom",
"Enable SCIM": "Enable SCIM",
"Enter a descriptive name": "Enter a descriptive name",
"I've saved my {{credential}}": "I've saved my {{credential}}",
"Important": "Important",
"Make sure to copy your {{credential}} now. You won't be able to see it again!": "Make sure to copy your {{credential}} now. You won't be able to see it again!",
"Never": "Never",
"Revoke {{credential}}": "Revoke {{credential}}",
"SCIM endpoint URL": "SCIM endpoint URL",
"SCIM provisioning": "SCIM provisioning",
"SCIM takes precedence over SSO group sync while enabled.": "SCIM takes precedence over SSO group sync while enabled.",
"You have reached the maximum of {{max}} SCIM tokens. Delete an existing token to create a new one.": "You have reached the maximum of {{max}} SCIM tokens. Delete an existing token to create a new one.",
"SCIM token": "SCIM token",
"SCIM tokens": "SCIM tokens",
"This action cannot be undone. Your identity provider will stop syncing immediately.": "This action cannot be undone. Your identity provider will stop syncing immediately.",
"Toggle SCIM provisioning": "Toggle SCIM provisioning",
"Token": "Token"
} }
@@ -608,21 +608,25 @@
"Image exceeds 10MB limit.": "画像が10MBの制限を超えています", "Image exceeds 10MB limit.": "画像が10MBの制限を超えています",
"Image removed successfully": "画像を削除しました", "Image removed successfully": "画像を削除しました",
"API key": "APIキー", "API key": "APIキー",
"API key created successfully": "APIキーを作成しました",
"API keys": "APIキー", "API keys": "APIキー",
"API management": "API管理", "API management": "API管理",
"Are you sure you want to revoke this API key": "このAPIキーを無効にしてもよろしいですか",
"Create API Key": "APIキーを作成",
"Custom expiration date": "カスタム有効期限", "Custom expiration date": "カスタム有効期限",
"Enter a descriptive token name": "説明的なトークン名を入力してください", "Enter a descriptive token name": "説明的なトークン名を入力してください",
"Expiration": "有効期限", "Expiration": "有効期限",
"Expired": "期限切れ", "Expired": "期限切れ",
"Expires": "期限が切れます", "Expires": "期限が切れます",
"I've saved my API key": "APIキーを保存しました",
"Last use": "最終使用", "Last use": "最終使用",
"No API keys found": "APIキーが見つかりません", "No API keys found": "APIキーが見つかりません",
"No expiration": "期限なし", "No expiration": "期限なし",
"Revoke API key": "APIキーを無効にする",
"Revoked successfully": "無効にしました", "Revoked successfully": "無効にしました",
"Select expiration date": "有効期限を選択してください", "Select expiration date": "有効期限を選択してください",
"This action cannot be undone. Any applications using this API key will stop working.": "この操作は取り消せません。このAPIキーを使用しているアプリケーションは動作しなくなります", "This action cannot be undone. Any applications using this API key will stop working.": "この操作は取り消せません。このAPIキーを使用しているアプリケーションは動作しなくなります",
"Update": "Update", "Update API key": "APIキーを更新",
"Update {{credential}}": "Update {{credential}}",
"Manage API keys for all users in the workspace": "ワークスペース内のすべてのユーザーのAPIキーを管理", "Manage API keys for all users in the workspace": "ワークスペース内のすべてのユーザーのAPIキーを管理",
"Restrict API key creation to admins": "APIキーの作成を管理者のみに制限する", "Restrict API key creation to admins": "APIキーの作成を管理者のみに制限する",
"Only admins and owners can create new API keys. Existing member keys will continue to work.": "新しいAPIキーを作成できるのは管理者とオーナーのみです。既存のメンバーキーは引き続き有効です。", "Only admins and owners can create new API keys. Existing member keys will continue to work.": "新しいAPIキーを作成できるのは管理者とオーナーのみです。既存のメンバーキーは引き続き有効です。",
@@ -876,29 +880,5 @@
"Try a different search term.": "別の検索語を試してください。", "Try a different search term.": "別の検索語を試してください。",
"Try again": "再試行", "Try again": "再試行",
"Untitled chat": "無題のチャット", "Untitled chat": "無題のチャット",
"What can I help you with?": "何をお手伝いしましょうか?", "What can I help you with?": "何をお手伝いしましょうか?"
"Are you sure you want to revoke this {{credential}}": "Are you sure you want to revoke this {{credential}}",
"Automatically provision users and groups from your identity provider via SCIM.": "Automatically provision users and groups from your identity provider via SCIM.",
"Configure your identity provider with this URL to provision users and groups.": "Configure your identity provider with this URL to provision users and groups.",
"Create {{credential}}": "Create {{credential}}",
"{{credential}} created": "{{credential}} created",
"{{credential}} created successfully": "{{credential}} created successfully",
"Created by": "Created by",
"Custom": "Custom",
"Enable SCIM": "Enable SCIM",
"Enter a descriptive name": "Enter a descriptive name",
"I've saved my {{credential}}": "I've saved my {{credential}}",
"Important": "Important",
"Make sure to copy your {{credential}} now. You won't be able to see it again!": "Make sure to copy your {{credential}} now. You won't be able to see it again!",
"Never": "Never",
"Revoke {{credential}}": "Revoke {{credential}}",
"SCIM endpoint URL": "SCIM endpoint URL",
"SCIM provisioning": "SCIM provisioning",
"SCIM takes precedence over SSO group sync while enabled.": "SCIM takes precedence over SSO group sync while enabled.",
"You have reached the maximum of {{max}} SCIM tokens. Delete an existing token to create a new one.": "You have reached the maximum of {{max}} SCIM tokens. Delete an existing token to create a new one.",
"SCIM token": "SCIM token",
"SCIM tokens": "SCIM tokens",
"This action cannot be undone. Your identity provider will stop syncing immediately.": "This action cannot be undone. Your identity provider will stop syncing immediately.",
"Toggle SCIM provisioning": "Toggle SCIM provisioning",
"Token": "Token"
} }
@@ -608,21 +608,25 @@
"Image exceeds 10MB limit.": "이미지가 10MB 용량 제한을 초과합니다.", "Image exceeds 10MB limit.": "이미지가 10MB 용량 제한을 초과합니다.",
"Image removed successfully": "이미지가 성공적으로 제거되었습니다", "Image removed successfully": "이미지가 성공적으로 제거되었습니다",
"API key": "API 키", "API key": "API 키",
"API key created successfully": "API 키 생성 완료",
"API keys": "API 키", "API keys": "API 키",
"API management": "API 관리", "API management": "API 관리",
"Are you sure you want to revoke this API key": "이 API 키를 취소하시겠습니까?",
"Create API Key": "API 키 생성",
"Custom expiration date": "사용자 정의 만료일", "Custom expiration date": "사용자 정의 만료일",
"Enter a descriptive token name": "토큰 이름을 입력하세요", "Enter a descriptive token name": "토큰 이름을 입력하세요",
"Expiration": "만료", "Expiration": "만료",
"Expired": "만료됨", "Expired": "만료됨",
"Expires": "만료일", "Expires": "만료일",
"I've saved my API key": "API 키를 저장했습니다",
"Last use": "최근 사용", "Last use": "최근 사용",
"No API keys found": "API 키를 찾을 수 없습니다", "No API keys found": "API 키를 찾을 수 없습니다",
"No expiration": "유효기간 없음", "No expiration": "유효기간 없음",
"Revoke API key": "API 키 취소",
"Revoked successfully": "성공적으로 취소되었습니다", "Revoked successfully": "성공적으로 취소되었습니다",
"Select expiration date": "만료일 선택", "Select expiration date": "만료일 선택",
"This action cannot be undone. Any applications using this API key will stop working.": "이 작업은 되돌릴 수 없습니다. 이 API 키를 사용하는 모든 응용 프로그램이 작동을 멈출 것입니다.", "This action cannot be undone. Any applications using this API key will stop working.": "이 작업은 되돌릴 수 없습니다. 이 API 키를 사용하는 모든 응용 프로그램이 작동을 멈출 것입니다.",
"Update": "Update", "Update API key": "API 키 갱신",
"Update {{credential}}": "Update {{credential}}",
"Manage API keys for all users in the workspace": "워크스페이스 내 모든 사용자의 API 키 관리", "Manage API keys for all users in the workspace": "워크스페이스 내 모든 사용자의 API 키 관리",
"Restrict API key creation to admins": "API 키 생성 권한을 관리자에게만 제한합니다", "Restrict API key creation to admins": "API 키 생성 권한을 관리자에게만 제한합니다",
"Only admins and owners can create new API keys. Existing member keys will continue to work.": "새로운 API 키는 관리자와 소유자만 생성할 수 있습니다. 기존 멤버 키는 계속 사용할 수 있습니다.", "Only admins and owners can create new API keys. Existing member keys will continue to work.": "새로운 API 키는 관리자와 소유자만 생성할 수 있습니다. 기존 멤버 키는 계속 사용할 수 있습니다.",
@@ -876,29 +880,5 @@
"Try a different search term.": "다른 검색어를 사용해 보세요.", "Try a different search term.": "다른 검색어를 사용해 보세요.",
"Try again": "다시 시도", "Try again": "다시 시도",
"Untitled chat": "제목 없는 채팅", "Untitled chat": "제목 없는 채팅",
"What can I help you with?": "무엇을 도와드릴까요?", "What can I help you with?": "무엇을 도와드릴까요?"
"Are you sure you want to revoke this {{credential}}": "Are you sure you want to revoke this {{credential}}",
"Automatically provision users and groups from your identity provider via SCIM.": "Automatically provision users and groups from your identity provider via SCIM.",
"Configure your identity provider with this URL to provision users and groups.": "Configure your identity provider with this URL to provision users and groups.",
"Create {{credential}}": "Create {{credential}}",
"{{credential}} created": "{{credential}} created",
"{{credential}} created successfully": "{{credential}} created successfully",
"Created by": "Created by",
"Custom": "Custom",
"Enable SCIM": "Enable SCIM",
"Enter a descriptive name": "Enter a descriptive name",
"I've saved my {{credential}}": "I've saved my {{credential}}",
"Important": "Important",
"Make sure to copy your {{credential}} now. You won't be able to see it again!": "Make sure to copy your {{credential}} now. You won't be able to see it again!",
"Never": "Never",
"Revoke {{credential}}": "Revoke {{credential}}",
"SCIM endpoint URL": "SCIM endpoint URL",
"SCIM provisioning": "SCIM provisioning",
"SCIM takes precedence over SSO group sync while enabled.": "SCIM takes precedence over SSO group sync while enabled.",
"You have reached the maximum of {{max}} SCIM tokens. Delete an existing token to create a new one.": "You have reached the maximum of {{max}} SCIM tokens. Delete an existing token to create a new one.",
"SCIM token": "SCIM token",
"SCIM tokens": "SCIM tokens",
"This action cannot be undone. Your identity provider will stop syncing immediately.": "This action cannot be undone. Your identity provider will stop syncing immediately.",
"Toggle SCIM provisioning": "Toggle SCIM provisioning",
"Token": "Token"
} }
@@ -608,21 +608,25 @@
"Image exceeds 10MB limit.": "Afbeelding overschrijdt de limiet van 10MB.", "Image exceeds 10MB limit.": "Afbeelding overschrijdt de limiet van 10MB.",
"Image removed successfully": "Afbeelding succesvol verwijderd", "Image removed successfully": "Afbeelding succesvol verwijderd",
"API key": "API-sleutel", "API key": "API-sleutel",
"API key created successfully": "API-sleutel succesvol aangemaakt",
"API keys": "API-sleutels", "API keys": "API-sleutels",
"API management": "API-beheer", "API management": "API-beheer",
"Are you sure you want to revoke this API key": "Weet u zeker dat u deze API-sleutel wilt intrekken",
"Create API Key": "API-sleutel aanmaken",
"Custom expiration date": "Aangepaste vervaldatum", "Custom expiration date": "Aangepaste vervaldatum",
"Enter a descriptive token name": "Voer een beschrijvende tokennaam in", "Enter a descriptive token name": "Voer een beschrijvende tokennaam in",
"Expiration": "Vervaldatum", "Expiration": "Vervaldatum",
"Expired": "Verlopen", "Expired": "Verlopen",
"Expires": "Verloopt", "Expires": "Verloopt",
"I've saved my API key": "Ik heb mijn API-sleutel opgeslagen",
"Last use": "Laatst gebruikt", "Last use": "Laatst gebruikt",
"No API keys found": "Geen API-sleutels gevonden", "No API keys found": "Geen API-sleutels gevonden",
"No expiration": "Geen vervaldatum", "No expiration": "Geen vervaldatum",
"Revoke API key": "API-sleutel intrekken",
"Revoked successfully": "Succesvol ingetrokken", "Revoked successfully": "Succesvol ingetrokken",
"Select expiration date": "Selecteer vervaldatum", "Select expiration date": "Selecteer vervaldatum",
"This action cannot be undone. Any applications using this API key will stop working.": "Deze actie kan niet ongedaan worden gemaakt. Alle toepassingen die deze API-sleutel gebruiken, zullen niet meer werken.", "This action cannot be undone. Any applications using this API key will stop working.": "Deze actie kan niet ongedaan worden gemaakt. Alle toepassingen die deze API-sleutel gebruiken, zullen niet meer werken.",
"Update": "Update", "Update API key": "API-sleutel bijwerken",
"Update {{credential}}": "Update {{credential}}",
"Manage API keys for all users in the workspace": "Beheer API-sleutels voor alle gebruikers in de werkruimte", "Manage API keys for all users in the workspace": "Beheer API-sleutels voor alle gebruikers in de werkruimte",
"Restrict API key creation to admins": "Beperk het aanmaken van API-sleutels tot beheerders.", "Restrict API key creation to admins": "Beperk het aanmaken van API-sleutels tot beheerders.",
"Only admins and owners can create new API keys. Existing member keys will continue to work.": "Alleen beheerders en eigenaren kunnen nieuwe API-sleutels aanmaken. Bestaande leden-sleutels blijven werken.", "Only admins and owners can create new API keys. Existing member keys will continue to work.": "Alleen beheerders en eigenaren kunnen nieuwe API-sleutels aanmaken. Bestaande leden-sleutels blijven werken.",
@@ -876,29 +880,5 @@
"Try a different search term.": "Probeer een andere zoekterm.", "Try a different search term.": "Probeer een andere zoekterm.",
"Try again": "Probeer opnieuw", "Try again": "Probeer opnieuw",
"Untitled chat": "Chat zonder titel", "Untitled chat": "Chat zonder titel",
"What can I help you with?": "Waar kan ik je mee helpen?", "What can I help you with?": "Waar kan ik je mee helpen?"
"Are you sure you want to revoke this {{credential}}": "Are you sure you want to revoke this {{credential}}",
"Automatically provision users and groups from your identity provider via SCIM.": "Automatically provision users and groups from your identity provider via SCIM.",
"Configure your identity provider with this URL to provision users and groups.": "Configure your identity provider with this URL to provision users and groups.",
"Create {{credential}}": "Create {{credential}}",
"{{credential}} created": "{{credential}} created",
"{{credential}} created successfully": "{{credential}} created successfully",
"Created by": "Created by",
"Custom": "Custom",
"Enable SCIM": "Enable SCIM",
"Enter a descriptive name": "Enter a descriptive name",
"I've saved my {{credential}}": "I've saved my {{credential}}",
"Important": "Important",
"Make sure to copy your {{credential}} now. You won't be able to see it again!": "Make sure to copy your {{credential}} now. You won't be able to see it again!",
"Never": "Never",
"Revoke {{credential}}": "Revoke {{credential}}",
"SCIM endpoint URL": "SCIM endpoint URL",
"SCIM provisioning": "SCIM provisioning",
"SCIM takes precedence over SSO group sync while enabled.": "SCIM takes precedence over SSO group sync while enabled.",
"You have reached the maximum of {{max}} SCIM tokens. Delete an existing token to create a new one.": "You have reached the maximum of {{max}} SCIM tokens. Delete an existing token to create a new one.",
"SCIM token": "SCIM token",
"SCIM tokens": "SCIM tokens",
"This action cannot be undone. Your identity provider will stop syncing immediately.": "This action cannot be undone. Your identity provider will stop syncing immediately.",
"Toggle SCIM provisioning": "Toggle SCIM provisioning",
"Token": "Token"
} }
@@ -608,21 +608,25 @@
"Image exceeds 10MB limit.": "A imagem excede o limite de 10MB.", "Image exceeds 10MB limit.": "A imagem excede o limite de 10MB.",
"Image removed successfully": "Imagem removida com sucesso", "Image removed successfully": "Imagem removida com sucesso",
"API key": "Chave API", "API key": "Chave API",
"API key created successfully": "Chave API criada com sucesso",
"API keys": "Chaves API", "API keys": "Chaves API",
"API management": "Gestão de API", "API management": "Gestão de API",
"Are you sure you want to revoke this API key": "Tem certeza de que deseja revogar esta chave API",
"Create API Key": "Criar Chave API",
"Custom expiration date": "Data de expiração personalizada", "Custom expiration date": "Data de expiração personalizada",
"Enter a descriptive token name": "Insira um nome descritivo para o token", "Enter a descriptive token name": "Insira um nome descritivo para o token",
"Expiration": "Expiração", "Expiration": "Expiração",
"Expired": "Expirado", "Expired": "Expirado",
"Expires": "Expira", "Expires": "Expira",
"I've saved my API key": "Salvei minha chave API",
"Last use": "Último uso", "Last use": "Último uso",
"No API keys found": "Nenhuma chave API encontrada", "No API keys found": "Nenhuma chave API encontrada",
"No expiration": "Sem expiração", "No expiration": "Sem expiração",
"Revoke API key": "Revogar chave API",
"Revoked successfully": "Revogada com sucesso", "Revoked successfully": "Revogada com sucesso",
"Select expiration date": "Selecionar data de expiração", "Select expiration date": "Selecionar data de expiração",
"This action cannot be undone. Any applications using this API key will stop working.": "Esta ação não pode ser desfeita. Qualquer aplicação usando esta chave API deixará de funcionar.", "This action cannot be undone. Any applications using this API key will stop working.": "Esta ação não pode ser desfeita. Qualquer aplicação usando esta chave API deixará de funcionar.",
"Update": "Update", "Update API key": "Atualizar chave API",
"Update {{credential}}": "Update {{credential}}",
"Manage API keys for all users in the workspace": "Gerenciar chaves API para todos os usuários no espaço de trabalho", "Manage API keys for all users in the workspace": "Gerenciar chaves API para todos os usuários no espaço de trabalho",
"Restrict API key creation to admins": "Restringir a criação de chave de API aos administradores", "Restrict API key creation to admins": "Restringir a criação de chave de API aos administradores",
"Only admins and owners can create new API keys. Existing member keys will continue to work.": "Somente administradores e proprietários podem criar novas chaves de API. As chaves de membros já existentes continuarão funcionando.", "Only admins and owners can create new API keys. Existing member keys will continue to work.": "Somente administradores e proprietários podem criar novas chaves de API. As chaves de membros já existentes continuarão funcionando.",
@@ -876,29 +880,5 @@
"Try a different search term.": "Tente um termo de pesquisa diferente.", "Try a different search term.": "Tente um termo de pesquisa diferente.",
"Try again": "Tentar novamente", "Try again": "Tentar novamente",
"Untitled chat": "Chat sem título", "Untitled chat": "Chat sem título",
"What can I help you with?": "Com o que posso ajudar você?", "What can I help you with?": "Com o que posso ajudar você?"
"Are you sure you want to revoke this {{credential}}": "Are you sure you want to revoke this {{credential}}",
"Automatically provision users and groups from your identity provider via SCIM.": "Automatically provision users and groups from your identity provider via SCIM.",
"Configure your identity provider with this URL to provision users and groups.": "Configure your identity provider with this URL to provision users and groups.",
"Create {{credential}}": "Create {{credential}}",
"{{credential}} created": "{{credential}} created",
"{{credential}} created successfully": "{{credential}} created successfully",
"Created by": "Created by",
"Custom": "Custom",
"Enable SCIM": "Enable SCIM",
"Enter a descriptive name": "Enter a descriptive name",
"I've saved my {{credential}}": "I've saved my {{credential}}",
"Important": "Important",
"Make sure to copy your {{credential}} now. You won't be able to see it again!": "Make sure to copy your {{credential}} now. You won't be able to see it again!",
"Never": "Never",
"Revoke {{credential}}": "Revoke {{credential}}",
"SCIM endpoint URL": "SCIM endpoint URL",
"SCIM provisioning": "SCIM provisioning",
"SCIM takes precedence over SSO group sync while enabled.": "SCIM takes precedence over SSO group sync while enabled.",
"You have reached the maximum of {{max}} SCIM tokens. Delete an existing token to create a new one.": "You have reached the maximum of {{max}} SCIM tokens. Delete an existing token to create a new one.",
"SCIM token": "SCIM token",
"SCIM tokens": "SCIM tokens",
"This action cannot be undone. Your identity provider will stop syncing immediately.": "This action cannot be undone. Your identity provider will stop syncing immediately.",
"Toggle SCIM provisioning": "Toggle SCIM provisioning",
"Token": "Token"
} }
@@ -608,21 +608,25 @@
"Image exceeds 10MB limit.": "Изображение превышает предел 10MB.", "Image exceeds 10MB limit.": "Изображение превышает предел 10MB.",
"Image removed successfully": "Изображение успешно удалено", "Image removed successfully": "Изображение успешно удалено",
"API key": "API ключ", "API key": "API ключ",
"API key created successfully": "API ключ успешно создан",
"API keys": "API ключи", "API keys": "API ключи",
"API management": "Управление API", "API management": "Управление API",
"Are you sure you want to revoke this API key": "Вы уверены, что хотите отозвать этот API ключ",
"Create API Key": "Создать API ключ",
"Custom expiration date": "Пользовательская дата срока действия", "Custom expiration date": "Пользовательская дата срока действия",
"Enter a descriptive token name": "Введите понятное имя токена", "Enter a descriptive token name": "Введите понятное имя токена",
"Expiration": "Срок действия", "Expiration": "Срок действия",
"Expired": "Истек", "Expired": "Истек",
"Expires": "Истекает", "Expires": "Истекает",
"I've saved my API key": "Я сохранил мой API ключ",
"Last use": "Последнее использование", "Last use": "Последнее использование",
"No API keys found": "API ключи не найдены", "No API keys found": "API ключи не найдены",
"No expiration": "Не истекает", "No expiration": "Не истекает",
"Revoke API key": "Отозвать API ключ",
"Revoked successfully": "Отозван успешно", "Revoked successfully": "Отозван успешно",
"Select expiration date": "Выберете срок действия", "Select expiration date": "Выберете срок действия",
"This action cannot be undone. Any applications using this API key will stop working.": "Это действие необратимо. Любые приложения, использующие этот API ключ, перестанут работать.", "This action cannot be undone. Any applications using this API key will stop working.": "Это действие необратимо. Любые приложения, использующие этот API ключ, перестанут работать.",
"Update": "Update", "Update API key": "Обновить API ключ",
"Update {{credential}}": "Update {{credential}}",
"Manage API keys for all users in the workspace": "Управлять API ключами для всех пользователей в рабочей области", "Manage API keys for all users in the workspace": "Управлять API ключами для всех пользователей в рабочей области",
"Restrict API key creation to admins": "Ограничить создание API-ключей только администраторами.", "Restrict API key creation to admins": "Ограничить создание API-ключей только администраторами.",
"Only admins and owners can create new API keys. Existing member keys will continue to work.": "Только администраторы и владельцы могут создавать новые API-ключи. Существующие ключи участников продолжат работать.", "Only admins and owners can create new API keys. Existing member keys will continue to work.": "Только администраторы и владельцы могут создавать новые API-ключи. Существующие ключи участников продолжат работать.",
@@ -876,29 +880,5 @@
"Try a different search term.": "Попробуйте другой поисковый запрос.", "Try a different search term.": "Попробуйте другой поисковый запрос.",
"Try again": "Попробовать снова", "Try again": "Попробовать снова",
"Untitled chat": "Чат без названия", "Untitled chat": "Чат без названия",
"What can I help you with?": "Чем я могу вам помочь?", "What can I help you with?": "Чем я могу вам помочь?"
"Are you sure you want to revoke this {{credential}}": "Are you sure you want to revoke this {{credential}}",
"Automatically provision users and groups from your identity provider via SCIM.": "Automatically provision users and groups from your identity provider via SCIM.",
"Configure your identity provider with this URL to provision users and groups.": "Configure your identity provider with this URL to provision users and groups.",
"Create {{credential}}": "Create {{credential}}",
"{{credential}} created": "{{credential}} created",
"{{credential}} created successfully": "{{credential}} created successfully",
"Created by": "Created by",
"Custom": "Custom",
"Enable SCIM": "Enable SCIM",
"Enter a descriptive name": "Enter a descriptive name",
"I've saved my {{credential}}": "I've saved my {{credential}}",
"Important": "Important",
"Make sure to copy your {{credential}} now. You won't be able to see it again!": "Make sure to copy your {{credential}} now. You won't be able to see it again!",
"Never": "Never",
"Revoke {{credential}}": "Revoke {{credential}}",
"SCIM endpoint URL": "SCIM endpoint URL",
"SCIM provisioning": "SCIM provisioning",
"SCIM takes precedence over SSO group sync while enabled.": "SCIM takes precedence over SSO group sync while enabled.",
"You have reached the maximum of {{max}} SCIM tokens. Delete an existing token to create a new one.": "You have reached the maximum of {{max}} SCIM tokens. Delete an existing token to create a new one.",
"SCIM token": "SCIM token",
"SCIM tokens": "SCIM tokens",
"This action cannot be undone. Your identity provider will stop syncing immediately.": "This action cannot be undone. Your identity provider will stop syncing immediately.",
"Toggle SCIM provisioning": "Toggle SCIM provisioning",
"Token": "Token"
} }
@@ -608,21 +608,25 @@
"Image exceeds 10MB limit.": "Зображення має займати менше, ніж 10 МБ.", "Image exceeds 10MB limit.": "Зображення має займати менше, ніж 10 МБ.",
"Image removed successfully": "Зображення видалено", "Image removed successfully": "Зображення видалено",
"API key": "Ключ API", "API key": "Ключ API",
"API key created successfully": "Ключ API успішно створено",
"API keys": "Ключі API", "API keys": "Ключі API",
"API management": "Управління API", "API management": "Управління API",
"Are you sure you want to revoke this API key": "Ви впевнені, що хочете відкликати цей ключ API",
"Create API Key": "Створити ключ API",
"Custom expiration date": "Користувацька дата закінчення", "Custom expiration date": "Користувацька дата закінчення",
"Enter a descriptive token name": "Введіть описову назву токена", "Enter a descriptive token name": "Введіть описову назву токена",
"Expiration": "Термін дії", "Expiration": "Термін дії",
"Expired": "Закінчився", "Expired": "Закінчився",
"Expires": "Закінчується", "Expires": "Закінчується",
"I've saved my API key": "Я зберіг свій ключ API",
"Last use": "Останнє використання", "Last use": "Останнє використання",
"No API keys found": "Ключі API не знайдено", "No API keys found": "Ключі API не знайдено",
"No expiration": "Без терміну дії", "No expiration": "Без терміну дії",
"Revoke API key": "Відкликати ключ API",
"Revoked successfully": "Успішно відкликано", "Revoked successfully": "Успішно відкликано",
"Select expiration date": "Виберіть дату закінчення", "Select expiration date": "Виберіть дату закінчення",
"This action cannot be undone. Any applications using this API key will stop working.": "Цю дію не можна скасувати. Будь-які додатки, що використовують цей ключ API, перестануть працювати.", "This action cannot be undone. Any applications using this API key will stop working.": "Цю дію не можна скасувати. Будь-які додатки, що використовують цей ключ API, перестануть працювати.",
"Update": "Update", "Update API key": "Оновити ключ API",
"Update {{credential}}": "Update {{credential}}",
"Manage API keys for all users in the workspace": "Керувати ключами API для всіх користувачів у робочій області", "Manage API keys for all users in the workspace": "Керувати ключами API для всіх користувачів у робочій області",
"Restrict API key creation to admins": "Обмежити створення API-ключів лише для адміністраторів", "Restrict API key creation to admins": "Обмежити створення API-ключів лише для адміністраторів",
"Only admins and owners can create new API keys. Existing member keys will continue to work.": "Тільки адміністратори та власники можуть створювати нові API-ключі. Існуючі ключі учасників і надалі працюватимуть.", "Only admins and owners can create new API keys. Existing member keys will continue to work.": "Тільки адміністратори та власники можуть створювати нові API-ключі. Існуючі ключі учасників і надалі працюватимуть.",
@@ -876,29 +880,5 @@
"Try a different search term.": "Спробуйте інший пошуковий запит.", "Try a different search term.": "Спробуйте інший пошуковий запит.",
"Try again": "Спробувати ще раз", "Try again": "Спробувати ще раз",
"Untitled chat": "Чат без назви", "Untitled chat": "Чат без назви",
"What can I help you with?": "Чим я можу вам допомогти?", "What can I help you with?": "Чим я можу вам допомогти?"
"Are you sure you want to revoke this {{credential}}": "Are you sure you want to revoke this {{credential}}",
"Automatically provision users and groups from your identity provider via SCIM.": "Automatically provision users and groups from your identity provider via SCIM.",
"Configure your identity provider with this URL to provision users and groups.": "Configure your identity provider with this URL to provision users and groups.",
"Create {{credential}}": "Create {{credential}}",
"{{credential}} created": "{{credential}} created",
"{{credential}} created successfully": "{{credential}} created successfully",
"Created by": "Created by",
"Custom": "Custom",
"Enable SCIM": "Enable SCIM",
"Enter a descriptive name": "Enter a descriptive name",
"I've saved my {{credential}}": "I've saved my {{credential}}",
"Important": "Important",
"Make sure to copy your {{credential}} now. You won't be able to see it again!": "Make sure to copy your {{credential}} now. You won't be able to see it again!",
"Never": "Never",
"Revoke {{credential}}": "Revoke {{credential}}",
"SCIM endpoint URL": "SCIM endpoint URL",
"SCIM provisioning": "SCIM provisioning",
"SCIM takes precedence over SSO group sync while enabled.": "SCIM takes precedence over SSO group sync while enabled.",
"You have reached the maximum of {{max}} SCIM tokens. Delete an existing token to create a new one.": "You have reached the maximum of {{max}} SCIM tokens. Delete an existing token to create a new one.",
"SCIM token": "SCIM token",
"SCIM tokens": "SCIM tokens",
"This action cannot be undone. Your identity provider will stop syncing immediately.": "This action cannot be undone. Your identity provider will stop syncing immediately.",
"Toggle SCIM provisioning": "Toggle SCIM provisioning",
"Token": "Token"
} }
@@ -608,21 +608,25 @@
"Image exceeds 10MB limit.": "图片超过10MB限制。", "Image exceeds 10MB limit.": "图片超过10MB限制。",
"Image removed successfully": "图片删除成功", "Image removed successfully": "图片删除成功",
"API key": "API密钥", "API key": "API密钥",
"API key created successfully": "API密钥创建成功",
"API keys": "API密钥", "API keys": "API密钥",
"API management": "API管理", "API management": "API管理",
"Are you sure you want to revoke this API key": "确定要撤销此API密钥吗",
"Create API Key": "创建API密钥",
"Custom expiration date": "自定义到期日期", "Custom expiration date": "自定义到期日期",
"Enter a descriptive token name": "输入描述性令牌名称", "Enter a descriptive token name": "输入描述性令牌名称",
"Expiration": "到期", "Expiration": "到期",
"Expired": "已过期", "Expired": "已过期",
"Expires": "到期", "Expires": "到期",
"I've saved my API key": "我已保存我的API密钥",
"Last use": "上次使用", "Last use": "上次使用",
"No API keys found": "找不到API密钥", "No API keys found": "找不到API密钥",
"No expiration": "无到期", "No expiration": "无到期",
"Revoke API key": "撤销API密钥",
"Revoked successfully": "撤销成功", "Revoked successfully": "撤销成功",
"Select expiration date": "选择到期日期", "Select expiration date": "选择到期日期",
"This action cannot be undone. Any applications using this API key will stop working.": "此操作无法撤销。使用此API密钥的任何应用程序将停止工作。", "This action cannot be undone. Any applications using this API key will stop working.": "此操作无法撤销。使用此API密钥的任何应用程序将停止工作。",
"Update": "Update", "Update API key": "更新API密钥",
"Update {{credential}}": "Update {{credential}}",
"Manage API keys for all users in the workspace": "管理工作空间中所有用户的API密钥", "Manage API keys for all users in the workspace": "管理工作空间中所有用户的API密钥",
"Restrict API key creation to admins": "仅限管理员创建 API 密钥。", "Restrict API key creation to admins": "仅限管理员创建 API 密钥。",
"Only admins and owners can create new API keys. Existing member keys will continue to work.": "只有管理员和所有者可以创建新的 API 密钥。现有成员密钥将继续有效。", "Only admins and owners can create new API keys. Existing member keys will continue to work.": "只有管理员和所有者可以创建新的 API 密钥。现有成员密钥将继续有效。",
@@ -876,29 +880,5 @@
"Try a different search term.": "请尝试其他搜索词。", "Try a different search term.": "请尝试其他搜索词。",
"Try again": "重试", "Try again": "重试",
"Untitled chat": "未命名聊天", "Untitled chat": "未命名聊天",
"What can I help you with?": "我能帮您做什么?", "What can I help you with?": "我能帮您做什么?"
"Are you sure you want to revoke this {{credential}}": "Are you sure you want to revoke this {{credential}}",
"Automatically provision users and groups from your identity provider via SCIM.": "Automatically provision users and groups from your identity provider via SCIM.",
"Configure your identity provider with this URL to provision users and groups.": "Configure your identity provider with this URL to provision users and groups.",
"Create {{credential}}": "Create {{credential}}",
"{{credential}} created": "{{credential}} created",
"{{credential}} created successfully": "{{credential}} created successfully",
"Created by": "Created by",
"Custom": "Custom",
"Enable SCIM": "Enable SCIM",
"Enter a descriptive name": "Enter a descriptive name",
"I've saved my {{credential}}": "I've saved my {{credential}}",
"Important": "Important",
"Make sure to copy your {{credential}} now. You won't be able to see it again!": "Make sure to copy your {{credential}} now. You won't be able to see it again!",
"Never": "Never",
"Revoke {{credential}}": "Revoke {{credential}}",
"SCIM endpoint URL": "SCIM endpoint URL",
"SCIM provisioning": "SCIM provisioning",
"SCIM takes precedence over SSO group sync while enabled.": "SCIM takes precedence over SSO group sync while enabled.",
"You have reached the maximum of {{max}} SCIM tokens. Delete an existing token to create a new one.": "You have reached the maximum of {{max}} SCIM tokens. Delete an existing token to create a new one.",
"SCIM token": "SCIM token",
"SCIM tokens": "SCIM tokens",
"This action cannot be undone. Your identity provider will stop syncing immediately.": "This action cannot be undone. Your identity provider will stop syncing immediately.",
"Toggle SCIM provisioning": "Toggle SCIM provisioning",
"Token": "Token"
} }
+13 -12
View File
@@ -38,8 +38,8 @@
"@aws-sdk/s3-request-presigner": "3.1014.0", "@aws-sdk/s3-request-presigner": "3.1014.0",
"@clickhouse/client": "^1.18.2", "@clickhouse/client": "^1.18.2",
"@fastify/cookie": "^11.0.2", "@fastify/cookie": "^11.0.2",
"@fastify/multipart": "^9.4.0", "@fastify/multipart": "^10.0.0",
"@fastify/static": "^9.0.0", "@fastify/static": "^9.1.3",
"@keyv/redis": "^5.1.6", "@keyv/redis": "^5.1.6",
"@langchain/core": "1.1.39", "@langchain/core": "1.1.39",
"@langchain/textsplitters": "1.0.1", "@langchain/textsplitters": "1.0.1",
@@ -48,19 +48,19 @@
"@nestjs-labs/nestjs-ioredis": "^11.0.4", "@nestjs-labs/nestjs-ioredis": "^11.0.4",
"@nestjs/bullmq": "^11.0.4", "@nestjs/bullmq": "^11.0.4",
"@nestjs/cache-manager": "^3.1.0", "@nestjs/cache-manager": "^3.1.0",
"@nestjs/common": "^11.1.18", "@nestjs/common": "^11.1.19",
"@nestjs/config": "^4.0.3", "@nestjs/config": "^4.0.4",
"@nestjs/core": "^11.1.18", "@nestjs/core": "^11.1.19",
"@nestjs/event-emitter": "^3.0.1", "@nestjs/event-emitter": "^3.0.1",
"@nestjs/jwt": "11.0.2", "@nestjs/jwt": "11.0.2",
"@nestjs/mapped-types": "^2.1.1", "@nestjs/mapped-types": "^2.1.1",
"@nestjs/passport": "^11.0.5", "@nestjs/passport": "^11.0.5",
"@nestjs/platform-fastify": "^11.1.18", "@nestjs/platform-fastify": "^11.1.19",
"@nestjs/platform-socket.io": "^11.1.18", "@nestjs/platform-socket.io": "^11.1.19",
"@nestjs/schedule": "^6.1.1", "@nestjs/schedule": "^6.1.3",
"@nestjs/terminus": "^11.1.1", "@nestjs/terminus": "^11.1.1",
"@nestjs/throttler": "^6.5.0", "@nestjs/throttler": "^6.5.0",
"@nestjs/websockets": "^11.1.18", "@nestjs/websockets": "^11.1.19",
"@node-saml/passport-saml": "^5.1.0", "@node-saml/passport-saml": "^5.1.0",
"@react-email/components": "1.0.10", "@react-email/components": "1.0.10",
"@react-email/render": "2.0.4", "@react-email/render": "2.0.4",
@@ -69,7 +69,7 @@
"ai-sdk-ollama": "^3.8.1", "ai-sdk-ollama": "^3.8.1",
"bcrypt": "^6.0.0", "bcrypt": "^6.0.0",
"bowser": "^2.14.1", "bowser": "^2.14.1",
"bullmq": "^5.71.0", "bullmq": "^5.76.0",
"cache-manager": "^7.2.8", "cache-manager": "^7.2.8",
"cheerio": "^1.2.0", "cheerio": "^1.2.0",
"class-transformer": "^0.5.1", "class-transformer": "^0.5.1",
@@ -117,15 +117,16 @@
"tmp-promise": "^3.0.3", "tmp-promise": "^3.0.3",
"tseep": "^1.3.1", "tseep": "^1.3.1",
"typesense": "^3.0.5", "typesense": "^3.0.5",
"undici": "7.24.0",
"ws": "^8.20.0", "ws": "^8.20.0",
"yauzl": "^3.2.1", "yauzl": "^3.2.1",
"zod": "^4.3.6" "zod": "^4.3.6"
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "^9.28.0", "@eslint/js": "^9.28.0",
"@nestjs/cli": "^11.0.18", "@nestjs/cli": "^11.0.21",
"@nestjs/schematics": "^11.0.10", "@nestjs/schematics": "^11.0.10",
"@nestjs/testing": "^11.1.18", "@nestjs/testing": "^11.1.19",
"@types/bcrypt": "^6.0.0", "@types/bcrypt": "^6.0.0",
"@types/debounce": "^1.2.4", "@types/debounce": "^1.2.4",
"@types/fs-extra": "^11.0.4", "@types/fs-extra": "^11.0.4",
@@ -53,6 +53,7 @@ import { EnvironmentService } from '../../integrations/environment/environment.s
import { TokenService } from '../auth/services/token.service'; import { TokenService } from '../auth/services/token.service';
import { JwtAttachmentPayload, JwtType } from '../auth/dto/jwt-payload'; import { JwtAttachmentPayload, JwtType } from '../auth/dto/jwt-payload';
import * as path from 'path'; import * as path from 'path';
import { sanitize } from 'sanitize-filename-ts';
import { AttachmentInfoDto, RemoveIconDto } from './dto/attachment.dto'; import { AttachmentInfoDto, RemoveIconDto } from './dto/attachment.dto';
import { PageAccessService } from '../page/page-access/page-access.service'; import { PageAccessService } from '../page/page-access/page-access.service';
import { AuditEvent, AuditResource } from '../../common/events/audit-events'; import { AuditEvent, AuditResource } from '../../common/events/audit-events';
@@ -356,6 +357,10 @@ export class AttachmentController {
throw new BadRequestException('Invalid image attachment type'); throw new BadRequestException('Invalid image attachment type');
} }
if (!fileName || sanitize(fileName) !== fileName) {
throw new BadRequestException('Invalid file name');
}
const filenameWithoutExt = path.basename(fileName, path.extname(fileName)); const filenameWithoutExt = path.basename(fileName, path.extname(fileName));
if (!isValidUUID(filenameWithoutExt)) { if (!isValidUUID(filenameWithoutExt)) {
throw new BadRequestException('Invalid file id'); throw new BadRequestException('Invalid file id');
@@ -13,10 +13,6 @@ import { CreateUserDto } from '../../auth/dto/create-user.dto';
export class UpdateUserDto extends PartialType( export class UpdateUserDto extends PartialType(
OmitType(CreateUserDto, ['password'] as const), OmitType(CreateUserDto, ['password'] as const),
) { ) {
@IsOptional()
@IsString()
avatarUrl: string;
@IsOptional() @IsOptional()
@IsBoolean() @IsBoolean()
fullPageWidth: boolean; fullPageWidth: boolean;
@@ -110,10 +110,6 @@ export class UserService {
user.email = updateUserDto.email; user.email = updateUserDto.email;
} }
if (updateUserDto.avatarUrl) {
user.avatarUrl = updateUserDto.avatarUrl;
}
if (updateUserDto.locale) { if (updateUserDto.locale) {
user.locale = updateUserDto.locale; user.locale = updateUserDto.locale;
} }
@@ -5,15 +5,10 @@ import {
IsBoolean, IsBoolean,
IsInt, IsInt,
IsOptional, IsOptional,
IsString,
Min, Min,
} from 'class-validator'; } from 'class-validator';
export class UpdateWorkspaceDto extends PartialType(CreateWorkspaceDto) { export class UpdateWorkspaceDto extends PartialType(CreateWorkspaceDto) {
@IsOptional()
@IsString()
logo: string;
@IsOptional() @IsOptional()
@IsArray() @IsArray()
emailDomains: string[]; emailDomains: string[];
@@ -39,6 +39,8 @@ import {
} from '../../common/helpers/prosemirror/utils'; } from '../../common/helpers/prosemirror/utils';
import { htmlToMarkdown } from '@docmost/editor-ext'; import { htmlToMarkdown } from '@docmost/editor-ext';
type AllowedAttachment = { id: string; fileName: string; filePath: string };
@Injectable() @Injectable()
export class ExportService { export class ExportService {
private readonly logger = new Logger(ExportService.name); private readonly logger = new Logger(ExportService.name);
@@ -272,6 +274,12 @@ export class ExportService {
computeLocalPath(tree, format, null, '', slugIdToPath); computeLocalPath(tree, format, null, '', slugIdToPath);
// Batch resolve attachments once for the whole export so we only run the
// owning-page view check a single time, regardless of page count.
const allowedAttachments = includeAttachments
? await this.resolveAccessibleAttachments(tree, userId, ignorePermissions)
: new Map<string, AllowedAttachment>();
const stack: { folder: JSZip; parentPageId: string | null }[] = [ const stack: { folder: JSZip; parentPageId: string | null }[] = [
{ folder: zip, parentPageId: null }, { folder: zip, parentPageId: null },
]; ];
@@ -301,7 +309,7 @@ export class ExportService {
); );
if (includeAttachments) { if (includeAttachments) {
await this.zipAttachments(updatedJsonContent, page.spaceId, folder); await this.zipAttachments(updatedJsonContent, folder, allowedAttachments);
updatedJsonContent = updatedJsonContent =
updateAttachmentUrlsToLocalPaths(updatedJsonContent); updateAttachmentUrlsToLocalPaths(updatedJsonContent);
} }
@@ -347,31 +355,80 @@ export class ExportService {
zip.file('docmost-metadata.json', JSON.stringify(metadata, null, 2)); zip.file('docmost-metadata.json', JSON.stringify(metadata, null, 2));
} }
async zipAttachments(prosemirrorJson: any, spaceId: string, zip: JSZip) { async zipAttachments(
prosemirrorJson: any,
zip: JSZip,
allowed: Map<string, AllowedAttachment>,
) {
const attachmentIds = getAttachmentIds(prosemirrorJson); const attachmentIds = getAttachmentIds(prosemirrorJson);
if (attachmentIds.length > 0) { await Promise.all(
const attachments = await this.db attachmentIds.map(async (id) => {
.selectFrom('attachments') const attachment = allowed.get(id);
.select(['id', 'fileName', 'filePath']) if (!attachment) return;
.where('id', 'in', attachmentIds) try {
.where('spaceId', '=', spaceId) const fileBuffer = await this.storageService.read(
.execute(); attachment.filePath,
);
const filePath = `/files/${attachment.id}/${attachment.fileName}`;
zip.file(filePath, fileBuffer);
} catch (err) {
this.logger.debug(`Attachment export error ${attachment.id}`, err);
}
}),
);
}
await Promise.all( private async resolveAccessibleAttachments(
attachments.map(async (attachment) => { tree: PageExportTree,
try { userId: string | undefined,
const fileBuffer = await this.storageService.read( ignorePermissions: boolean,
attachment.filePath, ): Promise<Map<string, AllowedAttachment>> {
); const allAttachmentIds = new Set<string>();
const filePath = `/files/${attachment.id}/${attachment.fileName}`; let spaceId: string | undefined;
zip.file(filePath, fileBuffer); for (const siblings of Object.values(tree)) {
} catch (err) { for (const page of siblings) {
this.logger.debug(`Attachment export error ${attachment.id}`, err); if (!spaceId) spaceId = page.spaceId;
} for (const id of getAttachmentIds(getProsemirrorContent(page.content))) {
}), allAttachmentIds.add(id);
}
}
}
if (allAttachmentIds.size === 0 || !spaceId) {
return new Map();
}
const attachments = await this.db
.selectFrom('attachments')
.select(['id', 'fileName', 'filePath', 'pageId'])
.where('id', 'in', [...allAttachmentIds])
.where('spaceId', '=', spaceId)
.execute();
let visible = attachments;
if (!ignorePermissions && userId) {
const ownerPageIds = [
...new Set(
attachments
.map((a) => a.pageId)
.filter((id): id is string => !!id),
),
];
const accessible = ownerPageIds.length
? await this.pagePermissionRepo.filterAccessiblePageIds({
pageIds: ownerPageIds,
userId,
spaceId,
})
: [];
const accessibleSet = new Set(accessible);
visible = attachments.filter(
(a) => a.pageId && accessibleSet.has(a.pageId),
); );
} }
return new Map(visible.map((a) => [a.id, a]));
} }
async turnPageMentionsToLinks( async turnPageMentionsToLinks(
@@ -0,0 +1,67 @@
import { resolve, sep } from 'path';
import { LocalDriver } from './local.driver';
type FullPath = (filePath: string) => string;
describe('LocalDriver._fullPath', () => {
const ROOT = resolve('/data/storage');
const driver = new LocalDriver({ storagePath: ROOT });
const fullPath = ((driver as any)._fullPath as FullPath).bind(driver);
describe('legitimate inputs (behavior preserved)', () => {
it.each([
['workspace-id/avatars/uuid.png', `${ROOT}${sep}workspace-id${sep}avatars${sep}uuid.png`],
['workspace-id/files/uuid/file.pdf', `${ROOT}${sep}workspace-id${sep}files${sep}uuid${sep}file.pdf`],
['a/b/c/d/e.bin', `${ROOT}${sep}a${sep}b${sep}c${sep}d${sep}e.bin`],
['', ROOT],
['.', ROOT],
['./x/y.png', `${ROOT}${sep}x${sep}y.png`],
['a//b', `${ROOT}${sep}a${sep}b`],
['a/b/../c', `${ROOT}${sep}a${sep}c`],
])('resolves %j to %j', (input, expected) => {
expect(fullPath(input)).toBe(expected);
});
});
describe('traversal rejected', () => {
it.each([
'../etc/passwd',
'../../../etc/passwd',
'workspace/../../../etc/passwd',
'..',
'../..',
'a/../../..',
])('throws for %j', (input) => {
expect(() => fullPath(input)).toThrow('Invalid file path');
});
});
describe('absolute path rejected', () => {
it.each([
'/etc/passwd',
'/root/.ssh/id_rsa',
sep + 'absolute',
])('throws for %j', (input) => {
expect(() => fullPath(input)).toThrow('Invalid file path');
});
});
describe('prefix-confusion rejected', () => {
it('rejects a sibling directory whose name starts with the storage root', () => {
const siblingDriver = new LocalDriver({ storagePath: '/data/storage' });
const siblingFullPath = ((siblingDriver as any)._fullPath as FullPath).bind(siblingDriver);
// Attempt to reach /data/storage-evil/secret by traversal:
// resolve('/data/storage', '../storage-evil/secret') === '/data/storage-evil/secret'
// Without the `+ sep` guard, a startsWith check would match.
expect(() => siblingFullPath('../storage-evil/secret')).toThrow('Invalid file path');
});
});
describe('storage root itself', () => {
it('accepts the root when input resolves to it', () => {
expect(fullPath('')).toBe(ROOT);
expect(fullPath('.')).toBe(ROOT);
expect(fullPath('a/..')).toBe(ROOT);
});
});
});
@@ -3,7 +3,7 @@ import {
LocalStorageConfig, LocalStorageConfig,
StorageOption, StorageOption,
} from '../interfaces'; } from '../interfaces';
import { join, dirname } from 'path'; import { dirname, resolve, sep } from 'path';
import * as fs from 'fs-extra'; import * as fs from 'fs-extra';
import { Readable } from 'stream'; import { Readable } from 'stream';
import { createReadStream, createWriteStream } from 'node:fs'; import { createReadStream, createWriteStream } from 'node:fs';
@@ -17,7 +17,12 @@ export class LocalDriver implements StorageDriver {
} }
private _fullPath(filePath: string): string { private _fullPath(filePath: string): string {
return join(this.config.storagePath, filePath); const storageRoot = resolve(this.config.storagePath);
const fullPath = resolve(storageRoot, filePath);
if (fullPath !== storageRoot && !fullPath.startsWith(storageRoot + sep)) {
throw new Error('Invalid file path');
}
return fullPath;
} }
async upload(filePath: string, file: Buffer | Readable): Promise<void> { async upload(filePath: string, file: Buffer | Readable): Promise<void> {
+5 -5
View File
@@ -62,7 +62,7 @@
"cross-env": "^10.1.0", "cross-env": "^10.1.0",
"date-fns": "^4.1.0", "date-fns": "^4.1.0",
"diff": "8.0.3", "diff": "8.0.3",
"dompurify": "^3.3.3", "dompurify": "3.4.1",
"fractional-indexing-jittered": "^1.0.0", "fractional-indexing-jittered": "^1.0.0",
"highlight.js": "^11.11.1", "highlight.js": "^11.11.1",
"image-dimensions": "^2.5.0", "image-dimensions": "^2.5.0",
@@ -102,9 +102,9 @@
"y-prosemirror": "1.3.7", "y-prosemirror": "1.3.7",
"glob": "13.0.6", "glob": "13.0.6",
"ws": "8.20.0", "ws": "8.20.0",
"dompurify": "3.3.3", "dompurify": "3.4.1",
"tmp": "0.2.5", "tmp": "0.2.5",
"hono": "4.12.12", "hono": "4.12.14",
"mermaid": "11.13.0", "mermaid": "11.13.0",
"nanoid@^3": "3.3.8", "nanoid@^3": "3.3.8",
"socket.io-parser": "4.2.6", "socket.io-parser": "4.2.6",
@@ -123,7 +123,7 @@
"flatted": "3.4.2", "flatted": "3.4.2",
"picomatch@<2.3.2": "2.3.2", "picomatch@<2.3.2": "2.3.2",
"picomatch@>=4.0.0 <4.0.4": "4.0.4", "picomatch@>=4.0.0 <4.0.4": "4.0.4",
"fastify": "5.8.3", "fastify": "5.8.5",
"yaml@>=1.0.0 <1.10.3": "1.10.3", "yaml@>=1.0.0 <1.10.3": "1.10.3",
"yaml@>=2.0.0 <2.8.3": "2.8.3", "yaml@>=2.0.0 <2.8.3": "2.8.3",
"path-to-regexp@^8": "8.4.0", "path-to-regexp@^8": "8.4.0",
@@ -131,7 +131,7 @@
"@xmldom/xmldom": "0.8.12", "@xmldom/xmldom": "0.8.12",
"handlebars": "4.7.9", "handlebars": "4.7.9",
"axios": "1.15.0", "axios": "1.15.0",
"langsmith": "0.5.18", "langsmith": "0.5.19",
"follow-redirects": "1.16.0" "follow-redirects": "1.16.0"
}, },
"neverBuiltDependencies": [] "neverBuiltDependencies": []
+238 -252
View File
File diff suppressed because it is too large Load Diff