refactor: rename ImageStorage → ObjectStorage
Some checks failed
CI / Check / Test (push) Failing after 46s

This commit is contained in:
2026-06-03 01:33:08 +02:00
parent d94ccbe057
commit f262417971
23 changed files with 79 additions and 79 deletions

View File

@@ -4,20 +4,20 @@ use async_trait::async_trait;
use domain::{ use domain::{
errors::DomainError, errors::DomainError,
events::DomainEvent, events::DomainEvent,
ports::{EventHandler, ImageRefCommand, ImageStorage}, ports::{EventHandler, ImageRefCommand, ObjectStorage},
}; };
use crate::Format; use crate::Format;
pub struct ImageConversionHandler { pub struct ImageConversionHandler {
storage: Arc<dyn ImageStorage>, storage: Arc<dyn ObjectStorage>,
image_ref: Arc<dyn ImageRefCommand>, image_ref: Arc<dyn ImageRefCommand>,
format: Format, format: Format,
} }
impl ImageConversionHandler { impl ImageConversionHandler {
pub fn new( pub fn new(
storage: Arc<dyn ImageStorage>, storage: Arc<dyn ObjectStorage>,
image_ref: Arc<dyn ImageRefCommand>, image_ref: Arc<dyn ImageRefCommand>,
format: Format, format: Format,
) -> Self { ) -> Self {

View File

@@ -7,14 +7,14 @@ pub use config::{ConversionConfig, Format};
pub use handler::ImageConversionHandler; pub use handler::ImageConversionHandler;
use domain::ports::{ use domain::ports::{
EventHandler, EventPublisher, ImageRefCommand, ImageRefQuery, ImageStorage, PeriodicJob, EventHandler, EventPublisher, ImageRefCommand, ImageRefQuery, ObjectStorage, PeriodicJob,
}; };
use std::sync::Arc; use std::sync::Arc;
type ConversionPair = (Arc<dyn EventHandler>, Arc<dyn PeriodicJob>); type ConversionPair = (Arc<dyn EventHandler>, Arc<dyn PeriodicJob>);
pub fn build( pub fn build(
image_storage: Arc<dyn ImageStorage>, object_storage: Arc<dyn ObjectStorage>,
image_ref_command: Arc<dyn ImageRefCommand>, image_ref_command: Arc<dyn ImageRefCommand>,
image_ref_query: Arc<dyn ImageRefQuery>, image_ref_query: Arc<dyn ImageRefQuery>,
event_publisher: Arc<dyn EventPublisher>, event_publisher: Arc<dyn EventPublisher>,
@@ -27,7 +27,7 @@ pub fn build(
let format = config.format; let format = config.format;
let handler = Arc::new(ImageConversionHandler::new( let handler = Arc::new(ImageConversionHandler::new(
Arc::clone(&image_storage), Arc::clone(&object_storage),
image_ref_command, image_ref_command,
format, format,
)) as Arc<dyn EventHandler>; )) as Arc<dyn EventHandler>;

View File

@@ -1,5 +1,5 @@
use super::*; use super::*;
use image_storage::ImageStorageAdapter; use object_storage::ObjectStorageAdapter;
use object_store::memory::InMemory; use object_store::memory::InMemory;
use std::sync::Mutex; use std::sync::Mutex;
@@ -27,8 +27,8 @@ impl ImageRefCommand for MockImageRef {
} }
} }
fn in_memory_storage() -> Arc<ImageStorageAdapter> { fn in_memory_storage() -> Arc<ObjectStorageAdapter> {
Arc::new(ImageStorageAdapter::new(Arc::new(InMemory::new()))) Arc::new(ObjectStorageAdapter::new(Arc::new(InMemory::new())))
} }
fn tiny_jpeg() -> Vec<u8> { fn tiny_jpeg() -> Vec<u8> {
@@ -44,7 +44,7 @@ async fn ignores_non_image_stored_events() {
let storage = in_memory_storage(); let storage = in_memory_storage();
let image_ref = MockImageRef::new(); let image_ref = MockImageRef::new();
let handler = ImageConversionHandler::new( let handler = ImageConversionHandler::new(
Arc::clone(&storage) as Arc<dyn ImageStorage>, Arc::clone(&storage) as Arc<dyn ObjectStorage>,
Arc::clone(&image_ref) as Arc<dyn ImageRefCommand>, Arc::clone(&image_ref) as Arc<dyn ImageRefCommand>,
Format::Avif, Format::Avif,
); );
@@ -68,7 +68,7 @@ async fn skips_already_converted_avif_key() {
.unwrap(); .unwrap();
let image_ref = MockImageRef::new(); let image_ref = MockImageRef::new();
let handler = ImageConversionHandler::new( let handler = ImageConversionHandler::new(
Arc::clone(&storage) as Arc<dyn ImageStorage>, Arc::clone(&storage) as Arc<dyn ObjectStorage>,
Arc::clone(&image_ref) as Arc<dyn ImageRefCommand>, Arc::clone(&image_ref) as Arc<dyn ImageRefCommand>,
Format::Avif, Format::Avif,
); );
@@ -92,7 +92,7 @@ async fn skips_already_converted_webp_key() {
.unwrap(); .unwrap();
let image_ref = MockImageRef::new(); let image_ref = MockImageRef::new();
let handler = ImageConversionHandler::new( let handler = ImageConversionHandler::new(
Arc::clone(&storage) as Arc<dyn ImageStorage>, Arc::clone(&storage) as Arc<dyn ObjectStorage>,
Arc::clone(&image_ref) as Arc<dyn ImageRefCommand>, Arc::clone(&image_ref) as Arc<dyn ImageRefCommand>,
Format::Webp, Format::Webp,
); );
@@ -113,7 +113,7 @@ async fn converts_jpeg_to_avif_and_swaps_key() {
storage.store("avatars/u1", &tiny_jpeg()).await.unwrap(); storage.store("avatars/u1", &tiny_jpeg()).await.unwrap();
let image_ref = MockImageRef::new(); let image_ref = MockImageRef::new();
let handler = ImageConversionHandler::new( let handler = ImageConversionHandler::new(
Arc::clone(&storage) as Arc<dyn ImageStorage>, Arc::clone(&storage) as Arc<dyn ObjectStorage>,
Arc::clone(&image_ref) as Arc<dyn ImageRefCommand>, Arc::clone(&image_ref) as Arc<dyn ImageRefCommand>,
Format::Avif, Format::Avif,
); );
@@ -139,7 +139,7 @@ async fn converts_jpeg_to_webp_and_swaps_key() {
storage.store("avatars/u1", &tiny_jpeg()).await.unwrap(); storage.store("avatars/u1", &tiny_jpeg()).await.unwrap();
let image_ref = MockImageRef::new(); let image_ref = MockImageRef::new();
let handler = ImageConversionHandler::new( let handler = ImageConversionHandler::new(
Arc::clone(&storage) as Arc<dyn ImageStorage>, Arc::clone(&storage) as Arc<dyn ObjectStorage>,
Arc::clone(&image_ref) as Arc<dyn ImageRefCommand>, Arc::clone(&image_ref) as Arc<dyn ImageRefCommand>,
Format::Webp, Format::Webp,
); );

View File

@@ -5,17 +5,17 @@ use async_trait::async_trait;
use domain::{ use domain::{
errors::DomainError, errors::DomainError,
events::DomainEvent, events::DomainEvent,
ports::{EventHandler, ImageStorage}, ports::{EventHandler, ObjectStorage},
}; };
use futures::StreamExt; use futures::StreamExt;
use object_store::{ObjectStore, path::Path}; use object_store::{ObjectStore, path::Path};
use std::sync::Arc; use std::sync::Arc;
pub struct ImageStorageAdapter { pub struct ObjectStorageAdapter {
store: Arc<dyn ObjectStore>, store: Arc<dyn ObjectStore>,
} }
impl ImageStorageAdapter { impl ObjectStorageAdapter {
pub fn new(store: Arc<dyn ObjectStore>) -> Self { pub fn new(store: Arc<dyn ObjectStore>) -> Self {
Self { store } Self { store }
} }
@@ -26,7 +26,7 @@ impl ImageStorageAdapter {
} }
#[async_trait] #[async_trait]
impl ImageStorage for ImageStorageAdapter { impl ObjectStorage for ObjectStorageAdapter {
async fn store(&self, key: &str, image_bytes: &[u8]) -> Result<String, DomainError> { async fn store(&self, key: &str, image_bytes: &[u8]) -> Result<String, DomainError> {
let path = Path::from(key); let path = Path::from(key);
self.store self.store
@@ -78,12 +78,12 @@ impl ImageStorage for ImageStorageAdapter {
} }
pub struct ImageCleanupHandler { pub struct ImageCleanupHandler {
image_storage: Arc<dyn ImageStorage>, object_storage: Arc<dyn ObjectStorage>,
} }
impl ImageCleanupHandler { impl ImageCleanupHandler {
pub fn new(image_storage: Arc<dyn ImageStorage>) -> Self { pub fn new(object_storage: Arc<dyn ObjectStorage>) -> Self {
Self { image_storage } Self { object_storage }
} }
} }
@@ -97,15 +97,15 @@ impl EventHandler for ImageCleanupHandler {
let Some(path) = poster_path else { let Some(path) = poster_path else {
return Ok(()); return Ok(());
}; };
if let Err(e) = self.image_storage.delete(path.value()).await { if let Err(e) = self.object_storage.delete(path.value()).await {
tracing::warn!("image cleanup failed for {}: {e}", path.value()); tracing::warn!("image cleanup failed for {}: {e}", path.value());
} }
Ok(()) Ok(())
} }
} }
pub fn create() -> anyhow::Result<Arc<dyn ImageStorage>> { pub fn create() -> anyhow::Result<Arc<dyn ObjectStorage>> {
Ok(Arc::new(ImageStorageAdapter::from_config( Ok(Arc::new(ObjectStorageAdapter::from_config(
StorageConfig::from_env()?, StorageConfig::from_env()?,
))) )))
} }

View File

@@ -1,8 +1,8 @@
use super::*; use super::*;
use object_store::memory::InMemory; use object_store::memory::InMemory;
fn adapter() -> ImageStorageAdapter { fn adapter() -> ObjectStorageAdapter {
ImageStorageAdapter::new(Arc::new(InMemory::new())) ObjectStorageAdapter::new(Arc::new(InMemory::new()))
} }
#[tokio::test] #[tokio::test]
@@ -46,7 +46,7 @@ async fn cleanup_handler_deletes_on_movie_deleted() {
let inner = Arc::new(adapter()); let inner = Arc::new(adapter());
inner.store("some-uuid", b"img").await.unwrap(); inner.store("some-uuid", b"img").await.unwrap();
let path = PosterPath::new("some-uuid".to_string()).unwrap(); let path = PosterPath::new("some-uuid".to_string()).unwrap();
let handler = ImageCleanupHandler::new(Arc::clone(&inner) as Arc<dyn ImageStorage>); let handler = ImageCleanupHandler::new(Arc::clone(&inner) as Arc<dyn ObjectStorage>);
handler handler
.handle(&DomainEvent::MovieDeleted { .handle(&DomainEvent::MovieDeleted {
movie_id: MovieId::from_uuid(uuid::Uuid::new_v4()), movie_id: MovieId::from_uuid(uuid::Uuid::new_v4()),

View File

@@ -5,7 +5,7 @@ use domain::{
errors::DomainError, errors::DomainError,
events::DomainEvent, events::DomainEvent,
ports::{ ports::{
EventHandler, EventPublisher, ImageStorage, MetadataClient, MovieRepository, EventHandler, EventPublisher, ObjectStorage, MetadataClient, MovieRepository,
PosterFetcherClient, PosterFetcherClient,
}, },
value_objects::{ExternalMetadataId, MovieId, PosterPath}, value_objects::{ExternalMetadataId, MovieId, PosterPath},
@@ -15,7 +15,7 @@ pub struct PosterSyncHandler {
movie_repository: Arc<dyn MovieRepository>, movie_repository: Arc<dyn MovieRepository>,
metadata_client: Arc<dyn MetadataClient>, metadata_client: Arc<dyn MetadataClient>,
poster_fetcher: Arc<dyn PosterFetcherClient>, poster_fetcher: Arc<dyn PosterFetcherClient>,
image_storage: Arc<dyn ImageStorage>, object_storage: Arc<dyn ObjectStorage>,
event_publisher: Arc<dyn EventPublisher>, event_publisher: Arc<dyn EventPublisher>,
max_retries: u32, max_retries: u32,
} }
@@ -25,7 +25,7 @@ impl PosterSyncHandler {
movie_repository: Arc<dyn MovieRepository>, movie_repository: Arc<dyn MovieRepository>,
metadata_client: Arc<dyn MetadataClient>, metadata_client: Arc<dyn MetadataClient>,
poster_fetcher: Arc<dyn PosterFetcherClient>, poster_fetcher: Arc<dyn PosterFetcherClient>,
image_storage: Arc<dyn ImageStorage>, object_storage: Arc<dyn ObjectStorage>,
event_publisher: Arc<dyn EventPublisher>, event_publisher: Arc<dyn EventPublisher>,
max_retries: u32, max_retries: u32,
) -> Self { ) -> Self {
@@ -33,7 +33,7 @@ impl PosterSyncHandler {
movie_repository, movie_repository,
metadata_client, metadata_client,
poster_fetcher, poster_fetcher,
image_storage, object_storage,
event_publisher, event_publisher,
max_retries, max_retries,
} }
@@ -67,7 +67,7 @@ impl PosterSyncHandler {
let image_bytes = self.poster_fetcher.fetch_poster_bytes(&poster_url).await?; let image_bytes = self.poster_fetcher.fetch_poster_bytes(&poster_url).await?;
let stored_path = self let stored_path = self
.image_storage .object_storage
.store(&movie_id.value().to_string(), &image_bytes) .store(&movie_id.value().to_string(), &image_bytes)
.await?; .await?;
if let Err(e) = self if let Err(e) = self

View File

@@ -8,7 +8,7 @@ use domain::{
events::DomainEvent, events::DomainEvent,
models::{CastMember, CrewMember, Genre, Keyword, MovieProfile}, models::{CastMember, CrewMember, Genre, Keyword, MovieProfile},
ports::{ ports::{
EventHandler, ImageStorage, MovieEnrichmentClient, MovieProfileRepository, MovieRepository, EventHandler, ObjectStorage, MovieEnrichmentClient, MovieProfileRepository, MovieRepository,
PersonCommand, SearchCommand, PersonCommand, SearchCommand,
}, },
value_objects::MovieId, value_objects::MovieId,
@@ -229,7 +229,7 @@ pub struct EnrichmentHandler {
pub profile_repo: Arc<dyn MovieProfileRepository>, pub profile_repo: Arc<dyn MovieProfileRepository>,
pub person_command: Arc<dyn PersonCommand>, pub person_command: Arc<dyn PersonCommand>,
pub search_command: Arc<dyn SearchCommand>, pub search_command: Arc<dyn SearchCommand>,
pub image_storage: Arc<dyn ImageStorage>, pub object_storage: Arc<dyn ObjectStorage>,
http: reqwest::Client, http: reqwest::Client,
} }
@@ -240,7 +240,7 @@ impl EnrichmentHandler {
profile_repo: Arc<dyn MovieProfileRepository>, profile_repo: Arc<dyn MovieProfileRepository>,
person_command: Arc<dyn PersonCommand>, person_command: Arc<dyn PersonCommand>,
search_command: Arc<dyn SearchCommand>, search_command: Arc<dyn SearchCommand>,
image_storage: Arc<dyn ImageStorage>, object_storage: Arc<dyn ObjectStorage>,
) -> Self { ) -> Self {
Self { Self {
enrichment_client, enrichment_client,
@@ -248,7 +248,7 @@ impl EnrichmentHandler {
profile_repo, profile_repo,
person_command, person_command,
search_command, search_command,
image_storage, object_storage,
http: reqwest::Client::new(), http: reqwest::Client::new(),
} }
} }
@@ -259,14 +259,14 @@ impl EnrichmentHandler {
continue; continue;
}; };
let key = format!("cast{path}"); let key = format!("cast{path}");
if self.image_storage.get(&key).await.is_ok() { if self.object_storage.get(&key).await.is_ok() {
continue; continue;
} }
let url = format!("https://image.tmdb.org/t/p/w185{path}"); let url = format!("https://image.tmdb.org/t/p/w185{path}");
match self.http.get(&url).send().await { match self.http.get(&url).send().await {
Ok(resp) if resp.status().is_success() => { Ok(resp) if resp.status().is_success() => {
if let Ok(bytes) = resp.bytes().await if let Ok(bytes) = resp.bytes().await
&& let Err(e) = self.image_storage.store(&key, &bytes).await && let Err(e) = self.object_storage.store(&key, &bytes).await
{ {
tracing::debug!("cast photo store failed for {path}: {e}"); tracing::debug!("cast photo store failed for {path}: {e}");
} }

View File

@@ -1,7 +1,7 @@
use std::sync::Arc; use std::sync::Arc;
use domain::ports::{ use domain::ports::{
AuthService, DiaryExporter, DiaryRepository, DocumentParser, EventPublisher, ImageStorage, AuthService, DiaryExporter, DiaryRepository, DocumentParser, EventPublisher, ObjectStorage,
ImportProfileRepository, ImportSessionRepository, MetadataClient, MovieProfileRepository, ImportProfileRepository, ImportSessionRepository, MetadataClient, MovieProfileRepository,
MovieRepository, PasswordHasher, PersonCommand, PersonQuery, PosterFetcherClient, MovieRepository, PasswordHasher, PersonCommand, PersonQuery, PosterFetcherClient,
RemoteWatchlistRepository, ReviewRepository, SearchCommand, SearchPort, SocialQueryPort, RemoteWatchlistRepository, ReviewRepository, SearchCommand, SearchPort, SocialQueryPort,
@@ -42,7 +42,7 @@ pub struct Services {
pub password_hasher: Arc<dyn PasswordHasher>, pub password_hasher: Arc<dyn PasswordHasher>,
pub metadata: Arc<dyn MetadataClient>, pub metadata: Arc<dyn MetadataClient>,
pub poster_fetcher: Arc<dyn PosterFetcherClient>, pub poster_fetcher: Arc<dyn PosterFetcherClient>,
pub image_storage: Arc<dyn ImageStorage>, pub object_storage: Arc<dyn ObjectStorage>,
pub event_publisher: Arc<dyn EventPublisher>, pub event_publisher: Arc<dyn EventPublisher>,
pub diary_exporter: Arc<dyn DiaryExporter>, pub diary_exporter: Arc<dyn DiaryExporter>,
pub document_parser: Arc<dyn DocumentParser>, pub document_parser: Arc<dyn DocumentParser>,

View File

@@ -52,7 +52,7 @@ pub async fn execute(ctx: &AppContext, cmd: SyncPosterCommand) -> Result<(), Dom
let stored_path = ctx let stored_path = ctx
.services .services
.image_storage .object_storage
.store(&movie_id.value().to_string(), &image_bytes) .store(&movie_id.value().to_string(), &image_bytes)
.await?; .await?;

View File

@@ -6,7 +6,7 @@ use domain::testing::{
}; };
use domain::{ use domain::{
ports::{ ports::{
AuthService, DiaryExporter, DiaryRepository, DocumentParser, EventPublisher, ImageStorage, AuthService, DiaryExporter, DiaryRepository, DocumentParser, EventPublisher, ObjectStorage,
ImportProfileRepository, ImportSessionRepository, MetadataClient, MovieProfileRepository, ImportProfileRepository, ImportSessionRepository, MetadataClient, MovieProfileRepository,
MovieRepository, PasswordHasher, PersonCommand, PersonQuery, PosterFetcherClient, MovieRepository, PasswordHasher, PersonCommand, PersonQuery, PosterFetcherClient,
ReviewRepository, SearchCommand, SearchPort, StatsRepository, UserProfileFieldsRepository, ReviewRepository, SearchCommand, SearchPort, StatsRepository, UserProfileFieldsRepository,
@@ -16,7 +16,7 @@ use domain::{
testing::{ testing::{
FakeAuthService, FakeMetadataClient, FakePasswordHasher, InMemoryMovieRepository, FakeAuthService, FakeMetadataClient, FakePasswordHasher, InMemoryMovieRepository,
InMemoryReviewRepository, InMemoryUserRepository, InMemoryWatchlistRepository, InMemoryReviewRepository, InMemoryUserRepository, InMemoryWatchlistRepository,
NoopEventPublisher, NoopImageStorage, PanicDiaryExporter, PanicDiaryRepository, NoopEventPublisher, NoopObjectStorage, PanicDiaryExporter, PanicDiaryRepository,
PanicDocumentParser, PanicImportProfileRepository, PanicImportSessionRepository, PanicDocumentParser, PanicImportProfileRepository, PanicImportSessionRepository,
PanicMovieProfileRepository, PanicPersonCommand, PanicPersonQuery, PanicPosterFetcher, PanicMovieProfileRepository, PanicPersonCommand, PanicPersonQuery, PanicPosterFetcher,
PanicProfileFieldsRepo, PanicSearchCommand, PanicSearchPort, PanicStatsRepository, PanicProfileFieldsRepo, PanicSearchCommand, PanicSearchPort, PanicStatsRepository,
@@ -38,7 +38,7 @@ pub struct TestContextBuilder {
pub stats_repo: Arc<dyn StatsRepository>, pub stats_repo: Arc<dyn StatsRepository>,
pub metadata_client: Arc<dyn MetadataClient>, pub metadata_client: Arc<dyn MetadataClient>,
pub poster_fetcher: Arc<dyn PosterFetcherClient>, pub poster_fetcher: Arc<dyn PosterFetcherClient>,
pub image_storage: Arc<dyn ImageStorage>, pub object_storage: Arc<dyn ObjectStorage>,
pub event_publisher: Arc<dyn EventPublisher>, pub event_publisher: Arc<dyn EventPublisher>,
pub auth_service: Arc<dyn AuthService>, pub auth_service: Arc<dyn AuthService>,
pub password_hasher: Arc<dyn PasswordHasher>, pub password_hasher: Arc<dyn PasswordHasher>,
@@ -70,7 +70,7 @@ impl TestContextBuilder {
stats_repo: Arc::new(PanicStatsRepository), stats_repo: Arc::new(PanicStatsRepository),
metadata_client: Arc::new(FakeMetadataClient), metadata_client: Arc::new(FakeMetadataClient),
poster_fetcher: Arc::new(PanicPosterFetcher), poster_fetcher: Arc::new(PanicPosterFetcher),
image_storage: Arc::new(NoopImageStorage), object_storage: Arc::new(NoopObjectStorage),
event_publisher: NoopEventPublisher::new(), event_publisher: NoopEventPublisher::new(),
auth_service: Arc::new(FakeAuthService), auth_service: Arc::new(FakeAuthService),
password_hasher: Arc::new(FakePasswordHasher), password_hasher: Arc::new(FakePasswordHasher),
@@ -172,7 +172,7 @@ impl TestContextBuilder {
password_hasher: self.password_hasher, password_hasher: self.password_hasher,
metadata: self.metadata_client, metadata: self.metadata_client,
poster_fetcher: self.poster_fetcher, poster_fetcher: self.poster_fetcher,
image_storage: self.image_storage, object_storage: self.object_storage,
event_publisher: self.event_publisher, event_publisher: self.event_publisher,
diary_exporter: self.diary_exporter, diary_exporter: self.diary_exporter,
document_parser: self.document_parser, document_parser: self.document_parser,

View File

@@ -21,10 +21,10 @@ pub async fn execute(ctx: &AppContext, cmd: UpdateProfileCommand) -> Result<(),
)); ));
} }
if let Some(old_path) = user.avatar_path() { if let Some(old_path) = user.avatar_path() {
let _ = ctx.services.image_storage.delete(old_path).await; let _ = ctx.services.object_storage.delete(old_path).await;
} }
let key = format!("avatars/{}", user_id.value()); let key = format!("avatars/{}", user_id.value());
let stored = ctx.services.image_storage.store(&key, &bytes).await?; let stored = ctx.services.object_storage.store(&key, &bytes).await?;
if let Err(e) = ctx if let Err(e) = ctx
.services .services
.event_publisher .event_publisher
@@ -49,10 +49,10 @@ pub async fn execute(ctx: &AppContext, cmd: UpdateProfileCommand) -> Result<(),
)); ));
} }
if let Some(old_path) = user.banner_path() { if let Some(old_path) = user.banner_path() {
let _ = ctx.services.image_storage.delete(old_path).await; let _ = ctx.services.object_storage.delete(old_path).await;
} }
let key = format!("banners/{}", user_id.value()); let key = format!("banners/{}", user_id.value());
let stored = ctx.services.image_storage.store(&key, &bytes).await?; let stored = ctx.services.object_storage.store(&key, &bytes).await?;
if let Err(e) = ctx if let Err(e) = ctx
.services .services
.event_publisher .event_publisher

View File

@@ -11,7 +11,7 @@ pub async fn execute(ctx: &AppContext, id: WrapUpId) -> Result<(), DomainError>
.await? .await?
.ok_or_else(|| DomainError::NotFound("wrap-up not found".into()))?; .ok_or_else(|| DomainError::NotFound("wrap-up not found".into()))?;
let storage = WrapUpStorage::new(ctx.services.image_storage.clone()); let storage = WrapUpStorage::new(ctx.services.object_storage.clone());
let _ = storage.delete_video(&id).await; let _ = storage.delete_video(&id).await;
ctx.repos.wrapup_repo.delete(&id).await ctx.repos.wrapup_repo.delete(&id).await

View File

@@ -46,7 +46,7 @@ pub async fn execute(
.await?; .await?;
if let Some(ref renderer) = ctx.services.video_renderer { if let Some(ref renderer) = ctx.services.video_renderer {
let asset_storage = WrapUpStorage::new(ctx.services.image_storage.clone()); let asset_storage = WrapUpStorage::new(ctx.services.object_storage.clone());
let poster_images = asset_storage.resolve_poster_images(&report.poster_paths).await; let poster_images = asset_storage.resolve_poster_images(&report.poster_paths).await;
let cast_images = asset_storage let cast_images = asset_storage
.resolve_cast_images(&report.top_cast_profile_paths) .resolve_cast_images(&report.top_cast_profile_paths)

View File

@@ -1,14 +1,14 @@
use domain::errors::DomainError; use domain::errors::DomainError;
use domain::ports::ImageStorage; use domain::ports::ObjectStorage;
use domain::value_objects::WrapUpId; use domain::value_objects::WrapUpId;
use std::sync::Arc; use std::sync::Arc;
pub struct WrapUpStorage { pub struct WrapUpStorage {
inner: Arc<dyn ImageStorage>, inner: Arc<dyn ObjectStorage>,
} }
impl WrapUpStorage { impl WrapUpStorage {
pub fn new(storage: Arc<dyn ImageStorage>) -> Self { pub fn new(storage: Arc<dyn ObjectStorage>) -> Self {
Self { inner: storage } Self { inner: storage }
} }

View File

@@ -184,7 +184,7 @@ pub trait PosterFetcherClient: Send + Sync {
} }
#[async_trait] #[async_trait]
pub trait ImageStorage: Send + Sync { pub trait ObjectStorage: Send + Sync {
/// Stores `image_bytes` at `key` and returns the stored key. /// Stores `image_bytes` at `key` and returns the stored key.
async fn store(&self, key: &str, image_bytes: &[u8]) -> Result<String, DomainError>; async fn store(&self, key: &str, image_bytes: &[u8]) -> Result<String, DomainError>;
async fn get(&self, key: &str) -> Result<Vec<u8>, DomainError>; async fn get(&self, key: &str) -> Result<Vec<u8>, DomainError>;

View File

@@ -20,7 +20,7 @@ use crate::{
}, },
ports::{ ports::{
AuthService, DiaryExporter, DiaryRepository, DocumentParser, EventPublisher, FeedSortBy, AuthService, DiaryExporter, DiaryRepository, DocumentParser, EventPublisher, FeedSortBy,
FollowingFilter, GeneratedToken, ImageStorage, ImportProfileRepository, FollowingFilter, GeneratedToken, ObjectStorage, ImportProfileRepository,
ImportSessionRepository, MetadataClient, MetadataSearchCriteria, MovieProfileRepository, ImportSessionRepository, MetadataClient, MetadataSearchCriteria, MovieProfileRepository,
MovieRepository, PasswordHasher, PersonCommand, PersonQuery, PosterFetcherClient, MovieRepository, PasswordHasher, PersonCommand, PersonQuery, PosterFetcherClient,
ReviewRepository, SearchCommand, SearchPort, StatsRepository, UserProfileFieldsRepository, ReviewRepository, SearchCommand, SearchPort, StatsRepository, UserProfileFieldsRepository,
@@ -351,12 +351,12 @@ impl EventPublisher for NoopEventPublisher {
} }
} }
// ── NoopImageStorage ────────────────────────────────────────────────────────── // ── NoopObjectStorage ──────────────────────────────────────────────────────────
pub struct NoopImageStorage; pub struct NoopObjectStorage;
#[async_trait] #[async_trait]
impl ImageStorage for NoopImageStorage { impl ObjectStorage for NoopObjectStorage {
async fn store(&self, key: &str, _image_bytes: &[u8]) -> Result<String, DomainError> { async fn store(&self, key: &str, _image_bytes: &[u8]) -> Result<String, DomainError> {
Ok(format!("noop://{key}")) Ok(format!("noop://{key}"))
} }

View File

@@ -2,7 +2,7 @@ use std::sync::Arc;
use anyhow::Context; use anyhow::Context;
use domain::ports::{ use domain::ports::{
AuthService, ImageStorage, LocalApContentQuery, MetadataClient, PasswordHasher, AuthService, ObjectStorage, LocalApContentQuery, MetadataClient, PasswordHasher,
PosterFetcherClient, UserProfileFieldsRepository, WatchEventRepository, WebhookTokenRepository, PosterFetcherClient, UserProfileFieldsRepository, WatchEventRepository, WebhookTokenRepository,
}; };
@@ -128,7 +128,7 @@ pub fn build_poster_fetcher() -> anyhow::Result<Arc<dyn PosterFetcherClient>> {
poster_fetcher::create() poster_fetcher::create()
} }
pub fn build_image_storage() -> anyhow::Result<Arc<dyn ImageStorage>> { pub fn build_object_storage() -> anyhow::Result<Arc<dyn ObjectStorage>> {
image_storage::create() image_storage::create()
} }

View File

@@ -13,7 +13,7 @@ pub async fn get_image(
if key.starts_with("http://") || key.starts_with("https://") { if key.starts_with("http://") || key.starts_with("https://") {
return axum::response::Redirect::temporary(&key).into_response(); return axum::response::Redirect::temporary(&key).into_response();
} }
match state.app_ctx.services.image_storage.get(&key).await { match state.app_ctx.services.object_storage.get(&key).await {
Ok(bytes) => { Ok(bytes) => {
let mime = infer::get(&bytes) let mime = infer::get(&bytes)
.map(|t| t.mime_type()) .map(|t| t.mime_type())

View File

@@ -172,7 +172,7 @@ pub async fn get_video(State(state): State<AppState>, Path(id): Path<Uuid>) -> i
match state match state
.app_ctx .app_ctx
.services .services
.image_storage .object_storage
.get_stream(&video_key) .get_stream(&video_key)
.await .await
{ {

View File

@@ -57,7 +57,7 @@ async fn wire_dependencies() -> anyhow::Result<(AppState, axum::Router)> {
let (auth_service, password_hasher) = factory::build_auth_adapters()?; let (auth_service, password_hasher) = factory::build_auth_adapters()?;
let metadata_client = factory::build_metadata_client()?; let metadata_client = factory::build_metadata_client()?;
let poster_fetcher = factory::build_poster_fetcher()?; let poster_fetcher = factory::build_poster_fetcher()?;
let image_storage = factory::build_image_storage()?; let object_storage = factory::build_object_storage()?;
let db = factory::build_database_adapters(&backend, &database_url).await?; let db = factory::build_database_adapters(&backend, &database_url).await?;
let ap_content_repo = db.ap_content; let ap_content_repo = db.ap_content;
@@ -201,7 +201,7 @@ async fn wire_dependencies() -> anyhow::Result<(AppState, axum::Router)> {
password_hasher, password_hasher,
metadata: metadata_client, metadata: metadata_client,
poster_fetcher, poster_fetcher,
image_storage, object_storage,
event_publisher: event_publisher_arc, event_publisher: event_publisher_arc,
diary_exporter: Arc::new(ExportAdapter) as Arc<dyn DiaryExporter>, diary_exporter: Arc::new(ExportAdapter) as Arc<dyn DiaryExporter>,
document_parser: Arc::new(ImporterDocumentParser) as Arc<dyn DocumentParser>, document_parser: Arc::new(ImporterDocumentParser) as Arc<dyn DocumentParser>,

View File

@@ -19,7 +19,7 @@ use domain::{
collections::{PageParams, Paginated}, collections::{PageParams, Paginated},
}, },
ports::{ ports::{
AuthService, DiaryRepository, EventPublisher, GeneratedToken, ImageStorage, MetadataClient, AuthService, DiaryRepository, EventPublisher, GeneratedToken, ObjectStorage, MetadataClient,
MovieRepository, PasswordHasher, PersonCommand, PersonQuery, PosterFetcherClient, MovieRepository, PasswordHasher, PersonCommand, PersonQuery, PosterFetcherClient,
ReviewRepository, SearchCommand, SearchPort, StatsRepository, UserRepository, ReviewRepository, SearchCommand, SearchPort, StatsRepository, UserRepository,
WatchlistRepository, WatchlistRepository,
@@ -194,7 +194,7 @@ impl PosterFetcherClient for Panic {
} }
} }
#[async_trait::async_trait] #[async_trait::async_trait]
impl ImageStorage for Panic { impl ObjectStorage for Panic {
async fn store(&self, _: &str, _: &[u8]) -> Result<String, DomainError> { async fn store(&self, _: &str, _: &[u8]) -> Result<String, DomainError> {
panic!() panic!()
} }
@@ -670,7 +670,7 @@ pub fn make_test_state(auth_service: Arc<dyn AuthService>) -> crate::state::AppS
password_hasher: Arc::clone(&repo) as _, password_hasher: Arc::clone(&repo) as _,
metadata: Arc::clone(&repo) as _, metadata: Arc::clone(&repo) as _,
poster_fetcher: Arc::clone(&repo) as _, poster_fetcher: Arc::clone(&repo) as _,
image_storage: Arc::clone(&repo) as _, object_storage: Arc::clone(&repo) as _,
event_publisher: Arc::clone(&repo) as _, event_publisher: Arc::clone(&repo) as _,
diary_exporter: Arc::clone(&repo) as _, diary_exporter: Arc::clone(&repo) as _,
document_parser: Arc::clone(&repo) as _, document_parser: Arc::clone(&repo) as _,

View File

@@ -18,7 +18,7 @@ use domain::{
SearchQuery, SearchResults, User, SearchQuery, SearchResults, User,
}, },
ports::{ ports::{
AuthService, EventPublisher, GeneratedToken, ImageStorage, MetadataClient, AuthService, EventPublisher, GeneratedToken, ObjectStorage, MetadataClient,
MetadataSearchCriteria, PasswordHasher, PersonCommand, PersonQuery, PosterFetcherClient, MetadataSearchCriteria, PasswordHasher, PersonCommand, PersonQuery, PosterFetcherClient,
SearchCommand, SearchPort, UserRepository, SearchCommand, SearchPort, UserRepository,
}, },
@@ -61,9 +61,9 @@ impl PosterFetcherClient for PanicFetcher {
} }
} }
struct PanicImageStorage; struct PanicObjectStorage;
#[async_trait] #[async_trait]
impl ImageStorage for PanicImageStorage { impl ObjectStorage for PanicObjectStorage {
async fn store(&self, _: &str, _: &[u8]) -> Result<String, DomainError> { async fn store(&self, _: &str, _: &[u8]) -> Result<String, DomainError> {
panic!() panic!()
} }
@@ -430,7 +430,7 @@ async fn test_app() -> Router {
password_hasher: Arc::new(PanicHasher), password_hasher: Arc::new(PanicHasher),
metadata: Arc::new(PanicMeta), metadata: Arc::new(PanicMeta),
poster_fetcher: Arc::new(PanicFetcher), poster_fetcher: Arc::new(PanicFetcher),
image_storage: Arc::new(PanicImageStorage), object_storage: Arc::new(PanicObjectStorage),
event_publisher: Arc::new(NoopEventPublisher), event_publisher: Arc::new(NoopEventPublisher),
diary_exporter: Arc::new(PanicExporter), diary_exporter: Arc::new(PanicExporter),
document_parser: Arc::new(PanicDocumentParser), document_parser: Arc::new(PanicDocumentParser),

View File

@@ -34,7 +34,7 @@ async fn main() -> anyhow::Result<()> {
let (auth_service, password_hasher) = auth::create()?; let (auth_service, password_hasher) = auth::create()?;
let metadata_client = metadata::create()?; let metadata_client = metadata::create()?;
let poster_fetcher = poster_fetcher::create()?; let poster_fetcher = poster_fetcher::create()?;
let image_storage = image_storage::create()?; let object_storage = image_storage::create()?;
let db = db::connect(&database_url, &backend).await?; let db = db::connect(&database_url, &backend).await?;
let (event_publisher_arc, consumer_arc) = event_bus::create(&db.db_pool).await?; let (event_publisher_arc, consumer_arc) = event_bus::create(&db.db_pool).await?;
@@ -100,7 +100,7 @@ async fn main() -> anyhow::Result<()> {
password_hasher, password_hasher,
metadata: metadata_client, metadata: metadata_client,
poster_fetcher, poster_fetcher,
image_storage, object_storage,
event_publisher: event_publisher_arc, event_publisher: event_publisher_arc,
diary_exporter: Arc::new(ExportAdapter) as Arc<dyn DiaryExporter>, diary_exporter: Arc::new(ExportAdapter) as Arc<dyn DiaryExporter>,
document_parser: Arc::new(ImporterDocumentParser) as Arc<dyn DocumentParser>, document_parser: Arc::new(ImporterDocumentParser) as Arc<dyn DocumentParser>,
@@ -155,7 +155,7 @@ async fn main() -> anyhow::Result<()> {
Arc::clone(&ctx.repos.movie_profile), Arc::clone(&ctx.repos.movie_profile),
Arc::clone(&ctx.repos.person_command), Arc::clone(&ctx.repos.person_command),
Arc::clone(&ctx.repos.search_command), Arc::clone(&ctx.repos.search_command),
Arc::clone(&ctx.services.image_storage), Arc::clone(&ctx.services.object_storage),
)) as Arc<dyn EventHandler>; )) as Arc<dyn EventHandler>;
let job = Arc::new(application::jobs::EnrichmentStalenessJob::new(ctx.clone())) let job = Arc::new(application::jobs::EnrichmentStalenessJob::new(ctx.clone()))
as Arc<dyn PeriodicJob>; as Arc<dyn PeriodicJob>;
@@ -170,7 +170,7 @@ async fn main() -> anyhow::Result<()> {
// ── Image conversion ────────────────────────────────────────────────────── // ── Image conversion ──────────────────────────────────────────────────────
let conversion = image_converter::build( let conversion = image_converter::build(
Arc::clone(&ctx.services.image_storage), Arc::clone(&ctx.services.object_storage),
image_ref_command, image_ref_command,
image_ref_query, image_ref_query,
Arc::clone(&ctx.services.event_publisher), Arc::clone(&ctx.services.event_publisher),
@@ -210,13 +210,13 @@ async fn main() -> anyhow::Result<()> {
Arc::clone(&ctx.repos.movie), Arc::clone(&ctx.repos.movie),
Arc::clone(&ctx.services.metadata), Arc::clone(&ctx.services.metadata),
Arc::clone(&ctx.services.poster_fetcher), Arc::clone(&ctx.services.poster_fetcher),
Arc::clone(&ctx.services.image_storage), Arc::clone(&ctx.services.object_storage),
Arc::clone(&ctx.services.event_publisher), Arc::clone(&ctx.services.event_publisher),
3, 3,
)) as Arc<dyn EventHandler>; )) as Arc<dyn EventHandler>;
let cleanup = Arc::new(image_storage::ImageCleanupHandler::new(Arc::clone( let cleanup = Arc::new(image_storage::ImageCleanupHandler::new(Arc::clone(
&ctx.services.image_storage, &ctx.services.object_storage,
))) as Arc<dyn EventHandler>; ))) as Arc<dyn EventHandler>;
#[cfg(not(feature = "federation"))] #[cfg(not(feature = "federation"))]