using LiteCqrs; using TeleWave.Api.Common; using TeleWave.Application.Admin.Users.BlockUser; using TeleWave.Application.Admin.Users.CreateUser; using TeleWave.Application.Admin.Users.DeleteUser; using TeleWave.Application.Admin.Users.ListUsers; using TeleWave.Application.Admin.Users.ResetPassword; using TeleWave.Application.Admin.Users.UnblockUser; using TeleWave.Application.Common.Interfaces; using TeleWave.Application.Common.Models; using TeleWave.Infrastructure.Identity; namespace TeleWave.Api.Endpoints; public static class AdminUserEndpoints { public static IEndpointRouteBuilder MapAdminUserEndpoints(this IEndpointRouteBuilder app) { var admin = app.MapGroup("/api/admin/users") .WithTags("Admin.Users") .RequireAuthorization(policy => policy.RequireRole(RoleNames.Admin)); admin.MapGet("", ListUsers).Produces>(); admin.MapPost("", CreateUser).Produces(StatusCodes.Status201Created); admin.MapPost("/{id:guid}/block", BlockUser).Produces(StatusCodes.Status204NoContent); admin.MapPost("/{id:guid}/unblock", UnblockUser).Produces(StatusCodes.Status204NoContent); admin .MapPost("/{id:guid}/password", ResetPassword) .Produces(StatusCodes.Status204NoContent); admin.MapDelete("/{id:guid}", DeleteUser).Produces(StatusCodes.Status204NoContent); return app; } private static async Task ListUsers( [AsParameters] ListUsersFilter filter, ISender sender, CancellationToken cancellationToken ) { var result = await sender.Send( new ListUsersQuery( filter.Page is > 0 ? filter.Page.Value : 1, filter.PageSize is > 0 ? filter.PageSize.Value : 20, filter.Search, filter.RoleId, filter.IsBlocked, filter.Sort, filter.Desc ?? false ), cancellationToken ); return Results.Ok(result); } private static async Task CreateUser( CreateUserBody body, ISender sender, CancellationToken cancellationToken ) { var result = await sender.Send( new CreateUserCommand(body.UserName, body.Password, body.RoleId), cancellationToken ); return result.IsSuccess ? Results.Created( $"/api/admin/users/{result.Value}", new CreatedIdResponse(result.Value) ) : result.ToHttpResult(); } private static async Task BlockUser( Guid id, ISender sender, CancellationToken cancellationToken ) { var result = await sender.Send(new BlockUserCommand(id), cancellationToken); return result.ToHttpResult(); } private static async Task UnblockUser( Guid id, ISender sender, CancellationToken cancellationToken ) { var result = await sender.Send(new UnblockUserCommand(id), cancellationToken); return result.ToHttpResult(); } private static async Task ResetPassword( Guid id, ResetPasswordBody body, ISender sender, CancellationToken cancellationToken ) { var result = await sender.Send( new ResetUserPasswordCommand(id, body.NewPassword), cancellationToken ); return result.ToHttpResult(); } private static async Task DeleteUser( Guid id, ISender sender, CancellationToken cancellationToken ) { var result = await sender.Send(new DeleteUserCommand(id), cancellationToken); return result.ToHttpResult(); } } /// /// Фильтр и страница списка пользователей. Все поля nullable намеренно: обязательный параметр в query /// превращает запрос без него в 400 ещё до хендлера — ошибку в таком виде отладить тяжело. Умолчания /// подставляет сам хендлер, а не объявление: при AsParameters обязательность определяется /// nullable-типом, а не наличием значения по умолчанию в конструкторе. /// public sealed record ListUsersFilter( int? Page, int? PageSize, string? Search, Guid? RoleId, bool? IsBlocked, string? Sort, bool? Desc ); public sealed record CreateUserBody(string UserName, string Password, Guid RoleId); public sealed record ResetPasswordBody(string NewPassword);