using System.Net; using Amazon.S3; using Amazon.S3.Model; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using mws.backend.dotnet.application.Common; using mws.backend.dotnet.application.Documents; namespace mws.backend.dotnet.infrastructure.Storage; public class S3DocumentContentStore( IAmazonS3 s3, IOptions opt, ILogger log) : IDocumentContentStore { private string Bucket => opt.Value.Bucket; private static string Key(Guid projectId, Guid documentId) => $"{projectId:N}/{documentId:N}.html"; public Task EnsureBucketAsync(Guid projectId, CancellationToken ct) => Task.CompletedTask; public async Task UploadAsync(Guid projectId, Guid documentId, string contentType, Stream body, CancellationToken ct) { try { await s3.PutObjectAsync(new PutObjectRequest { BucketName = Bucket, Key = Key(projectId, documentId), InputStream = body, ContentType = contentType, AutoCloseStream = false, }, ct); } catch (AmazonS3Exception ex) { log.LogError(ex, "MinIO upload failed for document {DocumentId}", documentId); throw new StorageUnavailableException("Document upload failed", ex); } } public async Task DownloadAsync(Guid projectId, Guid documentId, CancellationToken ct) { try { var response = await s3.GetObjectAsync(Bucket, Key(projectId, documentId), ct); return response.ResponseStream; } catch (AmazonS3Exception ex) when (ex.StatusCode == HttpStatusCode.NotFound) { return Stream.Null; } catch (AmazonS3Exception ex) { log.LogError(ex, "MinIO download failed for document {DocumentId}", documentId); throw new StorageUnavailableException("Document download failed", ex); } } public async Task ExistsAsync(Guid projectId, Guid documentId, CancellationToken ct) { try { await s3.GetObjectMetadataAsync(Bucket, Key(projectId, documentId), ct); return true; } catch (AmazonS3Exception ex) when (ex.StatusCode == HttpStatusCode.NotFound) { return false; } } public async Task DeleteAsync(Guid projectId, Guid documentId, CancellationToken ct) { try { await s3.DeleteObjectAsync(Bucket, Key(projectId, documentId), ct); } catch (AmazonS3Exception ex) { log.LogWarning(ex, "MinIO delete failed for document {DocumentId}", documentId); } } }