diff --git a/BeReadyBackend.sln.DotSettings.user b/BeReadyBackend.sln.DotSettings.user index 873db30..a2b99c0 100644 --- a/BeReadyBackend.sln.DotSettings.user +++ b/BeReadyBackend.sln.DotSettings.user @@ -1,6 +1,8 @@  ForceIncluded ForceIncluded + ForceIncluded + ForceIncluded ForceIncluded ForceIncluded ForceIncluded diff --git a/BeReadyBackend/BeReadyBackend.csproj b/BeReadyBackend/BeReadyBackend.csproj index 37a2e8d..5f24e7c 100644 --- a/BeReadyBackend/BeReadyBackend.csproj +++ b/BeReadyBackend/BeReadyBackend.csproj @@ -10,6 +10,7 @@ + diff --git a/BeReadyBackend/Endpoints/Posts/GetAllPostsEndpoint.cs b/BeReadyBackend/Endpoints/Posts/GetAllPostsEndpoint.cs index 520549e..a490ba5 100644 --- a/BeReadyBackend/Endpoints/Posts/GetAllPostsEndpoint.cs +++ b/BeReadyBackend/Endpoints/Posts/GetAllPostsEndpoint.cs @@ -7,7 +7,7 @@ using FastEndpoints; namespace BeReadyBackend.Endpoints.Posts; -public class GetAllPostsEndpoint(PostsRepository postsRepository, UserService userService) : EndpointWithoutRequest> +public class GetAllPostsEndpoint(PostsRepository postsRepository, UserService userService, StorageService storageService) : EndpointWithoutRequest> { public override void Configure() { @@ -20,20 +20,25 @@ public class GetAllPostsEndpoint(PostsRepository postsRepository, UserService us List posts = await postsRepository.ListAsync(new GetPostNotMeSpec(userId), ct); - List result = posts.Select(x => new GetPostDto + List result = posts.Select(x => { - Id = x.Id, - Libelle = x.Libelle, - CreationDate = x.CreationDate, - Likes = x.Likes, - Proof = x.User?.UserRandomChallenges? + string? proof = x.User?.UserRandomChallenges? .SingleOrDefault(u => u.RandomChallenge?.GeneratedAt is not null && DateOnly.FromDateTime(u.RandomChallenge.GeneratedAt.Value) == DateOnly.FromDateTime(x.CreationDate)) - ?.Proof, - UserId = x.UserId, - Username = x.User?.Username, - IsLiked = x.UserPosts?.Count(y => y.UserId == userId) > 0 + ?.Proof; + + return new GetPostDto + { + Id = x.Id, + Libelle = x.Libelle, + CreationDate = x.CreationDate, + Likes = x.Likes, + Proof = proof is not null ? storageService.GetUrl(proof) : null, + UserId = x.UserId, + Username = x.User?.Username, + IsLiked = x.UserPosts?.Count(y => y.UserId == userId) > 0 + }; }).ToList(); await Send.OkAsync(result, ct); diff --git a/BeReadyBackend/Endpoints/RandomChallenges/PatchProofEndpoint.cs b/BeReadyBackend/Endpoints/RandomChallenges/PatchProofEndpoint.cs index f1b8ef6..540aaa8 100644 --- a/BeReadyBackend/Endpoints/RandomChallenges/PatchProofEndpoint.cs +++ b/BeReadyBackend/Endpoints/RandomChallenges/PatchProofEndpoint.cs @@ -16,9 +16,10 @@ public class RandomChallengeProofRequest public class PatchProofEndpoint( UsersRepository usersRepository, UserRandomChallengesRepository userRandomChallengesRepository, - UserService userService, RandomChallengesRepository randomChallengesRepository, - PostsRepository postsRepository) : Endpoint + PostsRepository postsRepository, + UserService userService, + StorageService storageService) : Endpoint { public override void Configure() { @@ -70,13 +71,9 @@ public class PatchProofEndpoint( return; } - // Encodage en base64 - using MemoryStream memoryStream = new(); - await req.Proof.CopyToAsync(memoryStream, ct); - byte[] proofBytes = memoryStream.ToArray(); - - userRandomChallenge.Proof = Convert.ToBase64String(proofBytes); + string key = await storageService.UploadFile(req.Proof, $"random-challenges/{req.RandomChallengeId}/{userId}", ct); + userRandomChallenge.Proof = key; user.TotalChallenge++; UserRandomChallenge? lastChallenge = await userRandomChallengesRepository.SingleOrDefaultAsync(new GetLastRandomChallengeSpec(userId), ct); diff --git a/BeReadyBackend/Endpoints/Users/GetAllUserProofsEndpoint.cs b/BeReadyBackend/Endpoints/Users/GetAllUserProofsEndpoint.cs index abe6d68..e8a96ee 100644 --- a/BeReadyBackend/Endpoints/Users/GetAllUserProofsEndpoint.cs +++ b/BeReadyBackend/Endpoints/Users/GetAllUserProofsEndpoint.cs @@ -7,7 +7,7 @@ using FastEndpoints; namespace BeReadyBackend.Endpoints.Users; -public class GetAllUserProofsEndpoint(UserRandomChallengesRepository userRandomChallengesRepository, UserService userService, AutoMapper.IMapper mapper) +public class GetAllUserProofsEndpoint(UserRandomChallengesRepository userRandomChallengesRepository, UserService userService, StorageService storageService) : EndpointWithoutRequest> { public override void Configure() @@ -22,7 +22,10 @@ public class GetAllUserProofsEndpoint(UserRandomChallengesRepository userRandomC List userProofs = await userRandomChallengesRepository.ListAsync(new GetUserProofByUserIdSpec(userId), ct); List proofs = userProofs - .Select(mapper.Map) + .Select(x => new GetUserProofDto + { + Proof = storageService.GetUrl(x.Proof!) + }) .ToList(); await Send.OkAsync(proofs, ct); diff --git a/BeReadyBackend/Program.cs b/BeReadyBackend/Program.cs index 48133c7..4ea703c 100644 --- a/BeReadyBackend/Program.cs +++ b/BeReadyBackend/Program.cs @@ -1,9 +1,10 @@ +using Amazon.S3; +using Amazon.S3.Model; using AutoMapper; using AutoMapper.EquivalencyExpression; using BeReadyBackend; using BeReadyBackend.Hubs; using BeReadyBackend.MappingProfiles; -using BeReadyBackend.Models; using FastEndpoints; using FastEndpoints.Swagger; using FastEndpoints.Security; @@ -53,6 +54,7 @@ builder.Services.AddHttpContextAccessor(); builder.Services.AddScoped(); builder.Services.AddScoped(); +builder.Services.AddScoped(); MapperConfiguration mappingConfig = new(mc => { @@ -65,6 +67,31 @@ MapperConfiguration mappingConfig = new(mc => AutoMapper.IMapper mapper = mappingConfig.CreateMapper(); builder.Services.AddSingleton(mapper); +// RUSTFS +IConfigurationSection config = builder.Configuration.GetSection("RustFS"); + +AmazonS3Client s3Client = new( + config["AccessKey"], + config["SecretKey"], + new AmazonS3Config + { + ServiceURL = config["ServiceUrl"], + ForcePathStyle = true + } +); + +ListBucketsResponse? buckets = await s3Client.ListBucketsAsync(); +bool exist = buckets?.Buckets?.Any(x => x.BucketName == config["BucketName"]) == true; +if (!exist) +{ + await s3Client.PutBucketAsync(new PutBucketRequest + { + BucketName = config["BucketName"] + }); +} + +builder.Services.AddSingleton(s3Client); + WebApplication app = builder.Build(); app.UseAuthentication() .UseAuthorization() diff --git a/BeReadyBackend/Services/StorageService.cs b/BeReadyBackend/Services/StorageService.cs new file mode 100644 index 0000000..95683e4 --- /dev/null +++ b/BeReadyBackend/Services/StorageService.cs @@ -0,0 +1,37 @@ +using Amazon.S3; +using Amazon.S3.Model; + +namespace BeReadyBackend.Services; + +public class StorageService(IAmazonS3 amazonS3, IConfiguration config) +{ + private readonly string _bucket = config["RustFs:BucketName"]!; + private readonly string _url = config["RustFs:ServiceUrl"]!; + + public async Task UploadFile(IFormFile file, string path, CancellationToken ct) + { + if (file.Length == 0) throw new Exception("Fichier vide"); + + string key = $"{path}/{Guid.NewGuid()}"; + + using MemoryStream memoryStream = new(); + await file.CopyToAsync(memoryStream, ct); + memoryStream.Position = 0; + + PutObjectRequest uploadRequest = new() + { + BucketName = _bucket, + ContentType = file.ContentType, + InputStream = memoryStream, + Key = key, + }; + + await amazonS3.PutObjectAsync(uploadRequest, ct); + return key; + } + + public string? GetUrl(string key) + { + return string.IsNullOrEmpty(key) ? null : $"{_url}/{_bucket}/{key}"; + } +} \ No newline at end of file diff --git a/BeReadyBackend/appsettings.Development.json b/BeReadyBackend/appsettings.Development.json index 0c208ae..35669f2 100644 --- a/BeReadyBackend/appsettings.Development.json +++ b/BeReadyBackend/appsettings.Development.json @@ -4,5 +4,11 @@ "Default": "Information", "Microsoft.AspNetCore": "Warning" } + }, + "RustFS": { + "ServiceUrl": "https://stockage.sanchezvende.fr", + "AccessKey": "Admin_Beready_Exam2026", + "SecretKey": "4dm1n-Pr0j_2026-B3r34d7", + "BucketName": "images" } }