feat: Add Storage Explorer with CLI terminal, alias management, and file browser

- Add CLI terminal for executing mc commands from the web interface
  - Command history with up/down arrow navigation
  - Dark theme with monospace font
  - Auto-scroll output and loading states

- Add alias management page
  - List all MinIO aliases with connection status
  - Add, edit, delete aliases
  - Test connection functionality

- Add file browser for navigating bucket contents
  - Breadcrumb navigation (alias > bucket > path)
  - File/folder table with size, type, last modified
  - Upload files (drag-and-drop support)
  - Download, rename, delete operations
  - Create new folders
  - Multi-select for batch delete

Backend:
- New API routes: /api/terminal, /api/aliases, /api/browser
- Multer middleware for file uploads
- Extended minio.service.js with file operations

Frontend:
- New Explorer component with Aliases/Terminal tabs
- Zustand stores for terminal and explorer state
- i18n translations (English and German)
This commit is contained in:
Paul Nothaft
2026-01-04 22:19:03 +01:00
parent feae9882bc
commit f24ed78c38
28 changed files with 3844 additions and 3 deletions
+1
View File
@@ -3,6 +3,7 @@
"navigation": {
"dashboard": "Übersicht",
"buckets": "Buckets",
"explorer": "Explorer",
"users": "Benutzer",
"policies": "Richtlinien",
"reports": "Berichte",
+133
View File
@@ -0,0 +1,133 @@
{
"title": "Speicher-Explorer",
"tabs": {
"aliases": "Aliase",
"terminal": "Terminal"
},
"aliases": {
"title": "MinIO Aliase",
"addAlias": "Alias hinzufügen",
"editAlias": "Alias bearbeiten",
"deleteAlias": "Alias löschen",
"testConnection": "Verbindung testen",
"columns": {
"name": "Name",
"endpoint": "Endpunkt",
"status": "Status",
"actions": "Aktionen"
},
"status": {
"connected": "Verbunden",
"disconnected": "Getrennt",
"unknown": "Unbekannt",
"testing": "Teste..."
},
"dialog": {
"title": "Alias-Konfiguration",
"aliasName": "Alias-Name",
"endpoint": "Endpunkt-URL",
"accessKey": "Zugriffsschlüssel",
"secretKey": "Geheimer Schlüssel",
"aliasNameHelper": "Ein eindeutiger Name für diese Verbindung (z.B. produktion, staging)",
"endpointHelper": "MinIO Server-URL (z.B. https://minio.example.com)"
},
"empty": {
"title": "Keine Aliase konfiguriert",
"message": "Fügen Sie einen MinIO-Alias hinzu, um Dateien zu durchsuchen"
},
"messages": {
"created": "Alias erfolgreich erstellt",
"updated": "Alias erfolgreich aktualisiert",
"deleted": "Alias erfolgreich gelöscht",
"testSuccess": "Verbindung erfolgreich",
"testFailed": "Verbindung fehlgeschlagen"
}
},
"terminal": {
"title": "MinIO CLI Terminal",
"placeholder": "mc Befehl eingeben (z.B. mc ls myminio)",
"clear": "Löschen",
"execute": "Ausführen",
"executing": "Wird ausgeführt...",
"running": "Wird ausgeführt...",
"hint": "Pfeiltasten für Befehlsverlauf verwenden",
"welcomeMessage": "Willkommen im MinIO CLI Terminal. Geben Sie 'mc' Befehle ein, um mit Ihren MinIO-Servern zu interagieren.",
"helpText": "Tipp: Verwenden Sie Pfeiltasten hoch/runter für den Befehlsverlauf"
},
"browser": {
"title": "Datei-Browser",
"selectBucket": "Bucket auswählen",
"upload": "Hochladen",
"createFolder": "Ordner erstellen",
"newFolder": "Neuer Ordner",
"download": "Herunterladen",
"delete": "Löschen",
"rename": "Umbenennen",
"copy": "Kopieren",
"refresh": "Aktualisieren",
"deleteItem": "Element löschen",
"selected": "{{count}} Element(e) ausgewählt",
"columns": {
"name": "Name",
"size": "Größe",
"type": "Typ",
"lastModified": "Zuletzt geändert",
"actions": "Aktionen"
},
"types": {
"file": "Datei",
"folder": "Ordner"
},
"empty": {
"title": "Keine Dateien",
"message": "Dieser Ordner ist leer. Laden Sie Dateien hoch oder erstellen Sie einen neuen Ordner."
},
"errors": {
"missingParams": "Fehlender Alias- oder Bucket-Parameter"
},
"uploadDialog": {
"title": "Dateien hochladen",
"dropzone": "Dateien hierher ziehen",
"or": "oder",
"browse": "Dateien durchsuchen",
"selectedFiles": "{{count}} Datei(en) ausgewählt",
"uploading": "Wird hochgeladen..."
},
"createFolderDialog": {
"title": "Ordner erstellen",
"nameLabel": "Ordnername",
"nameHelper": "Geben Sie einen Namen für den neuen Ordner ein",
"errors": {
"required": "Ordnername ist erforderlich",
"invalidChars": "Ordnername darf kein / oder \\ enthalten",
"dotStart": "Ordnername darf nicht mit einem Punkt beginnen"
}
},
"renameDialog": {
"title": "Umbenennen",
"newNameLabel": "Neuer Name",
"errors": {
"required": "Name ist erforderlich",
"same": "Neuer Name muss sich vom aktuellen Namen unterscheiden",
"invalidChars": "Name darf kein / oder \\ enthalten"
}
},
"deleteConfirm": {
"title": "{{name}} löschen?",
"message": "Diese Aktion kann nicht rückgängig gemacht werden.",
"folderWarning": "Dieser Ordner und sein gesamter Inhalt werden dauerhaft gelöscht."
},
"messages": {
"folderCreated": "Ordner erfolgreich erstellt",
"folderCreateFailed": "Ordner konnte nicht erstellt werden",
"renamed": "Element erfolgreich umbenannt",
"renameFailed": "Element konnte nicht umbenannt werden",
"deleted": "Element erfolgreich gelöscht",
"deleteFailed": "Element konnte nicht gelöscht werden",
"downloadStarted": "Download gestartet",
"downloadFailed": "Datei konnte nicht heruntergeladen werden",
"uploaded": "Dateien erfolgreich hochgeladen",
"uploadFailed": "Dateien konnten nicht hochgeladen werden"
}
}
}
+1
View File
@@ -3,6 +3,7 @@
"navigation": {
"dashboard": "Dashboard",
"buckets": "Buckets",
"explorer": "Explorer",
"users": "Users",
"policies": "Policies",
"reports": "Reports",
+133
View File
@@ -0,0 +1,133 @@
{
"title": "Storage Explorer",
"tabs": {
"aliases": "Aliases",
"terminal": "Terminal"
},
"aliases": {
"title": "MinIO Aliases",
"addAlias": "Add Alias",
"editAlias": "Edit Alias",
"deleteAlias": "Delete Alias",
"testConnection": "Test Connection",
"columns": {
"name": "Name",
"endpoint": "Endpoint",
"status": "Status",
"actions": "Actions"
},
"status": {
"connected": "Connected",
"disconnected": "Disconnected",
"unknown": "Unknown",
"testing": "Testing..."
},
"dialog": {
"title": "Alias Configuration",
"aliasName": "Alias Name",
"endpoint": "Endpoint URL",
"accessKey": "Access Key",
"secretKey": "Secret Key",
"aliasNameHelper": "A unique name for this connection (e.g., production, staging)",
"endpointHelper": "MinIO server URL (e.g., https://minio.example.com)"
},
"empty": {
"title": "No aliases configured",
"message": "Add a MinIO alias to start browsing files"
},
"messages": {
"created": "Alias created successfully",
"updated": "Alias updated successfully",
"deleted": "Alias deleted successfully",
"testSuccess": "Connection successful",
"testFailed": "Connection failed"
}
},
"terminal": {
"title": "MinIO CLI Terminal",
"placeholder": "Enter mc command (e.g., mc ls myminio)",
"clear": "Clear",
"execute": "Execute",
"executing": "Executing...",
"running": "Running...",
"hint": "Use arrow keys to navigate command history",
"welcomeMessage": "Welcome to MinIO CLI Terminal. Type 'mc' commands to interact with your MinIO servers.",
"helpText": "Tip: Use up/down arrow keys to navigate command history"
},
"browser": {
"title": "File Browser",
"selectBucket": "Select Bucket",
"upload": "Upload",
"createFolder": "Create Folder",
"newFolder": "New Folder",
"download": "Download",
"delete": "Delete",
"rename": "Rename",
"copy": "Copy",
"refresh": "Refresh",
"deleteItem": "Delete Item",
"selected": "{{count}} item(s) selected",
"columns": {
"name": "Name",
"size": "Size",
"type": "Type",
"lastModified": "Last Modified",
"actions": "Actions"
},
"types": {
"file": "File",
"folder": "Folder"
},
"empty": {
"title": "No files",
"message": "This folder is empty. Upload files or create a new folder."
},
"errors": {
"missingParams": "Missing alias or bucket parameter"
},
"uploadDialog": {
"title": "Upload Files",
"dropzone": "Drag and drop files here",
"or": "or",
"browse": "Browse Files",
"selectedFiles": "{{count}} file(s) selected",
"uploading": "Uploading..."
},
"createFolderDialog": {
"title": "Create Folder",
"nameLabel": "Folder Name",
"nameHelper": "Enter a name for the new folder",
"errors": {
"required": "Folder name is required",
"invalidChars": "Folder name cannot contain / or \\",
"dotStart": "Folder name cannot start with a dot"
}
},
"renameDialog": {
"title": "Rename",
"newNameLabel": "New Name",
"errors": {
"required": "Name is required",
"same": "New name must be different from current name",
"invalidChars": "Name cannot contain / or \\"
}
},
"deleteConfirm": {
"title": "Delete {{name}}?",
"message": "This action cannot be undone.",
"folderWarning": "This folder and all its contents will be permanently deleted."
},
"messages": {
"folderCreated": "Folder created successfully",
"folderCreateFailed": "Failed to create folder",
"renamed": "Item renamed successfully",
"renameFailed": "Failed to rename item",
"deleted": "Item deleted successfully",
"deleteFailed": "Failed to delete item",
"downloadStarted": "Download started",
"downloadFailed": "Failed to download file",
"uploaded": "Files uploaded successfully",
"uploadFailed": "Failed to upload files"
}
}
}