Files
k-photos/crates/domain/src/catalog/ports.rs
Gabriel Kaszewski 0077caa743 feat: safe deletion, album/asset delete, trash, README update
- volume-aware deletion: read-only volumes remove DB only, writable
  volumes soft-delete to trash with configurable grace period
- trash page with restore, worker purge sweep (TRASH_RETENTION_DAYS)
- album delete endpoint + sidebar trash icon
- asset delete from timeline selection toolbar
- all listing queries exclude trashed assets (deleted_at IS NULL)
- timeline ordered by EXIF capture date, date-summary endpoint
- README rewritten with features, setup, full env var table
2026-06-01 01:57:53 +02:00

146 lines
4.7 KiB
Rust

use super::entities::{
Asset, AssetFilters, AssetMetadata, AssetStack, DerivativeAsset, DerivativeProfile,
DuplicateGroup, MetadataSource,
};
use crate::common::errors::DomainError;
use crate::common::value_objects::{Checksum, StructuredData, SystemId};
use async_trait::async_trait;
use bytes::Bytes;
// --- AssetRepository ---
#[async_trait]
pub trait AssetRepository: Send + Sync {
async fn find_by_id(&self, id: &SystemId) -> Result<Option<Asset>, DomainError>;
async fn find_by_checksum(&self, checksum: &Checksum) -> Result<Vec<Asset>, DomainError>;
async fn find_by_owner(
&self,
owner_id: &SystemId,
limit: u32,
offset: u32,
) -> Result<Vec<Asset>, DomainError>;
async fn count_by_owner(&self, owner_id: &SystemId) -> Result<u64, DomainError>;
async fn search(
&self,
owner_id: &SystemId,
filters: &AssetFilters,
limit: u32,
offset: u32,
) -> Result<Vec<Asset>, DomainError>;
async fn count_search(
&self,
owner_id: &SystemId,
filters: &AssetFilters,
) -> Result<u64, DomainError>;
async fn date_summary(
&self,
owner_id: &SystemId,
) -> Result<Vec<(chrono::NaiveDate, u64)>, DomainError>;
async fn save(&self, asset: &Asset) -> Result<(), DomainError>;
async fn delete(&self, id: &SystemId) -> Result<(), DomainError>;
async fn soft_delete(
&self,
id: &SystemId,
deleted_by: &SystemId,
) -> Result<(), DomainError>;
async fn restore(&self, id: &SystemId) -> Result<(), DomainError>;
async fn find_trashed_before(
&self,
cutoff: chrono::DateTime<chrono::Utc>,
) -> Result<Vec<Asset>, DomainError>;
async fn count_trashed(&self, owner_id: &SystemId) -> Result<u64, DomainError>;
async fn find_trashed_by_owner(
&self,
owner_id: &SystemId,
limit: u32,
offset: u32,
) -> Result<Vec<Asset>, DomainError>;
}
// --- AssetMetadataRepository ---
#[async_trait]
pub trait AssetMetadataRepository: Send + Sync {
async fn find_by_asset(&self, asset_id: &SystemId) -> Result<Vec<AssetMetadata>, DomainError>;
async fn find_by_assets(
&self,
asset_ids: &[SystemId],
) -> Result<Vec<AssetMetadata>, DomainError>;
async fn find_by_asset_and_source(
&self,
asset_id: &SystemId,
source: MetadataSource,
) -> Result<Option<AssetMetadata>, DomainError>;
async fn save(&self, metadata: &AssetMetadata) -> Result<(), DomainError>;
async fn delete_by_asset_and_source(
&self,
asset_id: &SystemId,
source: MetadataSource,
) -> Result<(), DomainError>;
}
// --- AssetStackRepository ---
#[async_trait]
pub trait AssetStackRepository: Send + Sync {
async fn find_by_id(&self, id: &SystemId) -> Result<Option<AssetStack>, DomainError>;
async fn find_by_owner(&self, owner_id: &SystemId) -> Result<Vec<AssetStack>, DomainError>;
async fn find_by_asset(&self, asset_id: &SystemId) -> Result<Vec<AssetStack>, DomainError>;
async fn save(&self, stack: &AssetStack) -> Result<(), DomainError>;
async fn delete(&self, id: &SystemId) -> Result<(), DomainError>;
}
// --- DerivativeRepository ---
#[async_trait]
pub trait DerivativeRepository: Send + Sync {
async fn find_by_asset(&self, asset_id: &SystemId)
-> Result<Vec<DerivativeAsset>, DomainError>;
async fn find_by_asset_and_profile(
&self,
asset_id: &SystemId,
profile: DerivativeProfile,
) -> Result<Option<DerivativeAsset>, DomainError>;
async fn save(&self, derivative: &DerivativeAsset) -> Result<(), DomainError>;
async fn delete(&self, id: &SystemId) -> Result<(), DomainError>;
}
// --- DuplicateRepository ---
#[async_trait]
pub trait DuplicateRepository: Send + Sync {
async fn find_by_id(&self, id: &SystemId) -> Result<Option<DuplicateGroup>, DomainError>;
async fn find_unresolved(
&self,
limit: u32,
offset: u32,
) -> Result<Vec<DuplicateGroup>, DomainError>;
async fn find_by_asset(&self, asset_id: &SystemId) -> Result<Vec<DuplicateGroup>, DomainError>;
async fn save(&self, group: &DuplicateGroup) -> Result<(), DomainError>;
}
// --- MetadataExtractorPort ---
pub trait MetadataExtractorPort: Send + Sync {
fn extract(&self, bytes: &Bytes) -> Result<StructuredData, DomainError>;
}
// --- ThumbnailGeneratorPort ---
pub struct ThumbnailOutput {
pub bytes: Bytes,
pub width: u32,
pub height: u32,
pub mime_type: String,
}
pub trait ThumbnailGeneratorPort: Send + Sync {
fn generate(
&self,
source: &Bytes,
width: u32,
height: u32,
format: &str,
) -> Result<ThumbnailOutput, DomainError>;
}