Created one part of all endpoints to admin the user

This commit is contained in:
2026-02-21 18:32:32 +01:00
parent ac515b8d61
commit e0a4e88eca
21 changed files with 340 additions and 0 deletions
+2
View File
@@ -0,0 +1,2 @@
<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_003ARepositoryBaseOfT_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E2_003Fresharper_002Dhost_003FSourcesCache_003F5023b5be698d783ffe9f42b2e944a85a7a66b61bc5e19c76c591036343fd16_003FRepositoryBaseOfT_002Ecs/@EntryIndexedValue">ForceIncluded</s:String></wpf:ResourceDictionary>
+2
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="BCrypt.Net-Next" Version="4.1.0" />
<PackageReference Include="FastEndpoints" Version="7.2.0" />
<PackageReference Include="FastEndpoints.Security" Version="7.2.0" />
<PackageReference Include="FastEndpoints.Swagger" Version="7.2.0" />
@@ -20,6 +21,7 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.20" />
<PackageReference Include="PasswordGenerator" Version="2.1.0" />
<PackageReference Include="Plainquire.Page" Version="7.0.1" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2"/>
</ItemGroup>
+10
View File
@@ -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; }
}
@@ -0,0 +1,6 @@
namespace BeReadyBackend.DTO.Users;
public class GetUserChallengeDto
{
}
@@ -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; }
}
+12
View File
@@ -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; }
}
@@ -0,0 +1,7 @@
namespace BeReadyBackend.DTO.Users;
public class GetUserProofDto
{
public int Id { get; set; }
public string? Proof { get; set; }
}
@@ -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; }
}
@@ -0,0 +1,7 @@
namespace BeReadyBackend.DTO.Users;
public class PatchUserDesignationDto
{
public int Id { get; set; }
public int DesignationId { get; set; }
}
@@ -0,0 +1,7 @@
namespace BeReadyBackend.DTO.Users;
public class PatchUserPasswordDto
{
public int Id { get; set; }
public string? Password { get; set; }
}
+10
View File
@@ -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; }
}
@@ -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<CreateUserDto>
{
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<User>(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);
}
}
@@ -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<DeleteUserRequest>
{
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);
}
}
@@ -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<List<GetUserDto>>
{
public override void Configure()
{
Get("/OverallRanking/");
AllowAnonymous();
}
public override async Task HandleAsync(CancellationToken ct)
{
await Send.OkAsync(await usersRepository.ProjectToListAsync<GetUserDto>(new GetUsersByScoreSpec(), ct), ct);
}
}
@@ -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<PatchUserDesignationDto>
{
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);
}
}
@@ -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<PatchUserPasswordDto>
{
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);
}
}
@@ -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<UpdateUserDto>
{
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);
}
}
@@ -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<UnlockAchievementDto, UserAchievement>();
CreateMap<CreateUserDto, User>();
CreateMap<UpdateUserDto, User>()
.ForMember(dest => dest.Id, opt => opt.Ignore());
CreateMap<PatchUserDesignationDto, User>()
.ForMember(dest => dest.Id, opt => opt.Ignore());
}
}
@@ -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<User, GetUserDto>()
.ForMember(dest => dest.GetUserStatsDto, opt => opt.MapFrom(src => src));
CreateMap<User, GetUserDetailsDto>()
.ForMember(dest => dest.GetUserStatsDto, opt => opt.MapFrom(src => src));
CreateMap<User, GetUserStatsDto>();
CreateMap<User, GetUserChallengeDto>();
CreateMap<User, GetUserProofDto>();
}
}
@@ -0,0 +1,13 @@
using Ardalis.Specification;
using BeReadyBackend.Models;
namespace BeReadyBackend.Specifications.Users;
public class GetUserByCriteriaSpec : SingleResultSpecification<User>
{
public GetUserByCriteriaSpec(string username, string email, int? id)
{
Query
.Where(x => (x.Username == username || x.Email == email) && x.Id != id);
}
}
@@ -0,0 +1,13 @@
using Ardalis.Specification;
using BeReadyBackend.Models;
namespace BeReadyBackend.Specifications.Users;
public class GetUsersByScoreSpec : Specification<User>
{
public GetUsersByScoreSpec()
{
Query
.OrderByDescending(x => x.Score);
}
}