"""Factory for user-scoped cloud storage backends.""" from __future__ import annotations from storage.cloud_base import CloudResourceAdapter, MutableCloudResourceAdapter def build_cloud_backend(provider: str, credentials: dict): if provider == "google_drive": from storage.google_drive_backend import GoogleDriveBackend # lazy import return GoogleDriveBackend(credentials) if provider == "onedrive": from storage.onedrive_backend import OneDriveBackend # lazy import return OneDriveBackend(credentials) if provider == "nextcloud": from storage.nextcloud_backend import NextcloudBackend # lazy import from storage.cloud_utils import normalize_nextcloud_url # lazy import server_url = credentials["server_url"] username = credentials["username"] # Normalize to canonical WebDAV root idempotently; validate SSRF before construction try: server_url = normalize_nextcloud_url(server_url, username) except ValueError: # URL may already be canonical or validation rejected it — let the # constructor's validate_cloud_url call surface the error pass return NextcloudBackend( server_url, username, credentials["password"], ) if provider == "webdav": from storage.webdav_backend import WebDAVBackend # lazy import return WebDAVBackend( credentials["server_url"], credentials["username"], credentials["password"], ) raise ValueError(f"Unknown provider: {provider}") def build_cloud_resource_adapter(provider: str, credentials: dict) -> CloudResourceAdapter: """Build a CloudResourceAdapter for the given provider and credentials. Returns a provider instance that implements the CloudResourceAdapter read-only interface (list_folder, get_capabilities, merge_item_capabilities). All four supported providers implement CloudResourceAdapter as a mixin alongside their StorageBackend interface. Raises: ValueError: If provider is not one of the four supported cloud providers. """ backend = build_cloud_backend(provider, credentials) if not isinstance(backend, CloudResourceAdapter): raise ValueError( f"Provider {provider!r} backend does not implement CloudResourceAdapter" ) return backend def build_mutable_cloud_adapter(provider: str, credentials: dict) -> MutableCloudResourceAdapter: """Build a MutableCloudResourceAdapter for the given provider and credentials. Returns a provider instance that implements the Phase 13 mutable-operation contract (create_folder, rename, move, delete, upload_file) in addition to the read-only CloudResourceAdapter interface. Raises: ValueError: If provider is not one of the four supported cloud providers, or if the provider backend does not implement MutableCloudResourceAdapter. """ backend = build_cloud_backend(provider, credentials) if not isinstance(backend, MutableCloudResourceAdapter): raise ValueError( f"Provider {provider!r} backend does not implement MutableCloudResourceAdapter. " "All Phase 13 providers must subclass MutableCloudResourceAdapter." ) return backend