backbone-bucket 0.1.50

Bucket Bounded Context: File Storage Module for Backbone Framework
Documentation
//! Bucket Module
//!
//! Generated by metaphor-schema. Enhanced with runtime implementations.
//!
//! This module provides:
//! - Domain entities and repositories
//! - Application services
//! - HTTP and gRPC handlers
//! - Route configuration
//! - State machine enforcement
//! - Validation rules runtime
//! - RBAC middleware
//! - Trigger execution system
//! - Computed fields
//! - Workflow orchestrator

#![recursion_limit = "1024"]

// Generated modules
pub mod domain;
pub mod infrastructure;
pub mod application;
pub mod presentation;
pub mod seeders;

// <<< CUSTOM - bucket-serving surface (see docs/serving.md)
pub mod error;
pub mod storage;
pub mod auth;
pub mod config;

// ─── Essential (every consumer wires these) ─────────────────────────────
pub use error::{BucketError, BucketResult};
pub use config::{BucketConfig, ConfigEnvError, S3Config, ServingConfig, ServingMode, StorageConfig};

// ─── Storage backends ───────────────────────────────────────────────────
// `ObjectStorage` is the boundary trait. `LocalStorage` ships always;
// `S3Storage` is gated on the `s3` feature; `InMemoryStorage` on `test-utils`.
pub use storage::{ObjectMeta, ObjectStorage, LocalStorage};
#[cfg(feature = "s3")]
pub use storage::S3Storage;
#[cfg(feature = "test-utils")]
pub use storage::InMemoryStorage;

// ─── Auth slots (consumer-implemented) ──────────────────────────────────
pub use auth::{
    ArcAuthzPolicy, AuthExtractor, AuthzDecision, AuthzPolicy, DefaultOwnerOnlyPolicy, HasOwnerId,
};

// ─── File operations (single-shot programmatic API) ─────────────────────
pub use application::service::{FileMeta, FileService};

// ─── HTTP surfaces (serving + multipart upload) ─────────────────────────
pub use presentation::http::{lookup_by_key as lookup_file_by_key, serving_router, ServingContext};
pub use presentation::http::{
    upload_router, UploadConfig, UploadContext, DEFAULT_CHUNK_BODY_LIMIT, DEFAULT_UPLOAD_BODY_LIMIT,
};
// END CUSTOM
// Re-exports for convenience - Domain entities
pub use domain::entity::*;

// Re-exports - State Machine
pub use domain::state_machine::*;

// Re-exports - Computed Fields
pub use domain::computed::{HasComputedFields, ComputedFieldValues};

// Re-exports - Permissions
pub use domain::permission::{Action, FieldRestriction, PermissionChecker, PermissionError, PermissionResult};

// Re-exports - Infrastructure
pub use infrastructure::persistence::*;

// Re-exports - Application services
pub use application::service::AccessLogService;
pub use application::service::BucketService;
pub use application::service::ContentHashService;
pub use application::service::ConversionJobService;
pub use application::service::FileCommentService;
pub use application::service::FileLockService;
pub use application::service::FileShareService;
pub use application::service::FileVersionService;
pub use application::service::ProcessingJobService;
pub use application::service::StoredFileService;
pub use application::service::ThumbnailService;
pub use application::service::UploadSessionService;
pub use application::service::UserQuotaService;

// <<< CUSTOM - Custom service re-exports
pub use application::service::LockingService;
pub use application::service::DeduplicationService;
pub use application::service::MultipartUploadService;
pub use application::service::ConversionService;
#[allow(deprecated)]
pub use application::service::CdnService;
pub use application::service::VideoThumbnailService;
pub use application::service::DocumentPreviewService;
mod bucket_module; // Phase 6: http_routes() — regeneration-safe BucketModule extensions
pub use bucket_module::RouterOptions;
// END CUSTOM
// Re-exports - Validation
pub use application::validator::{ValidationError, ValidationResult};

// Re-exports - Triggers
pub use application::triggers::*;

// Re-exports - Workflows
pub use application::workflows::*;

use std::sync::Arc;
use axum::Router;
use sqlx::PgPool;

