From 6063aafd9e28976f8b08c972f622faaec0c92f1f Mon Sep 17 00:00:00 2001 From: Artemy <109195690+stalcker2288969@users.noreply.github.com> Date: Sun, 13 Jul 2025 19:03:01 +0700 Subject: [PATCH] Refactor friend request and media services, remove console commands Refactored the friend request command service and SignalR hub to return and broadcast FriendshipDto objects, improving real-time updates. Enhanced media upload and download logic to support file storage and retrieval, including database integration. Removed all command classes and related infrastructure from the Govor.Console project, streamlining the console client. Updated dependency injection and interfaces to reflect these changes. --- .../FriendsRequestQueryControllerTests.cs | 10 +- .../IntegrationTests/Hubs/FriendsHubTests.cs | 91 ++- .../AdminStuff/FriendshipsController.cs | 21 +- .../Friends/FriendsRequestQueryController.cs | 6 +- Govor.API/Controllers/MediaController.cs | 59 +- .../ConfigurationProgramExtensions.cs | 2 + Govor.API/Hubs/ChatsHub.cs | 32 +- Govor.API/Hubs/FriendsHub.cs | 29 +- .../Friends/IFriendRequestCommandService.cs | 8 +- .../Interfaces/Medias/IMediaService.cs | 11 +- .../Friends/FriendRequestCommandService.cs | 17 +- .../Services/Messages/MediaService.cs | 79 ++- .../Commands/AcceptFriendRequestCommand.cs | 42 -- .../AdminListAllFriendshipsCommand.cs | 58 -- .../AdminListUserFriendshipsCommand.cs | 65 -- .../Commands/AdminRemoveFriendshipCommand.cs | 57 -- Govor.Console/Commands/BaseCommand.cs | 65 -- Govor.Console/Commands/BlockUserCommand.cs | 43 -- Govor.Console/Commands/HelpCommand.cs | 49 -- Govor.Console/Commands/ICommand.cs | 8 - Govor.Console/Commands/ListFriendsCommand.cs | 40 -- .../Commands/ListIncomingRequestsCommand.cs | 44 -- .../Commands/ListOutgoingRequestsCommand.cs | 53 -- Govor.Console/Commands/LoginCommand.cs | 51 -- Govor.Console/Commands/RegisterCommand.cs | 53 -- .../Commands/RejectFriendRequestCommand.cs | 43 -- Govor.Console/Commands/SearchUserCommand.cs | 51 -- .../Commands/SendFriendRequestCommand.cs | 42 -- Govor.Console/Commands/UnblockUserCommand.cs | 43 -- Govor.Console/FriendsHubClient.cs | 171 ++--- Govor.Console/Govor.Console.csproj | 1 + Govor.Console/Program.cs | 413 ++---------- Govor.Contracts/Govor.Contracts.csproj | 6 + .../Requests/MediaUploadRequest.cs | 5 +- .../Responses/SignalR/MessageEditResponse.cs | 9 +- Govor.Core/Models/MediaFile.cs | 1 + .../Models/Users/PrivacyUserSettings.cs | 2 +- ...01943_AddUploaderIdToMediaFile.Designer.cs | 596 ++++++++++++++++++ ...20250713101943_AddUploaderIdToMediaFile.cs | 31 + .../Migrations/GovorDbContextModelSnapshot.cs | 3 + 40 files changed, 1029 insertions(+), 1381 deletions(-) delete mode 100644 Govor.Console/Commands/AcceptFriendRequestCommand.cs delete mode 100644 Govor.Console/Commands/AdminListAllFriendshipsCommand.cs delete mode 100644 Govor.Console/Commands/AdminListUserFriendshipsCommand.cs delete mode 100644 Govor.Console/Commands/AdminRemoveFriendshipCommand.cs delete mode 100644 Govor.Console/Commands/BaseCommand.cs delete mode 100644 Govor.Console/Commands/BlockUserCommand.cs delete mode 100644 Govor.Console/Commands/HelpCommand.cs delete mode 100644 Govor.Console/Commands/ICommand.cs delete mode 100644 Govor.Console/Commands/ListFriendsCommand.cs delete mode 100644 Govor.Console/Commands/ListIncomingRequestsCommand.cs delete mode 100644 Govor.Console/Commands/ListOutgoingRequestsCommand.cs delete mode 100644 Govor.Console/Commands/LoginCommand.cs delete mode 100644 Govor.Console/Commands/RegisterCommand.cs delete mode 100644 Govor.Console/Commands/RejectFriendRequestCommand.cs delete mode 100644 Govor.Console/Commands/SearchUserCommand.cs delete mode 100644 Govor.Console/Commands/SendFriendRequestCommand.cs delete mode 100644 Govor.Console/Commands/UnblockUserCommand.cs create mode 100644 Govor.Data/Migrations/20250713101943_AddUploaderIdToMediaFile.Designer.cs create mode 100644 Govor.Data/Migrations/20250713101943_AddUploaderIdToMediaFile.cs diff --git a/Govor.API.Tests/IntegrationTests/Controllers/FriendsRequestQueryControllerTests.cs b/Govor.API.Tests/IntegrationTests/Controllers/FriendsRequestQueryControllerTests.cs index 8b6c204..41b0c58 100644 --- a/Govor.API.Tests/IntegrationTests/Controllers/FriendsRequestQueryControllerTests.cs +++ b/Govor.API.Tests/IntegrationTests/Controllers/FriendsRequestQueryControllerTests.cs @@ -48,7 +48,10 @@ public class FriendsRequestQueryControllerTests var currentId = _fixture.Create(); var friendships = _fixture.CreateMany().ToList(); var dtos = friendships.Select(f => new FriendshipDto() - { AddresseeId = f.AddresseeId, RequesterId = f.RequesterId }).ToList(); + { + AddresseeId = f.AddresseeId, + RequesterId = f.RequesterId + }).ToList(); _currentUserServiceMock.Setup(c => c.GetCurrentUserId()) .Returns(currentId); @@ -125,7 +128,10 @@ public class FriendsRequestQueryControllerTests var currentId = _fixture.Create(); var friendships = _fixture.CreateMany().ToList(); var dtos = friendships.Select(f => new FriendshipDto() - { AddresseeId = f.AddresseeId, RequesterId = f.RequesterId }).ToList(); + { + AddresseeId = f.AddresseeId, + RequesterId = f.RequesterId + }).ToList(); _currentUserServiceMock.Setup(c => c.GetCurrentUserId()) .Returns(currentId); diff --git a/Govor.API.Tests/IntegrationTests/Hubs/FriendsHubTests.cs b/Govor.API.Tests/IntegrationTests/Hubs/FriendsHubTests.cs index 9f88695..57010f7 100644 --- a/Govor.API.Tests/IntegrationTests/Hubs/FriendsHubTests.cs +++ b/Govor.API.Tests/IntegrationTests/Hubs/FriendsHubTests.cs @@ -1,8 +1,12 @@ +using AutoFixture; +using AutoMapper; using Govor.API.Hubs; using Govor.Application.Exceptions.FriendsService; using Govor.Application.Interfaces.Friends; using Govor.Application.Interfaces.Infrastructure.Extensions; +using Govor.Contracts.DTOs; using Govor.Contracts.Responses.SignalR; +using Govor.Core.Models; using Microsoft.AspNetCore.SignalR; using Microsoft.Extensions.Logging; using Moq; @@ -17,26 +21,34 @@ public class FriendsHubTests private Mock _clientsMock = null!; private Mock _clientProxyMock = null!; private Mock> _loggerMock = null!; + private Mock _mapperMock = null!; private FriendsHub _hub = null!; + private Fixture _fixture; private readonly Guid _userId = Guid.NewGuid(); private readonly Guid _targetUserId = Guid.NewGuid(); [SetUp] public void Setup() { + _fixture = new Fixture(); + _fixture.Behaviors.OfType().ToList().ForEach(b => _fixture.Behaviors.Remove(b)); + _fixture.Behaviors.Add(new OmitOnRecursionBehavior()); + _friendRequestServiceMock = new Mock(); _currentUserServiceMock = new Mock(); _clientsMock = new Mock(); _clientProxyMock = new Mock(); _loggerMock = new Mock>(); - + _mapperMock = new Mock(); + _currentUserServiceMock.Setup(x => x.GetCurrentUserId()).Returns(_userId); - _clientsMock.Setup(c => c.User(It.IsAny())).Returns(_clientProxyMock.Object); - + _clientsMock.Setup(c => c.Group(It.IsAny())).Returns(_clientProxyMock.Object); + _hub = new FriendsHub( _friendRequestServiceMock.Object, _currentUserServiceMock.Object, - _loggerMock.Object) + _loggerMock.Object, + _mapperMock.Object) { Clients = _clientsMock.Object }; @@ -46,6 +58,26 @@ public class FriendsHubTests [Test] public async Task SendRequest_ShouldReturnCreated_WhenSuccess() { + var friendship = _fixture.Build() + .With(f => f.Status, FriendshipStatus.Pending) + .With(f => f.RequesterId, _userId) + .With(f =>f.AddresseeId, _targetUserId) + .Create(); + + var dto = new FriendshipDto() + { + Id = friendship.Id, + Status = FriendshipStatus.Accepted, + AddresseeId = friendship.AddresseeId, + RequesterId = friendship.RequesterId, + }; + + _mapperMock.Setup(f => f.Map(friendship)) + .Returns(dto); + + _friendRequestServiceMock.Setup(f => f.SendAsync(_userId, _targetUserId)) + .ReturnsAsync(friendship); + // Act var result = await _hub.SendRequest(_targetUserId); @@ -57,7 +89,7 @@ public class FriendsHubTests _clientProxyMock.Verify(c => c.SendCoreAsync( "FriendRequestReceived", - It.Is(o => o[0]!.Equals(_userId)), + It.Is(o => o[0]!.Equals(dto)), default), Times.Once); } @@ -129,19 +161,35 @@ public class FriendsHubTests public async Task AcceptRequest_ShouldReturnOk_WhenSuccess() { // Arrange - var friendshipId = Guid.NewGuid(); + var friendship = _fixture.Build() + .With(f => f.Status, FriendshipStatus.Pending) + .Create(); + var dto = new FriendshipDto() + { + Id = friendship.Id, + Status = FriendshipStatus.Accepted, + AddresseeId = friendship.AddresseeId, + RequesterId = friendship.RequesterId + }; + + _mapperMock.Setup(f => f.Map(friendship)) + .Returns(dto); + + _friendRequestServiceMock.Setup(f => f.AcceptAsync(friendship.Id, _userId)) + .ReturnsAsync(friendship); // Act - var result = await _hub.AcceptRequest(friendshipId); + var result = await _hub.AcceptRequest(friendship.Id); // Assert Assert.That(result.Status, Is.EqualTo(HubResultStatus.Success)); - _friendRequestServiceMock.Verify(s => s.AcceptAsync(friendshipId, _userId), Times.Once); + _friendRequestServiceMock.Verify(s => s.AcceptAsync(friendship.Id, _userId), Times.Once); _clientProxyMock.Verify(c => c.SendCoreAsync( "FriendRequestAccepted", - It.Is(o => o[0]!.Equals(friendshipId)), + It.Is(o => o.Length == 1 && o[0] == dto), default), Times.Once); + } [Test] @@ -201,18 +249,33 @@ public class FriendsHubTests public async Task RejectRequest_ShouldReturnOk_WhenSuccess() { // Arrange - var friendshipId = Guid.NewGuid(); - + var friendship = _fixture.Build() + .With(f => f.Status, FriendshipStatus.Pending) + .Create(); + var dto = new FriendshipDto() + { + Id = friendship.Id, + Status = FriendshipStatus.Accepted, + AddresseeId = friendship.AddresseeId, + RequesterId = friendship.RequesterId + }; + + _mapperMock.Setup(f => f.Map(friendship)) + .Returns(dto); + + _friendRequestServiceMock.Setup(f => f.RejectAsync(friendship.Id, _userId)) + .ReturnsAsync(friendship); + // Act - var result = await _hub.RejectRequest(friendshipId); + var result = await _hub.RejectRequest(friendship.Id); // Assert Assert.That(result.Status, Is.EqualTo(HubResultStatus.Success)); - _friendRequestServiceMock.Verify(s => s.RejectAsync(friendshipId, _userId), Times.Once); + _friendRequestServiceMock.Verify(s => s.RejectAsync(friendship.Id, _userId), Times.Once); _clientProxyMock.Verify(c => c.SendCoreAsync( "FriendRequestRejected", - It.Is(o => o[0]!.Equals(friendshipId)), + It.Is(o => o[0]!.Equals(dto)), default), Times.Once); } diff --git a/Govor.API/Controllers/AdminStuff/FriendshipsController.cs b/Govor.API/Controllers/AdminStuff/FriendshipsController.cs index 502abf4..94b5225 100644 --- a/Govor.API/Controllers/AdminStuff/FriendshipsController.cs +++ b/Govor.API/Controllers/AdminStuff/FriendshipsController.cs @@ -29,7 +29,7 @@ public class FriendshipsController : Controller { _logger.LogInformation("Get all friendships by administrator"); var result = await _friendshipsRepository.GetAllAsync(); - return Ok(BuildFriendshipDtos(result)); + return Ok(result); } catch (NotFoundException ex) { @@ -53,7 +53,7 @@ public class FriendshipsController : Controller { _logger.LogInformation($"Get user's {userId} all friendships by administrator"); var result = await _friendshipsRepository.FindByUserIdAsync(userId); - return Ok(BuildFriendshipDtos(result)); + return Ok(result); } catch (NotFoundByKeyException ex) { @@ -89,21 +89,4 @@ public class FriendshipsController : Controller 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, - }).ToList(); } \ No newline at end of file diff --git a/Govor.API/Controllers/Friends/FriendsRequestQueryController.cs b/Govor.API/Controllers/Friends/FriendsRequestQueryController.cs index 77deda3..f3f8bda 100644 --- a/Govor.API/Controllers/Friends/FriendsRequestQueryController.cs +++ b/Govor.API/Controllers/Friends/FriendsRequestQueryController.cs @@ -56,7 +56,11 @@ public class FriendsRequestQueryController : Controller { try { - var result = await _friendsService.GetResponsesAsync(_currentUserService.GetCurrentUserId()); + var userId = _currentUserService.GetCurrentUserId(); + + _logger.LogInformation("Getting responses by user {userId}", userId); + + var result = await _friendsService.GetResponsesAsync(userId); var response = _mapper.Map>(result); return Ok(response); diff --git a/Govor.API/Controllers/MediaController.cs b/Govor.API/Controllers/MediaController.cs index b7f00d8..d842430 100644 --- a/Govor.API/Controllers/MediaController.cs +++ b/Govor.API/Controllers/MediaController.cs @@ -1,4 +1,5 @@ using Govor.Application.Interfaces; +using Govor.Application.Interfaces.Infrastructure.Extensions; using Govor.Application.Interfaces.Medias; using Govor.Contracts.Requests; using Govor.Core.Models; @@ -15,27 +16,67 @@ public class MediaController : Controller { private readonly ILogger _logger; private readonly IMediaService _mediaService; - - public MediaController(ILogger logger, IMediaService mediaService) + private readonly ICurrentUserService _currentUserService; + + public MediaController( + ILogger logger, + IMediaService mediaService, + ICurrentUserService currentUserService) { _logger = logger; _mediaService = mediaService; + _currentUserService = currentUserService; } [HttpPost("upload")] - [RequestSizeLimit(100_000_000)]// ~100MB + [RequestSizeLimit(100_000_000)] // ~100MB public async Task Upload([FromForm] MediaUploadRequest request) { try { - var result = await _mediaService.UploadMediaAsync(new Media(request.Data, request.FileName, request.Type, - request.MimeType, request.EncryptedKey)); - - return Ok(result); + if (!ModelState.IsValid) + return BadRequest(ModelState); + + // Чтение байт из IFormFile + using var memoryStream = new MemoryStream(); + await request.Data.CopyToAsync(memoryStream); + byte[] fileBytes = memoryStream.ToArray(); + + var media = new Media( + _currentUserService.GetCurrentUserId(), + DateTime.UtcNow, + fileBytes, + request.FileName, + request.Type, + request.MimeType, + request.EncryptedKey + ); + + var result = await _mediaService.UploadMediaAsync(media); + return Ok(result); } catch (Exception ex) { - return StatusCode(500, ex); + _logger.LogError(ex, "Error uploading media"); + return StatusCode(500, "Internal server error"); } } -} \ No newline at end of file + + [HttpGet("download/{id}")] + public async Task Download(Guid id) + { + try + { + if (!ModelState.IsValid) + return BadRequest(ModelState); + + var media = await _mediaService.GetMediaByIdAsync(id); + return File(media.Data, media.MineType, media.FileName); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error downloading media"); + return StatusCode(500, "Internal server error"); + } + } +} diff --git a/Govor.API/Extensions/ConfigurationProgramExtensions.cs b/Govor.API/Extensions/ConfigurationProgramExtensions.cs index c0b5812..2689d8a 100644 --- a/Govor.API/Extensions/ConfigurationProgramExtensions.cs +++ b/Govor.API/Extensions/ConfigurationProgramExtensions.cs @@ -7,6 +7,7 @@ using Govor.Application.Interfaces; using Govor.Application.Interfaces.Authentication; using Govor.Application.Interfaces.Friends; using Govor.Application.Interfaces.Infrastructure.Extensions; +using Govor.Application.Interfaces.Medias; using Govor.Application.Interfaces.Messages; using Govor.Application.Services; using Govor.Application.Services.Authentication; @@ -65,6 +66,7 @@ public static class ConfigurationProgramExtensions services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); // Auto Mapper services.AddAutoMapper(typeof(MappingProfile)); diff --git a/Govor.API/Hubs/ChatsHub.cs b/Govor.API/Hubs/ChatsHub.cs index 5db3c95..4869472 100644 --- a/Govor.API/Hubs/ChatsHub.cs +++ b/Govor.API/Hubs/ChatsHub.cs @@ -198,11 +198,25 @@ public class ChatsHub : Hub { var result = await _messageCommandService.EditMessageAsync(editMessageParam); - if (!result.IsSuccess) + if (!result.IsSuccess || result.OriginalMessage == null) return LogAndError(editor, request.MessageId, "Edit message error", result.Exception); - return HubResult.Ok(); + var response = new MessageEditResponse() + { + MessageId = result.messageId, + EditorId = editor, + RecipientId = result.OriginalMessage.RecipientId, + RecipientType = result.OriginalMessage.RecipientType, + NewEncryptedContent = request.NewEncryptedContent, + EditedAt = editMessageParam.EditedAt, + }; + + await NotifyClientsAboutEdit(response); + + _logger.LogInformation("Message {MessageId} edited successfully by {editor}", request.MessageId, editor); + + return HubResult.Ok(response); } catch (UnauthorizedAccessException ex) { @@ -259,6 +273,20 @@ public class ChatsHub : Hub } } + private async Task NotifyClientsAboutEdit(MessageEditResponse response) + { + if (response.RecipientType == RecipientType.User) + { + await Clients.Group(response.EditorId.ToString()).SendAsync("MessageEdit", response); + if (response.EditorId != response.RecipientId) + await Clients.Group(response.RecipientId.ToString()).SendAsync("MessageEdit", response); + } + else + { + await Clients.Group($"group_{response.RecipientId}").SendAsync("MessageEdit", response); + } + } + // Logging helpers private HubResult LogAndError(Guid userId, Guid targetId, string message, Exception ex) { diff --git a/Govor.API/Hubs/FriendsHub.cs b/Govor.API/Hubs/FriendsHub.cs index 1e9e309..b252080 100644 --- a/Govor.API/Hubs/FriendsHub.cs +++ b/Govor.API/Hubs/FriendsHub.cs @@ -1,6 +1,9 @@ +using System.ComponentModel.DataAnnotations; +using AutoMapper; using Govor.Application.Exceptions.FriendsService; using Govor.Application.Interfaces.Friends; using Govor.Application.Interfaces.Infrastructure.Extensions; +using Govor.Contracts.DTOs; using Govor.Contracts.Responses.SignalR; using Microsoft.AspNetCore.SignalR; @@ -11,12 +14,17 @@ public class FriendsHub : Hub private readonly ILogger _logger; private readonly IFriendRequestCommandService _friendRequestService; private readonly ICurrentUserService _currentUserService; + private readonly IMapper _mapper; - public FriendsHub(IFriendRequestCommandService friendRequestService, ICurrentUserService currentUserService, ILogger logger) + public FriendsHub(IFriendRequestCommandService friendRequestService, + ICurrentUserService currentUserService, + ILogger logger, + IMapper mapper) { _friendRequestService = friendRequestService; _currentUserService = currentUserService; _logger = logger; + _mapper = mapper; } public override async Task OnConnectedAsync() @@ -70,9 +78,10 @@ public class FriendsHub : Hub try { var userId = _currentUserService.GetCurrentUserId(); - await _friendRequestService.SendAsync(userId, targetUserId); - await Clients.User(targetUserId.ToString()) - .SendAsync("FriendRequestReceived", userId); + var friendship = await _friendRequestService.SendAsync(userId, targetUserId); + + await Clients.Group(targetUserId.ToString()) + .SendAsync("FriendRequestReceived", _mapper.Map(friendship)); _logger.LogInformation($"Friend request received for user {targetUserId} from {userId}."); return HubResult.Created(); @@ -104,9 +113,9 @@ public class FriendsHub : Hub try { var userId = _currentUserService.GetCurrentUserId(); - await _friendRequestService.AcceptAsync(friendshipId, userId); - await Clients.User(userId.ToString()) - .SendAsync("FriendRequestAccepted", friendshipId); + var friendship = await _friendRequestService.AcceptAsync(friendshipId, userId); + await Clients.Group(userId.ToString()) + .SendAsync("FriendRequestAccepted", _mapper.Map(friendship)); _logger.LogInformation($"Friend request accepted for user {userId} from {userId}."); return HubResult.Ok(); @@ -133,9 +142,9 @@ public class FriendsHub : Hub try { var userId = _currentUserService.GetCurrentUserId(); - await _friendRequestService.RejectAsync(friendshipId, userId); - await Clients.User(userId.ToString()) - .SendAsync("FriendRequestRejected", friendshipId); + var friendship = await _friendRequestService.RejectAsync(friendshipId, userId); + await Clients.Group(userId.ToString()) + .SendAsync("FriendRequestRejected", _mapper.Map(friendship)); _logger.LogInformation($"Friend request rejected for user {userId} from {userId}."); return HubResult.Ok(); diff --git a/Govor.Application/Interfaces/Friends/IFriendRequestCommandService.cs b/Govor.Application/Interfaces/Friends/IFriendRequestCommandService.cs index 21f9d24..3d3c4e4 100644 --- a/Govor.Application/Interfaces/Friends/IFriendRequestCommandService.cs +++ b/Govor.Application/Interfaces/Friends/IFriendRequestCommandService.cs @@ -1,8 +1,10 @@ +using Govor.Core.Models; + namespace Govor.Application.Interfaces.Friends; public interface IFriendRequestCommandService { - Task SendAsync(Guid fromUserId, Guid toUserId); - Task AcceptAsync(Guid requestId, Guid currentUserId); - Task RejectAsync(Guid requestId, Guid currentUserId); + Task SendAsync(Guid fromUserId, Guid toUserId); + Task AcceptAsync(Guid requestId, Guid currentUserId); + Task RejectAsync(Guid requestId, Guid currentUserId); } diff --git a/Govor.Application/Interfaces/Medias/IMediaService.cs b/Govor.Application/Interfaces/Medias/IMediaService.cs index 6b0e3b3..48a8232 100644 --- a/Govor.Application/Interfaces/Medias/IMediaService.cs +++ b/Govor.Application/Interfaces/Medias/IMediaService.cs @@ -6,9 +6,16 @@ public interface IMediaService { public Task UploadMediaAsync(Media file); public Task DeleteMediaAsync(Guid fileId); - public Task GetMediaAsync(string url); + public Task GetMediaByUrlAsync(string url); + public Task GetMediaByIdAsync(Guid mediaId); } -public record Media(byte[] Data, string FileName, MediaType Type, string MineType, string EncryptedKey); +public record Media(Guid UploaderId, + DateTime UploadedOn, + byte[] Data, + string FileName, + MediaType Type, + string MineType, + string EncryptedKey); public record MediaUploadResult(Guid? MediaId, string Url); \ No newline at end of file diff --git a/Govor.Application/Services/Friends/FriendRequestCommandService.cs b/Govor.Application/Services/Friends/FriendRequestCommandService.cs index e4e8780..3e6b4bb 100644 --- a/Govor.Application/Services/Friends/FriendRequestCommandService.cs +++ b/Govor.Application/Services/Friends/FriendRequestCommandService.cs @@ -15,7 +15,7 @@ public class FriendRequestCommandService : IFriendRequestCommandService _friendshipsRepository = friendshipsRepository; } - public async Task SendAsync(Guid fromUserId, Guid toUserId) + public async Task SendAsync(Guid fromUserId, Guid toUserId) { if (fromUserId == toUserId) throw new InvalidOperationException("Cannot send a request to self user"); @@ -23,16 +23,20 @@ public class FriendRequestCommandService : IFriendRequestCommandService if (_friendshipsRepository.Exist(fromUserId, toUserId)) throw new RequestAlreadySentException(fromUserId, toUserId); - await _friendshipsRepository.AddAsync(new Friendship + var friendship = new Friendship { Id = Guid.NewGuid(), RequesterId = fromUserId, AddresseeId = toUserId, Status = FriendshipStatus.Pending - }); + }; + + await _friendshipsRepository.AddAsync(friendship); + + return friendship; } - public async Task AcceptAsync(Guid requestId, Guid currentUserId) + public async Task AcceptAsync(Guid requestId, Guid currentUserId) { try { @@ -46,6 +50,8 @@ public class FriendRequestCommandService : IFriendRequestCommandService friendship.Status = FriendshipStatus.Accepted; await _friendshipsRepository.UpdateAsync(friendship); + + return friendship; } catch (NotFoundByKeyException ex) { @@ -53,7 +59,7 @@ public class FriendRequestCommandService : IFriendRequestCommandService } } - public async Task RejectAsync(Guid requestId, Guid currentUserId) + public async Task RejectAsync(Guid requestId, Guid currentUserId) { try { @@ -67,6 +73,7 @@ public class FriendRequestCommandService : IFriendRequestCommandService friendship.Status = FriendshipStatus.Rejected; await _friendshipsRepository.UpdateAsync(friendship); + return friendship; } catch (NotFoundByKeyException ex) { diff --git a/Govor.Application/Services/Messages/MediaService.cs b/Govor.Application/Services/Messages/MediaService.cs index 46df591..21bba28 100644 --- a/Govor.Application/Services/Messages/MediaService.cs +++ b/Govor.Application/Services/Messages/MediaService.cs @@ -1,20 +1,53 @@ using Govor.Application.Interfaces; using Govor.Application.Interfaces.Medias; +using Govor.Core.Models; +using Govor.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; namespace Govor.Application.Services.Messages; public class MediaService : IMediaService { + private ILogger _logger; private IStorageService _storageService; + private GovorDbContext _dbContext; - public MediaService(IStorageService storageService) + public MediaService(IStorageService storageService, GovorDbContext dbContext, ILogger logger) { _storageService = storageService; + _dbContext = dbContext; + _logger = logger; } - public Task UploadMediaAsync(Media file) + public async Task UploadMediaAsync(Media file) { - throw new NotImplementedException(); + try + { + var url = await _storageService.SaveAsync(file.Data, file.FileName); + + var mediaId = Guid.NewGuid(); + + _dbContext.MediaFiles.Add(new MediaFile() + { + Id = mediaId, + UploaderId = file.UploaderId, + DateCreated = file.UploadedOn, + MediaType = file.Type, + MineType = file.MineType, + Url = url + }); + + await _dbContext.SaveChangesAsync(); + + _logger.LogInformation($"Media uploaded: {url} with id: {mediaId} by {file.UploaderId}"); + + return new MediaUploadResult(mediaId, url); + } + catch (ArgumentException ex) + { + throw new InvalidOperationException($"An error occured while uploading the media file: {ex.Message}"); + } } public Task DeleteMediaAsync(Guid fileId) @@ -22,8 +55,46 @@ public class MediaService : IMediaService throw new NotImplementedException(); } - public Task GetMediaAsync(string url) + public Task GetMediaByUrlAsync(string url) { throw new NotImplementedException(); } + + public async Task GetMediaByIdAsync(Guid mediaId) + { + try + { + var mediaFile = await _dbContext.MediaFiles + .AsNoTracking() + .FirstOrDefaultAsync(x => x.Id == mediaId) + ?? throw new KeyNotFoundException("No media found"); + + // Загрузить бинарные данные из хранилища + Stream dataStream = await _storageService.LoadAsync(mediaFile.Url); + + // Считать поток в byte[] + using var memoryStream = new MemoryStream(); + await dataStream.CopyToAsync(memoryStream); + var contentBytes = memoryStream.ToArray(); + + _logger.LogInformation($"Media found: {mediaFile.MediaType} with id: {mediaFile.Id} and url: {mediaFile.Url}"); + + // Вернуть объект Media + return new Media( + mediaFile.UploaderId, + mediaFile.DateCreated, + contentBytes, + string.Empty, + mediaFile.MediaType, + mediaFile.MineType, + string.Empty + ); + } + catch (FileNotFoundException ex) + { + _logger.LogWarning(ex, "Media file not found on storage."); + throw; + } + } + } \ No newline at end of file diff --git a/Govor.Console/Commands/AcceptFriendRequestCommand.cs b/Govor.Console/Commands/AcceptFriendRequestCommand.cs deleted file mode 100644 index a81be53..0000000 --- a/Govor.Console/Commands/AcceptFriendRequestCommand.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.Threading.Tasks; -using Microsoft.AspNetCore.SignalR.Client; - -namespace Govor.ConsoleClient.Commands -{ - public class AcceptFriendRequestCommand : BaseCommand - { - public override async Task ExecuteAsync(string? argument) - { - if (!EnsureLoggedIn() || !EnsureHubConnection()) return; - - Guid friendshipId; - if (string.IsNullOrWhiteSpace(argument) || !Guid.TryParse(argument, out friendshipId)) - { - Console.Write("Введите ID заявки, которую хотите принять: "); - var input = Console.ReadLine(); - if (string.IsNullOrWhiteSpace(input) || !Guid.TryParse(input, out friendshipId)) - { - Console.WriteLine("[Ошибка] Неверный или пустой ID заявки."); - return; - } - } - - try - { - // API uses Hub for this: AcceptFriendRequest(Guid friendshipId) - await HubConnection.InvokeAsync("AcceptRequest", friendshipId); - Console.WriteLine("Заявка в друзья принята."); - } - catch (Exception ex) - { - Console.WriteLine($"[Ошибка принятия заявки] {ex.Message}"); - } - } - - public override string GetHelp() - { - return "/accept [ID_заявки] - Принять входящую заявку в друзья. Если ID не указан, запросит ввод."; - } - } -} diff --git a/Govor.Console/Commands/AdminListAllFriendshipsCommand.cs b/Govor.Console/Commands/AdminListAllFriendshipsCommand.cs deleted file mode 100644 index 226c547..0000000 --- a/Govor.Console/Commands/AdminListAllFriendshipsCommand.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net.Http; -using System.Net.Http.Json; -using System.Text.Json; -using System.Threading.Tasks; -using Govor.Contracts.DTOs; // Required for FriendshipDto - -namespace Govor.ConsoleClient.Commands -{ - public class AdminListAllFriendshipsCommand : BaseCommand - { - public override async Task ExecuteAsync(string? argument) - { - if (!EnsureLoggedIn()) return; - // Consider adding an admin role check here if possible, - // though the API itself is protected by [Authorize(Roles = "Admin")] - - Console.WriteLine("Получение всех дружеских связей (только для администраторов)..."); - try - { - var json = await HttpClientService.GetAsync("api/admin/Friendships"); - - var friendships = JsonSerializer.Deserialize>(json, new JsonSerializerOptions - { - PropertyNameCaseInsensitive = true - }); - - if (friendships != null && friendships.Any()) - { - Console.WriteLine("Все дружеские связи в системе:"); - foreach (var f in friendships) - { - Console.WriteLine($"- ID: {f.Id}, Пользователь1: {f.RequesterId}, Пользователь2: {f.AddresseeId}, Статус: {f.Status}"); - } - } - else - { - Console.WriteLine("Дружеские связи не найдены в системе."); - } - } - catch (HttpRequestException httpEx) when (httpEx.StatusCode == System.Net.HttpStatusCode.Forbidden) - { - Console.WriteLine("[Ошибка] Доступ запрещен. Эта команда только для администраторов."); - } - catch (Exception ex) - { - Console.WriteLine($"[Ошибка] {ex.Message}"); - } - } - - public override string GetHelp() - { - return "/adminlistallfs - (Админ) Показать все дружеские связи в системе."; - } - } -} diff --git a/Govor.Console/Commands/AdminListUserFriendshipsCommand.cs b/Govor.Console/Commands/AdminListUserFriendshipsCommand.cs deleted file mode 100644 index 96b4539..0000000 --- a/Govor.Console/Commands/AdminListUserFriendshipsCommand.cs +++ /dev/null @@ -1,65 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net.Http; -using System.Net.Http.Json; -using System.Text.Json; -using System.Threading.Tasks; -using Govor.Contracts.DTOs; // Required for FriendshipDto - -namespace Govor.ConsoleClient.Commands -{ - public class AdminListUserFriendshipsCommand : BaseCommand - { - public override async Task ExecuteAsync(string? argument) - { - if (!EnsureLoggedIn()) return; - - Guid userId; - if (string.IsNullOrWhiteSpace(argument) || !Guid.TryParse(argument, out userId)) - { - Console.Write("Введите ID пользователя для просмотра его связей: "); - var input = Console.ReadLine(); - if (string.IsNullOrWhiteSpace(input) || !Guid.TryParse(input, out userId)) - { - Console.WriteLine("[Ошибка] Неверный или пустой ID пользователя."); - return; - } - } - - Console.WriteLine($"Получение всех связей для пользователя {userId} (только для администраторов)..."); - try - { - var response = await HttpClientService.GetAsync($"api/admin/Friendships/{userId}"); - - var friendships = JsonSerializer.Deserialize>(response); - - if (friendships != null && friendships.Any()) - { - Console.WriteLine($"Дружеские связи для пользователя {userId}:"); - foreach (var f in friendships) - { - Console.WriteLine($"- ID: {f.Id}, Пользователь1: {f.RequesterId}, Пользователь2: {f.AddresseeId}, Статус: {f.Status}"); - } - } - else - { - Console.WriteLine($"Дружеские связи для пользователя {userId} не найдены."); - } - } - catch (HttpRequestException httpEx) when (httpEx.StatusCode == System.Net.HttpStatusCode.Forbidden) - { - Console.WriteLine("[Ошибка] Доступ запрещен. Эта команда только для администраторов."); - } - catch (Exception ex) - { - Console.WriteLine($"[Ошибка] {ex.Message}"); - } - } - - public override string GetHelp() - { - return "/adminlistuserfs [ID_пользователя] - (Админ) Показать все дружеские связи для указанного пользователя. Если ID не указан, запросит ввод."; - } - } -} diff --git a/Govor.Console/Commands/AdminRemoveFriendshipCommand.cs b/Govor.Console/Commands/AdminRemoveFriendshipCommand.cs deleted file mode 100644 index aa55ff9..0000000 --- a/Govor.Console/Commands/AdminRemoveFriendshipCommand.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System; -using System.Net.Http; -using System.Threading.Tasks; - -namespace Govor.ConsoleClient.Commands -{ - public class AdminRemoveFriendshipCommand : BaseCommand - { - public override async Task ExecuteAsync(string? argument) - { - if (!EnsureLoggedIn()) return; - - Guid friendshipId; - if (string.IsNullOrWhiteSpace(argument) || !Guid.TryParse(argument, out friendshipId)) - { - Console.Write("Введите ID дружеской связи для удаления: "); - var input = Console.ReadLine(); - if (string.IsNullOrWhiteSpace(input) || !Guid.TryParse(input, out friendshipId)) - { - Console.WriteLine("[Ошибка] Неверный или пустой ID связи."); - return; - } - } - - Console.WriteLine($"Удаление дружеской связи {friendshipId} (только для администраторов)..."); - try - { - // The API endpoint is: [HttpPost] public async Task RemoveFriendship(Guid Id) - // It expects the Id in the query string, like: api/admin/Friendships?Id=GUID - // Or it might be a POST with x-www-form-urlencoded data or JSON body, need to check API implementation. - // Based on `[HttpPost] public async Task RemoveFriendship(Guid Id)`, it's likely a query parameter or form data. - // Let's assume query parameter for now as it's simpler for HttpPost. - - var response = await HttpClientService.PostAsync($"api/admin/Friendships?Id={friendshipId}", null); - - Console.WriteLine($"Дружеская связь {friendshipId} успешно удалена."); - } - catch (HttpRequestException httpEx) when (httpEx.StatusCode == System.Net.HttpStatusCode.Forbidden) - { - Console.WriteLine("[Ошибка] Доступ запрещен. Эта команда только для администраторов."); - } - catch (HttpRequestException httpEx) when (httpEx.StatusCode == System.Net.HttpStatusCode.NotFound) - { - Console.WriteLine($"[Ошибка] Дружеская связь с ID {friendshipId} не найдена."); - } - catch (Exception ex) - { - Console.WriteLine($"[Ошибка] {ex.Message}"); - } - } - - public override string GetHelp() - { - return "/adminremovefs [ID_связи] - (Админ) Удалить дружескую связь по ее ID. Если ID не указан, запросит ввод."; - } - } -} diff --git a/Govor.Console/Commands/BaseCommand.cs b/Govor.Console/Commands/BaseCommand.cs deleted file mode 100644 index 9706fbe..0000000 --- a/Govor.Console/Commands/BaseCommand.cs +++ /dev/null @@ -1,65 +0,0 @@ -using Microsoft.AspNetCore.SignalR.Client; -using System; -using System.Threading.Tasks; - -namespace Govor.ConsoleClient.Commands -{ - public abstract class BaseCommand : ICommand - { - protected static FriendsClient? FriendsClient { get; private set; } - protected static HttpClientService HttpClientService { get; private set; } = null!; - protected static Func GetAuthToken { get; private set; } = null!; - protected static Action SetAuthToken { get; private set; } = null!; - protected static Func InitializeHubConnectionAsync { get; private set; } = null!; - protected static HubConnection HubConnection => _getHubConnection?.Invoke(); - - private static Func _getHubConnection = null!; - - public static void InitializeServices( - FriendsClient? friendsClient, - HttpClientService httpClientService, - Func getAuthToken, - Action setAuthToken, - Func initializeHubConnectionAsync, - Func getHubConnection) - { - FriendsClient = friendsClient; - HttpClientService = httpClientService; - GetAuthToken = getAuthToken; - SetAuthToken = setAuthToken; - InitializeHubConnectionAsync = initializeHubConnectionAsync; - _getHubConnection = getHubConnection; - } - - public abstract Task ExecuteAsync(string? argument); - public abstract string GetHelp(); - - protected bool EnsureLoggedIn() - { - if (GetAuthToken() == null) - { - Console.WriteLine("[Ошибка] Сначала войдите в систему. Используйте /login или /reg."); - return false; - } - - if (FriendsClient == null && !(this is LoginCommand || this is RegisterCommand)) - { - Console.WriteLine("[Ошибка] Клиент для работы с друзьями не инициализирован. Попробуйте войти снова."); - return false; - } - - return true; - } - - protected bool EnsureHubConnection() - { - var hub = _getHubConnection?.Invoke(); - if (hub == null || hub.State != HubConnectionState.Connected) - { - Console.WriteLine("[Ошибка] SignalR соединение не установлено. Попробуйте войти снова или проверьте соединение."); - return false; - } - return true; - } - } -} diff --git a/Govor.Console/Commands/BlockUserCommand.cs b/Govor.Console/Commands/BlockUserCommand.cs deleted file mode 100644 index bf83a39..0000000 --- a/Govor.Console/Commands/BlockUserCommand.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System; -using System.Threading.Tasks; -using Microsoft.AspNetCore.SignalR.Client; - -namespace Govor.ConsoleClient.Commands -{ - public class BlockUserCommand : BaseCommand - { - public override async Task ExecuteAsync(string? argument) - { - if (!EnsureLoggedIn() || !EnsureHubConnection()) return; - - Guid targetUserId; - if (string.IsNullOrWhiteSpace(argument) || !Guid.TryParse(argument, out targetUserId)) - { - Console.Write("Введите ID пользователя, которого хотите заблокировать: "); - var input = Console.ReadLine(); - if (string.IsNullOrWhiteSpace(input) || !Guid.TryParse(input, out targetUserId)) - { - Console.WriteLine("[Ошибка] Неверный или пустой ID пользователя."); - return; - } - } - - try - { - // API uses Hub for this: BlockUser(Guid targetUserId) - // Located in Govor.API/Hubs/FriendsHub.cs - await HubConnection.InvokeAsync("BlockUser", targetUserId); - Console.WriteLine($"Пользователь {targetUserId} заблокирован."); - } - catch (Exception ex) - { - Console.WriteLine($"[Ошибка блокировки пользователя] {ex.Message}"); - } - } - - public override string GetHelp() - { - return "/block [ID_пользователя] - Заблокировать пользователя. Если ID не указан, запросит ввод."; - } - } -} diff --git a/Govor.Console/Commands/HelpCommand.cs b/Govor.Console/Commands/HelpCommand.cs deleted file mode 100644 index c331a83..0000000 --- a/Govor.Console/Commands/HelpCommand.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace Govor.ConsoleClient.Commands -{ - public class HelpCommand : BaseCommand - { - private readonly Dictionary _availableCommands; - - // The HelpCommand needs access to the list of all commands to display their help messages. - // This will be injected from Program.cs where the commands are registered. - public HelpCommand(Dictionary availableCommands) - { - _availableCommands = availableCommands; - } - - public override Task ExecuteAsync(string? argument) - { - Console.WriteLine("Доступные команды:"); - if (!string.IsNullOrWhiteSpace(argument)) - { - string commandKey = argument.StartsWith("/") ? argument : "/" + argument; - if (_availableCommands.TryGetValue(commandKey.ToLower(), out var specificCommand)) - { - Console.WriteLine(specificCommand.GetHelp()); - } - else - { - Console.WriteLine($"Команда '{argument}' не найдена. Введите /help для списка всех команд."); - } - } - else - { - foreach (var commandEntry in _availableCommands.OrderBy(c => c.Key)) - { - Console.WriteLine(commandEntry.Value.GetHelp()); - } - } - return Task.CompletedTask; - } - - public override string GetHelp() - { - return "/help [команда] - Показать список всех команд или помощь по конкретной команде."; - } - } -} diff --git a/Govor.Console/Commands/ICommand.cs b/Govor.Console/Commands/ICommand.cs deleted file mode 100644 index 0e3c8ae..0000000 --- a/Govor.Console/Commands/ICommand.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Govor.ConsoleClient.Commands -{ - public interface ICommand - { - Task ExecuteAsync(string? argument); - string GetHelp(); - } -} diff --git a/Govor.Console/Commands/ListFriendsCommand.cs b/Govor.Console/Commands/ListFriendsCommand.cs deleted file mode 100644 index 1d4cf92..0000000 --- a/Govor.Console/Commands/ListFriendsCommand.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; -using System.Linq; -using System.Threading.Tasks; - -namespace Govor.ConsoleClient.Commands -{ - public class ListFriendsCommand : BaseCommand - { - public override async Task ExecuteAsync(string? argument) - { - if (!EnsureLoggedIn()) return; - - try - { - var friends = await FriendsClient.GetFriendsAsync(); - if (friends.Any()) - { - Console.WriteLine("Ваши друзья:"); - foreach (var f in friends) - { - Console.WriteLine($"- {f.Username} | был онлайн: {f.WasOnline} [{f.Id}]"); - } - } - else - { - Console.WriteLine("У вас пока нет друзей."); - } - } - catch (Exception ex) - { - Console.WriteLine($"[Ошибка] {ex.Message}"); - } - } - - public override string GetHelp() - { - return "/friends - Показать список ваших друзей."; - } - } -} diff --git a/Govor.Console/Commands/ListIncomingRequestsCommand.cs b/Govor.Console/Commands/ListIncomingRequestsCommand.cs deleted file mode 100644 index 3bfa6a7..0000000 --- a/Govor.Console/Commands/ListIncomingRequestsCommand.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System; -using System.Linq; -using System.Threading.Tasks; - -namespace Govor.ConsoleClient.Commands -{ - public class ListIncomingRequestsCommand : BaseCommand - { - public override async Task ExecuteAsync(string? argument) - { - if (!EnsureLoggedIn()) return; - - try - { - // This still uses REST client as per existing FriendsClient - var requests = await FriendsClient.GetIncomingRequestsAsync(); - if (requests.Any()) - { - Console.WriteLine("Входящие заявки в друзья:"); - foreach (var r in requests) - { - // Assuming you want to show who sent the request. - // The FriendshipDto contains RequesterId and AddresseeId. - // If current user is AddresseeId, then RequesterId is the one who sent it. - Console.WriteLine($"- Запрос ID: {r.Id}. От пользователя ID: {r.RequesterId}. Статус: {r.Status}. (принять через /accept {r.Id})"); - } - } - else - { - Console.WriteLine("У вас нет входящих заявок в друзья."); - } - } - catch (Exception ex) - { - Console.WriteLine($"[Ошибка] {ex.Message}"); - } - } - - public override string GetHelp() - { - return "/incoming - Показать список входящих заявок в друзья."; - } - } -} diff --git a/Govor.Console/Commands/ListOutgoingRequestsCommand.cs b/Govor.Console/Commands/ListOutgoingRequestsCommand.cs deleted file mode 100644 index ffa272d..0000000 --- a/Govor.Console/Commands/ListOutgoingRequestsCommand.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System; -using System.Linq; -using System.Net.Http; -using System.Net.Http.Json; -using System.Text.Json; -using System.Threading.Tasks; -using Govor.Contracts.DTOs; // Required for FriendshipDto - -namespace Govor.ConsoleClient.Commands -{ - public class ListOutgoingRequestsCommand : BaseCommand - { - public override async Task ExecuteAsync(string? argument) - { - if (!EnsureLoggedIn()) return; - - try - { - // This functionality maps to GET api/friends/responses in FriendsRequestQueryController - // FriendsClient doesn't have a dedicated method, so we use HttpClientService or direct HttpClient - var json = await HttpClientService.GetAsync("api/friends/responses"); - - var requests = JsonSerializer.Deserialize>(json, new JsonSerializerOptions - { - PropertyNameCaseInsensitive = true - }); - - if (requests != null && requests.Any()) - { - Console.WriteLine("Отправленные вами заявки в друзья (ожидают ответа):"); - foreach (var r in requests) - { - // If current user is RequesterId, then AddresseeId is the one they sent to. - Console.WriteLine($"- Запрос ID: {r.Id}. Пользователю ID: {r.AddresseeId}. Статус: {r.Status}."); - } - } - else - { - Console.WriteLine("У вас нет отправленных заявок, ожидающих ответа."); - } - } - catch (Exception ex) - { - Console.WriteLine($"[Ошибка] {ex.Message}"); - } - } - - public override string GetHelp() - { - return "/outgoing - Показать список отправленных вами заявок в друзья, ожидающих ответа."; - } - } -} diff --git a/Govor.Console/Commands/LoginCommand.cs b/Govor.Console/Commands/LoginCommand.cs deleted file mode 100644 index 9f2d017..0000000 --- a/Govor.Console/Commands/LoginCommand.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System; -using System.Net.Http.Headers; -using System.Threading.Tasks; - -namespace Govor.ConsoleClient.Commands -{ - public class LoginCommand : BaseCommand - { - public override async Task ExecuteAsync(string? argument) - { - Console.Write("username: "); - var loginUsername = Console.ReadLine(); - - Console.Write("password: "); - var loginPassword = Console.ReadLine(); - - if (string.IsNullOrWhiteSpace(loginUsername) || string.IsNullOrWhiteSpace(loginPassword)) - { - Console.WriteLine("[Ошибка] Имя пользователя и пароль не могут быть пустыми."); - return; - } - - try - { - var authToken = await HttpClientService.LoginAsync(loginUsername, loginPassword); - SetAuthToken(authToken); - - // Initialize FriendsClient after successful login - var sharedClient = new HttpClient { BaseAddress = new Uri(HttpClientService.GetBaseUrl()) }; - sharedClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authToken); - var friendsClient = new FriendsClient(sharedClient); - - - - Program.UpdateFriendsClient(friendsClient); // <-- единственный нужный вызов - - await InitializeHubConnectionAsync(); - Console.WriteLine("[Успех] Вход выполнен. Токен сохранен."); - } - catch (Exception ex) - { - Console.WriteLine($"[Ошибка] {ex.Message}"); - } - } - - public override string GetHelp() - { - return "/login - Войти в существующий аккаунт."; - } - } -} diff --git a/Govor.Console/Commands/RegisterCommand.cs b/Govor.Console/Commands/RegisterCommand.cs deleted file mode 100644 index 2dd84e7..0000000 --- a/Govor.Console/Commands/RegisterCommand.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System; -using System.Net.Http.Headers; -using System.Threading.Tasks; - -namespace Govor.ConsoleClient.Commands -{ - public class RegisterCommand : BaseCommand - { - public override async Task ExecuteAsync(string? argument) - { - Console.Write("username: "); - var regUsername = Console.ReadLine(); - - Console.Write("password: "); - var regPassword = Console.ReadLine(); - - Console.Write("invitation code: "); - var inviteCode = Console.ReadLine(); - - if (string.IsNullOrWhiteSpace(regUsername) || string.IsNullOrWhiteSpace(regPassword) || string.IsNullOrWhiteSpace(inviteCode)) - { - Console.WriteLine("[Ошибка] Имя пользователя, пароль и код приглашения не могут быть пустыми."); - return; - } - - try - { - var authToken = await HttpClientService.RegisterAsync(regUsername, regPassword, inviteCode); - SetAuthToken(authToken); - - // Initialize FriendsClient after successful registration - var sharedClient = new HttpClient { BaseAddress = new Uri(HttpClientService.GetBaseUrl()) }; - sharedClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authToken); - var friendsClient = new FriendsClient(sharedClient); - - // Re-initialize services in BaseCommand with the new FriendsClient - Program.UpdateFriendsClient(friendsClient); // <-- единственный нужный вызов - - await InitializeHubConnectionAsync(); - Console.WriteLine("[Успех] Регистрация завершена. Токен сохранен."); - } - catch (Exception ex) - { - Console.WriteLine($"[Ошибка] {ex.Message}"); - } - } - - public override string GetHelp() - { - return "/reg - Зарегистрировать новый аккаунт с помощью кода приглашения."; - } - } -} diff --git a/Govor.Console/Commands/RejectFriendRequestCommand.cs b/Govor.Console/Commands/RejectFriendRequestCommand.cs deleted file mode 100644 index afd9673..0000000 --- a/Govor.Console/Commands/RejectFriendRequestCommand.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System; -using System.Threading.Tasks; -using Microsoft.AspNetCore.SignalR.Client; - -namespace Govor.ConsoleClient.Commands -{ - public class RejectFriendRequestCommand : BaseCommand - { - public override async Task ExecuteAsync(string? argument) - { - if (!EnsureLoggedIn() || !EnsureHubConnection()) return; - - Guid friendshipId; - if (string.IsNullOrWhiteSpace(argument) || !Guid.TryParse(argument, out friendshipId)) - { - Console.Write("Введите ID заявки, которую хотите отклонить: "); - var input = Console.ReadLine(); - if (string.IsNullOrWhiteSpace(input) || !Guid.TryParse(input, out friendshipId)) - { - Console.WriteLine("[Ошибка] Неверный или пустой ID заявки."); - return; - } - } - - try - { - // API uses Hub for this: RejectFriendRequest(Guid friendshipId) - // Located in Govor.API/Hubs/FriendsHub.cs - await HubConnection.InvokeAsync("RejectRequest", friendshipId); - Console.WriteLine("Заявка в друзья отклонена."); - } - catch (Exception ex) - { - Console.WriteLine($"[Ошибка отклонения заявки] {ex.Message}"); - } - } - - public override string GetHelp() - { - return "/reject [ID_заявки] - Отклонить входящую заявку в друзья. Если ID не указан, запросит ввод."; - } - } -} diff --git a/Govor.Console/Commands/SearchUserCommand.cs b/Govor.Console/Commands/SearchUserCommand.cs deleted file mode 100644 index 1c00d31..0000000 --- a/Govor.Console/Commands/SearchUserCommand.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System; -using System.Linq; -using System.Threading.Tasks; - -namespace Govor.ConsoleClient.Commands -{ - public class SearchUserCommand : BaseCommand - { - public override async Task ExecuteAsync(string? argument) - { - if (!EnsureLoggedIn()) return; - - if (string.IsNullOrWhiteSpace(argument)) - { - Console.Write("Введите имя пользователя для поиска: "); - argument = Console.ReadLine(); - if (string.IsNullOrWhiteSpace(argument)) - { - Console.WriteLine("[Ошибка] Поисковый запрос не может быть пустым."); - return; - } - } - - try - { - var foundUsers = await FriendsClient.SearchAsync(argument); - if (foundUsers.Any()) - { - Console.WriteLine("Найденные пользователи:"); - foreach (var user in foundUsers) - { - Console.WriteLine($"- {user.Username} [{user.Id}]"); - } - } - else - { - Console.WriteLine("Пользователи не найдены."); - } - } - catch (Exception ex) - { - Console.WriteLine($"[Ошибка] {ex.Message}"); - } - } - - public override string GetHelp() - { - return "/search [имя_пользователя] - Найти пользователей по имени. Если имя не указано, запросит ввод."; - } - } -} diff --git a/Govor.Console/Commands/SendFriendRequestCommand.cs b/Govor.Console/Commands/SendFriendRequestCommand.cs deleted file mode 100644 index 62c65fe..0000000 --- a/Govor.Console/Commands/SendFriendRequestCommand.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.Threading.Tasks; -using Microsoft.AspNetCore.SignalR.Client; - -namespace Govor.ConsoleClient.Commands -{ - public class SendFriendRequestCommand : BaseCommand - { - public override async Task ExecuteAsync(string? argument) - { - if (!EnsureLoggedIn() || !EnsureHubConnection()) return; - - Guid targetUserId; - if (string.IsNullOrWhiteSpace(argument) || !Guid.TryParse(argument, out targetUserId)) - { - Console.Write("Введите ID пользователя, которому хотите отправить запрос: "); - var input = Console.ReadLine(); - if (string.IsNullOrWhiteSpace(input) || !Guid.TryParse(input, out targetUserId)) - { - Console.WriteLine("[Ошибка] Неверный или пустой ID пользователя."); - return; - } - } - - try - { - // API uses Hub for this: SendFriendRequest(Guid targetUserId) - await HubConnection.InvokeAsync("SendRequest", targetUserId); - Console.WriteLine("Запрос в друзья отправлен."); - } - catch (Exception ex) - { - Console.WriteLine($"[Ошибка отправки запроса] {ex.Message}"); - } - } - - public override string GetHelp() - { - return "/friend [ID_пользователя] - Отправить запрос в друзья пользователю с указанным ID. Если ID не указан, запросит ввод."; - } - } -} diff --git a/Govor.Console/Commands/UnblockUserCommand.cs b/Govor.Console/Commands/UnblockUserCommand.cs deleted file mode 100644 index 5fa17fc..0000000 --- a/Govor.Console/Commands/UnblockUserCommand.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System; -using System.Threading.Tasks; -using Microsoft.AspNetCore.SignalR.Client; - -namespace Govor.ConsoleClient.Commands -{ - public class UnblockUserCommand : BaseCommand - { - public override async Task ExecuteAsync(string? argument) - { - if (!EnsureLoggedIn() || !EnsureHubConnection()) return; - - Guid targetUserId; - if (string.IsNullOrWhiteSpace(argument) || !Guid.TryParse(argument, out targetUserId)) - { - Console.Write("Введите ID пользователя, которого хотите разблокировать: "); - var input = Console.ReadLine(); - if (string.IsNullOrWhiteSpace(input) || !Guid.TryParse(input, out targetUserId)) - { - Console.WriteLine("[Ошибка] Неверный или пустой ID пользователя."); - return; - } - } - - try - { - // API uses Hub for this: UnblockUser(Guid targetUserId) - // Located in Govor.API/Hubs/FriendsHub.cs - await HubConnection.InvokeAsync("UnblockUser", targetUserId); - Console.WriteLine($"Пользователь {targetUserId} разблокирован."); - } - catch (Exception ex) - { - Console.WriteLine($"[Ошибка разблокировки пользователя] {ex.Message}"); - } - } - - public override string GetHelp() - { - return "/unblock [ID_пользователя] - Разблокировать пользователя. Если ID не указан, запросит ввод."; - } - } -} diff --git a/Govor.Console/FriendsHubClient.cs b/Govor.Console/FriendsHubClient.cs index f8667d2..fdc342b 100644 --- a/Govor.Console/FriendsHubClient.cs +++ b/Govor.Console/FriendsHubClient.cs @@ -1,157 +1,70 @@ +using Microsoft.AspNetCore.SignalR.Client; using System; using System.Threading.Tasks; -using Microsoft.AspNetCore.SignalR; -using Microsoft.AspNetCore.SignalR.Client; +using Govor.Contracts.Responses.SignalR; -class FriendsHubClient +namespace Govor.ConsoleClient; + + +public class FriendsHubClient { - private readonly HubConnection _connection; + private readonly string _hubUrl; + private readonly string _jwtToken; + private HubConnection _connection; public FriendsHubClient(string hubUrl, string jwtToken) + { + _hubUrl = hubUrl; + _jwtToken = jwtToken; + } + + public async Task ConnectAsync() { _connection = new HubConnectionBuilder() - .WithUrl(hubUrl, options => + .WithUrl($"{_hubUrl}/friends", options => { - options.AccessTokenProvider = () => Task.FromResult(jwtToken); + options.AccessTokenProvider = () => Task.FromResult(_jwtToken); }) .WithAutomaticReconnect() .Build(); - // Подписка на события от сервера - _connection.On("FriendRequestReceived", OnFriendRequestReceived); - _connection.On("FriendRequestAccepted", OnFriendRequestAccepted); - _connection.On("FriendRequestRejected", OnFriendRequestRejected); - } + // Событие: получен входящий запрос в друзья + _connection.On("FriendRequestReceived", userId => + { + Console.WriteLine($"📨 Friend request received from: {userId}"); + }); - private void OnFriendRequestReceived(Guid userId) - { - Console.WriteLine($"[Event] Получен запрос в друзья от пользователя: {userId}"); - } + // Событие: запрос принят + _connection.On("FriendRequestAccepted", friendshipId => + { + Console.WriteLine($"✅ Friend request accepted! Friendship ID: {friendshipId}"); + }); - private void OnFriendRequestAccepted(Guid friendshipId) - { - Console.WriteLine($"[Event] Запрос в друзья принят. ID дружбы: {friendshipId}"); - } + // Событие: запрос отклонён + _connection.On("FriendRequestRejected", friendshipId => + { + Console.WriteLine($"❌ Friend request rejected! Friendship ID: {friendshipId}"); + }); - private void OnFriendRequestRejected(Guid friendshipId) - { - Console.WriteLine($"[Event] Запрос в друзья отклонён. ID дружбы: {friendshipId}"); - } - - public async Task StartAsync() - { await _connection.StartAsync(); - Console.WriteLine("Подключено к FriendsHub"); + Console.WriteLine("✅ Connected to FriendsHub"); } - public async Task StopAsync() + public async Task SendFriendRequestAsync(Guid targetUserId) { - await _connection.StopAsync(); - Console.WriteLine("Отключено от FriendsHub"); + var result = await _connection.InvokeAsync>("SendRequest", targetUserId); + Console.WriteLine($"📤 Friend request sent. Status: {result.Status}"); } - public async Task SendRequest(Guid targetUserId) + public async Task AcceptFriendRequestAsync(Guid friendshipId) { - try - { - await _connection.InvokeAsync("SendRequest", targetUserId); - Console.WriteLine($"Запрос на добавление в друзья отправлен пользователю {targetUserId}"); - } - catch (HubException ex) - { - Console.WriteLine($"Ошибка при отправке запроса: {ex.Message}"); - } + var result = await _connection.InvokeAsync>("AcceptRequest", friendshipId); + Console.WriteLine($"👍 Accept result: {result.Status}"); } - public async Task AcceptRequest(Guid friendshipId) + public async Task RejectFriendRequestAsync(Guid friendshipId) { - try - { - await _connection.InvokeAsync("AcceptRequest", friendshipId); - Console.WriteLine($"Запрос в друзья с ID {friendshipId} принят"); - } - catch (HubException ex) - { - Console.WriteLine($"Ошибка при принятии запроса: {ex.Message}"); - } - } - - public async Task RejectRequest(Guid friendshipId) - { - try - { - await _connection.InvokeAsync("RejectRequest", friendshipId); - Console.WriteLine($"Запрос в друзья с ID {friendshipId} отклонён"); - } - catch (HubException ex) - { - Console.WriteLine($"Ошибка при отклонении запроса: {ex.Message}"); - } - } -} - -class Program -{ - static async Task TestMain(string[] args) - { - // Адрес хаба SignalR - string hubUrl = "https://yourserver.com/api/friends"; - - // JWT токен для аутентификации (замени на настоящий) - string jwtToken = "your_jwt_token_here"; - - var client = new FriendsHubClient(hubUrl, jwtToken); - - await client.StartAsync(); - - Console.WriteLine("Команды:"); - Console.WriteLine("send - Отправить запрос в друзья"); - Console.WriteLine("accept - Принять запрос"); - Console.WriteLine("reject - Отклонить запрос"); - Console.WriteLine("exit - Выйти"); - - while (true) - { - Console.Write("> "); - var input = Console.ReadLine(); - if (string.IsNullOrWhiteSpace(input)) - continue; - - var parts = input.Split(' ', 2, StringSplitOptions.RemoveEmptyEntries); - var command = parts[0].ToLowerInvariant(); - - if (command == "exit") - break; - - if (parts.Length < 2) - { - Console.WriteLine("Ошибка: не хватает аргументов"); - continue; - } - - if (!Guid.TryParse(parts[1], out var id)) - { - Console.WriteLine("Ошибка: некорректный GUID"); - continue; - } - - switch (command) - { - case "send": - await client.SendRequest(id); - break; - case "accept": - await client.AcceptRequest(id); - break; - case "reject": - await client.RejectRequest(id); - break; - default: - Console.WriteLine("Неизвестная команда"); - break; - } - } - - await client.StopAsync(); + var result = await _connection.InvokeAsync>("RejectRequest", friendshipId); + Console.WriteLine($"👎 Reject result: {result.Status}"); } } diff --git a/Govor.Console/Govor.Console.csproj b/Govor.Console/Govor.Console.csproj index 6f99954..0f9919f 100644 --- a/Govor.Console/Govor.Console.csproj +++ b/Govor.Console/Govor.Console.csproj @@ -10,6 +10,7 @@ + diff --git a/Govor.Console/Program.cs b/Govor.Console/Program.cs index 2247a05..9ac0bbf 100644 --- a/Govor.Console/Program.cs +++ b/Govor.Console/Program.cs @@ -1,382 +1,53 @@ -using Microsoft.AspNetCore.SignalR.Client; -using System; -using System.Collections.Generic; -using System.IdentityModel.Tokens.Jwt; -using System.Linq; -using System.Net.Http; -using System.Net.Http.Headers; -using System.Threading.Tasks; -using Govor.ConsoleClient.Commands; -using Govor.Contracts.Requests.SignalR; -using Govor.Contracts.Responses.SignalR; -using Govor.Core.Models; -using Govor.Core.Models.Messages; +using Govor.ConsoleClient; -/*==================================== - *Личные сообщения| Егор - *==================================== - * [15:59] Вы добавили (Егор) в друзья - * - * [16:30] Егор >> Привет! - * [16:31] Ты >> Че как? - * - *------------------------------------ - * >> "Пойдем завтра гулять?!" - */ - -namespace Govor.ConsoleClient +internal class Program { - class Program + private const string HubBaseUrl = "https://govor-team-govor-88b3.twc1.net/hubs"; + private static string jwtToken = "eyJhbGciOiJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGRzaWctbW9yZSNobWFjLXNoYTI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI4NDRjNzkyMi1hNjdlLTRlMzctYWI0MC0wNThlZDE5NzM5NjMiLCJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dzLzIwMDgvMDYvaWRlbnRpdHkvY2xhaW1zL3JvbGUiOiJBZG1pbiIsImV4cCI6MTc1MjQ4MzE0MH0.ECXLPwIljdeWgWUJtBiXXQXKMFC8Iw0x7_zdjbsUe1I"; // сюда подставьте валидный JWT + + static async Task Main() { - static string baseUrl = "https://govor-team-govor-88b3.twc1.net"; - //static string baseUrl = "https://localhost:7155"; - static string? AuthToken = null; - static HttpClientService HttpService = new(baseUrl); - private static FriendsClient? _friendsClient; // Renamed to avoid conflict with BaseCommand - static HubConnection? _hubConnection; - static Dictionary> ChatHistory = new(); - static string? CurrentChatUser = null; - static Guid CurrentChatUserId = Guid.Empty; + Console.Write("🔑 JWT Token: "); + jwtToken = Console.ReadLine(); - private static Dictionary _commands = new(); + var client = new FriendsHubClient(HubBaseUrl, jwtToken); + await client.ConnectAsync(); - static async Task Main() + while (true) { - FriendsHubClient client = new FriendsHubClient($"{baseUrl}/hubs/friends", "eyJhbGciOiJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGRzaWctbW9yZSNobWFjLXNoYTI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI4NDRjNzkyMi1hNjdlLTRlMzctYWI0MC0wNThlZDE5NzM5NjMiLCJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dzLzIwMDgvMDYvaWRlbnRpdHkvY2xhaW1zL3JvbGUiOiJBZG1pbiIsImV4cCI6MTc1MjMzMjE2NX0.mGSjSCvguEXkdHqHcbZ3eUH0S8c82kz3XP89potEh1k"); - - await client.StartAsync(); - await client.AcceptRequest(Guid.Parse("3ba77c0c-7522-47be-8e77-ea47bd6c6e69")); - - return; - InitializeCommands(); - BaseCommand.InitializeServices( - _friendsClient, // Initially null, will be set by Login/Register commands - HttpService, - () => AuthToken, - (token) => AuthToken = token, - InitializeHubConnection, - () => _hubConnection - ); + Console.WriteLine("\n1. Send friend request"); + Console.WriteLine("2. Accept request"); + Console.WriteLine("3. Reject request"); + Console.Write("➡ Choose option: "); + var option = Console.ReadLine(); - LoginWindow(); - - while (true) + switch (option) { - Console.Write(CurrentChatUser == null ? "/> " : ">> "); - var input = Console.ReadLine(); - - if (string.IsNullOrWhiteSpace(input)) continue; - - if (input.StartsWith("/")) - await HandleCommandAsync(input); - else if (CurrentChatUser != null) - await SendMessageAsync(input); - else - Console.WriteLine("[Система] Введите команду, например /help или /friends"); + case "1": + Console.Write("Target UserId: "); + if (Guid.TryParse(Console.ReadLine(), out var targetId)) + { + await client.SendFriendRequestAsync(targetId); + } + break; + case "2": + Console.Write("FriendshipId to accept: "); + if (Guid.TryParse(Console.ReadLine(), out var acceptId)) + { + await client.AcceptFriendRequestAsync(acceptId); + } + break; + case "3": + Console.Write("FriendshipId to reject: "); + if (Guid.TryParse(Console.ReadLine(), out var rejectId)) + { + await client.RejectFriendRequestAsync(rejectId); + } + break; + default: + Console.WriteLine("❗ Unknown option"); + break; } } - - static void InitializeCommands() - { - _commands = new Dictionary - { - { "/login", new LoginCommand() }, - { "/reg", new RegisterCommand() }, - { "/search", new SearchUserCommand() }, - { "/friends", new ListFriendsCommand() }, - { "/friend", new SendFriendRequestCommand() }, // Alias for SendFriendRequest - { "/sendrequest", new SendFriendRequestCommand() }, - { "/accept", new AcceptFriendRequestCommand() }, - { "/reject", new RejectFriendRequestCommand() }, - { "/incoming", new ListIncomingRequestsCommand() }, - { "/outgoing", new ListOutgoingRequestsCommand() }, - { "/block", new BlockUserCommand() }, - { "/unblock", new UnblockUserCommand() }, - - { "/adminlistallfs", new AdminListAllFriendshipsCommand() }, - { "/adminlistuserfs", new AdminListUserFriendshipsCommand() }, - { "/adminremovefs", new AdminRemoveFriendshipCommand() }, - // Help command is special as it needs the list of commands - // It will be added after other commands are initialized - }; - _commands.Add("/help", new HelpCommand(_commands)); - _commands.Add("-h", new HelpCommand(_commands)); // Alias for help - - // Add other general commands not refactored yet if any, or create classes for them - // For example, /exit, /chat, /back will be handled separately for now or made into commands - } - - // Public static method to allow commands (like Login/Register) to update the FriendsClient - public static void UpdateFriendsClient(FriendsClient newFriendsClient) - { - _friendsClient = newFriendsClient; - // Re-initialize services in BaseCommand with the new FriendsClient - BaseCommand.InitializeServices( - _friendsClient, - HttpService, - () => AuthToken, - (token) => AuthToken = token, - InitializeHubConnection, - () => _hubConnection - ); - } - - - static async Task HandleCommandAsync(string input) - { - var parts = input.Split(new[] { ' ' }, 2, StringSplitOptions.RemoveEmptyEntries); - var commandKey = parts[0].ToLower(); - var argument = parts.Length > 1 ? parts[1] : null; - - if (_commands.TryGetValue(commandKey, out var commandInstance)) - { - try - { - await commandInstance.ExecuteAsync(argument); - } - catch (Exception ex) - { - Console.WriteLine($"[Критическая ошибка выполнения команды {commandKey}]: {ex.Message}"); - // Log detailed exception: Console.WriteLine(ex.ToString()); - } - } - else - { - // Handle non-refactored commands for now - switch (commandKey) - { - case "/exit": - Console.WriteLine("[Система] Выход..."); - if (_hubConnection != null) - { - await _hubConnection.StopAsync(); - await _hubConnection.DisposeAsync(); - } - Environment.Exit(0); - break; - case "/chat": - if (string.IsNullOrEmpty(argument)) - { - Console.WriteLine("[Ошибка] Укажите имя друга и ID через пробел (например /chat Egor GUID)"); - break; - } - - var chatArgs = argument.Split(' ', 2); - if (chatArgs.Length < 2 || !Guid.TryParse(chatArgs[1], out var chatUserId)) - { - Console.WriteLine("[Ошибка] Неверный формат. Укажите имя друга и ID (например /chat Egor GUID)"); - break; - } - - CurrentChatUser = chatArgs[0]; - CurrentChatUserId = chatUserId; - ShowChat(CurrentChatUser); - break; - - case "/back": - CurrentChatUser = null; - CurrentChatUserId = Guid.Empty; - Console.Clear(); // Optionally clear console when going back - LoginWindow(); // Or some other general view - break; - default: - Console.WriteLine($"[Ошибка] Неизвестная команда: {commandKey}. Введите /help для списка команд."); - break; - } - } - } - - static async Task InitializeHubConnection() - { - if (string.IsNullOrEmpty(AuthToken)) - { - // This check might be redundant if commands calling this ensure login first - Console.WriteLine("[Ошибка] Токен аутентификации отсутствует. Пожалуйста, войдите в систему."); - return; - } - - if (_hubConnection != null && _hubConnection.State != HubConnectionState.Disconnected) - { - Console.WriteLine("[SignalR] Соединение уже установлено или устанавливается."); - return; - } - - _hubConnection = new HubConnectionBuilder() - .WithUrl($"{baseUrl}/hubs/chats", options => - { - options.AccessTokenProvider = () => Task.FromResult(AuthToken); - }) - .Build(); - - _hubConnection.On("ReceiveMessage", (message) => - { - var myId = GetMyUserId(); - var senderName = message.SenderId == myId ? "Ты" : CurrentChatUser ?? message.SenderId.ToString() ?? "Неизвестно"; - var displayMessage = $"[{message.SentAt:HH:mm}] {senderName} >> {message.EncryptedContent}"; - - // Determine chat key based on sender/recipient, ensuring consistency - string chatKey = message.SenderId == myId ? message.RecipientId.ToString() : message.SenderId.ToString(); - string activeChatDisplayUser = CurrentChatUser; // User whose chat window is open - - // If the message belongs to the currently active chat window - if (CurrentChatUser != null && (message.SenderId == CurrentChatUserId || (message.RecipientId == CurrentChatUserId && message.SenderId == myId))) - { - if (!ChatHistory.ContainsKey(activeChatDisplayUser)) - { - ChatHistory[activeChatDisplayUser] = new List(); - } - ChatHistory[activeChatDisplayUser].Add(displayMessage); - - // Smartly update console without disrupting user input too much - int originalCursorTop = Console.CursorTop; - int originalCursorLeft = Console.CursorLeft; - Console.MoveBufferArea(0, originalCursorTop, Console.BufferWidth, 1, 0, originalCursorTop +1); // scroll current line down - Console.SetCursorPosition(0, originalCursorTop); - Console.WriteLine($" * {displayMessage}"); - Console.SetCursorPosition(originalCursorLeft, originalCursorTop +1); // +1 because writeline added a line - Console.Write(">> "); // Re-display prompt part - } - else // Notification for message not in the current chat - { - Console.WriteLine($"\n[Новое сообщение от {message.SenderId.ToString()}]: {message.EncryptedContent}"); - Console.Write(CurrentChatUser == null ? "/> " : ">> "); // Re-display prompt - } - }); - - _hubConnection.On("MessageSent", (message) => - { - // Optional: Handle confirmation that message was sent successfully by the server - // For console, maybe a subtle notification or log if verbose mode is on. - // Example: Console.WriteLine($"[Система] Сообщение для {message.RecipientUsername} доставлено на сервер."); - }); - - _hubConnection.On("FriendRequestReceived", (message) => // Assuming message is a simple string notification - { - Console.WriteLine($"\n[Система] Новый запрос в друзья: {message}"); - Console.Write(CurrentChatUser == null ? "/> " : ">> "); - }); - - _hubConnection.On("FriendRequestAccepted", (message) => - { - Console.WriteLine($"\n[Система] Ваш запрос в друзья принят: {message}"); - Console.Write(CurrentChatUser == null ? "/> " : ">> "); - }); - - _hubConnection.On("FriendRemoved", (message) => - { - Console.WriteLine($"\n[Система] Пользователь удалил вас из друзей или вы удалили его: {message}"); - Console.Write(CurrentChatUser == null ? "/> " : ">> "); - }); - - _hubConnection.On("UserBlocked", (message) => - { - Console.WriteLine($"\n[Система] Пользователь заблокирован: {message}"); - Console.Write(CurrentChatUser == null ? "/> " : ">> "); - }); - _hubConnection.On("UserUnblocked", (message) => - { - Console.WriteLine($"\n[Система] Пользователь разблокирован: {message}"); - Console.Write(CurrentChatUser == null ? "/> " : ">> "); - }); - - - try - { - await _hubConnection.StartAsync(); - Console.WriteLine("[SignalR] Соединение установлено."); - } - catch (Exception ex) - { - Console.WriteLine($"[SignalR Ошибка] {ex.Message}"); - } - } - - static Guid GetMyUserId() - { - if (AuthToken == null) return Guid.Empty; - var handler = new JwtSecurityTokenHandler(); - if (!handler.CanReadToken(AuthToken)) return Guid.Empty; - var jwtToken = handler.ReadJwtToken(AuthToken); - var userIdClaim = jwtToken.Claims.FirstOrDefault(claim => claim.Type == "userId" || claim.Type == System.Security.Claims.ClaimTypes.NameIdentifier); - return userIdClaim != null && Guid.TryParse(userIdClaim.Value, out var userId) ? userId : Guid.Empty; - } - - - static void ShowChat(string user) - { - Console.Clear(); - Console.WriteLine($"/*===================================="); - Console.WriteLine($" * Личные сообщения | {user} ({CurrentChatUserId})"); - Console.WriteLine($" * (/back для выхода из чата)"); - Console.WriteLine($" *===================================="); - - if (!ChatHistory.ContainsKey(user)) - { - ChatHistory[user] = new List(); - // Potentially load chat history here via an API call if desired - // ChatHistory[user].Add($"[Система] Начало чата с {user}. Введите сообщение или /back."); - } - else - { - foreach (var msg in ChatHistory[user]) - Console.WriteLine(" * " + msg); - } - Console.WriteLine(" *------------------------------------"); - } - - static async Task SendMessageAsync(string messageContent) - { - if (string.IsNullOrWhiteSpace(messageContent) || _hubConnection == null || CurrentChatUserId == Guid.Empty) return; - - var myUserId = GetMyUserId(); - if (myUserId == Guid.Empty) - { - Console.WriteLine("[Ошибка] Не удалось определить ID пользователя. Попробуйте войти снова."); - return; - } - - var messageRequest = new MessageRequest - { - RecipientId = CurrentChatUserId, - EncryptedContent = messageContent, - RecipientType = RecipientType.User - }; - - try - { - await _hubConnection.InvokeAsync("Send", messageRequest); - - var displayMessage = $"[{DateTime.Now:HH:mm}] Ты >> {messageContent}"; - if (!ChatHistory.ContainsKey(CurrentChatUser!)) - { - ChatHistory[CurrentChatUser!] = new List(); - } - ChatHistory[CurrentChatUser!].Add(displayMessage); - // No Console.WriteLine here, message will be echoed by "ReceiveMessage" if server sends it to self, - // or we rely on user seeing their own typed message. - // The ReceiveMessage handler should ideally handle self-messages for consistent display. - } - catch (Exception ex) - { - Console.WriteLine($"[Ошибка отправки] {ex.Message}"); - } - } - - static void LoginWindow() - { - Console.WriteLine($"/*===================================="); - Console.WriteLine($" * Добро пожаловать в Govor!"); - Console.WriteLine($" *===================================="); - Console.WriteLine(" * /reg - создать аккаунт "); - Console.WriteLine(" * /login - войти"); - Console.WriteLine(" * /help - список команд"); - } } -} - -// LoginResponse class was here, can be removed if not used elsewhere or moved to a Contracts project. -// Assuming it's implicitly handled by HttpClientService or was specific to the old Program.cs structure. -// public class LoginResponse -// { -// public string token { get; set; } -// } \ No newline at end of file +} \ No newline at end of file diff --git a/Govor.Contracts/Govor.Contracts.csproj b/Govor.Contracts/Govor.Contracts.csproj index 20e0a8f..1618bf5 100644 --- a/Govor.Contracts/Govor.Contracts.csproj +++ b/Govor.Contracts/Govor.Contracts.csproj @@ -10,4 +10,10 @@ + + + ..\..\..\..\..\Program Files\dotnet\shared\Microsoft.AspNetCore.App\8.0.17\Microsoft.AspNetCore.Http.Features.dll + + + diff --git a/Govor.Contracts/Requests/MediaUploadRequest.cs b/Govor.Contracts/Requests/MediaUploadRequest.cs index 4408dc1..32a8a99 100644 --- a/Govor.Contracts/Requests/MediaUploadRequest.cs +++ b/Govor.Contracts/Requests/MediaUploadRequest.cs @@ -1,10 +1,13 @@ +using System.ComponentModel.DataAnnotations; using Govor.Core.Models.Messages; +using Microsoft.AspNetCore.Http; namespace Govor.Contracts.Requests; public class MediaUploadRequest { - public byte[] Data { get; set; } + [Required] + public IFormFile Data { get; set; } public string FileName { get; set; } public string EncryptedKey { get; set; } = string.Empty; public MediaType Type { get; set; } diff --git a/Govor.Contracts/Responses/SignalR/MessageEditResponse.cs b/Govor.Contracts/Responses/SignalR/MessageEditResponse.cs index a82ecca..f03c638 100644 --- a/Govor.Contracts/Responses/SignalR/MessageEditResponse.cs +++ b/Govor.Contracts/Responses/SignalR/MessageEditResponse.cs @@ -1,6 +1,13 @@ +using Govor.Core.Models.Messages; + namespace Govor.Contracts.Responses.SignalR; public class MessageEditResponse { - + public Guid MessageId { get; set; } + public Guid EditorId { get; set; } + public Guid RecipientId { get; init; } + public RecipientType RecipientType{get; init; } + public string NewEncryptedContent { get; set; } = string.Empty; + public DateTime EditedAt { get; set; } } \ No newline at end of file diff --git a/Govor.Core/Models/MediaFile.cs b/Govor.Core/Models/MediaFile.cs index 6023416..50d8e59 100644 --- a/Govor.Core/Models/MediaFile.cs +++ b/Govor.Core/Models/MediaFile.cs @@ -5,6 +5,7 @@ namespace Govor.Core.Models; public class MediaFile { public Guid Id { get; set; } + public Guid UploaderId { get; set; } public string Url { get; set; } public MediaType MediaType { get; set; } public string MineType { get; set; } diff --git a/Govor.Core/Models/Users/PrivacyUserSettings.cs b/Govor.Core/Models/Users/PrivacyUserSettings.cs index 1fece99..c0a19a8 100644 --- a/Govor.Core/Models/Users/PrivacyUserSettings.cs +++ b/Govor.Core/Models/Users/PrivacyUserSettings.cs @@ -25,7 +25,7 @@ public enum WhoCan { None = 0, OnlyFriends = 1, - EveryoneCanSend = 2, + Everyone = 2, } public enum DeletingMessagesVia diff --git a/Govor.Data/Migrations/20250713101943_AddUploaderIdToMediaFile.Designer.cs b/Govor.Data/Migrations/20250713101943_AddUploaderIdToMediaFile.Designer.cs new file mode 100644 index 0000000..69fad3a --- /dev/null +++ b/Govor.Data/Migrations/20250713101943_AddUploaderIdToMediaFile.Designer.cs @@ -0,0 +1,596 @@ +// +using System; +using Govor.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Govor.Data.Migrations +{ + [DbContext(typeof(GovorDbContext))] + [Migration("20250713101943_AddUploaderIdToMediaFile")] + partial class AddUploaderIdToMediaFile + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.6") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Govor.Core.Models.ChatGroup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("ImageId") + .HasColumnType("char(36)"); + + b.Property("IsChannel") + .HasColumnType("tinyint(1)"); + + b.Property("IsPrivate") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.HasKey("Id"); + + b.ToTable("ChatGroups"); + }); + + modelBuilder.Entity("Govor.Core.Models.Friendship", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AddresseeId") + .HasColumnType("char(36)"); + + b.Property("RequesterId") + .HasColumnType("char(36)"); + + b.Property("Status") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("AddresseeId"); + + b.HasIndex("RequesterId"); + + b.ToTable("Friendships"); + }); + + modelBuilder.Entity("Govor.Core.Models.GroupAdmins", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("GroupId") + .HasColumnType("char(36)"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("GroupId"); + + b.ToTable("GroupAdmins"); + }); + + modelBuilder.Entity("Govor.Core.Models.GroupInvitation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("EndDate") + .HasColumnType("datetime(6)"); + + b.Property("GroupId") + .HasColumnType("char(36)"); + + b.Property("InvitationCode") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("MaxParticipants") + .HasColumnType("int"); + + b.Property("UserMakerId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("GroupId"); + + b.HasIndex("UserMakerId"); + + b.ToTable("GroupInvitations"); + }); + + modelBuilder.Entity("Govor.Core.Models.GroupMembership", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("GroupId") + .HasColumnType("char(36)"); + + b.Property("InvitationId") + .IsRequired() + .HasColumnType("char(36)"); + + b.Property("IsBanned") + .HasColumnType("tinyint(1)"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("GroupId"); + + b.HasIndex("InvitationId"); + + b.ToTable("GroupMemberships"); + }); + + modelBuilder.Entity("Govor.Core.Models.Invitation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("Code") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("DateCreated") + .HasColumnType("datetime(6)"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("EndDate") + .HasColumnType("datetime(6)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("IsAdmin") + .HasColumnType("tinyint(1)"); + + b.Property("MaxParticipants") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("Invitations"); + }); + + modelBuilder.Entity("Govor.Core.Models.MediaFile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("DateCreated") + .HasColumnType("datetime(6)"); + + b.Property("MediaType") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("MineType") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("varchar(128)"); + + b.Property("UploaderId") + .HasColumnType("char(36)"); + + b.Property("Url") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("MediaFiles"); + }); + + modelBuilder.Entity("Govor.Core.Models.Messages.MediaAttachments", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("MediaFileId") + .HasColumnType("char(36)"); + + b.Property("MessageId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("MediaFileId"); + + b.HasIndex("MessageId"); + + b.ToTable("MediaAttachments"); + }); + + modelBuilder.Entity("Govor.Core.Models.Messages.Message", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ChatGroupId") + .HasColumnType("char(36)"); + + b.Property("EditedAt") + .HasColumnType("datetime(6)"); + + b.Property("EncryptedContent") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IsEdited") + .HasColumnType("tinyint(1)"); + + b.Property("PrivateChatId") + .HasColumnType("char(36)"); + + b.Property("RecipientId") + .HasColumnType("char(36)"); + + b.Property("RecipientType") + .HasColumnType("int"); + + b.Property("ReplyToMessageId") + .HasColumnType("char(36)"); + + b.Property("SenderId") + .HasColumnType("char(36)"); + + b.Property("SentAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("ChatGroupId"); + + b.HasIndex("PrivateChatId"); + + b.ToTable("Messages"); + }); + + modelBuilder.Entity("Govor.Core.Models.Messages.MessageReaction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("MessageId") + .HasColumnType("char(36)"); + + b.Property("ReactedAt") + .HasColumnType("datetime(6)"); + + b.Property("ReactionCode") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("varchar(64)"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.HasIndex("MessageId", "UserId") + .IsUnique(); + + b.ToTable("MessageReactions"); + }); + + modelBuilder.Entity("Govor.Core.Models.Messages.MessageView", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("MessageId") + .HasColumnType("char(36)"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("ViewedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("MessageId", "UserId") + .IsUnique(); + + b.ToTable("MessageViews"); + }); + + modelBuilder.Entity("Govor.Core.Models.PrivateChat", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("UserAId") + .HasColumnType("char(36)"); + + b.Property("UserBId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.ToTable("PrivateChats"); + }); + + modelBuilder.Entity("Govor.Core.Models.Users.Admin", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("UserId"); + + b.ToTable("Admins"); + }); + + modelBuilder.Entity("Govor.Core.Models.Users.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedOn") + .HasColumnType("date"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IconId") + .HasColumnType("char(36)"); + + b.Property("InviteId") + .HasColumnType("char(36)"); + + b.Property("PasswordHash") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Username") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("WasOnline") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("InviteId"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Govor.Core.Models.Friendship", b => + { + b.HasOne("Govor.Core.Models.Users.User", "Addressee") + .WithMany("ReceivedFriendRequests") + .HasForeignKey("AddresseeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Govor.Core.Models.Users.User", "Requester") + .WithMany("SentFriendRequests") + .HasForeignKey("RequesterId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Addressee"); + + b.Navigation("Requester"); + }); + + modelBuilder.Entity("Govor.Core.Models.GroupAdmins", b => + { + b.HasOne("Govor.Core.Models.ChatGroup", null) + .WithMany("Admins") + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Govor.Core.Models.GroupInvitation", b => + { + b.HasOne("Govor.Core.Models.ChatGroup", null) + .WithMany("InviteCodes") + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Govor.Core.Models.Users.User", "UserMaker") + .WithMany() + .HasForeignKey("UserMakerId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("UserMaker"); + }); + + modelBuilder.Entity("Govor.Core.Models.GroupMembership", b => + { + b.HasOne("Govor.Core.Models.ChatGroup", null) + .WithMany("Members") + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Govor.Core.Models.GroupInvitation", null) + .WithMany() + .HasForeignKey("InvitationId") + .OnDelete(DeleteBehavior.SetNull) + .IsRequired(); + }); + + modelBuilder.Entity("Govor.Core.Models.Messages.MediaAttachments", b => + { + b.HasOne("Govor.Core.Models.MediaFile", "MediaFile") + .WithMany() + .HasForeignKey("MediaFileId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Govor.Core.Models.Messages.Message", "Message") + .WithMany("MediaAttachments") + .HasForeignKey("MessageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaFile"); + + b.Navigation("Message"); + }); + + modelBuilder.Entity("Govor.Core.Models.Messages.Message", b => + { + b.HasOne("Govor.Core.Models.ChatGroup", null) + .WithMany("Messages") + .HasForeignKey("ChatGroupId"); + + b.HasOne("Govor.Core.Models.PrivateChat", null) + .WithMany("Messages") + .HasForeignKey("PrivateChatId"); + }); + + modelBuilder.Entity("Govor.Core.Models.Messages.MessageReaction", b => + { + b.HasOne("Govor.Core.Models.Messages.Message", "Message") + .WithMany("Reactions") + .HasForeignKey("MessageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Govor.Core.Models.Users.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Message"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Govor.Core.Models.Messages.MessageView", b => + { + b.HasOne("Govor.Core.Models.Messages.Message", null) + .WithMany("MessageViews") + .HasForeignKey("MessageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Govor.Core.Models.Users.Admin", b => + { + b.HasOne("Govor.Core.Models.Users.User", "User") + .WithOne() + .HasForeignKey("Govor.Core.Models.Users.Admin", "UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Govor.Core.Models.Users.User", b => + { + b.HasOne("Govor.Core.Models.Invitation", "Invite") + .WithMany("Users") + .HasForeignKey("InviteId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Invite"); + }); + + modelBuilder.Entity("Govor.Core.Models.ChatGroup", b => + { + b.Navigation("Admins"); + + b.Navigation("InviteCodes"); + + b.Navigation("Members"); + + b.Navigation("Messages"); + }); + + modelBuilder.Entity("Govor.Core.Models.Invitation", b => + { + b.Navigation("Users"); + }); + + modelBuilder.Entity("Govor.Core.Models.Messages.Message", b => + { + b.Navigation("MediaAttachments"); + + b.Navigation("MessageViews"); + + b.Navigation("Reactions"); + }); + + modelBuilder.Entity("Govor.Core.Models.PrivateChat", b => + { + b.Navigation("Messages"); + }); + + modelBuilder.Entity("Govor.Core.Models.Users.User", b => + { + b.Navigation("ReceivedFriendRequests"); + + b.Navigation("SentFriendRequests"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Govor.Data/Migrations/20250713101943_AddUploaderIdToMediaFile.cs b/Govor.Data/Migrations/20250713101943_AddUploaderIdToMediaFile.cs new file mode 100644 index 0000000..db02898 --- /dev/null +++ b/Govor.Data/Migrations/20250713101943_AddUploaderIdToMediaFile.cs @@ -0,0 +1,31 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Govor.Data.Migrations +{ + /// + public partial class AddUploaderIdToMediaFile : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "UploaderId", + table: "MediaFiles", + type: "char(36)", + nullable: false, + defaultValue: new Guid("00000000-0000-0000-0000-000000000000"), + collation: "ascii_general_ci"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "UploaderId", + table: "MediaFiles"); + } + } +} diff --git a/Govor.Data/Migrations/GovorDbContextModelSnapshot.cs b/Govor.Data/Migrations/GovorDbContextModelSnapshot.cs index 6937a5e..33d36a5 100644 --- a/Govor.Data/Migrations/GovorDbContextModelSnapshot.cs +++ b/Govor.Data/Migrations/GovorDbContextModelSnapshot.cs @@ -215,6 +215,9 @@ namespace Govor.Data.Migrations .HasMaxLength(128) .HasColumnType("varchar(128)"); + b.Property("UploaderId") + .HasColumnType("char(36)"); + b.Property("Url") .IsRequired() .HasColumnType("longtext");