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