mirror of
https://github.com/Govor-team/Govor.git
synced 2026-07-21 11:44:56 +00:00
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.
This commit is contained in:
@@ -48,7 +48,10 @@ public class FriendsRequestQueryControllerTests
|
||||
var currentId = _fixture.Create<Guid>();
|
||||
var friendships = _fixture.CreateMany<Friendship>().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<Guid>();
|
||||
var friendships = _fixture.CreateMany<Friendship>().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);
|
||||
|
||||
@@ -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<IHubCallerClients> _clientsMock = null!;
|
||||
private Mock<IClientProxy> _clientProxyMock = null!;
|
||||
private Mock<ILogger<FriendsHub>> _loggerMock = null!;
|
||||
private Mock<IMapper> _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<ThrowingRecursionBehavior>().ToList().ForEach(b => _fixture.Behaviors.Remove(b));
|
||||
_fixture.Behaviors.Add(new OmitOnRecursionBehavior());
|
||||
|
||||
_friendRequestServiceMock = new Mock<IFriendRequestCommandService>();
|
||||
_currentUserServiceMock = new Mock<ICurrentUserService>();
|
||||
_clientsMock = new Mock<IHubCallerClients>();
|
||||
_clientProxyMock = new Mock<IClientProxy>();
|
||||
_loggerMock = new Mock<ILogger<FriendsHub>>();
|
||||
|
||||
_mapperMock = new Mock<IMapper>();
|
||||
|
||||
_currentUserServiceMock.Setup(x => x.GetCurrentUserId()).Returns(_userId);
|
||||
_clientsMock.Setup(c => c.User(It.IsAny<string>())).Returns(_clientProxyMock.Object);
|
||||
|
||||
_clientsMock.Setup(c => c.Group(It.IsAny<string>())).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<Friendship>()
|
||||
.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<FriendshipDto>(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<object[]>(o => o[0]!.Equals(_userId)),
|
||||
It.Is<object[]>(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<Friendship>()
|
||||
.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<FriendshipDto>(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<object[]>(o => o[0]!.Equals(friendshipId)),
|
||||
It.Is<object[]>(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<Friendship>()
|
||||
.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<FriendshipDto>(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<object[]>(o => o[0]!.Equals(friendshipId)),
|
||||
It.Is<object[]>(o => o[0]!.Equals(dto)),
|
||||
default), Times.Once);
|
||||
}
|
||||
|
||||
|
||||
@@ -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<Guid> ex)
|
||||
{
|
||||
@@ -89,21 +89,4 @@ public class FriendshipsController : Controller
|
||||
return StatusCode(500, new { error = "Internal server error." });
|
||||
}
|
||||
}
|
||||
|
||||
private List<UserDto> BuildUserDtos(IEnumerable<User> users) => users.Select(user => new UserDto
|
||||
{
|
||||
Id = user.Id,
|
||||
Username = user.Username,
|
||||
Description = user.Description,
|
||||
WasOnline = user.WasOnline,
|
||||
IconId = user.IconId
|
||||
}).ToList();
|
||||
|
||||
private List<FriendshipDto> BuildFriendshipDtos(IEnumerable<Friendship> friendships) => friendships.Select(f => new FriendshipDto
|
||||
{
|
||||
Id = f.Id,
|
||||
Status = f.Status,
|
||||
AddresseeId = f.AddresseeId,
|
||||
RequesterId = f.RequesterId,
|
||||
}).ToList();
|
||||
}
|
||||
@@ -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<List<FriendshipDto>>(result);
|
||||
|
||||
return Ok(response);
|
||||
|
||||
@@ -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<MediaController> _logger;
|
||||
private readonly IMediaService _mediaService;
|
||||
|
||||
public MediaController(ILogger<MediaController> logger, IMediaService mediaService)
|
||||
private readonly ICurrentUserService _currentUserService;
|
||||
|
||||
public MediaController(
|
||||
ILogger<MediaController> 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<IActionResult> 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("download/{id}")]
|
||||
public async Task<IActionResult> 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<IVerifyFriendship, VerifyFriendship>();
|
||||
services.AddScoped<IUserGroupsService, UserGroupsService>();
|
||||
services.AddScoped<IMessagesLoader, MessagesLoader>();
|
||||
services.AddScoped<IMediaService, MediaService>();
|
||||
|
||||
// Auto Mapper
|
||||
services.AddAutoMapper(typeof(MappingProfile));
|
||||
|
||||
@@ -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<MessageEditResponse>(editor, request.MessageId, "Edit message error",
|
||||
result.Exception);
|
||||
|
||||
return HubResult<MessageEditResponse>.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<MessageEditResponse>.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<T> LogAndError<T>(Guid userId, Guid targetId, string message, Exception ex)
|
||||
{
|
||||
|
||||
@@ -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<FriendsHub> _logger;
|
||||
private readonly IFriendRequestCommandService _friendRequestService;
|
||||
private readonly ICurrentUserService _currentUserService;
|
||||
private readonly IMapper _mapper;
|
||||
|
||||
public FriendsHub(IFriendRequestCommandService friendRequestService, ICurrentUserService currentUserService, ILogger<FriendsHub> logger)
|
||||
public FriendsHub(IFriendRequestCommandService friendRequestService,
|
||||
ICurrentUserService currentUserService,
|
||||
ILogger<FriendsHub> 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<FriendshipDto>(friendship));
|
||||
|
||||
_logger.LogInformation($"Friend request received for user {targetUserId} from {userId}.");
|
||||
return HubResult<object>.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<FriendshipDto>(friendship));
|
||||
|
||||
_logger.LogInformation($"Friend request accepted for user {userId} from {userId}.");
|
||||
return HubResult<object>.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<FriendshipDto>(friendship));
|
||||
|
||||
_logger.LogInformation($"Friend request rejected for user {userId} from {userId}.");
|
||||
return HubResult<object>.Ok();
|
||||
|
||||
@@ -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<Friendship> SendAsync(Guid fromUserId, Guid toUserId);
|
||||
Task<Friendship> AcceptAsync(Guid requestId, Guid currentUserId);
|
||||
Task<Friendship> RejectAsync(Guid requestId, Guid currentUserId);
|
||||
}
|
||||
|
||||
@@ -6,9 +6,16 @@ public interface IMediaService
|
||||
{
|
||||
public Task<MediaUploadResult> UploadMediaAsync(Media file);
|
||||
public Task DeleteMediaAsync(Guid fileId);
|
||||
public Task<MediaUploadResult> GetMediaAsync(string url);
|
||||
public Task<Media> GetMediaByUrlAsync(string url);
|
||||
public Task<Media> 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);
|
||||
@@ -15,7 +15,7 @@ public class FriendRequestCommandService : IFriendRequestCommandService
|
||||
_friendshipsRepository = friendshipsRepository;
|
||||
}
|
||||
|
||||
public async Task SendAsync(Guid fromUserId, Guid toUserId)
|
||||
public async Task<Friendship> 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<Friendship> 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<Guid> ex)
|
||||
{
|
||||
@@ -53,7 +59,7 @@ public class FriendRequestCommandService : IFriendRequestCommandService
|
||||
}
|
||||
}
|
||||
|
||||
public async Task RejectAsync(Guid requestId, Guid currentUserId)
|
||||
public async Task<Friendship> 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<Guid> ex)
|
||||
{
|
||||
|
||||
@@ -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<MediaService> _logger;
|
||||
private IStorageService _storageService;
|
||||
private GovorDbContext _dbContext;
|
||||
|
||||
public MediaService(IStorageService storageService)
|
||||
public MediaService(IStorageService storageService, GovorDbContext dbContext, ILogger<MediaService> logger)
|
||||
{
|
||||
_storageService = storageService;
|
||||
_dbContext = dbContext;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public Task<MediaUploadResult> UploadMediaAsync(Media file)
|
||||
public async Task<MediaUploadResult> 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<MediaUploadResult> GetMediaAsync(string url)
|
||||
public Task<Media> GetMediaByUrlAsync(string url)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public async Task<Media> 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 не указан, запросит ввод.";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<List<FriendshipDto>>(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 - (Админ) Показать все дружеские связи в системе.";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<List<FriendshipDto>>(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 не указан, запросит ввод.";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<IActionResult> 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<IActionResult> 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 не указан, запросит ввод.";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<string?> GetAuthToken { get; private set; } = null!;
|
||||
protected static Action<string> SetAuthToken { get; private set; } = null!;
|
||||
protected static Func<Task> InitializeHubConnectionAsync { get; private set; } = null!;
|
||||
protected static HubConnection HubConnection => _getHubConnection?.Invoke();
|
||||
|
||||
private static Func<HubConnection?> _getHubConnection = null!;
|
||||
|
||||
public static void InitializeServices(
|
||||
FriendsClient? friendsClient,
|
||||
HttpClientService httpClientService,
|
||||
Func<string?> getAuthToken,
|
||||
Action<string> setAuthToken,
|
||||
Func<Task> initializeHubConnectionAsync,
|
||||
Func<HubConnection?> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 не указан, запросит ввод.";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<string, ICommand> _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<string, ICommand> 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 [команда] - Показать список всех команд или помощь по конкретной команде.";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
namespace Govor.ConsoleClient.Commands
|
||||
{
|
||||
public interface ICommand
|
||||
{
|
||||
Task ExecuteAsync(string? argument);
|
||||
string GetHelp();
|
||||
}
|
||||
}
|
||||
@@ -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 - Показать список ваших друзей.";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 - Показать список входящих заявок в друзья.";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<List<FriendshipDto>>(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 - Показать список отправленных вами заявок в друзья, ожидающих ответа.";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 - Войти в существующий аккаунт.";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 - Зарегистрировать новый аккаунт с помощью кода приглашения.";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 не указан, запросит ввод.";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 [имя_пользователя] - Найти пользователей по имени. Если имя не указано, запросит ввод.";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 не указан, запросит ввод.";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 не указан, запросит ввод.";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<Guid>("FriendRequestReceived", OnFriendRequestReceived);
|
||||
_connection.On<Guid>("FriendRequestAccepted", OnFriendRequestAccepted);
|
||||
_connection.On<Guid>("FriendRequestRejected", OnFriendRequestRejected);
|
||||
}
|
||||
// Событие: получен входящий запрос в друзья
|
||||
_connection.On<string>("FriendRequestReceived", userId =>
|
||||
{
|
||||
Console.WriteLine($"📨 Friend request received from: {userId}");
|
||||
});
|
||||
|
||||
private void OnFriendRequestReceived(Guid userId)
|
||||
{
|
||||
Console.WriteLine($"[Event] Получен запрос в друзья от пользователя: {userId}");
|
||||
}
|
||||
// Событие: запрос принят
|
||||
_connection.On<string>("FriendRequestAccepted", friendshipId =>
|
||||
{
|
||||
Console.WriteLine($"✅ Friend request accepted! Friendship ID: {friendshipId}");
|
||||
});
|
||||
|
||||
private void OnFriendRequestAccepted(Guid friendshipId)
|
||||
{
|
||||
Console.WriteLine($"[Event] Запрос в друзья принят. ID дружбы: {friendshipId}");
|
||||
}
|
||||
// Событие: запрос отклонён
|
||||
_connection.On<string>("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<HubResult<object>>("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<HubResult<object>>("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 <userId> - Отправить запрос в друзья");
|
||||
Console.WriteLine("accept <friendshipId> - Принять запрос");
|
||||
Console.WriteLine("reject <friendshipId> - Отклонить запрос");
|
||||
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<HubResult<object>>("RejectRequest", friendshipId);
|
||||
Console.WriteLine($"👎 Reject result: {result.Status}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="8.0.5" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
+42
-371
@@ -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<string, List<string>> ChatHistory = new();
|
||||
static string? CurrentChatUser = null;
|
||||
static Guid CurrentChatUserId = Guid.Empty;
|
||||
Console.Write("🔑 JWT Token: ");
|
||||
jwtToken = Console.ReadLine();
|
||||
|
||||
private static Dictionary<string, ICommand> _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<string, ICommand>
|
||||
{
|
||||
{ "/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<UserMessageResponse>("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<string>();
|
||||
}
|
||||
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<UserMessageResponse>("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<string>("FriendRequestReceived", (message) => // Assuming message is a simple string notification
|
||||
{
|
||||
Console.WriteLine($"\n[Система] Новый запрос в друзья: {message}");
|
||||
Console.Write(CurrentChatUser == null ? "/> " : ">> ");
|
||||
});
|
||||
|
||||
_hubConnection.On<string>("FriendRequestAccepted", (message) =>
|
||||
{
|
||||
Console.WriteLine($"\n[Система] Ваш запрос в друзья принят: {message}");
|
||||
Console.Write(CurrentChatUser == null ? "/> " : ">> ");
|
||||
});
|
||||
|
||||
_hubConnection.On<string>("FriendRemoved", (message) =>
|
||||
{
|
||||
Console.WriteLine($"\n[Система] Пользователь удалил вас из друзей или вы удалили его: {message}");
|
||||
Console.Write(CurrentChatUser == null ? "/> " : ">> ");
|
||||
});
|
||||
|
||||
_hubConnection.On<string>("UserBlocked", (message) =>
|
||||
{
|
||||
Console.WriteLine($"\n[Система] Пользователь заблокирован: {message}");
|
||||
Console.Write(CurrentChatUser == null ? "/> " : ">> ");
|
||||
});
|
||||
_hubConnection.On<string>("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<string>();
|
||||
// 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<string>();
|
||||
}
|
||||
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; }
|
||||
// }
|
||||
}
|
||||
@@ -10,4 +10,10 @@
|
||||
<ProjectReference Include="..\Govor.Core\Govor.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="Microsoft.AspNetCore.Http.Features">
|
||||
<HintPath>..\..\..\..\..\Program Files\dotnet\shared\Microsoft.AspNetCore.App\8.0.17\Microsoft.AspNetCore.Http.Features.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
@@ -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; }
|
||||
|
||||
@@ -25,7 +25,7 @@ public enum WhoCan
|
||||
{
|
||||
None = 0,
|
||||
OnlyFriends = 1,
|
||||
EveryoneCanSend = 2,
|
||||
Everyone = 2,
|
||||
}
|
||||
|
||||
public enum DeletingMessagesVia
|
||||
|
||||
@@ -0,0 +1,596 @@
|
||||
// <auto-generated />
|
||||
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
|
||||
{
|
||||
/// <inheritdoc />
|
||||
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<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.IsRequired()
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("varchar(500)");
|
||||
|
||||
b.Property<Guid>("ImageId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<bool>("IsChannel")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<bool>("IsPrivate")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("varchar(100)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("ChatGroups");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Govor.Core.Models.Friendship", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<Guid>("AddresseeId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<Guid>("RequesterId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("AddresseeId");
|
||||
|
||||
b.HasIndex("RequesterId");
|
||||
|
||||
b.ToTable("Friendships");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Govor.Core.Models.GroupAdmins", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<Guid>("GroupId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("GroupId");
|
||||
|
||||
b.ToTable("GroupAdmins");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Govor.Core.Models.GroupInvitation", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.IsRequired()
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("varchar(500)");
|
||||
|
||||
b.Property<DateTime>("EndDate")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<Guid>("GroupId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<string>("InvitationCode")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("varchar(200)");
|
||||
|
||||
b.Property<int>("MaxParticipants")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<Guid>("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<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<Guid>("GroupId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<Guid?>("InvitationId")
|
||||
.IsRequired()
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<bool>("IsBanned")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<Guid>("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<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<string>("Code")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<DateTime>("DateCreated")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<DateTime>("EndDate")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<bool>("IsAdmin")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<int>("MaxParticipants")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Invitations");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Govor.Core.Models.MediaFile", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<DateTime>("DateCreated")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("MediaType")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("MineType")
|
||||
.IsRequired()
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("varchar(128)");
|
||||
|
||||
b.Property<Guid>("UploaderId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<string>("Url")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("MediaFiles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Govor.Core.Models.Messages.MediaAttachments", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<Guid>("MediaFileId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<Guid>("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<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<Guid?>("ChatGroupId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<DateTime?>("EditedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("EncryptedContent")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<bool>("IsEdited")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<Guid?>("PrivateChatId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<Guid>("RecipientId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<int>("RecipientType")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<Guid?>("ReplyToMessageId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<Guid>("SenderId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<DateTime>("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<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<Guid>("MessageId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<DateTime>("ReactedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("ReactionCode")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("varchar(64)");
|
||||
|
||||
b.Property<Guid>("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<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<Guid>("MessageId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<DateTime>("ViewedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("MessageId", "UserId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("MessageViews");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Govor.Core.Models.PrivateChat", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<Guid>("UserAId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<Guid>("UserBId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("PrivateChats");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Govor.Core.Models.Users.Admin", b =>
|
||||
{
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.HasKey("UserId");
|
||||
|
||||
b.ToTable("Admins");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Govor.Core.Models.Users.User", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<DateOnly>("CreatedOn")
|
||||
.HasColumnType("date");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<Guid>("IconId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<Guid>("InviteId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<string>("PasswordHash")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Username")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<DateTime>("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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Govor.Data.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddUploaderIdToMediaFile : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<Guid>(
|
||||
name: "UploaderId",
|
||||
table: "MediaFiles",
|
||||
type: "char(36)",
|
||||
nullable: false,
|
||||
defaultValue: new Guid("00000000-0000-0000-0000-000000000000"),
|
||||
collation: "ascii_general_ci");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "UploaderId",
|
||||
table: "MediaFiles");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -215,6 +215,9 @@ namespace Govor.Data.Migrations
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("varchar(128)");
|
||||
|
||||
b.Property<Guid>("UploaderId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<string>("Url")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
Reference in New Issue
Block a user