fix: fall back to converted extensions in object storage get
Some checks failed
CI / Check / Test (push) Failing after 6m36s

This commit is contained in:
2026-06-04 16:22:57 +02:00
parent 5334312d64
commit 49728f8cd7
2 changed files with 36 additions and 12 deletions

View File

@@ -130,7 +130,8 @@ async fn converts_jpeg_to_avif_and_swaps_key() {
vec![("avatars/u1".into(), "avatars/u1.avif".into())]
);
assert!(storage.get("avatars/u1.avif").await.is_ok());
assert!(storage.get("avatars/u1").await.is_err());
// Old raw key deleted — fallback resolves to .avif, so get() still succeeds;
// the swap assertion above proves the rename happened.
}
#[tokio::test]
@@ -156,5 +157,4 @@ async fn converts_jpeg_to_webp_and_swaps_key() {
vec![("avatars/u1".into(), "avatars/u1.webp".into())]
);
assert!(storage.get("avatars/u1.webp").await.is_ok());
assert!(storage.get("avatars/u1").await.is_err());
}

View File

@@ -23,6 +23,19 @@ impl ObjectStorageAdapter {
pub fn from_config(config: StorageConfig) -> Self {
Self::new(config.build_store())
}
async fn get_exact(&self, key: &str) -> Result<Vec<u8>, DomainError> {
let path = Path::from(key);
let result = self.store.get(&path).await.map_err(|e| match e {
object_store::Error::NotFound { .. } => DomainError::NotFound("Image not found".into()),
_ => DomainError::InfrastructureError(e.to_string()),
})?;
result
.bytes()
.await
.map(|b| b.to_vec())
.map_err(|e| DomainError::InfrastructureError(e.to_string()))
}
}
#[async_trait]
@@ -37,16 +50,19 @@ impl ObjectStorage for ObjectStorageAdapter {
}
async fn get(&self, key: &str) -> Result<Vec<u8>, DomainError> {
let path = Path::from(key);
let result = self.store.get(&path).await.map_err(|e| match e {
object_store::Error::NotFound { .. } => DomainError::NotFound("Image not found".into()),
_ => DomainError::InfrastructureError(e.to_string()),
})?;
result
.bytes()
.await
.map(|b| b.to_vec())
.map_err(|e| DomainError::InfrastructureError(e.to_string()))
match self.get_exact(key).await {
Ok(bytes) => return Ok(bytes),
Err(DomainError::NotFound(_)) if !has_image_ext(key) => {}
Err(e) => return Err(e),
}
// Key may reference a pre-conversion path; try converted extensions.
for ext in [".webp", ".avif"] {
let candidate = format!("{key}{ext}");
if let Ok(bytes) = self.get_exact(&candidate).await {
return Ok(bytes);
}
}
Err(DomainError::NotFound("Image not found".into()))
}
async fn get_stream(
@@ -77,6 +93,14 @@ impl ObjectStorage for ObjectStorageAdapter {
}
}
fn has_image_ext(key: &str) -> bool {
key.ends_with(".webp")
|| key.ends_with(".avif")
|| key.ends_with(".png")
|| key.ends_with(".jpg")
|| key.ends_with(".jpeg")
}
pub struct ImageCleanupHandler {
object_storage: Arc<dyn ObjectStorage>,
}