using System.Collections.Concurrent; using System.Net; using System.Text; using Amazon.Runtime; using Amazon.S3; using Amazon.S3.Model; using InnovEnergy.Lib.S3Utils.DataTypes; using InnovEnergy.Lib.Utils; using S3Bucket = InnovEnergy.Lib.S3Utils.DataTypes.S3Bucket; using S3Region = InnovEnergy.Lib.S3Utils.DataTypes.S3Region; namespace InnovEnergy.Lib.S3Utils; public static class S3 { private static readonly ConcurrentDictionary S3ClientCache = new(); // QOL method public static S3Bucket Bucket(this S3Region region, String name) => new ( Name: name, Region: region ); // QOL method public static S3Url Path(this S3Bucket bucket, String path) => new ( Bucket: bucket, Path: path ); public static IAsyncEnumerable ListObjects(this S3Bucket bucket) => ListObjects(bucket, null); public static IAsyncEnumerable ListObjects(this S3Bucket bucket, String? pathPrefix) { return bucket .Region .GetS3Client() .Paginators .ListObjectsV2(new() { BucketName = bucket.Name, Prefix = pathPrefix }) .S3Objects .Select(o => new S3Url(o.Key, bucket)); } public static Task ListAllBuckets(this S3Region region) { return region .GetS3Client() .ListBucketsAsync(); } public static Task PutObject(this S3Url path, String data, Encoding encoding) => path.PutObject(encoding.GetBytes(data)); public static Task PutObject(this S3Url path, String data) => path.PutObject(data, Encoding.UTF8); public static Task PutObject(this S3Url path, Byte[] data) => path.PutObject(new MemoryStream(data)); public static async Task PutObject(this S3Url path, Stream data) { var request = new PutObjectRequest { BucketName = path.Bucket.Name, Key = path.Path, InputStream = data }; var response = await path .Bucket .Region .GetS3Client() .PutObjectAsync(request); return response.HttpStatusCode == HttpStatusCode.OK; } public static Task GetObjectAsString(this S3Url path) => GetObjectAsString(path, Encoding.UTF8); public static async Task GetObjectAsString(this S3Url path, Encoding encoding) { await using var stream = await GetObjectAsStream(path); using var reader = new StreamReader(stream, encoding); return await reader.ReadToEndAsync(); } public static async Task GetObjectAsStream(this S3Url path) { var request = new GetObjectRequest { BucketName = path.Bucket.Name, Key = path.Path }; var response = await path .Bucket .Region .GetS3Client() .GetObjectAsync(request); return response.ResponseStream; } // public static async Task CheckRoleExists(this S3Region region, String roleId) // { // // // var response = await region // .GetIamClient() // .GetRoleAsync(new GetRoleRequest(){RoleName = roleId}); // return response.HttpStatusCode != HttpStatusCode.NotFound; // } public static async Task> GetObject(this S3Url url) { // beautiful await using stream soup... await using var stream = await url.GetObjectAsStream(); using var memoryStream = new MemoryStream(); await stream.CopyToAsync(memoryStream); return memoryStream.ToArray(); } public static IAsyncEnumerable GetObjectLineByLine(this S3Url url) => GetObjectLineByLine(url, Encoding.UTF8); public static async IAsyncEnumerable GetObjectLineByLine(this S3Url url, Encoding encoding) { await using var stream = await url.GetObjectAsStream(); using var reader = new StreamReader(stream, encoding); while (true) { var line = await reader.ReadLineAsync(); if (line is not null) yield return line; else yield break; } } public static async Task PutBucket(this S3Region region, string name) { var request = new PutBucketRequest { BucketName = name }; var response = await region.GetS3Client().PutBucketAsync(request); if (response.HttpStatusCode == HttpStatusCode.OK) { // Define CORS configuration rules var corsConfiguration = new CORSConfiguration { Rules = new List { new CORSRule { AllowedHeaders = new List { "*" }, AllowedMethods = new List { "GET", "HEAD" }, AllowedOrigins = new List { "*" }, ExposeHeaders = new List() // Empty list as per your settings } } }; // Create a PutCORSConfigurationRequest var putCorsRequest = new PutCORSConfigurationRequest { BucketName = name, Configuration = corsConfiguration }; // Set the CORS configuration for the bucket var corsResponse = await region.GetS3Client().PutCORSConfigurationAsync(putCorsRequest); if (corsResponse.HttpStatusCode == HttpStatusCode.OK) { return region.Bucket(name); } else { Console.WriteLine("Failed to set CORS configuration."); return null; } } else { Console.WriteLine("Failed to create bucket."); return null; } } // public static async Task PutBucket(this S3Region region, String name) // { // var request = new PutBucketRequest { BucketName = name }; // // var response = await region // .GetS3Client() // .PutBucketAsync(request); // // return response.HttpStatusCode switch // { // HttpStatusCode.OK => region.Bucket(name), // _ => null // }; // } public static async Task PutCors(this S3Bucket bucket, CORSConfiguration corsConfiguration) { var request = new PutCORSConfigurationRequest { BucketName = bucket.Name, Configuration = corsConfiguration }; var response = await bucket .GetS3Client() .PutCORSConfigurationAsync(request); return response.HttpStatusCode == HttpStatusCode.OK; } public static async Task DeleteBucket(this S3Bucket bucket) { var request = new DeleteBucketRequest { BucketName = bucket.Name }; var response = await bucket .GetS3Client() .DeleteBucketAsync(request); return response.HttpStatusCode == HttpStatusCode.OK; } private static AmazonS3Client GetS3Client(this S3Url url ) => url.Bucket.GetS3Client(); private static AmazonS3Client GetS3Client(this S3Bucket bucket) => bucket.Region.GetS3Client(); private static AmazonS3Client GetS3Client(this S3Region region) { return S3ClientCache.GetOrAdd(region, CreateS3Client); // Memoize } private static AmazonS3Client CreateS3Client(S3Region region) => new ( credentials: new BasicAWSCredentials(region.Credentials.Key, region.Credentials.Secret), clientConfig: new() { ServiceURL = region.Name.EnsureStartsWith("https://"), ForcePathStyle = true, } ); }