feat: pluggable storage backends (S3, Azure, local) with admin UI

Implement 4-phase external storage backends architecture:

Phase 1 - Foundation:
- BlobStorageBackend trait (application/ports/blob_storage_ports.rs)
- LocalBlobBackend: extracted all tokio::fs ops from DedupService
- S3BlobBackend: AWS SDK with custom endpoint support (MinIO, R2, B2)
- DedupService refactored to use Arc<dyn BlobStorageBackend>

Phase 2 - Admin Panel:
- StorageSettingsService with DB persistence + env override
- Storage tab in admin panel (backend selector, S3 form, provider presets)
- GET/PUT/POST endpoints for storage settings + connection test
- i18n keys (en/es) and BEM CSS

Phase 3 - Migration:
- MigrationBlobBackend decorator (dual-read: target-first + source fallback)
- Background migration job with parallel transfers + progress tracking
- Migration UI (progress bar, ETA, pause/resume/verify/complete)
- 6 admin API endpoints for migration lifecycle

Phase 4 - Enterprise Extras:
- CachedBlobBackend: LRU disk cache for remote backends
- EncryptedBlobBackend: AES-256-GCM at-rest encryption
- AzureBlobBackend: Azure Blob Storage support
- RetryBlobBackend: exponential backoff for transient errors
- Decorator composition in DI: retry → encryption → cache

All 223 tests passing, clippy clean, fmt verified.
This commit is contained in:
Diocrafts
2026-04-14 21:33:38 +02:00
parent 6fc632af7e
commit cd3733b459
26 changed files with 6870 additions and 308 deletions
+44 -1
View File
@@ -566,7 +566,50 @@
"error_password_short": "Password must be at least 8 characters",
"error_generic": "Failed",
"error_network": "Network error: {{message}}",
"error_create_user": "Failed to create user"
"error_create_user": "Failed to create user",
"tab_storage": "Storage",
"storage_title": "Storage Backend",
"storage_current_backend": "Active Backend",
"storage_total_blobs": "Total Blobs",
"storage_total_size": "Total Size",
"storage_dedup_ratio": "Dedup Ratio",
"storage_backend": "Backend Type",
"storage_local": "Local Filesystem",
"storage_s3": "S3-Compatible",
"storage_provider_preset": "Provider Preset",
"storage_preset_custom": "Custom",
"storage_endpoint_url": "Endpoint URL",
"storage_endpoint_hint": "Leave empty for Amazon S3 default",
"storage_bucket": "Bucket",
"storage_region": "Region",
"storage_access_key": "Access Key ID",
"storage_secret_key": "Secret Access Key",
"storage_secret_configured": "A secret key is already configured",
"storage_key_placeholder": "Leave empty to keep current value",
"storage_path_style": "Force Path Style",
"storage_path_style_hint": "Required for MinIO and some S3-compatible providers",
"storage_test_connection": "Test Connection",
"storage_test_success": "Connection successful",
"storage_test_failure": "Connection failed",
"storage_save": "Save",
"storage_saved": "Storage settings saved successfully",
"storage_migration": "Backend Migration",
"storage_migration_coming_soon": "Backend migration will be available in a future update.",
"migration_status_label": "Status:",
"migration_start": "Start Migration",
"migration_pause": "Pause",
"migration_resume": "Resume",
"migration_verify": "Verify Integrity",
"migration_complete": "Finalize",
"migration_started": "Migration started",
"migration_paused_msg": "Migration paused",
"migration_resumed_msg": "Migration resumed",
"migration_completed_msg": "Migration finalized. Restart the server to use the new backend.",
"migration_verifying": "Verifying…",
"migration_verify_passed": "Verification passed",
"migration_verify_failed": "Verification failed",
"migration_failed_blobs": "failed blobs",
"testing": "Testing…"
},
"profile": {
"page_title": "Profile",
+44 -1
View File
@@ -566,7 +566,50 @@
"error_password_short": "La contraseña debe tener al menos 8 caracteres",
"error_generic": "Error",
"error_network": "Error de red: {{message}}",
"error_create_user": "Error al crear usuario"
"error_create_user": "Error al crear usuario",
"tab_storage": "Almacenamiento",
"storage_title": "Backend de Almacenamiento",
"storage_current_backend": "Backend Activo",
"storage_total_blobs": "Total de Blobs",
"storage_total_size": "Tamaño Total",
"storage_dedup_ratio": "Ratio de Dedup",
"storage_backend": "Tipo de Backend",
"storage_local": "Sistema de Archivos Local",
"storage_s3": "Compatible con S3",
"storage_provider_preset": "Proveedor Preconfigurado",
"storage_preset_custom": "Personalizado",
"storage_endpoint_url": "URL del Endpoint",
"storage_endpoint_hint": "Dejar vacío para usar Amazon S3 por defecto",
"storage_bucket": "Bucket",
"storage_region": "Región",
"storage_access_key": "Access Key ID",
"storage_secret_key": "Secret Access Key",
"storage_secret_configured": "Ya hay una clave secreta configurada",
"storage_key_placeholder": "Dejar vacío para mantener el valor actual",
"storage_path_style": "Forzar Path Style",
"storage_path_style_hint": "Requerido para MinIO y algunos proveedores compatibles con S3",
"storage_test_connection": "Probar Conexión",
"storage_test_success": "Conexión exitosa",
"storage_test_failure": "Conexión fallida",
"storage_save": "Guardar",
"storage_saved": "Configuración de almacenamiento guardada correctamente",
"storage_migration": "Migración de Backend",
"storage_migration_coming_soon": "La migración de backend estará disponible en una futura actualización.",
"migration_status_label": "Estado:",
"migration_start": "Iniciar Migración",
"migration_pause": "Pausar",
"migration_resume": "Reanudar",
"migration_verify": "Verificar Integridad",
"migration_complete": "Finalizar",
"migration_started": "Migración iniciada",
"migration_paused_msg": "Migración pausada",
"migration_resumed_msg": "Migración reanudada",
"migration_completed_msg": "Migración finalizada. Reinicia el servidor para usar el nuevo backend.",
"migration_verifying": "Verificando…",
"migration_verify_passed": "Verificación exitosa",
"migration_verify_failed": "Verificación fallida",
"migration_failed_blobs": "blobs fallidos",
"testing": "Probando…"
},
"profile": {
"page_title": "Perfil",