From 42348c863f778d8f3cc1a28ba81c0263ee50b701 Mon Sep 17 00:00:00 2001 From: Artemy <109195690+stalcker2288969@users.noreply.github.com> Date: Sun, 6 Jul 2025 17:43:10 +0700 Subject: [PATCH] Add admin friendships controller and improve friend request handling Introduces FriendshipsController for admin operations, including listing and removing friendships. Updates routing for admin controllers, improves error handling in UsersController, and enhances FriendsController and related services to include requester details in friendship DTOs. Refactors friend request acceptance to use query parameters and updates console client for improved user feedback and local development configuration. --- .../AdminStuff/FriendshipsController.cs | 109 ++++++++++++++++++ .../AdminStuff/InviteUserController.cs | 2 +- .../Controllers/AdminStuff/UsersController.cs | 7 ++ Govor.API/Controllers/FriendsController.cs | 7 +- Govor.Application/Services/FriendsService.cs | 7 +- Govor.Console/FriendsClient.cs | 3 +- Govor.Console/Program.cs | 14 ++- Govor.Contracts/DTOs/FriendshipDto.cs | 1 + 8 files changed, 134 insertions(+), 16 deletions(-) create mode 100644 Govor.API/Controllers/AdminStuff/FriendshipsController.cs diff --git a/Govor.API/Controllers/AdminStuff/FriendshipsController.cs b/Govor.API/Controllers/AdminStuff/FriendshipsController.cs new file mode 100644 index 0000000..94160e0 --- /dev/null +++ b/Govor.API/Controllers/AdminStuff/FriendshipsController.cs @@ -0,0 +1,109 @@ +using Govor.Contracts.DTOs; +using Govor.Core.Models; +using Govor.Core.Repositories.Friendships; +using Govor.Data.Repositories.Exceptions; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace Govor.API.Controllers.AdminStuff; + +[ApiController] +[Route("api/admin/[controller]")] +[Authorize(Roles = "Admin")] +public class FriendshipsController : Controller +{ + private readonly ILogger _logger; + private readonly IFriendshipsRepository _friendshipsRepository; + + public FriendshipsController(ILogger logger, IFriendshipsRepository friendshipsRepository) + { + _logger = logger; + _friendshipsRepository = friendshipsRepository; + } + + [HttpGet] + public async Task Get() + { + try + { + _logger.LogInformation("Get all friendships by administrator"); + var result = await _friendshipsRepository.GetAllAsync(); + return Ok(BuildFriendshipDtos(result)); + } + catch (NotFoundException ex) + { + _logger.LogWarning(ex.Message); + return NotFound(ex.Message); + } + catch (Exception e) + { + _logger.LogError(e, e.Message); + return StatusCode(500, new { error = "Internal server error." }); + } + } + + [HttpGet("{userId}")] + public async Task GetByUserId(Guid userId) + { + if(userId == Guid.Empty) + return BadRequest("User id can't be empty."); + + try + { + _logger.LogInformation($"Get user's {userId} all friendships by administrator"); + var result = await _friendshipsRepository.FindByUserIdAsync(userId); + return Ok(BuildFriendshipDtos(result)); + } + catch (NotFoundByKeyException ex) + { + _logger.LogWarning(ex.Message); + return NotFound(ex.Message); + } + catch (Exception e) + { + _logger.LogError(e, e.Message); + return StatusCode(500, new { error = "Internal server error." }); + } + } + + [HttpPost] + public async Task RemoveFriendship(Guid Id) + { + if(Id == Guid.Empty) + return BadRequest("FS id can't be empty."); + try + { + var result = await _friendshipsRepository.GetByIdAsync(Id); + await _friendshipsRepository.RemoveAsync(result); + return Ok(); + } + catch (NotFoundByKeyException ex) + { + _logger.LogWarning(ex.Message); + return NotFound(ex.Message); + } + catch (Exception ex) + { + _logger.LogError(ex, ex.Message); + return StatusCode(500, new { error = "Internal server error." }); + } + } + + private List BuildUserDtos(IEnumerable users) => users.Select(user => new UserDto + { + Id = user.Id, + Username = user.Username, + Description = user.Description, + WasOnline = user.WasOnline, + IconId = user.IconId + }).ToList(); + + private List BuildFriendshipDtos(IEnumerable friendships) => friendships.Select(f => new FriendshipDto + { + Id = f.Id, + Status = f.Status, + AddresseeId = f.AddresseeId, + RequesterId = f.RequesterId, + Requester = BuildUserDtos([f.Requester]).First(), + }).ToList(); +} \ No newline at end of file diff --git a/Govor.API/Controllers/AdminStuff/InviteUserController.cs b/Govor.API/Controllers/AdminStuff/InviteUserController.cs index 372549a..d5d28c1 100644 --- a/Govor.API/Controllers/AdminStuff/InviteUserController.cs +++ b/Govor.API/Controllers/AdminStuff/InviteUserController.cs @@ -7,7 +7,7 @@ using Microsoft.AspNetCore.Mvc; namespace Govor.API.Controllers.AdminStuff; -[Route("api/[controller]")] +[Route("api/admin/[controller]")] [ApiController] [Authorize(Roles = "Admin")] public class InviteUserController : Controller diff --git a/Govor.API/Controllers/AdminStuff/UsersController.cs b/Govor.API/Controllers/AdminStuff/UsersController.cs index 8dd1c6a..dd7e67f 100644 --- a/Govor.API/Controllers/AdminStuff/UsersController.cs +++ b/Govor.API/Controllers/AdminStuff/UsersController.cs @@ -1,6 +1,7 @@ using Govor.API.Services.AdminsStuff.Interfaces; using Govor.Contracts.Responses.Admins; using Govor.Core.Models; +using Govor.Data.Repositories.Exceptions; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -48,6 +49,11 @@ public class UsersController : Controller var read = await _users.GetUserById(id); return Ok(BuildUserDtos([read]).First()); } + catch (NotFoundByKeyException ex) + { + _logger.LogWarning(ex, ex.Message); + return NotFound(ex.Message); + } catch (Exception e) { _logger.LogError(e, e.Message); @@ -55,6 +61,7 @@ public class UsersController : Controller } } + private List BuildUserDtos(IEnumerable users) => users.Select(user => new UserResponse { Id = user.Id, diff --git a/Govor.API/Controllers/FriendsController.cs b/Govor.API/Controllers/FriendsController.cs index d49a732..cfa6295 100644 --- a/Govor.API/Controllers/FriendsController.cs +++ b/Govor.API/Controllers/FriendsController.cs @@ -98,7 +98,7 @@ public class FriendsController : Controller } [HttpPost("accept")] - public async Task AcceptFriend(Guid friendshipId) + public async Task AcceptFriend([FromQuery] Guid friendshipId) { if (friendshipId == Guid.Empty) return BadRequest("Requester ID is invalid"); @@ -144,7 +144,7 @@ public class FriendsController : Controller return StatusCode(500, new { error = "Internal server error." }); } } - + private List BuildUserDtos(IEnumerable users) => users.Select(user => new UserDto { Id = user.Id, @@ -159,6 +159,7 @@ public class FriendsController : Controller Id = f.Id, Status = f.Status, AddresseeId = f.AddresseeId, - RequesterId = f.RequesterId + RequesterId = f.RequesterId, + Requester = BuildUserDtos([f.Requester]).First(), }).ToList(); } diff --git a/Govor.Application/Services/FriendsService.cs b/Govor.Application/Services/FriendsService.cs index c68f2bb..e24dcd4 100644 --- a/Govor.Application/Services/FriendsService.cs +++ b/Govor.Application/Services/FriendsService.cs @@ -90,6 +90,7 @@ public class FriendsService : IFriendsService var friendships = await _friendshipsRepository.FindByUserIdAsync(userId); return friendships + .Where(f => f.Status == FriendshipStatus.Accepted) .Select(f => f.RequesterId == userId ? f.Addressee : f.Requester) .ToList(); } @@ -103,10 +104,8 @@ public class FriendsService : IFriendsService { try { - var user = await _usersRepository.FindByIdAsync(userId); - return user.ReceivedFriendRequests - .Where(f => f.Status == FriendshipStatus.Pending) - .ToList(); + var friendships = await _friendshipsRepository.FindByUserIdAsync(userId); + return friendships.Where(f => f.AddresseeId == userId && f.Status == FriendshipStatus.Pending).ToList(); } catch (NotFoundByKeyException ex) { diff --git a/Govor.Console/FriendsClient.cs b/Govor.Console/FriendsClient.cs index 19e9b2d..21593f7 100644 --- a/Govor.Console/FriendsClient.cs +++ b/Govor.Console/FriendsClient.cs @@ -36,8 +36,7 @@ namespace Govor.ConsoleClient public async Task AcceptFriendRequestAsync(Guid requesterId) { - var content = JsonContent.Create(requesterId); - var response = await _client.PostAsync("/api/friends/accept", content); + var response = await _client.PostAsync($"/api/friends/accept?friendshipId={requesterId}", null); response.EnsureSuccessStatusCode(); } diff --git a/Govor.Console/Program.cs b/Govor.Console/Program.cs index e804006..2a622ef 100644 --- a/Govor.Console/Program.cs +++ b/Govor.Console/Program.cs @@ -25,8 +25,10 @@ namespace Govor.ConsoleClient { class Program { + //static string baseUrl = "https://govor-team-govor-88b3.twc1.net"; + static string baseUrl = "https://localhost:7155"; static string? AuthToken = null; - static HttpClientService HttpService = new("https://govor-team-govor-88b3.twc1.net"); // поменяй URL на свой + static HttpClientService HttpService = new(baseUrl); // поменяй URL на свой private static FriendsClient? friendsClient; static HubConnection? _hubConnection; static Dictionary> ChatHistory = new(); @@ -72,7 +74,7 @@ namespace Govor.ConsoleClient { AuthToken = await HttpService.LoginAsync(loginUsername, loginPassword); HttpClient sharedClient = new(); - sharedClient.BaseAddress = new Uri("https://govor-team-govor-88b3.twc1.net"); + sharedClient.BaseAddress = new Uri(baseUrl); sharedClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", AuthToken); friendsClient = new FriendsClient(sharedClient); @@ -98,7 +100,7 @@ namespace Govor.ConsoleClient { AuthToken = await HttpService.RegisterAsync(regUsername, regPassword, inviteCode); HttpClient sharedClient = new(); - sharedClient.BaseAddress = new Uri("https://govor-team-govor-88b3.twc1.net"); + sharedClient.BaseAddress = new Uri(baseUrl); sharedClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", AuthToken); friendsClient = new FriendsClient(sharedClient); @@ -165,7 +167,7 @@ namespace Govor.ConsoleClient Console.WriteLine("[Ошибка] Сначала войдите в систему."); break; } - Console.Write("Введите ID пользователя, чью заявку принимаете: "); + Console.Write("Введите ID заявки, которую хотите принять принимаете: "); var acceptId = Guid.Parse(Console.ReadLine()); await friendsClient.AcceptFriendRequestAsync(acceptId); Console.WriteLine("Принято"); @@ -179,7 +181,7 @@ namespace Govor.ConsoleClient var requests = await friendsClient.GetIncomingRequestsAsync(); foreach (var r in requests) { - Console.WriteLine($"Запрос от: {r.RequesterId} (добавьте через /accept {r.RequesterId})"); + Console.WriteLine($"Запрос от: {r.Requester.Username}| {r.RequesterId} | Был онлайн: {r.Requester.WasOnline} (добавьте через /accept {r.Id})"); } break; case "/chat": @@ -221,7 +223,7 @@ namespace Govor.ConsoleClient } _hubConnection = new HubConnectionBuilder() - .WithUrl("https://govor-team-govor-88b3.twc1.net/api/chats", options => + .WithUrl($"{baseUrl}/api/chats", options => { options.AccessTokenProvider = () => Task.FromResult(AuthToken); }) diff --git a/Govor.Contracts/DTOs/FriendshipDto.cs b/Govor.Contracts/DTOs/FriendshipDto.cs index 85065e1..f2b6ca1 100644 --- a/Govor.Contracts/DTOs/FriendshipDto.cs +++ b/Govor.Contracts/DTOs/FriendshipDto.cs @@ -8,4 +8,5 @@ public class FriendshipDto public Guid RequesterId { get; set; } public Guid AddresseeId { get; set; } public FriendshipStatus Status { get; set; } + public UserDto Requester { get; set; } } \ No newline at end of file