/// Bucket module configuration
///
/// Use the builder pattern to configure and register this module:
///
/// ```text
/// let bucket = BucketModule::builder()
///     .with_database(pool.clone())
///     .build()?;
///
/// let router = bucket.routes();
/// ```
pub struct BucketModule {
    pub access_log_service: Arc<AccessLogService>,
    pub bucket_service: Arc<BucketService>,
    pub content_hash_service: Arc<ContentHashService>,
    pub conversion_job_service: Arc<ConversionJobService>,
    pub file_comment_service: Arc<FileCommentService>,
    pub file_lock_service: Arc<FileLockService>,
    pub file_share_service: Arc<FileShareService>,
    pub file_version_service: Arc<FileVersionService>,
    pub processing_job_service: Arc<ProcessingJobService>,
    pub stored_file_service: Arc<StoredFileService>,
    pub thumbnail_service: Arc<ThumbnailService>,
    pub upload_session_service: Arc<UploadSessionService>,
    pub user_quota_service: Arc<UserQuotaService>,
    // <<< CUSTOM - Custom business logic services
    pub locking_service: Arc<LockingService>,
    pub deduplication_service: Arc<DeduplicationService>,
    pub multipart_upload_service: Arc<MultipartUploadService>,
    pub conversion_service: Arc<ConversionService>,
    #[allow(deprecated)]
    pub cdn_service: Arc<CdnService>,
    pub video_thumbnail_service: Arc<VideoThumbnailService>,
    pub document_preview_service: Arc<DocumentPreviewService>,
    /// Storage backend (mode-B serving + uploads). None until `.with_storage()` is called.
    pub storage: Option<Arc<dyn ObjectStorage>>,
    /// Bucket-module config (storage + serving). None until `.with_config()` is called.
    pub bucket_config: Option<Arc<BucketConfig>>,
    /// High-level file operations service, wired when both `storage` and `bucket_config` are set.
    pub file_service: Option<Arc<FileService>>,
    /// Direct handle to the StoredFile repository — used by the mode-B
    /// serving handler to look up files by `storage_key`.
    pub stored_file_repository: Arc<infrastructure::persistence::StoredFileRepository>,
    // END CUSTOM
}

impl BucketModule {
    /// Create a new module builder
    pub fn builder() -> BucketModuleBuilder {
        BucketModuleBuilder::new()
    }

    /// Configure HTTP routes for this module using Axum
    ///
    /// Returns an Axum Router with all 12 Backbone CRUD endpoints per entity.
    pub fn routes(&self) -> Router {
        use presentation::http::{
            create_bucket_routes,
            create_content_hash_routes,
            create_conversion_job_routes,
            create_file_comment_routes,
            create_file_lock_routes,
            create_file_share_routes,
            create_processing_job_routes,
            create_stored_file_routes,
            create_upload_session_routes,
            create_user_quota_routes,
        };

        Router::new()
            .merge(create_bucket_routes(self.bucket_service.clone()))
            .merge(create_content_hash_routes(self.content_hash_service.clone()))
            .merge(create_conversion_job_routes(self.conversion_job_service.clone()))
            .merge(create_file_comment_routes(self.file_comment_service.clone()))
            .merge(create_file_lock_routes(self.file_lock_service.clone()))
            .merge(create_file_share_routes(self.file_share_service.clone()))
            .merge(create_processing_job_routes(self.processing_job_service.clone()))
            .merge(create_stored_file_routes(self.stored_file_service.clone()))
            .merge(create_upload_session_routes(self.upload_session_service.clone()))
            .merge(create_user_quota_routes(self.user_quota_service.clone()))
    }
}

/// Builder for BucketModule
pub struct BucketModuleBuilder {
    db_pool: Option<PgPool>,
    // <<< CUSTOM - bucket-serving fields (see docs/serving.md)
    storage: Option<Arc<dyn ObjectStorage>>,
    bucket_config: Option<BucketConfig>,
    // END CUSTOM
}

impl BucketModuleBuilder {
    /// Create a new builder
    pub fn new() -> Self {
        Self {
            db_pool: None,
            // <<< CUSTOM
            storage: None,
            bucket_config: None,
            // END CUSTOM
        }
    }

    /// Set the database connection pool
    pub fn with_database(mut self, pool: PgPool) -> Self {
        self.db_pool = Some(pool);
        self
    }

    // <<< CUSTOM - bucket-serving builder methods (see docs/serving.md)

    /// Attach the bucket-module runtime config (storage + serving).
    pub fn with_config(mut self, config: BucketConfig) -> Self {
        self.bucket_config = Some(config);
        self
    }

    /// Attach the object-storage backend (`LocalStorage`, `S3Storage`, …).
    pub fn with_storage(mut self, storage: Arc<dyn ObjectStorage>) -> Self {
        self.storage = Some(storage);
        self
    }

    // END CUSTOM

