3c31695579
Adds the foundation for external file mounts: admin-configured backends (raw host filesystem in v1; sftp/webdav/… as future provider kinds) surfaced as a folder inside a user's drive. Mount contents are virtual/live-passthrough — read straight from the backend, never stored in storage.files — and are a deliberately separate, limited storage type (no dedup/sharing/trash/search). The feature is dark by default (OXICLOUD_ENABLE_EXTERNAL_MOUNTS=false). P1 scope (this PR): data model, the pluggable provider abstraction, and the read-only REST surface (mount listing + download). Read-write (P2), WebDAV/NextCloud path resolution (P3), and the admin UI (P4) follow. Core model - Mount root = a real storage.folders row; authorization for everything inside collapses onto that folder UUID (ltree-ancestry grant cascade). - Children are virtual, addressed by ext:<mount_id>:<base64url(node_id)> where node_id is provider-owned and opaque to the rest of the system. - A lock-free (arc-swap) MountRegistry maps mount-root UUID -> provider; a thin MountRouter::classify() is the single cheap hook handlers call before parsing an id as a UUID. With no mounts configured it always returns Regular, so existing code paths are unchanged. Added - migrations/20260805000000_external_mounts.sql (storage.external_mounts, kind + config JSONB) - domain/services/external_mount_id (id envelope + virtual etags) - application/ports/external_mount_ports (ExternalMountProvider, MountProviderFactory, repo port) - infrastructure local_fs_mount_provider (tokio::fs, symlink-escape-safe) + factory - application MountRegistry + MountRouter, pg ExternalMountRepository - DI wiring (AppState.mount_router), FeaturesConfig.enable_external_mounts - listing branch (FolderService::list_mount_dir_with_perms + folder_handler) and download branch (FileRetrievalService stat/open mount methods + file_handler) Authorization stays in the service layer (authz.require(Resource::Folder(mount_id))); handlers only classify. Cross-backend operations are out of scope for P1. Tests: 529 unit tests + 5 testcontainers integration tests (real Postgres 17), including end-to-end authorization (owner allowed, stranger denied). Line coverage of the new modules is 84–100% (cargo-llvm-cov). Known gap: file_handler::download_mount_file (HTTP glue) needs a full-app test (P4).
32 lines
1.2 KiB
Rust
32 lines
1.2 KiB
Rust
#![allow(async_fn_in_trait)]
|
|
|
|
// Export the main project modules
|
|
pub mod application;
|
|
pub mod common;
|
|
pub mod domain;
|
|
pub mod infrastructure;
|
|
pub mod interfaces;
|
|
|
|
// Test-only helpers for #[cfg(integration_tests)] modules across the
|
|
// crate (shared pool URL guard + pre-suite cleanup OnceCell).
|
|
#[cfg(integration_tests)]
|
|
pub mod integration_test_support;
|
|
|
|
// Shared testcontainers-backed harness for external-mount integration tests.
|
|
// Gated on `test` too because it links the `testcontainers` dev-dependency,
|
|
// which is only available to test targets (not the plain lib build).
|
|
#[cfg(all(test, integration_tests))]
|
|
mod mount_it_support;
|
|
|
|
// Phase 0 perf-benchmark support: deterministic image corpus generation/loading
|
|
// shared by `benches/thumbnails.rs` and `examples/bench_thumbnails_mem.rs`.
|
|
// Gated behind the `bench` feature so it adds nothing to normal builds.
|
|
#[cfg(feature = "bench")]
|
|
pub mod bench_support;
|
|
|
|
// Common public re-exports
|
|
pub use application::services::folder_service::FolderService;
|
|
pub use application::services::i18n_application_service::I18nApplicationService;
|
|
pub use domain::services::path_service::StoragePath;
|
|
pub use infrastructure::services::path_service::PathService;
|