From 04faddbb9a046a5f9d9c9360214741a0750541e4 Mon Sep 17 00:00:00 2001 From: Egamorf Date: Sat, 24 May 2025 17:14:39 +0200 Subject: [PATCH] Add backend upload file --- .../GameIdeas.Shared/Enum/BucketType.cs | 9 ++++ .../Exceptions/BucketTypeMissingException.cs | 3 ++ .../EnvironmentVariableMissingException.cs | 4 ++ .../Extensions/BucketExtension.cs | 19 +++++++ .../Controllers/FileController.cs | 27 ++++++++++ .../GameIdeas.WebAPI/GameIdeas.WebAPI.csproj | 1 + .../Services/Files/FileService.cs | 54 +++++++++++++++++++ .../Services/Files/IFileService.cs | 8 +++ 8 files changed, 125 insertions(+) create mode 100644 src/GameIdeas/GameIdeas.Shared/Enum/BucketType.cs create mode 100644 src/GameIdeas/GameIdeas.Shared/Exceptions/BucketTypeMissingException.cs create mode 100644 src/GameIdeas/GameIdeas.Shared/Exceptions/EnvironmentVariableMissingException.cs create mode 100644 src/GameIdeas/GameIdeas.Shared/Extensions/BucketExtension.cs create mode 100644 src/GameIdeas/Server/GameIdeas.WebAPI/Controllers/FileController.cs create mode 100644 src/GameIdeas/Server/GameIdeas.WebAPI/Services/Files/FileService.cs create mode 100644 src/GameIdeas/Server/GameIdeas.WebAPI/Services/Files/IFileService.cs diff --git a/src/GameIdeas/GameIdeas.Shared/Enum/BucketType.cs b/src/GameIdeas/GameIdeas.Shared/Enum/BucketType.cs new file mode 100644 index 0000000..fadf209 --- /dev/null +++ b/src/GameIdeas/GameIdeas.Shared/Enum/BucketType.cs @@ -0,0 +1,9 @@ +namespace GameIdeas.Shared.Enum; + +public enum BucketType +{ + GameIcon, + GameCover, + GameMedia, + UserIcon +} diff --git a/src/GameIdeas/GameIdeas.Shared/Exceptions/BucketTypeMissingException.cs b/src/GameIdeas/GameIdeas.Shared/Exceptions/BucketTypeMissingException.cs new file mode 100644 index 0000000..b924fd9 --- /dev/null +++ b/src/GameIdeas/GameIdeas.Shared/Exceptions/BucketTypeMissingException.cs @@ -0,0 +1,3 @@ +namespace GameIdeas.Shared.Exceptions; + +public class BucketTypeMissingException(string message) : Exception(message); diff --git a/src/GameIdeas/GameIdeas.Shared/Exceptions/EnvironmentVariableMissingException.cs b/src/GameIdeas/GameIdeas.Shared/Exceptions/EnvironmentVariableMissingException.cs new file mode 100644 index 0000000..4cc847b --- /dev/null +++ b/src/GameIdeas/GameIdeas.Shared/Exceptions/EnvironmentVariableMissingException.cs @@ -0,0 +1,4 @@ +namespace GameIdeas.Shared.Exceptions; + +public class EnvironmentVariableMissingException(string valueName) : + Exception($"Missing environment variable with key: {valueName}"); diff --git a/src/GameIdeas/GameIdeas.Shared/Extensions/BucketExtension.cs b/src/GameIdeas/GameIdeas.Shared/Extensions/BucketExtension.cs new file mode 100644 index 0000000..85959ad --- /dev/null +++ b/src/GameIdeas/GameIdeas.Shared/Extensions/BucketExtension.cs @@ -0,0 +1,19 @@ +using GameIdeas.Shared.Enum; +using GameIdeas.Shared.Exceptions; + +namespace GameIdeas.Shared.Extensions; + +public static class BucketExtension +{ + public static string GetBucket(this BucketType bucketType) + { + return bucketType switch + { + BucketType.GameIcon => "icons", + BucketType.GameCover => "covers", + BucketType.GameMedia => "medias", + BucketType.UserIcon => "users", + _ => throw new BucketTypeMissingException($"Bucket type {bucketType} not exist") + }; + } +} diff --git a/src/GameIdeas/Server/GameIdeas.WebAPI/Controllers/FileController.cs b/src/GameIdeas/Server/GameIdeas.WebAPI/Controllers/FileController.cs new file mode 100644 index 0000000..6228139 --- /dev/null +++ b/src/GameIdeas/Server/GameIdeas.WebAPI/Controllers/FileController.cs @@ -0,0 +1,27 @@ +using GameIdeas.Shared.Enum; +using GameIdeas.WebAPI.Services.Files; +using Microsoft.AspNetCore.Mvc; + +namespace GameIdeas.WebAPI.Controllers; + +public class FileController( + IFileService fileService, + ILoggerFactory loggerFactory) : Controller +{ + private readonly ILogger Logger = loggerFactory.CreateLogger(); + + [HttpPost] + [RequestSizeLimit(100_000_000_000)] // 100 GB + public async Task UploadFile([FromForm] IFormFile file, [FromQuery] BucketType type) + { + try + { + return Ok(await fileService.UploadFile(file, type)); + } + catch (Exception e) + { + Logger.LogError(e, "Failed uploading files"); + return StatusCode(500, e.Message); + } + } +} diff --git a/src/GameIdeas/Server/GameIdeas.WebAPI/GameIdeas.WebAPI.csproj b/src/GameIdeas/Server/GameIdeas.WebAPI/GameIdeas.WebAPI.csproj index 32e89b5..45700d1 100644 --- a/src/GameIdeas/Server/GameIdeas.WebAPI/GameIdeas.WebAPI.csproj +++ b/src/GameIdeas/Server/GameIdeas.WebAPI/GameIdeas.WebAPI.csproj @@ -20,6 +20,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/src/GameIdeas/Server/GameIdeas.WebAPI/Services/Files/FileService.cs b/src/GameIdeas/Server/GameIdeas.WebAPI/Services/Files/FileService.cs new file mode 100644 index 0000000..187cc2d --- /dev/null +++ b/src/GameIdeas/Server/GameIdeas.WebAPI/Services/Files/FileService.cs @@ -0,0 +1,54 @@ +using Minio; +using GameIdeas.Shared.Exceptions; +using Minio.DataModel.Args; +using GameIdeas.Shared.Enum; +using GameIdeas.Shared.Extensions; + +namespace GameIdeas.WebAPI.Services.Files; + +public class FileService : IFileService +{ + private readonly IMinioClient MinioClient; + + public FileService() + { + var minioEndpoint = Environment.GetEnvironmentVariable("MINIO_ENDPOINT") + ?? throw new EnvironmentVariableMissingException("MINIO_ENDPOINT"); + + var minioAccessKey = Environment.GetEnvironmentVariable("MINIO_ACCESS_KEY") + ?? throw new EnvironmentVariableMissingException("MINIO_ACCESS_KEY"); + + var minioSecretKey= Environment.GetEnvironmentVariable("MINIO_SECRET_KEY") + ?? throw new EnvironmentVariableMissingException("MINIO_SECRET_KEY"); + + MinioClient = new MinioClient() + .WithEndpoint(minioEndpoint) + .WithCredentials(minioAccessKey, minioSecretKey) + .Build(); + } + + public async Task UploadFile(IFormFile file, BucketType bucketType) + { + var fileName = Path.GetFileName(file.FileName); + var bucket = bucketType.GetBucket(); + + bool found = await MinioClient.BucketExistsAsync(new BucketExistsArgs().WithBucket(bucket)); + if (!found) + { + await MinioClient.MakeBucketAsync(new MakeBucketArgs().WithBucket(bucket)); + } + + using var stream = file.OpenReadStream(); + + var args = new PutObjectArgs() + .WithBucket(bucket) + .WithObject(fileName) + .WithStreamData(stream) + .WithObjectSize(file.Length) + .WithContentType(file.ContentType); + + await MinioClient.PutObjectAsync(args); + + return $"/{bucket}/{fileName}"; + } +} diff --git a/src/GameIdeas/Server/GameIdeas.WebAPI/Services/Files/IFileService.cs b/src/GameIdeas/Server/GameIdeas.WebAPI/Services/Files/IFileService.cs new file mode 100644 index 0000000..27dde6b --- /dev/null +++ b/src/GameIdeas/Server/GameIdeas.WebAPI/Services/Files/IFileService.cs @@ -0,0 +1,8 @@ +using GameIdeas.Shared.Enum; + +namespace GameIdeas.WebAPI.Services.Files; + +public interface IFileService +{ + Task UploadFile(IFormFile file, BucketType bucketType); +}