    /// Build the module with configured dependencies
    pub fn build(self) -> anyhow::Result<BucketModule> {
        let db_pool = self.db_pool
            .ok_or_else(|| anyhow::anyhow!("Database pool not configured"))?;

        // AccessLog service
        let access_log_repository = Arc::new(AccessLogRepository::new(db_pool.clone()));
        let access_log_service = Arc::new(AccessLogService::with_repository(access_log_repository.clone()));

        // Bucket service
        let bucket_repository = Arc::new(BucketRepository::new(db_pool.clone()));
        let bucket_service = Arc::new(BucketService::with_repository(bucket_repository.clone()));

        // ContentHash service
        let content_hash_repository = Arc::new(ContentHashRepository::new(db_pool.clone()));
        let content_hash_service = Arc::new(ContentHashService::with_repository(content_hash_repository.clone()));

        // ConversionJob service
        let conversion_job_repository = Arc::new(ConversionJobRepository::new(db_pool.clone()));
        let conversion_job_service = Arc::new(ConversionJobService::with_repository(conversion_job_repository.clone()));

        // FileComment service
        let file_comment_repository = Arc::new(FileCommentRepository::new(db_pool.clone()));
        let file_comment_service = Arc::new(FileCommentService::with_repository(file_comment_repository.clone()));

        // FileLock service
        let file_lock_repository = Arc::new(FileLockRepository::new(db_pool.clone()));
        let file_lock_service = Arc::new(FileLockService::with_repository(file_lock_repository.clone()));

        // FileShare service
        let file_share_repository = Arc::new(FileShareRepository::new(db_pool.clone()));
        let file_share_service = Arc::new(FileShareService::with_repository(file_share_repository.clone()));

        // FileVersion service
        let file_version_repository = Arc::new(FileVersionRepository::new(db_pool.clone()));
        let file_version_service = Arc::new(FileVersionService::with_repository(file_version_repository.clone()));

        // ProcessingJob service
        let processing_job_repository = Arc::new(ProcessingJobRepository::new(db_pool.clone()));
        let processing_job_service = Arc::new(ProcessingJobService::with_repository(processing_job_repository.clone()));

        // StoredFile service
        let stored_file_repository = Arc::new(StoredFileRepository::new(db_pool.clone()));
        let stored_file_service = Arc::new(StoredFileService::with_repository(stored_file_repository.clone()));

        // Thumbnail service
        let thumbnail_repository = Arc::new(ThumbnailRepository::new(db_pool.clone()));
        let thumbnail_service = Arc::new(ThumbnailService::with_repository(thumbnail_repository.clone()));

        // UploadSession service
        let upload_session_repository = Arc::new(UploadSessionRepository::new(db_pool.clone()));
        let upload_session_service = Arc::new(UploadSessionService::with_repository(upload_session_repository.clone()));

        // UserQuota service
        let user_quota_repository = Arc::new(UserQuotaRepository::new(db_pool.clone()));
        let user_quota_service = Arc::new(UserQuotaService::with_repository(user_quota_repository.clone()));
        // <<< CUSTOM - Custom business logic services
        let locking_service = Arc::new(LockingService::new(
            file_lock_repository, stored_file_repository.clone(),
        ));
        let deduplication_service = Arc::new(DeduplicationService::new(
            content_hash_repository, stored_file_repository.clone(),
        ));
        let multipart_upload_service = Arc::new(MultipartUploadService::new(
            upload_session_repository, bucket_repository.clone(), user_quota_repository,
        ));
        let conversion_service = Arc::new(ConversionService::new(
            conversion_job_repository, stored_file_repository.clone(),
        ));
        #[allow(deprecated)]
        let cdn_service = Arc::new(CdnService::new(
            stored_file_repository.clone(), bucket_repository,
        ));
        let video_thumbnail_service = Arc::new(VideoThumbnailService::new(
            processing_job_repository.clone(), thumbnail_repository.clone(), stored_file_repository.clone(),
        ));
        let document_preview_service = Arc::new(DocumentPreviewService::new(
            processing_job_repository, thumbnail_repository, stored_file_repository.clone(),
        ));

        // <<< CUSTOM - bucket-serving wiring (see docs/serving.md)
        let bucket_config = self.bucket_config.map(Arc::new);
        let storage = self.storage;
        let file_service = match (&storage, &bucket_config) {
            (Some(s), Some(c)) => Some(Arc::new(FileService::new(
                s.clone(),
                stored_file_repository.clone(),
                c.clone(),
            ))),
            _ => None,
        };
        // END CUSTOM

        Ok(BucketModule {
            access_log_service,
            bucket_service,
            content_hash_service,
            conversion_job_service,
            file_comment_service,
            file_lock_service,
            file_share_service,
            file_version_service,
            processing_job_service,
            stored_file_service,
            thumbnail_service,
            upload_session_service,
            user_quota_service,
            // <<< CUSTOM
            locking_service,
            deduplication_service,
            multipart_upload_service,
            conversion_service,
            cdn_service,
            video_thumbnail_service,
            document_preview_service,
            storage,
            bucket_config,
            file_service,
            stored_file_repository,
            // END CUSTOM
        })
    }
}

impl Default for BucketModuleBuilder {
    fn default() -> Self {
        Self::new()
    }
}