Implemented rustfs in app

This commit is contained in:
2026-06-05 11:05:50 +01:00
parent 2116737987
commit 6b5f18d486
8 changed files with 100 additions and 22 deletions
+2
View File
@@ -1,6 +1,8 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEndpoint_002ESend_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FSourcesCache_003Fc2fa49f697e4d07c58aa3f35484a5103de685287155db8b628f2cdce16b69_003FEndpoint_002ESend_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEndpoint_002ESetup_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FSourcesCache_003F656240c46e44c14018dc6190e77764a3404d76b559ed75474669c553eb6227dd_003FEndpoint_002ESetup_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AIConfiguration_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FSourcesCache_003F17a3a8d481919bdc3f1630b5a7deadb2cd394fa12fff3312e6abe29bfc4_003FIConfiguration_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AIFormFile_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FSourcesCache_003F4d6d6283215de958b1baadc74d6f99ce76f43090cfc86e26e51d845b779450_003FIFormFile_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AIHubClients_00601_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fd6ae5218e3a0462f8a94591a77a902637b750_003Faf_003F6ad738e4_003FIHubClients_00601_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AIHubContext_00601_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F731dbe51d65849048244493644b992cd78910_003Fda_003Fd14ae6ee_003FIHubContext_00601_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ARepositoryBaseOfT_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FSourcesCache_003F5023b5be698d783ffe9f42b2e944a85a7a66b61bc5e19c76c591036343fd16_003FRepositoryBaseOfT_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
+1
View File
@@ -10,6 +10,7 @@
<PackageReference Include="Ardalis.Specification.EntityFrameworkCore" Version="9.3.1" />
<PackageReference Include="AutoMapper" Version="16.0.0" />
<PackageReference Include="AutoMapper.Collection" Version="13.0.0" />
<PackageReference Include="AWSSDK.S3" Version="4.0.24" />
<PackageReference Include="BCrypt.Net-Next" Version="4.1.0" />
<PackageReference Include="FastEndpoints" Version="7.2.0" />
<PackageReference Include="FastEndpoints.Security" Version="7.2.0" />
@@ -7,7 +7,7 @@ using FastEndpoints;
namespace BeReadyBackend.Endpoints.Posts;
public class GetAllPostsEndpoint(PostsRepository postsRepository, UserService userService) : EndpointWithoutRequest<List<GetPostDto>>
public class GetAllPostsEndpoint(PostsRepository postsRepository, UserService userService, StorageService storageService) : EndpointWithoutRequest<List<GetPostDto>>
{
public override void Configure()
{
@@ -20,20 +20,25 @@ public class GetAllPostsEndpoint(PostsRepository postsRepository, UserService us
List<Post> posts = await postsRepository.ListAsync(new GetPostNotMeSpec(userId), ct);
List<GetPostDto> result = posts.Select(x => new GetPostDto
List<GetPostDto> 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);
@@ -16,9 +16,10 @@ public class RandomChallengeProofRequest
public class PatchProofEndpoint(
UsersRepository usersRepository,
UserRandomChallengesRepository userRandomChallengesRepository,
UserService userService,
RandomChallengesRepository randomChallengesRepository,
PostsRepository postsRepository) : Endpoint<RandomChallengeProofRequest>
PostsRepository postsRepository,
UserService userService,
StorageService storageService) : Endpoint<RandomChallengeProofRequest>
{
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);
@@ -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<List<GetUserProofDto>>
{
public override void Configure()
@@ -22,7 +22,10 @@ public class GetAllUserProofsEndpoint(UserRandomChallengesRepository userRandomC
List<UserRandomChallenge> userProofs = await userRandomChallengesRepository.ListAsync(new GetUserProofByUserIdSpec(userId), ct);
List<GetUserProofDto> proofs = userProofs
.Select(mapper.Map<GetUserProofDto>)
.Select(x => new GetUserProofDto
{
Proof = storageService.GetUrl(x.Proof!)
})
.ToList();
await Send.OkAsync(proofs, ct);
+28 -1
View File
@@ -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<UserService>();
builder.Services.AddScoped<AchievementService>();
builder.Services.AddScoped<StorageService>();
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<IAmazonS3>(s3Client);
WebApplication app = builder.Build();
app.UseAuthentication()
.UseAuthorization()
+37
View File
@@ -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<string> 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}";
}
}
@@ -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"
}
}