From e0a4e88eca1f3b3555e6e93ad8696fa1adb82358 Mon Sep 17 00:00:00 2001 From: sanchezvem Date: Sat, 21 Feb 2026 18:32:32 +0100 Subject: [PATCH] Created one part of all endpoints to admin the user --- BeReadyBackend.sln.DotSettings.user | 2 + BeReadyBackend/BeReadyBackend.csproj | 2 + BeReadyBackend/DTO/Users/CreateUserDto.cs | 10 +++++ .../DTO/Users/GetUserChallengeDto.cs | 6 +++ BeReadyBackend/DTO/Users/GetUserDetailsDto.cs | 13 ++++++ BeReadyBackend/DTO/Users/GetUserDto.cs | 12 +++++ BeReadyBackend/DTO/Users/GetUserProofDto.cs | 7 +++ BeReadyBackend/DTO/Users/GetUserStatsDto.cs | 12 +++++ .../DTO/Users/PatchUserDesignationDto.cs | 7 +++ .../DTO/Users/PatchUserPasswordDto.cs | 7 +++ BeReadyBackend/DTO/Users/UpdateUserDto.cs | 10 +++++ .../Endpoints/Users/CreateUserEndpoint.cs | 45 +++++++++++++++++++ .../Endpoints/Users/DeleteUserEndpoint.cs | 34 ++++++++++++++ .../Users/GetOverallRankingEndpoint.cs | 20 +++++++++ .../Users/PatchUserDesignationEndpoint.cs | 33 ++++++++++++++ .../Users/PatchUserPasswordEndpoint.cs | 34 ++++++++++++++ .../Endpoints/Users/UpdateUserEndpoint.cs | 40 +++++++++++++++++ .../MappingProfiles/DtoToEntityMappings.cs | 7 +++ .../MappingProfiles/EntityToDtoMappings.cs | 13 ++++++ .../Users/GetUserByCriteriaSpec.cs | 13 ++++++ .../Users/GetUsersByScoreSpec.cs | 13 ++++++ 21 files changed, 340 insertions(+) create mode 100644 BeReadyBackend.sln.DotSettings.user create mode 100644 BeReadyBackend/DTO/Users/CreateUserDto.cs create mode 100644 BeReadyBackend/DTO/Users/GetUserChallengeDto.cs create mode 100644 BeReadyBackend/DTO/Users/GetUserDetailsDto.cs create mode 100644 BeReadyBackend/DTO/Users/GetUserDto.cs create mode 100644 BeReadyBackend/DTO/Users/GetUserProofDto.cs create mode 100644 BeReadyBackend/DTO/Users/GetUserStatsDto.cs create mode 100644 BeReadyBackend/DTO/Users/PatchUserDesignationDto.cs create mode 100644 BeReadyBackend/DTO/Users/PatchUserPasswordDto.cs create mode 100644 BeReadyBackend/DTO/Users/UpdateUserDto.cs create mode 100644 BeReadyBackend/Endpoints/Users/CreateUserEndpoint.cs create mode 100644 BeReadyBackend/Endpoints/Users/DeleteUserEndpoint.cs create mode 100644 BeReadyBackend/Endpoints/Users/GetOverallRankingEndpoint.cs create mode 100644 BeReadyBackend/Endpoints/Users/PatchUserDesignationEndpoint.cs create mode 100644 BeReadyBackend/Endpoints/Users/PatchUserPasswordEndpoint.cs create mode 100644 BeReadyBackend/Endpoints/Users/UpdateUserEndpoint.cs create mode 100644 BeReadyBackend/Specifications/Users/GetUserByCriteriaSpec.cs create mode 100644 BeReadyBackend/Specifications/Users/GetUsersByScoreSpec.cs diff --git a/BeReadyBackend.sln.DotSettings.user b/BeReadyBackend.sln.DotSettings.user new file mode 100644 index 0000000..95ef1f5 --- /dev/null +++ b/BeReadyBackend.sln.DotSettings.user @@ -0,0 +1,2 @@ + + ForceIncluded \ No newline at end of file diff --git a/BeReadyBackend/BeReadyBackend.csproj b/BeReadyBackend/BeReadyBackend.csproj index 3b6044a..654e74a 100644 --- a/BeReadyBackend/BeReadyBackend.csproj +++ b/BeReadyBackend/BeReadyBackend.csproj @@ -10,6 +10,7 @@ + @@ -20,6 +21,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/BeReadyBackend/DTO/Users/CreateUserDto.cs b/BeReadyBackend/DTO/Users/CreateUserDto.cs new file mode 100644 index 0000000..9d2cb82 --- /dev/null +++ b/BeReadyBackend/DTO/Users/CreateUserDto.cs @@ -0,0 +1,10 @@ +namespace BeReadyBackend.DTO.Users; + +public class CreateUserDto +{ + public string? FirstName { get; set; } + public string? Name { get; set; } + public string? Username { get; set; } + public string? Email { get; set; } + public string? Password { get; set; } +} \ No newline at end of file diff --git a/BeReadyBackend/DTO/Users/GetUserChallengeDto.cs b/BeReadyBackend/DTO/Users/GetUserChallengeDto.cs new file mode 100644 index 0000000..a69e801 --- /dev/null +++ b/BeReadyBackend/DTO/Users/GetUserChallengeDto.cs @@ -0,0 +1,6 @@ +namespace BeReadyBackend.DTO.Users; + +public class GetUserChallengeDto +{ + +} \ No newline at end of file diff --git a/BeReadyBackend/DTO/Users/GetUserDetailsDto.cs b/BeReadyBackend/DTO/Users/GetUserDetailsDto.cs new file mode 100644 index 0000000..c51b67e --- /dev/null +++ b/BeReadyBackend/DTO/Users/GetUserDetailsDto.cs @@ -0,0 +1,13 @@ +namespace BeReadyBackend.DTO.Users; + +public class GetUserDetailsDto +{ + public int Id { get; set; } + public string? FirstName { get; set; } + public string? Name { get; set; } + public string? Username { get; set; } + public string? Email { get; set; } + public int DesignationId { get; set; } + public DateTime CreationDate { get; set; } + public GetUserStatsDto? GetUserStatsDto { get; set; } +} \ No newline at end of file diff --git a/BeReadyBackend/DTO/Users/GetUserDto.cs b/BeReadyBackend/DTO/Users/GetUserDto.cs new file mode 100644 index 0000000..aedfd59 --- /dev/null +++ b/BeReadyBackend/DTO/Users/GetUserDto.cs @@ -0,0 +1,12 @@ +namespace BeReadyBackend.DTO.Users; + +public class GetUserDto +{ + public int Id { get; set; } + public string? FirstName { get; set; } + public string? Name { get; set; } + public string? Username { get; set; } + public int DesignationId { get; set; } + public GetUserStatsDto? GetUserStatsDto { get; set; } + +} \ No newline at end of file diff --git a/BeReadyBackend/DTO/Users/GetUserProofDto.cs b/BeReadyBackend/DTO/Users/GetUserProofDto.cs new file mode 100644 index 0000000..8515b8c --- /dev/null +++ b/BeReadyBackend/DTO/Users/GetUserProofDto.cs @@ -0,0 +1,7 @@ +namespace BeReadyBackend.DTO.Users; + +public class GetUserProofDto +{ + public int Id { get; set; } + public string? Proof { get; set; } +} \ No newline at end of file diff --git a/BeReadyBackend/DTO/Users/GetUserStatsDto.cs b/BeReadyBackend/DTO/Users/GetUserStatsDto.cs new file mode 100644 index 0000000..8d238a5 --- /dev/null +++ b/BeReadyBackend/DTO/Users/GetUserStatsDto.cs @@ -0,0 +1,12 @@ +namespace BeReadyBackend.DTO.Users; + +public class GetUserStatsDto +{ + public int Id { get; set; } + public int Score { get; set; } + public int TotalWin { get; set; } + public int TotalChallenge { get; set; } + public int TotalPodium { get; set; } + public int TotalBonusChallenge { get; set; } + public int Series { get; set; } +} \ No newline at end of file diff --git a/BeReadyBackend/DTO/Users/PatchUserDesignationDto.cs b/BeReadyBackend/DTO/Users/PatchUserDesignationDto.cs new file mode 100644 index 0000000..124615a --- /dev/null +++ b/BeReadyBackend/DTO/Users/PatchUserDesignationDto.cs @@ -0,0 +1,7 @@ +namespace BeReadyBackend.DTO.Users; + +public class PatchUserDesignationDto +{ + public int Id { get; set; } + public int DesignationId { get; set; } +} \ No newline at end of file diff --git a/BeReadyBackend/DTO/Users/PatchUserPasswordDto.cs b/BeReadyBackend/DTO/Users/PatchUserPasswordDto.cs new file mode 100644 index 0000000..8682908 --- /dev/null +++ b/BeReadyBackend/DTO/Users/PatchUserPasswordDto.cs @@ -0,0 +1,7 @@ +namespace BeReadyBackend.DTO.Users; + +public class PatchUserPasswordDto +{ + public int Id { get; set; } + public string? Password { get; set; } +} \ No newline at end of file diff --git a/BeReadyBackend/DTO/Users/UpdateUserDto.cs b/BeReadyBackend/DTO/Users/UpdateUserDto.cs new file mode 100644 index 0000000..cd55129 --- /dev/null +++ b/BeReadyBackend/DTO/Users/UpdateUserDto.cs @@ -0,0 +1,10 @@ +namespace BeReadyBackend.DTO.Users; + +public class UpdateUserDto +{ + public int Id { get; set; } + public string? FirstName { get; set; } + public string? Name { get; set; } + public string? Username { get; set; } + public string? Email { get; set; } +} \ No newline at end of file diff --git a/BeReadyBackend/Endpoints/Users/CreateUserEndpoint.cs b/BeReadyBackend/Endpoints/Users/CreateUserEndpoint.cs new file mode 100644 index 0000000..836add6 --- /dev/null +++ b/BeReadyBackend/Endpoints/Users/CreateUserEndpoint.cs @@ -0,0 +1,45 @@ +using BeReadyBackend.DTO.Users; +using BeReadyBackend.Models; +using BeReadyBackend.Repositories; +using BeReadyBackend.Specifications.Users; +using FastEndpoints; +using PasswordGenerator; + +namespace BeReadyBackend.Endpoints.Users; + +public class CreateUserEndpoint(UsersRepository usersRepository, AutoMapper.IMapper mapper) : Endpoint +{ + public override void Configure() + { + Post("/Users/"); + AllowAnonymous(); + } + + public override async Task HandleAsync(CreateUserDto req, CancellationToken ct) + { + User? user = await usersRepository.FirstOrDefaultAsync(new GetUserByCriteriaSpec(req.Username!, req.Email!, null), ct); + + if (user is not null) + { + await Send.StringAsync("Un utilisateur possède déjà ce pseudo ou cette email", 400, cancellation: ct); + return; + } + + string salt = new Password().IncludeLowercase().IncludeUppercase().IncludeNumeric().LengthRequired(24).Next(); + + user = mapper.Map(req); + + user.CreationDate = DateTime.Now; + user.Salt = salt; + user.Password = BCrypt.Net.BCrypt.HashPassword(req.Password + salt); + user.Score = 0; + user.TotalWin = 0; + user.TotalChallenge = 0; + user.TotalPodium = 0; + user.TotalBonusChallenge = 0; + user.Series = 0; + + await usersRepository.AddAsync(user, ct); + await Send.OkAsync(ct); + } +} \ No newline at end of file diff --git a/BeReadyBackend/Endpoints/Users/DeleteUserEndpoint.cs b/BeReadyBackend/Endpoints/Users/DeleteUserEndpoint.cs new file mode 100644 index 0000000..1679207 --- /dev/null +++ b/BeReadyBackend/Endpoints/Users/DeleteUserEndpoint.cs @@ -0,0 +1,34 @@ +using BeReadyBackend.Models; +using BeReadyBackend.Repositories; +using BeReadyBackend.Specifications.Users; +using FastEndpoints; + +namespace BeReadyBackend.Endpoints.Users; + +public class DeleteUserRequest +{ + public int Id { get; set; } +} + +public class DeleteUserEndpoint(UsersRepository usersRepository) : Endpoint +{ + public override void Configure() + { + Delete("/Users/{@id}", x=>new {x.Id}); + AllowAnonymous(); + } + + public override async Task HandleAsync(DeleteUserRequest req, CancellationToken ct) + { + User? user = await usersRepository.SingleOrDefaultAsync(new GetUserByIdSpec(req.Id), ct); + + if (user == null) + { + await Send.NotFoundAsync(ct); + return; + } + + await usersRepository.DeleteAsync(user, ct); + await Send.OkAsync(ct); + } +} \ No newline at end of file diff --git a/BeReadyBackend/Endpoints/Users/GetOverallRankingEndpoint.cs b/BeReadyBackend/Endpoints/Users/GetOverallRankingEndpoint.cs new file mode 100644 index 0000000..44fdf0d --- /dev/null +++ b/BeReadyBackend/Endpoints/Users/GetOverallRankingEndpoint.cs @@ -0,0 +1,20 @@ +using BeReadyBackend.DTO.Users; +using BeReadyBackend.Repositories; +using BeReadyBackend.Specifications.Users; +using FastEndpoints; + +namespace BeReadyBackend.Endpoints.Users; + +public class GetOverallRankingEndpoint(UsersRepository usersRepository) : EndpointWithoutRequest> +{ + public override void Configure() + { + Get("/OverallRanking/"); + AllowAnonymous(); + } + + public override async Task HandleAsync(CancellationToken ct) + { + await Send.OkAsync(await usersRepository.ProjectToListAsync(new GetUsersByScoreSpec(), ct), ct); + } +} \ No newline at end of file diff --git a/BeReadyBackend/Endpoints/Users/PatchUserDesignationEndpoint.cs b/BeReadyBackend/Endpoints/Users/PatchUserDesignationEndpoint.cs new file mode 100644 index 0000000..a48e085 --- /dev/null +++ b/BeReadyBackend/Endpoints/Users/PatchUserDesignationEndpoint.cs @@ -0,0 +1,33 @@ +using BeReadyBackend.DTO.Users; +using BeReadyBackend.Models; +using BeReadyBackend.Repositories; +using BeReadyBackend.Specifications.Users; +using FastEndpoints; +using PasswordGenerator; + +namespace BeReadyBackend.Endpoints.Users; + +public class PatchUserDesignationEndpoint(UsersRepository usersRepository, AutoMapper.IMapper mapper) : Endpoint +{ + public override void Configure() + { + Patch("/Users/{@Id}/Designation", x => new {x.Id}); + AllowAnonymous(); + } + + public override async Task HandleAsync(PatchUserDesignationDto req, CancellationToken ct) + { + User? user = await usersRepository.SingleOrDefaultAsync(new GetUserByIdSpec(req.Id), ct); + + if (user is null) + { + await Send.NotFoundAsync(ct); + return; + } + + mapper.Map(req, user); + + await usersRepository.SaveChangesAsync(ct); + await Send.OkAsync(ct); + } +} \ No newline at end of file diff --git a/BeReadyBackend/Endpoints/Users/PatchUserPasswordEndpoint.cs b/BeReadyBackend/Endpoints/Users/PatchUserPasswordEndpoint.cs new file mode 100644 index 0000000..7ad833b --- /dev/null +++ b/BeReadyBackend/Endpoints/Users/PatchUserPasswordEndpoint.cs @@ -0,0 +1,34 @@ +using BeReadyBackend.DTO.Users; +using BeReadyBackend.Models; +using BeReadyBackend.Repositories; +using BeReadyBackend.Specifications.Users; +using FastEndpoints; +using PasswordGenerator; + +namespace BeReadyBackend.Endpoints.Users; + +public class PatchUserPasswordEndpoint(UsersRepository usersRepository) : Endpoint +{ + public override void Configure() + { + Patch("/Users/{@Id}/Password", x => new {x.Id}); + AllowAnonymous(); + } + + public override async Task HandleAsync(PatchUserPasswordDto req, CancellationToken ct) + { + User? user = await usersRepository.SingleOrDefaultAsync(new GetUserByIdSpec(req.Id), ct); + + if (user is null) + { + await Send.NotFoundAsync(ct); + return; + } + + string salt = new Password().IncludeLowercase().IncludeUppercase().IncludeNumeric().LengthRequired(24).Next(); + user.Password = BCrypt.Net.BCrypt.HashPassword(req.Password + salt); + + await usersRepository.SaveChangesAsync(ct); + await Send.OkAsync(ct); + } +} \ No newline at end of file diff --git a/BeReadyBackend/Endpoints/Users/UpdateUserEndpoint.cs b/BeReadyBackend/Endpoints/Users/UpdateUserEndpoint.cs new file mode 100644 index 0000000..5120342 --- /dev/null +++ b/BeReadyBackend/Endpoints/Users/UpdateUserEndpoint.cs @@ -0,0 +1,40 @@ +using BeReadyBackend.DTO.Users; +using BeReadyBackend.Models; +using BeReadyBackend.Repositories; +using BeReadyBackend.Specifications.Users; +using FastEndpoints; + +namespace BeReadyBackend.Endpoints.Users; + +public class UpdateUserEndpoint(UsersRepository usersRepository, AutoMapper.IMapper mapper) : Endpoint +{ + public override void Configure() + { + Put("/Users/{@Id}/", x => new {x.Id}); + AllowAnonymous(); + } + + public override async Task HandleAsync(UpdateUserDto req, CancellationToken ct) + { + User? user = await usersRepository.FirstOrDefaultAsync(new GetUserByCriteriaSpec(req.Username!, req.Email!, req.Id), ct); + + if (user is not null) + { + await Send.StringAsync("Un utilisateur possède déjà ce pseudo ou cette email", 400, cancellation: ct); + return; + } + + user = await usersRepository.SingleOrDefaultAsync(new GetUserByIdSpec(req.Id), ct); + + if (user is null) + { + await Send.NotFoundAsync(ct); + return; + } + + mapper.Map(req, user); + + await usersRepository.SaveChangesAsync(ct); + await Send.OkAsync(ct); + } +} \ No newline at end of file diff --git a/BeReadyBackend/MappingProfiles/DtoToEntityMappings.cs b/BeReadyBackend/MappingProfiles/DtoToEntityMappings.cs index ef0c5d1..069607f 100644 --- a/BeReadyBackend/MappingProfiles/DtoToEntityMappings.cs +++ b/BeReadyBackend/MappingProfiles/DtoToEntityMappings.cs @@ -1,5 +1,6 @@ using AutoMapper; using BeReadyBackend.DTO.Achievements; +using BeReadyBackend.DTO.Users; using BeReadyBackend.Models; namespace BeReadyBackend.MappingProfiles; @@ -9,5 +10,11 @@ public class DtoToEntityMappings : Profile public DtoToEntityMappings() { CreateMap(); + + CreateMap(); + CreateMap() + .ForMember(dest => dest.Id, opt => opt.Ignore()); + CreateMap() + .ForMember(dest => dest.Id, opt => opt.Ignore()); } } \ No newline at end of file diff --git a/BeReadyBackend/MappingProfiles/EntityToDtoMappings.cs b/BeReadyBackend/MappingProfiles/EntityToDtoMappings.cs index c1c09e5..b2d5eb7 100644 --- a/BeReadyBackend/MappingProfiles/EntityToDtoMappings.cs +++ b/BeReadyBackend/MappingProfiles/EntityToDtoMappings.cs @@ -1,5 +1,6 @@ using AutoMapper; using BeReadyBackend.DTO.Achievements; +using BeReadyBackend.DTO.Users; using BeReadyBackend.Models; namespace BeReadyBackend.MappingProfiles; @@ -14,5 +15,17 @@ public class EntityToDtoMappings : Profile .ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.Achievement!.Id)) .ForMember(dest => dest.Label, opt => opt.MapFrom(src => src.Achievement!.Label)) .ForMember(dest => dest.Description, opt => opt.MapFrom(src => src.Achievement!.Description)); + + CreateMap() + .ForMember(dest => dest.GetUserStatsDto, opt => opt.MapFrom(src => src)); + + CreateMap() + .ForMember(dest => dest.GetUserStatsDto, opt => opt.MapFrom(src => src)); + + CreateMap(); + + CreateMap(); + + CreateMap(); } } \ No newline at end of file diff --git a/BeReadyBackend/Specifications/Users/GetUserByCriteriaSpec.cs b/BeReadyBackend/Specifications/Users/GetUserByCriteriaSpec.cs new file mode 100644 index 0000000..bfcab29 --- /dev/null +++ b/BeReadyBackend/Specifications/Users/GetUserByCriteriaSpec.cs @@ -0,0 +1,13 @@ +using Ardalis.Specification; +using BeReadyBackend.Models; + +namespace BeReadyBackend.Specifications.Users; + +public class GetUserByCriteriaSpec : SingleResultSpecification +{ + public GetUserByCriteriaSpec(string username, string email, int? id) + { + Query + .Where(x => (x.Username == username || x.Email == email) && x.Id != id); + } +} \ No newline at end of file diff --git a/BeReadyBackend/Specifications/Users/GetUsersByScoreSpec.cs b/BeReadyBackend/Specifications/Users/GetUsersByScoreSpec.cs new file mode 100644 index 0000000..48b4d93 --- /dev/null +++ b/BeReadyBackend/Specifications/Users/GetUsersByScoreSpec.cs @@ -0,0 +1,13 @@ +using Ardalis.Specification; +using BeReadyBackend.Models; + +namespace BeReadyBackend.Specifications.Users; + +public class GetUsersByScoreSpec : Specification +{ + public GetUsersByScoreSpec() + { + Query + .OrderByDescending(x => x.Score); + } +} \ No newline at end of file