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",