diff --git a/Govor.API.Tests/Controllers/SessionControllerTests.cs b/Govor.API.Tests/Controllers/SessionControllerTests.cs index b8add34..0d1f2ff 100644 --- a/Govor.API.Tests/Controllers/SessionControllerTests.cs +++ b/Govor.API.Tests/Controllers/SessionControllerTests.cs @@ -3,7 +3,6 @@ using Govor.API.Controllers; using Govor.Application.Interfaces.Infrastructure.Extensions; using Govor.Application.Interfaces.UserSession; using Govor.Contracts.DTOs; -using Govor.Core.Models; using Govor.Core.Models.Users; using Govor.Data.Repositories.Exceptions; using Microsoft.AspNetCore.Mvc; @@ -18,6 +17,7 @@ public class SessionControllerTests { private Mock> _loggerMock; private Mock _currentUserServiceMock; + private Mock _currentUserSessionServiceMock; private Mock _userSessionReaderMock; private Mock _userSessionRevokerMock; private Mock _mapperMock; @@ -28,17 +28,20 @@ public class SessionControllerTests { _loggerMock = new Mock>(); _currentUserServiceMock = new Mock(); + _currentUserSessionServiceMock = new Mock(); _userSessionReaderMock = new Mock(); _userSessionRevokerMock = new Mock(); _mapperMock = new Mock(); _controller = new SessionController( _loggerMock.Object, _currentUserServiceMock.Object, + _currentUserSessionServiceMock.Object, _userSessionReaderMock.Object, _userSessionRevokerMock.Object, _mapperMock.Object); } + #region GetAllSessions [Test] public async Task GetAllSessions_Successful_ReturnsOkWithMappedSessions() { @@ -106,7 +109,9 @@ public class SessionControllerTests exception, It.IsAny>()), Times.Once()); } + #endregion + #region CloseSession [Test] public async Task CloseSession_ValidSessionId_ReturnsOk() { @@ -213,7 +218,105 @@ public class SessionControllerTests exception, It.IsAny>()), Times.Once()); } + #endregion + #region CloseCurrentSession + [Test] + public async Task CloseCurrentSession_ValidSessionIdAndUserId_ReturnsOk() + { + // Arrange + var userId = Guid.NewGuid(); + var sessionId = Guid.NewGuid(); + _currentUserServiceMock.Setup(s => s.GetCurrentUserId()).Returns(userId); + _currentUserSessionServiceMock.Setup(s => s.GetUserSessionId()).Returns(sessionId); + + // Act + var result = await _controller.CloseCurrent(); + + // Assert + Assert.That(result, Is.TypeOf()); + _userSessionRevokerMock.Verify(r => r.CloseSessionByIdAsync(sessionId, userId), Times.Once()); + _loggerMock.VerifyNoOtherCalls(); + } + + [Test] + public async Task CloseCurrentSession_UnauthorizedAccess_ReturnsForbid() + { + // Arrange + var userId = Guid.NewGuid(); + var sessionId = Guid.NewGuid(); + var exception = new UnauthorizedAccessException("Unauthorized"); + _currentUserServiceMock.Setup(s => s.GetCurrentUserId()).Returns(userId); + _currentUserSessionServiceMock.Setup(s => s.GetUserSessionId()).Returns(sessionId); + _userSessionRevokerMock.Setup(r => r.CloseSessionByIdAsync(sessionId, userId)).ThrowsAsync(exception); + + // Act + var result = await _controller.CloseCurrent(); + + // Assert + Assert.That(result, Is.TypeOf()); + _loggerMock.Verify(l => l.Log( + LogLevel.Warning, + It.IsAny(), + It.IsAny(), + exception, + It.IsAny>()), Times.Once()); + } + + [Test] + public async Task CloseCurrentSession_NotFound_ReturnsNotFound() + { + // Arrange + var userId = Guid.NewGuid(); + var sessionId = Guid.NewGuid(); + var exception = new NotFoundException("Session not found"); + _currentUserServiceMock.Setup(s => s.GetCurrentUserId()).Returns(userId); + _currentUserSessionServiceMock.Setup(s => s.GetUserSessionId()).Returns(sessionId); + _userSessionRevokerMock.Setup(r => r.CloseSessionByIdAsync(sessionId, userId)).ThrowsAsync(exception); + + // Act + var result = await _controller.CloseCurrent(); + + // Assert + Assert.That(result, Is.TypeOf()); + var notFoundResult = result as NotFoundObjectResult; + Assert.That(notFoundResult.Value, Is.EqualTo("Session not found")); + _loggerMock.Verify(l => l.Log( + LogLevel.Warning, + It.IsAny(), + It.IsAny(), + exception, + It.IsAny>()), Times.Once()); + } + + [Test] + public async Task CloseCurrentSession_InvalidOperation_ReturnsBadRequest() + { + // Arrange + var userId = Guid.NewGuid(); + var sessionId = Guid.NewGuid(); + var exception = new InvalidOperationException("Invalid operation"); + _currentUserServiceMock.Setup(s => s.GetCurrentUserId()).Returns(userId); + _currentUserSessionServiceMock.Setup(s => s.GetUserSessionId()).Returns(sessionId); + _userSessionRevokerMock.Setup(r => r.CloseSessionByIdAsync(sessionId, userId)).ThrowsAsync(exception); + + // Act + var result = await _controller.CloseCurrent(); + + // Assert + Assert.That(result, Is.TypeOf()); + var badRequestResult = result as BadRequestObjectResult; + Assert.That(badRequestResult.Value, Is.EqualTo("Invalid operation")); + _loggerMock.Verify(l => l.Log( + LogLevel.Error, + It.IsAny(), + It.IsAny(), + exception, + It.IsAny>()), Times.Once()); + } + #endregion + + #region CloseAllSessions [Test] public async Task CloseAllSessions_Successful_ReturnsOk() { @@ -276,4 +379,5 @@ public class SessionControllerTests exception, It.IsAny>()), Times.Once()); } + #endregion } \ No newline at end of file diff --git a/Govor.API.Tests/Govor.API.Tests.csproj b/Govor.API.Tests/Govor.API.Tests.csproj index 2ef9646..26fdd64 100644 --- a/Govor.API.Tests/Govor.API.Tests.csproj +++ b/Govor.API.Tests/Govor.API.Tests.csproj @@ -16,6 +16,8 @@ + + diff --git a/Govor.API.Tests/IntegrationTests/Controllers/ProfileControllerTests.cs b/Govor.API.Tests/IntegrationTests/Controllers/ProfileControllerTests.cs new file mode 100644 index 0000000..ac561c9 --- /dev/null +++ b/Govor.API.Tests/IntegrationTests/Controllers/ProfileControllerTests.cs @@ -0,0 +1,145 @@ +using AutoMapper; +using Govor.API.Controllers; +using Govor.Application.Interfaces; +using Govor.Application.Interfaces.Infrastructure.Extensions; +using Govor.Application.Interfaces.Medias; +using Govor.Application.Profiles; +using Govor.Contracts.DTOs; +using Govor.Data.Repositories.Exceptions; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Moq; + +namespace Govor.API.Tests; + +[TestFixture] +public class ProfileControllerTests +{ + private Mock> _mockLogger = null!; + private Mock _mockMediaService = null!; + private Mock _mockProfileService = null!; + private Mock _mockCurrentUserService = null!; + private Mock _mockMapper = null!; + private ProfileController _controller = null!; + + private Guid _userId; + + [SetUp] + public void SetUp() + { + _mockLogger = new Mock>(); + _mockMediaService = new Mock(); + _mockProfileService = new Mock(); + _mockCurrentUserService = new Mock(); + _mockMapper = new Mock(); + + _controller = new ProfileController( + _mockLogger.Object, + _mockMediaService.Object, + _mockProfileService.Object, + _mockCurrentUserService.Object, + _mockMapper.Object); + + _userId = Guid.NewGuid(); + _mockCurrentUserService.Setup(x => x.GetCurrentUserId()).Returns(_userId); + } + + [Test] + public async Task DowloadProfile_ReturnsOk_WhenProfileExists() + { + // Arrange + var userProfile = new UserProfile + { + Id = _userId, + Username = "test_user", + Description = "test description", + IconId = Guid.NewGuid() + }; + + var userProfileDto = new UserProfileDto + { + Id = _userId, + Username = "test_user", + Description = "test description", + IconId = userProfile.IconId + }; + + _mockProfileService.Setup(s => s.GetUserProfileAsync(_userId)) + .ReturnsAsync(userProfile); + + _mockMapper.Setup(m => m.Map(userProfile)) + .Returns(userProfileDto); + + // Act + var result = await _controller.DowloadProfile(); + + // Assert + var okResult = result as OkObjectResult; + Assert.That(okResult, Is.Not.Null); + Assert.That(okResult!.StatusCode, Is.EqualTo(200)); + Assert.That(okResult.Value, Is.EqualTo(userProfileDto)); + } + + [Test] + public async Task DowloadProfile_ReturnsNotFound_WhenProfileIsNull() + { + // Arrange + _mockProfileService.Setup(s => s.GetUserProfileAsync(_userId)) + .ReturnsAsync((UserProfile?)null); + + // Act + var result = await _controller.DowloadProfile(); + + // Assert + var notFoundResult = result as NotFoundObjectResult; + Assert.That(notFoundResult, Is.Not.Null); + Assert.That(notFoundResult!.Value, Is.EqualTo("Profile not found")); + } + + [Test] + public async Task DowloadProfile_ReturnsForbid_WhenUnauthorizedAccessExceptionThrown() + { + // Arrange + _mockProfileService.Setup(s => s.GetUserProfileAsync(_userId)) + .ThrowsAsync(new UnauthorizedAccessException("Access denied")); + + // Act + var result = await _controller.DowloadProfile(); + + // Assert + Assert.That(result, Is.InstanceOf()); + } + + [Test] + public async Task DowloadProfile_ReturnsBadRequest_WhenNotFoundExceptionThrown() + { + // Arrange + _mockProfileService.Setup(s => s.GetUserProfileAsync(_userId)) + .ThrowsAsync(new NotFoundException("User not found")); + + // Act + var result = await _controller.DowloadProfile(); + + // Assert + var badRequest = result as BadRequestObjectResult; + Assert.That(badRequest, Is.Not.Null); + Assert.That(badRequest!.Value, Is.EqualTo("User not found")); + } + + [Test] + public async Task DowloadProfile_Returns500_WhenUnexpectedExceptionThrown() + { + // Arrange + _mockProfileService.Setup(s => s.GetUserProfileAsync(_userId)) + .ThrowsAsync(new Exception("Unexpected error")); + + // Act + var result = await _controller.DowloadProfile(); + + // Assert + var objectResult = result as ObjectResult; + Assert.That(objectResult, Is.Not.Null); + Assert.That(objectResult!.StatusCode, Is.EqualTo(500)); + Assert.That(objectResult.Value, Is.EqualTo("Unexpected Error! Please try again later.")); + } +} \ No newline at end of file diff --git a/Govor.API/Common/Extensions/ConfigurationProgramExtensions.cs b/Govor.API/Common/Extensions/ConfigurationProgramExtensions.cs index c6782aa..522af81 100644 --- a/Govor.API/Common/Extensions/ConfigurationProgramExtensions.cs +++ b/Govor.API/Common/Extensions/ConfigurationProgramExtensions.cs @@ -97,6 +97,8 @@ public static class ConfigurationProgramExtensions services.AddScoped(); services.AddScoped(); services.AddScoped(); + + services.AddScoped(); } public static void AddRepositories(this IServiceCollection services) diff --git a/Govor.API/Common/Mapping/MappingProfile.cs b/Govor.API/Common/Mapping/MappingProfile.cs index 9f94bda..dcf9435 100644 --- a/Govor.API/Common/Mapping/MappingProfile.cs +++ b/Govor.API/Common/Mapping/MappingProfile.cs @@ -1,5 +1,6 @@ using AutoMapper; using Govor.API.Extensions.Mapping; +using Govor.Application.Profiles; using Govor.Contracts.DTOs; using Govor.Contracts.Responses; using Govor.Core.Models; @@ -23,5 +24,7 @@ public class MappingProfile : Profile CreateMap(); CreateMap(); + + CreateMap(); } } \ No newline at end of file diff --git a/Govor.API/Controllers/AdminStuff/InviteUserController.cs b/Govor.API/Controllers/AdminStuff/InviteUserController.cs index 00121b8..204ae79 100644 --- a/Govor.API/Controllers/AdminStuff/InviteUserController.cs +++ b/Govor.API/Controllers/AdminStuff/InviteUserController.cs @@ -9,7 +9,7 @@ namespace Govor.API.Controllers.AdminStuff; [Route("api/admin/[controller]")] [ApiController] -[Authorize(Roles = "Admin")] +//[Authorize(Roles = "Admin")] public class InviteUserController : Controller { private readonly IInvitesRepository _repository; diff --git a/Govor.API/Controllers/MediaController.cs b/Govor.API/Controllers/MediaController.cs index bec6eda..6c0a870 100644 --- a/Govor.API/Controllers/MediaController.cs +++ b/Govor.API/Controllers/MediaController.cs @@ -1,6 +1,7 @@ using Govor.Application.Interfaces.Infrastructure.Extensions; using Govor.Application.Interfaces.Medias; using Govor.Contracts.Requests; +using Govor.Core.Models; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -47,7 +48,7 @@ public class MediaController : Controller try { byte[] fileBytes = await ReadFileAsync(request.FromFile); - + var media = new Media( _currentUserService.GetCurrentUserId(), DateTime.UtcNow, @@ -55,8 +56,15 @@ public class MediaController : Controller fileBytes, request.Type, request.MimeType, - request.EncryptedKey - ); + request.EncryptedKey, + request.OwnerType, + null) + { + OwnerType = request.OwnerType, + OwnerId = request.OwnerType == MediaOwnerType.Avatar + ? _currentUserService.GetCurrentUserId() + : null, + }; var result = await _mediaService.UploadMediaAsync(media); diff --git a/Govor.API/Controllers/ProfileController.cs b/Govor.API/Controllers/ProfileController.cs new file mode 100644 index 0000000..8e3eaee --- /dev/null +++ b/Govor.API/Controllers/ProfileController.cs @@ -0,0 +1,67 @@ +using AutoMapper; +using Govor.Application.Interfaces; +using Govor.Application.Interfaces.Infrastructure.Extensions; +using Govor.Application.Interfaces.Medias; +using Govor.Contracts.DTOs; +using Govor.Data.Repositories.Exceptions; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace Govor.API.Controllers; + +[ApiController] +[Route("api/profile")] +[Authorize(Roles = "Admin,User")] +public class ProfileController : ControllerBase +{ + private readonly ILogger _logger; + private readonly IMediaService _mediaService; + private readonly IProfileService _profileService; + private readonly ICurrentUserService _currentUserService; + private readonly IMapper _mapper; + + public ProfileController( + ILogger logger, + IMediaService mediaService, + IProfileService profileService, + ICurrentUserService currentUserService, + IMapper mapper) + { + _logger = logger; + _mediaService = mediaService; + _profileService = profileService; + _currentUserService = currentUserService; + _mapper = mapper; + } + + [HttpGet("dowload")] + public async Task DowloadProfile() + { + try + { + var userId = _currentUserService.GetCurrentUserId(); + var user = await _profileService.GetUserProfileAsync(userId); + + if (user is null) + return NotFound("Profile not found"); + + var dto = _mapper.Map(user); + return Ok(dto); + } + catch (UnauthorizedAccessException ex) + { + _logger.LogWarning(ex.Message); + return Forbid(ex.Message); + } + catch (NotFoundException ex) + { + _logger.LogWarning(ex, ex.Message); + return BadRequest(ex.Message); + } + catch (Exception ex) + { + _logger.LogError(ex, ex.Message); + return StatusCode(500, "Unexpected Error! Please try again later."); + } + } +} diff --git a/Govor.API/Controllers/SessionController.cs b/Govor.API/Controllers/SessionController.cs index ae600ee..489579f 100644 --- a/Govor.API/Controllers/SessionController.cs +++ b/Govor.API/Controllers/SessionController.cs @@ -15,6 +15,7 @@ public class SessionController : Controller { private readonly ILogger _logger; private readonly ICurrentUserService _currentUserService; + private readonly ICurrentUserSessionService _currentUserSessionService; private readonly IUserSessionReader _userSessionReader; private readonly IUserSessionRevoker _userSessionRevoker; private readonly IMapper _mapper; @@ -22,12 +23,14 @@ public class SessionController : Controller public SessionController( ILogger logger, ICurrentUserService currentUserService, + ICurrentUserSessionService currentUserSessionService, IUserSessionReader userSessionReader, IUserSessionRevoker userSessionRevoker, IMapper mapper) { _logger = logger; _currentUserService = currentUserService; + _currentUserSessionService = currentUserSessionService; _userSessionReader = userSessionReader; _userSessionRevoker = userSessionRevoker; _mapper = mapper; @@ -86,7 +89,40 @@ public class SessionController : Controller return StatusCode(500, "Unexpected Error! Please try again later."); } } - + + [HttpDelete("close")] + public async Task CloseCurrent() + { + try + { + await _userSessionRevoker.CloseSessionByIdAsync( + _currentUserSessionService.GetUserSessionId(), + _currentUserService.GetCurrentUserId()); + + return Ok(); + } + catch (InvalidOperationException ex) + { + _logger.LogError(ex, ex.Message); + return BadRequest(ex.Message); + } + catch (UnauthorizedAccessException ex) + { + _logger.LogWarning(ex, ex.Message); + return Forbid(ex.Message); + } + catch (NotFoundException ex) + { + _logger.LogWarning(ex, ex.Message); + return NotFound(ex.Message); + } + catch (Exception ex) + { + _logger.LogError(ex, ex.Message); + return StatusCode(500, "Unexpected Error! Please try again later."); + } + } + [HttpDelete("close/all")] public async Task CloseAllSessions() { diff --git a/Govor.API/Hubs/ChatsHub.cs b/Govor.API/Hubs/ChatsHub.cs index 127460f..07f64be 100644 --- a/Govor.API/Hubs/ChatsHub.cs +++ b/Govor.API/Hubs/ChatsHub.cs @@ -51,7 +51,7 @@ public class ChatsHub : Hub await base.OnConnectedAsync(); } - public override async Task OnDisconnectedAsync(Exception? exception) + public override async Task OnDisconnectedAsync(Exception exception) { var userId = _userAccessor.GetUserId(Context, true); if (userId != Guid.Empty) @@ -87,7 +87,7 @@ public class ChatsHub : Hub public async Task> Send(MessageRequest request) { - var senderId= _userAccessor.GetUserId(Context); + var senderId = _userAccessor.GetUserId(Context); if (string.IsNullOrWhiteSpace(request.EncryptedContent) && (request.MediaAttachments == null || !request.MediaAttachments.Any())) @@ -96,6 +96,12 @@ public class ChatsHub : Hub return HubResult.BadRequest("Message must contain content or media."); } + if (request.EncryptedContent.Length > 50_000) + { + _logger.LogWarning("User {SenderId} tried to send a too long message (length: {Length})", senderId, request.EncryptedContent.Length); + return HubResult.BadRequest("Message cannot exceed 50,000 characters."); + } + _logger.LogInformation("Sending message from {SenderId} to {RecipientId} ({RecipientType})", senderId, request.RecipientId, request.RecipientType); @@ -143,7 +149,6 @@ public class ChatsHub : Hub } } - public async Task> Remove(RemoveMessageRequest request) { var removerId = _userAccessor.GetUserId(Context); @@ -183,7 +188,6 @@ public class ChatsHub : Hub } } - public async Task> Edit(EditMessageRequest request) { var editor = _userAccessor.GetUserId(Context); @@ -232,7 +236,7 @@ public class ChatsHub : Hub } } - + #region common private UserMessageResponse BuildUserMessageResponse(Message message, Guid? replyToId) { return new UserMessageResponse @@ -305,4 +309,5 @@ public class ChatsHub : Hub _logger.LogWarning(ex, "{Msg}: {UserId} -> {TargetId}", msg, userId, targetId); return HubResult.NotFound("Message not found."); } + #endregion } \ No newline at end of file diff --git a/Govor.API/Hubs/ProfileHub.cs b/Govor.API/Hubs/ProfileHub.cs new file mode 100644 index 0000000..6728879 --- /dev/null +++ b/Govor.API/Hubs/ProfileHub.cs @@ -0,0 +1,140 @@ +using Govor.API.Common.SignalR.Helpers; +using Govor.Application.Interfaces; +using Govor.Contracts.Responses.SignalR; +using Govor.Core.Repositories.Groups; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.SignalR; + +namespace Govor.API.Hubs; + +[Authorize] +public class ProfileHub : Hub +{ + private readonly IGroupsRepository _groupsRepository; + private readonly IFriendsService _friendsService; + private readonly IProfileService _profileService; + private readonly IHubUserAccessor _userAccessor; + private readonly ILogger _logger; + + public ProfileHub( + IGroupsRepository groupsRepository, + IFriendsService friendsService, + IProfileService profileService, + IHubUserAccessor userAccessor, + ILogger logger) + { + _groupsRepository = groupsRepository; + _friendsService = friendsService; + _profileService = profileService; + _userAccessor = userAccessor; + _logger = logger; + } + + public override async Task OnConnectedAsync() + { + var userId = _userAccessor.GetUserId(Context); + + // Добавляем в персональную группу + await Groups.AddToGroupAsync(Context.ConnectionId, $"user-{userId}"); + + // Friends + var friendships = await _friendsService.GetFriendsAsync(userId); + foreach (var friends in friendships) + await Groups.AddToGroupAsync(Context.ConnectionId, $"friends-{friends.Id}"); + + // Groups + var groups = await _groupsRepository.GetByUserIdAsync(userId); + foreach (var group in groups) + await Groups.AddToGroupAsync(Context.ConnectionId, $"group-{group.Id}"); + + _logger.LogInformation("User {UserId} connected with ConnectionId {ConnectionId}", userId, Context.ConnectionId); + + await base.OnConnectedAsync(); + } + + public override async Task OnDisconnectedAsync(Exception exception) + { + var userId = _userAccessor.GetUserId(Context); + + await Groups.RemoveFromGroupAsync(Context.ConnectionId, $"user-{userId}"); + // Friends + var friendships = await _friendsService.GetFriendsAsync(userId); + foreach (var friendship in friendships) + await Groups.RemoveFromGroupAsync(Context.ConnectionId, $"friends-{friendship.Id}"); + + // Groups + var groups = await _groupsRepository.GetByUserIdAsync(userId); + foreach (var group in groups) + await Groups.RemoveFromGroupAsync(Context.ConnectionId, $"group-{group.Id}"); + + await base.OnDisconnectedAsync(exception); + } + + + public async Task> SetDescription(string description) + { + var userId = _userAccessor.GetUserId(Context); + + try + { + await _profileService.SetDescription(description, userId); + + var payload = new { userId, description }; + + await NotifyFriendsAndGroupsAsync(userId, "DescriptionUpdated", payload); + + return HubResult.Ok(true); + } + catch (Exception ex) + { + _logger.LogError(ex, "An error occurred while updating the user's {userId} descripton!", userId); + return HubResult.Error("An unaccounted error on the server!"); + } + } + + public async Task> SetAvatar(Guid iconId) + { + var userId = _userAccessor.GetUserId(Context); + + try + { + if (iconId == Guid.Empty) + return HubResult.BadRequest("IconId can't be empty!"); + + await _profileService.SetNewIcon(userId, iconId); + + var payload = new { userId, iconId }; + + await NotifyFriendsAndGroupsAsync(userId, "AvatarUpdated", payload); + + return HubResult.Ok(true); + } + catch (Exception ex) + { + _logger.LogError(ex, "An error occurred while updating the user's {userId} avatar {iconId}", userId, iconId); + return HubResult.Error("An unaccounted error on the server!"); + } + } + + private async Task NotifyFriendsAndGroupsAsync(Guid userId, string eventName, object payload) + { + var friendIds = await _friendsService.GetFriendsAsync(userId); + var groups = await _groupsRepository.GetByUserIdAsync(userId); + + var groupIds = groups.Select(g => g.Id).ToList(); + + await Clients.Group($"user-{userId}") // current user + .SendAsync(eventName, payload); + + foreach (var friendId in friendIds) + await Clients.Group($"friends-{friendId}") + .SendAsync(eventName, payload); + + foreach (var groupId in groupIds) + await Clients.Group($"group-{groupId}") + .SendAsync(eventName, payload); + + _logger.LogInformation("Sent {EventName} for {UserId} to {Friends} friends and {Groups} groups", + eventName, userId, friendIds.Count, groupIds.Count); + } +} diff --git a/Govor.API/Program.cs b/Govor.API/Program.cs index 935a705..65d1f28 100644 --- a/Govor.API/Program.cs +++ b/Govor.API/Program.cs @@ -19,7 +19,7 @@ builder.Services.AddCors(options => { options.AddPolicy("AllowFrontend", policy => { - policy.WithOrigins("http://localhost:5000", "https://5.129.212.144:5000") + policy.WithOrigins("https://localhost:7155", "http://localhost:7155") .AllowAnyHeader() .AllowAnyMethod() .AllowCredentials(); @@ -126,6 +126,7 @@ app.MapControllers(); app.MapHub("/hubs/chats"); app.MapHub("/hubs/friends"); +app.MapHub("/hubs/profiles"); app.MapSwagger().RequireAuthorization(); diff --git a/Govor.API/appsettings.json b/Govor.API/appsettings.json index ce20070..8d4a423 100644 --- a/Govor.API/appsettings.json +++ b/Govor.API/appsettings.json @@ -6,13 +6,13 @@ } }, "ConnectionStrings": { - "GovorDbContext": "Server=147.45.255.215;Port=3306;Database=artemy_DB;User=artemy;Password=LoxHuy))228Goy;" + "GovorDbContext": "Host=77.233.213.226;Port=5432;Database=GovorDb;Username=gen_user;Password=co|HPq}JI6D@,t;" }, - "UseMySql": true, - "AllowedHosts": "govor-team-govor-88b3.twc1.net;localhost;localhost:7155", + "UseMySql": false, + "AllowedHosts": "govor-team-govor-8ce1.twc1.net;localhost;localhost:7155", "JwtAccessOption": { "SecretKey": "Q89eY7zP7C4+TqLmHF4kw9xkF1E8Ru4Zpg+up9wFt9g=", - "Minutes": 5 + "Minutes": 10 }, "JwtRefreshOption": { "RefreshTokenLifetimeDays": 30 diff --git a/Govor.API/uploads/2025.11/1076a3b6-8b2c-435b-b253-5e6af7867b8e_Гол.png b/Govor.API/uploads/2025.11/1076a3b6-8b2c-435b-b253-5e6af7867b8e_Гол.png new file mode 100644 index 0000000..e69de29 diff --git a/Govor.API/uploads/2025.11/13352c29-be8e-406d-a75c-321988cb8097_Гол.png b/Govor.API/uploads/2025.11/13352c29-be8e-406d-a75c-321988cb8097_Гол.png new file mode 100644 index 0000000..e69de29 diff --git a/Govor.API/uploads/2025.11/33567cf9-66b0-48b2-b681-6f0d1ecc3559_Имба.png b/Govor.API/uploads/2025.11/33567cf9-66b0-48b2-b681-6f0d1ecc3559_Имба.png new file mode 100644 index 0000000..2aac346 Binary files /dev/null and b/Govor.API/uploads/2025.11/33567cf9-66b0-48b2-b681-6f0d1ecc3559_Имба.png differ diff --git a/Govor.API/uploads/2025.11/46f20a02-3660-4f01-864c-5ec0726bbdb2_photo_2025-10-22_23-02-17.jpg b/Govor.API/uploads/2025.11/46f20a02-3660-4f01-864c-5ec0726bbdb2_photo_2025-10-22_23-02-17.jpg new file mode 100644 index 0000000..d18c75f Binary files /dev/null and b/Govor.API/uploads/2025.11/46f20a02-3660-4f01-864c-5ec0726bbdb2_photo_2025-10-22_23-02-17.jpg differ diff --git a/Govor.API/uploads/2025.11/8ada5627-87d3-4fec-9df0-60bac0c819bf_photo_2025-10-23_19-45-47.jpg b/Govor.API/uploads/2025.11/8ada5627-87d3-4fec-9df0-60bac0c819bf_photo_2025-10-23_19-45-47.jpg new file mode 100644 index 0000000..d86c3d7 Binary files /dev/null and b/Govor.API/uploads/2025.11/8ada5627-87d3-4fec-9df0-60bac0c819bf_photo_2025-10-23_19-45-47.jpg differ diff --git a/Govor.API/uploads/2025.11/b778c08a-1944-4689-9731-e56dbc0d70a9_photo_2025-10-22_23-02-17.jpg b/Govor.API/uploads/2025.11/b778c08a-1944-4689-9731-e56dbc0d70a9_photo_2025-10-22_23-02-17.jpg new file mode 100644 index 0000000..d18c75f Binary files /dev/null and b/Govor.API/uploads/2025.11/b778c08a-1944-4689-9731-e56dbc0d70a9_photo_2025-10-22_23-02-17.jpg differ diff --git a/Govor.API/uploads/2025.11/d1049058-3e2f-410f-b010-f91747f8091c_Быдло.png b/Govor.API/uploads/2025.11/d1049058-3e2f-410f-b010-f91747f8091c_Быдло.png new file mode 100644 index 0000000..3db0c43 Binary files /dev/null and b/Govor.API/uploads/2025.11/d1049058-3e2f-410f-b010-f91747f8091c_Быдло.png differ diff --git a/Govor.API/uploads/2025.11/fe755844-938b-4b86-9bb7-7dd1fcc7652e_photo_2025-10-23_19-45-47.jpg b/Govor.API/uploads/2025.11/fe755844-938b-4b86-9bb7-7dd1fcc7652e_photo_2025-10-23_19-45-47.jpg new file mode 100644 index 0000000..e69de29 diff --git a/Govor.Application.Tests/Govor.Application.Tests.csproj b/Govor.Application.Tests/Govor.Application.Tests.csproj index b9c4e8e..19dfd3d 100644 --- a/Govor.Application.Tests/Govor.Application.Tests.csproj +++ b/Govor.Application.Tests/Govor.Application.Tests.csproj @@ -16,6 +16,8 @@ + + diff --git a/Govor.Application.Tests/Services/Medias/AccesserToDownloadMediaServiceTests.cs b/Govor.Application.Tests/Services/Medias/AccesserToDownloadMediaServiceTests.cs index 183d562..990ec2a 100644 --- a/Govor.Application.Tests/Services/Medias/AccesserToDownloadMediaServiceTests.cs +++ b/Govor.Application.Tests/Services/Medias/AccesserToDownloadMediaServiceTests.cs @@ -31,7 +31,7 @@ public class AccesserToDownloadMediaServiceTests _groupId = Guid.NewGuid(); _mediaFileId = Guid.NewGuid(); - // Seed message from user to other user + // var message = new Message { Id = Guid.NewGuid(), @@ -40,7 +40,6 @@ public class AccesserToDownloadMediaServiceTests RecipientType = RecipientType.User }; - var media = new MediaFile { Id = _mediaFileId, @@ -48,7 +47,9 @@ public class AccesserToDownloadMediaServiceTests MineType = "image/png", MediaType = MediaType.Image, UploaderId = _userId, - DateCreated = DateTime.UtcNow + DateCreated = DateTime.UtcNow, + OwnerType = MediaOwnerType.Message, + OwnerId = message.Id }; var attachment = new MediaAttachments @@ -108,7 +109,9 @@ public class AccesserToDownloadMediaServiceTests MineType = "image/png", MediaType = MediaType.Image, UploaderId = _userId, - DateCreated = DateTime.UtcNow + DateCreated = DateTime.UtcNow, + OwnerType = MediaOwnerType.Message, + OwnerId = groupMessage.Id }; var attachment = new MediaAttachments @@ -144,9 +147,148 @@ public class AccesserToDownloadMediaServiceTests Assert.That(result, Is.False); } + [Test] + public async Task HasAccessAsync_ReturnsTrue_ForUserAvatar() + { + var avatarMedia = new MediaFile + { + Id = Guid.NewGuid(), + Url = "/media/avatar.png", + MineType = "image/png", + MediaType = MediaType.Image, + UploaderId = _userId, + OwnerType = MediaOwnerType.Avatar, + OwnerId = _userId, + DateCreated = DateTime.UtcNow + }; + + await _dbContext.MediaFiles.AddAsync(avatarMedia); + await _dbContext.SaveChangesAsync(); + + var result = await _accesser.HasAccessAsync(avatarMedia.Id, _userId); + Assert.That(result, Is.True); + } + + [Test] + public async Task HasAccessAsync_ReturnsFalse_ForOtherUserAvatar() + { + var avatarMedia = new MediaFile + { + Id = Guid.NewGuid(), + Url = "/media/avatar2.png", + MineType = "image/png", + MediaType = MediaType.Image, + UploaderId = _otherUserId, + OwnerType = MediaOwnerType.Avatar, + OwnerId = _otherUserId, + DateCreated = DateTime.UtcNow + }; + + await _dbContext.MediaFiles.AddAsync(avatarMedia); + await _dbContext.SaveChangesAsync(); + + var result = await _accesser.HasAccessAsync(avatarMedia.Id, _userId); + Assert.That(result, Is.False); + } + + [Test] + public async Task HasAccessAsync_ReturnsTrue_ForGroupAvatarMember() + { + var groupAvatar = new MediaFile + { + Id = Guid.NewGuid(), + Url = "/media/group_avatar.png", + MineType = "image/png", + MediaType = MediaType.Image, + UploaderId = _userId, + OwnerType = MediaOwnerType.GroupAvatar, + OwnerId = _groupId, + DateCreated = DateTime.UtcNow + }; + + var membership = new GroupMembership + { + Id = Guid.NewGuid(), + GroupId = _groupId, + UserId = _otherUserId + }; + + await _dbContext.MediaFiles.AddAsync(groupAvatar); + await _dbContext.GroupMemberships.AddAsync(membership); + await _dbContext.SaveChangesAsync(); + + var result = await _accesser.HasAccessAsync(groupAvatar.Id, _otherUserId); + Assert.That(result, Is.True); + } + + [Test] + public async Task HasAccessAsync_ReturnsFalse_ForGroupAvatarNonMember() + { + var groupAvatar = new MediaFile + { + Id = Guid.NewGuid(), + Url = "/media/group_avatar2.png", + MineType = "image/png", + MediaType = MediaType.Image, + UploaderId = _userId, + OwnerType = MediaOwnerType.GroupAvatar, + OwnerId = _groupId, + DateCreated = DateTime.UtcNow + }; + + await _dbContext.MediaFiles.AddAsync(groupAvatar); + await _dbContext.SaveChangesAsync(); + + var unrelatedUserId = Guid.NewGuid(); + var result = await _accesser.HasAccessAsync(groupAvatar.Id, unrelatedUserId); + Assert.That(result, Is.False); + } + + [Test] + public async Task HasAccessAsync_ReturnsTrue_ForSystemMedia() + { + var systemMedia = new MediaFile + { + Id = Guid.NewGuid(), + Url = "/media/system.png", + MineType = "image/png", + MediaType = MediaType.Image, + UploaderId = _userId, + OwnerType = MediaOwnerType.System, + DateCreated = DateTime.UtcNow + }; + + await _dbContext.MediaFiles.AddAsync(systemMedia); + await _dbContext.SaveChangesAsync(); + + var result = await _accesser.HasAccessAsync(systemMedia.Id, Guid.NewGuid()); + Assert.That(result, Is.True); + } + + [Test] + public async Task HasAccessAsync_ReturnsFalse_ForUnknownOwnerType() + { + var unknownMedia = new MediaFile + { + Id = Guid.NewGuid(), + Url = "/media/unknown.png", + MineType = "image/png", + MediaType = MediaType.Image, + UploaderId = _userId, + OwnerType = (MediaOwnerType)999, // + DateCreated = DateTime.UtcNow + }; + + await _dbContext.MediaFiles.AddAsync(unknownMedia); + await _dbContext.SaveChangesAsync(); + + var result = await _accesser.HasAccessAsync(unknownMedia.Id, _userId); + Assert.That(result, Is.False); + } + [TearDown] public void TearDown() { _dbContext.Dispose(); } -} +} \ No newline at end of file diff --git a/Govor.Application.Tests/Services/Messages/MessageCommandServiceTests.cs b/Govor.Application.Tests/Services/Messages/MessageCommandServiceTests.cs index 8cccb45..e517287 100644 --- a/Govor.Application.Tests/Services/Messages/MessageCommandServiceTests.cs +++ b/Govor.Application.Tests/Services/Messages/MessageCommandServiceTests.cs @@ -1,5 +1,6 @@ -using Govor.Application.Exceptions.VerifyFriendship; +using Govor.Application.Exceptions.VerifyFriendship; using Govor.Application.Interfaces; +using Govor.Application.Interfaces.Medias; using Govor.Application.Interfaces.Messages.Parameters; using Govor.Application.Services.Messages; using Govor.Core.Models; @@ -22,6 +23,7 @@ public class MessageCommandServiceTests private Mock _mockGroupsRepo; private Mock _mockVerifyFriendship; private Mock _mockPrivateChats; + private Mock _mockMediaService; private Mock> _mockLogger; private MessageCommandService _messageService; @@ -33,6 +35,7 @@ public class MessageCommandServiceTests _mockGroupsRepo = new Mock(); _mockVerifyFriendship = new Mock(); _mockPrivateChats = new Mock(); + _mockMediaService = new Mock(); _mockLogger = new Mock>(); _messageService = new MessageCommandService( @@ -41,6 +44,7 @@ public class MessageCommandServiceTests _mockGroupsRepo.Object, _mockVerifyFriendship.Object, _mockPrivateChats.Object, + _mockMediaService.Object, _mockLogger.Object); } @@ -52,7 +56,6 @@ public class MessageCommandServiceTests // Arrange var senderId = Guid.NewGuid(); var recipientId = Guid.NewGuid(); - var sendMessageParams = new SendMessage("Hello", null, recipientId, @@ -66,6 +69,7 @@ public class MessageCommandServiceTests _mockMessagesRepo.Setup(r => r.AddAsync(It.IsAny())).Returns(Task.CompletedTask); _mockPrivateChats.Setup(c => c.Exist(senderId, recipientId)).Returns(true); _mockPrivateChats.Setup(c => c.GetByMembersAsync(senderId, recipientId)).ReturnsAsync(new PrivateChat(){Id = recipientId}); + // Act var result = await _messageService.SendMessageAsync(sendMessageParams); // Assert @@ -79,7 +83,53 @@ public class MessageCommandServiceTests m.RecipientType == RecipientType.User && m.EncryptedContent == "Hello")), Times.Once); } - + + [Test] + public async Task SendMessageAsync_ToUser_When_AttachMediaThrowsException_ReturnsFailure() + { + // Arrange + var senderId = Guid.NewGuid(); + var recipientId = Guid.NewGuid(); + var sendMediaId = Guid.NewGuid(); + + var sendMessageParams = new SendMessage( + "Hello", + null, + recipientId, + RecipientType.User, + senderId, + DateTime.UtcNow, + new List { new SendMedia(sendMediaId, string.Empty) }); + + _mockUsersRepo.Setup(r => r.ExistsByIdAsync(recipientId)).ReturnsAsync(true); + _mockVerifyFriendship.Setup(v => v.VerifyAsync(senderId, recipientId)).Returns(Task.CompletedTask); + _mockMessagesRepo.Setup(r => r.AddAsync(It.IsAny())).Returns(Task.CompletedTask); + _mockPrivateChats.Setup(c => c.Exist(senderId, recipientId)).Returns(true); + _mockPrivateChats.Setup(c => c.GetByMembersAsync(senderId, recipientId)) + .ReturnsAsync(new PrivateChat { Id = recipientId }); + + // ! + _mockMediaService + .Setup(m => m.AttachToMessageAsync(sendMediaId, It.IsAny())) + .ThrowsAsync(new Exception("Unexpected DB error")); + + // Act + var result = await _messageService.SendMessageAsync(sendMessageParams); + + // Assert + Assert.That(result, Is.Not.Null); + Assert.That(result.IsSuccess, Is.False); + Assert.That(result.Exception, Is.Not.Null); + Assert.That(result.Exception.Message, Is.EqualTo("Unexpected DB error")); + + // Проверяем, что сообщение не было сохранено + _mockMessagesRepo.Verify(r => r.AddAsync(It.IsAny()), Times.Never); + + // Проверяем, что метод прикрепления медиа вызывался + _mockMediaService.Verify(m => m.AttachToMessageAsync(sendMediaId, It.IsAny()), Times.Once); + } + + [Test] public async Task SendMessageAsync_ToGroup_Success() { diff --git a/Govor.Application.Tests/Services/ProfileServiceTests.cs b/Govor.Application.Tests/Services/ProfileServiceTests.cs new file mode 100644 index 0000000..fa2dfb8 --- /dev/null +++ b/Govor.Application.Tests/Services/ProfileServiceTests.cs @@ -0,0 +1,131 @@ +using Govor.Application.Services; +using Govor.Core.Models.Users; +using Govor.Core.Repositories.Users; +using Microsoft.Extensions.Logging; +using Moq; + +namespace Govor.Application.Tests.Services; + +[TestFixture] +public class ProfileServiceTests +{ + private Mock _mockUsersRepo = null!; + private Mock> _mockLogger = null!; + private ProfileService _service = null!; + private User _testUser = null!; + + [SetUp] + public void SetUp() + { + _mockUsersRepo = new Mock(); + _mockLogger = new Mock>(); + + _service = new ProfileService(_mockUsersRepo.Object, _mockLogger.Object); + + _testUser = new User + { + Id = Guid.NewGuid(), + Username = "testuser", + Description = "old description", + IconId = Guid.NewGuid() + }; + } + + // ---------- GetUserProfileAsync ---------- + + [Test] + public async Task GetUserProfileAsync_ReturnsCorrectData() + { + // Arrange + _mockUsersRepo.Setup(r => r.FindByIdAsync(_testUser.Id)) + .ReturnsAsync(_testUser); + + // Act + var result = await _service.GetUserProfileAsync(_testUser.Id); + + // Assert + Assert.That(result, Is.Not.Null); + Assert.That(result.Id, Is.EqualTo(_testUser.Id)); + Assert.That(result.Username, Is.EqualTo("testuser")); + Assert.That(result.Description, Is.EqualTo("old description")); + Assert.That(result.IconId, Is.EqualTo(_testUser.IconId)); + + _mockUsersRepo.Verify(r => r.FindByIdAsync(_testUser.Id), Times.Once); + } + + [Test] + public void GetUserProfileAsync_Throws_WhenUserNotFound() + { + // Arrange + _mockUsersRepo.Setup(r => r.FindByIdAsync(It.IsAny())) + .ReturnsAsync((User)null!); + + // Act + Assert + Assert.ThrowsAsync(() => + _service.GetUserProfileAsync(Guid.NewGuid())); + } + + // ---------- SetDescription ---------- + + [Test] + public async Task SetDescription_UpdatesUserDescription() + { + // Arrange + var newDescription = "new cool description"; + _mockUsersRepo.Setup(r => r.FindByIdAsync(_testUser.Id)) + .ReturnsAsync(_testUser); + _mockUsersRepo.Setup(r => r.UpdateAsync(It.IsAny())) + .Returns(Task.CompletedTask); + + // Act + await _service.SetDescription(newDescription, _testUser.Id); + + // Assert + Assert.That(_testUser.Description, Is.EqualTo(newDescription)); + _mockUsersRepo.Verify(r => r.UpdateAsync(It.Is(u => u.Description == newDescription)), Times.Once); + } + + [Test] + public void SetDescription_Throws_WhenUserNotFound() + { + // Arrange + _mockUsersRepo.Setup(r => r.FindByIdAsync(It.IsAny())) + .ReturnsAsync((User)null!); + + // Act + Assert + Assert.ThrowsAsync(() => + _service.SetDescription("desc", Guid.NewGuid())); + } + + // ---------- SetNewIcon ---------- + + [Test] + public async Task SetNewIcon_UpdatesUserIconId() + { + // Arrange + var newIconId = Guid.NewGuid(); + _mockUsersRepo.Setup(r => r.FindByIdAsync(_testUser.Id)) + .ReturnsAsync(_testUser); + _mockUsersRepo.Setup(r => r.UpdateAsync(It.IsAny())) + .Returns(Task.CompletedTask); + + // Act + await _service.SetNewIcon(_testUser.Id, newIconId); + + // Assert + Assert.That(_testUser.IconId, Is.EqualTo(newIconId)); + _mockUsersRepo.Verify(r => r.UpdateAsync(It.Is(u => u.IconId == newIconId)), Times.Once); + } + + [Test] + public void SetNewIcon_Throws_WhenUserNotFound() + { + // Arrange + _mockUsersRepo.Setup(r => r.FindByIdAsync(It.IsAny())) + .ReturnsAsync((User)null!); + + // Act + Assert + Assert.ThrowsAsync(() => + _service.SetNewIcon(Guid.NewGuid(), Guid.NewGuid())); + } +} \ No newline at end of file diff --git a/Govor.Application.Tests/Services/UserSessions/UserSessionReaderTests.cs b/Govor.Application.Tests/Services/UserSessions/UserSessionReaderTests.cs index fbbcf6f..cc660fa 100644 --- a/Govor.Application.Tests/Services/UserSessions/UserSessionReaderTests.cs +++ b/Govor.Application.Tests/Services/UserSessions/UserSessionReaderTests.cs @@ -23,7 +23,12 @@ public class UserSessionReaderTests public void SetUp() { _fixture = new Fixture(); - + _fixture.Behaviors.OfType() + .ToList() + .ForEach(b => _fixture.Behaviors.Remove(b)); + + _fixture.Behaviors.Add(new OmitOnRecursionBehavior()); + _mockUserSessionsRepository = new Mock(); _mockLogger = new Mock>(); diff --git a/Govor.Application/Infrastructure/Validators/UsernameValidator.cs b/Govor.Application/Infrastructure/Validators/UsernameValidator.cs index 6b810c4..f38cb4e 100644 --- a/Govor.Application/Infrastructure/Validators/UsernameValidator.cs +++ b/Govor.Application/Infrastructure/Validators/UsernameValidator.cs @@ -1,4 +1,3 @@ -using System.Linq.Expressions; using System.Text.RegularExpressions; using Govor.Application.Exceptions.AuthService; using Govor.Application.Interfaces.Authentication; diff --git a/Govor.Application/Interfaces/IProfileService.cs b/Govor.Application/Interfaces/IProfileService.cs new file mode 100644 index 0000000..37a717f --- /dev/null +++ b/Govor.Application/Interfaces/IProfileService.cs @@ -0,0 +1,10 @@ +using Govor.Application.Profiles; + +namespace Govor.Application.Interfaces; + +public interface IProfileService +{ + public Task GetUserProfileAsync(Guid userId); + public Task SetDescription(string description, Guid userId); + public Task SetNewIcon(Guid userId, Guid iconId); +} diff --git a/Govor.Application/Interfaces/Medias/IMediaService.cs b/Govor.Application/Interfaces/Medias/IMediaService.cs index 30663ad..329e4d3 100644 --- a/Govor.Application/Interfaces/Medias/IMediaService.cs +++ b/Govor.Application/Interfaces/Medias/IMediaService.cs @@ -1,3 +1,4 @@ +using Govor.Core.Models; using Govor.Core.Models.Messages; namespace Govor.Application.Interfaces.Medias; @@ -8,6 +9,7 @@ public interface IMediaService public Task DeleteMediaAsync(Guid fileId); public Task GetMediaByUrlAsync(string url); public Task GetMediaByIdAsync(Guid mediaId); + Task AttachToMessageAsync(Guid mediaId, Guid messageId); } public record Media(Guid UploaderId, @@ -16,6 +18,8 @@ public record Media(Guid UploaderId, byte[] Data, MediaType Type, string MimeType, - string EncryptedKey); + string EncryptedKey, + MediaOwnerType OwnerType, + Guid? OwnerId); public record MediaUploadResult(Guid? MediaId, string Url); \ No newline at end of file diff --git a/Govor.Application/Profiles/UserProfile.cs b/Govor.Application/Profiles/UserProfile.cs new file mode 100644 index 0000000..10fdda7 --- /dev/null +++ b/Govor.Application/Profiles/UserProfile.cs @@ -0,0 +1,9 @@ +namespace Govor.Application.Profiles; + +public class UserProfile +{ + public Guid Id { get; set; } + public string Username { get; set; } = string.Empty; + public string? Description { get; set; } + public Guid? IconId { get; set; } +} diff --git a/Govor.Application/Services/Authentication/AuthService.cs b/Govor.Application/Services/Authentication/AuthService.cs index 1c4b2bf..fd60799 100644 --- a/Govor.Application/Services/Authentication/AuthService.cs +++ b/Govor.Application/Services/Authentication/AuthService.cs @@ -46,7 +46,7 @@ public class AuthService : IAccountService PasswordHash = passwordHash, Description = string.Empty, CreatedOn = DateOnly.FromDateTime(DateTime.UtcNow), - IconId = Guid.NewGuid(), + IconId = Guid.Empty, WasOnline = DateTime.UtcNow, InviteId = invitation.Id }; diff --git a/Govor.Application/Services/LocalStorageService.cs b/Govor.Application/Services/LocalStorageService.cs index 4ac849b..c7ebb3a 100644 --- a/Govor.Application/Services/LocalStorageService.cs +++ b/Govor.Application/Services/LocalStorageService.cs @@ -32,7 +32,7 @@ public class LocalStorageService : IStorageService var fullPath = Path.Combine(folder, uniqueFileName); await using var stream = new FileStream(fullPath, FileMode.Create); - stream.WriteAsync(data, 0, data.Length); + await stream.WriteAsync(data, 0, data.Length); return Path.Combine(date, uniqueFileName); } diff --git a/Govor.Application/Services/Medias/AccesserToDownloadMediaService.cs b/Govor.Application/Services/Medias/AccesserToDownloadMediaService.cs index 305ea53..c92c024 100644 --- a/Govor.Application/Services/Medias/AccesserToDownloadMediaService.cs +++ b/Govor.Application/Services/Medias/AccesserToDownloadMediaService.cs @@ -1,5 +1,5 @@ using Govor.Application.Interfaces.Medias; -using Govor.Core.Models.Messages; +using Govor.Core.Models; using Govor.Data; using Microsoft.EntityFrameworkCore; @@ -14,20 +14,35 @@ public class AccesserToDownloadMediaService : IAccesserToDownloadMedia _dbContext = dbContext; } - public async Task HasAccessAsync(Guid mediaFileId, Guid userId) + public async Task HasAccessAsync(Guid mediaId, Guid userId) { - return await _dbContext.MediaAttachments - .Include(ma => ma.Message) - .AnyAsync(ma => - ma.MediaFileId == mediaFileId && - ( - (ma.Message.RecipientType == RecipientType.User && - (ma.Message.SenderId == userId || ma.Message.RecipientId == userId)) - || - (ma.Message.RecipientType == RecipientType.Group && - _dbContext.GroupMemberships.Any(gm => - gm.GroupId == ma.Message.RecipientId && - gm.UserId == userId)) - )); + var media = await _dbContext.MediaFiles + .AsNoTracking() + .Where(m => m.Id == mediaId) + .Select(m => new { m.OwnerType, m.OwnerId, m.UploaderId }) + .FirstOrDefaultAsync(); + + if (media is null) + return false; + + return media.OwnerType switch + { + MediaOwnerType.Avatar => true, // media.OwnerId == userId + MediaOwnerType.GroupAvatar => await _dbContext.GroupMemberships + .AnyAsync(gm => gm.GroupId == media.OwnerId && gm.UserId == userId), + + MediaOwnerType.Message => await _dbContext.MediaAttachments + .AnyAsync(ma => + ma.MediaFileId == mediaId && + ( + ma.Message.SenderId == userId || + ma.Message.RecipientId == userId || + _dbContext.GroupMemberships.Any(gm => + gm.GroupId == ma.Message.RecipientId && gm.UserId == userId) + )), + + MediaOwnerType.System => true, + _ => false + }; } } \ No newline at end of file diff --git a/Govor.Application/Services/Medias/MediaService.cs b/Govor.Application/Services/Medias/MediaService.cs index 8e62ed6..226cacc 100644 --- a/Govor.Application/Services/Medias/MediaService.cs +++ b/Govor.Application/Services/Medias/MediaService.cs @@ -35,7 +35,9 @@ public class MediaService : IMediaService DateCreated = file.UploadedOn, MediaType = file.Type, MineType = file.MimeType, - Url = url + Url = url, + OwnerType = file.OwnerType, + OwnerId = file.OwnerId, }); await _dbContext.SaveChangesAsync(); @@ -87,7 +89,9 @@ public class MediaService : IMediaService contentBytes, mediaFile.MediaType, mediaFile.MineType, - string.Empty + string.Empty, + mediaFile.OwnerType, + mediaFile.OwnerId ); } catch (FileNotFoundException ex) @@ -96,4 +100,24 @@ public class MediaService : IMediaService throw; } } + public async Task AttachToMessageAsync(Guid mediaId, Guid messageId) + { + var mediaFile = await _dbContext.MediaFiles + .FirstOrDefaultAsync(x => x.Id == mediaId) + ?? throw new KeyNotFoundException($"No media found by given id {mediaId}"); + + if (mediaFile.OwnerType != MediaOwnerType.Message) + { + _logger.LogWarning("Attempt to attach already owned media {MediaId}", mediaId); + throw new InvalidOperationException($"Media {mediaId} is already attached to {mediaFile.OwnerType}"); + } + + mediaFile.OwnerType = MediaOwnerType.Message; + mediaFile.OwnerId = messageId; + + _dbContext.MediaFiles.Update(mediaFile); + await _dbContext.SaveChangesAsync(); + + _logger.LogInformation("Media {MediaId} successfully attached to message {MessageId}", mediaId, messageId); + } } \ No newline at end of file diff --git a/Govor.Application/Services/Messages/MessageCommandService.cs b/Govor.Application/Services/Messages/MessageCommandService.cs index abaa464..40c1914 100644 --- a/Govor.Application/Services/Messages/MessageCommandService.cs +++ b/Govor.Application/Services/Messages/MessageCommandService.cs @@ -1,4 +1,5 @@ -using Govor.Application.Interfaces; +using Govor.Application.Interfaces; +using Govor.Application.Interfaces.Medias; using Govor.Application.Interfaces.Messages; using Govor.Application.Interfaces.Messages.Parameters; using Govor.Core.Models; @@ -18,7 +19,8 @@ public class MessageCommandService : IMessageCommandService private readonly IUsersRepository _usersRepository; private readonly IGroupsRepository _groupsRepository; private readonly IPrivateChatsRepository _privateChats; - private readonly IVerifyFriendship _verifyFriendship; + private readonly IVerifyFriendship _verifyFriendship; + private readonly IMediaService _mediaService; private readonly ILogger _logger; public MessageCommandService( @@ -27,6 +29,7 @@ public class MessageCommandService : IMessageCommandService IGroupsRepository groupsRepository, IVerifyFriendship verifyFriendship, IPrivateChatsRepository privateChats, + IMediaService mediaService, ILogger logger) { _messagesRepository = messagesRepository; @@ -34,6 +37,7 @@ public class MessageCommandService : IMessageCommandService _groupsRepository = groupsRepository; _verifyFriendship = verifyFriendship; _privateChats = privateChats; + _mediaService = mediaService; _logger = logger; } @@ -97,6 +101,15 @@ public class MessageCommandService : IMessageCommandService }).ToList() ?? new List() }; + // If there is media, we link them. + if (sendParams.Media != null && sendParams.Media.Any()) + { + foreach (var media in sendParams.Media) + { + await _mediaService.AttachToMessageAsync(media.MediaId, messageId); + } + } + await _messagesRepository.AddAsync(message); _logger.LogInformation("Message {MessageId} from {SenderId} to {RecipientId} ({RecipientType}) saved successfully.", messageId, sendParams.FromUserId, sendParams.RecipientId, sendParams.RecipientType); return new SendMessageResult(true, null, message); diff --git a/Govor.Application/Services/ProfileService.cs b/Govor.Application/Services/ProfileService.cs new file mode 100644 index 0000000..2d97aa8 --- /dev/null +++ b/Govor.Application/Services/ProfileService.cs @@ -0,0 +1,48 @@ +using Govor.Application.Interfaces; +using Govor.Application.Profiles; +using Govor.Core.Repositories.Users; +using Microsoft.Extensions.Logging; + +namespace Govor.Application.Services; + +public class ProfileService : IProfileService +{ + private readonly ILogger _logger; + private readonly IUsersRepository _userRepository; + + public ProfileService(IUsersRepository userRepository, ILogger logger) + { + _userRepository = userRepository; + _logger = logger; + } + + public async Task GetUserProfileAsync(Guid userId) + { + _logger.LogInformation("Gettings user {userId} profile", userId); + var user = await _userRepository.FindByIdAsync(userId); + + return new UserProfile + { + Id = user.Id, + Username = user.Username, + Description = user.Description, + IconId = user.IconId + }; + } + + public async Task SetDescription(string description, Guid userId) + { + var user = await _userRepository.FindByIdAsync(userId); + + user.Description = description; + await _userRepository.UpdateAsync(user); + } + + public async Task SetNewIcon(Guid userId, Guid iconId) + { + var user = await _userRepository.FindByIdAsync(userId); + + user.IconId = iconId; + await _userRepository.UpdateAsync(user); + } +} diff --git a/Govor.Application/Services/VerifierFriendship.cs b/Govor.Application/Services/VerifierFriendship.cs index 7fc3c1a..1a57659 100644 --- a/Govor.Application/Services/VerifierFriendship.cs +++ b/Govor.Application/Services/VerifierFriendship.cs @@ -13,7 +13,7 @@ public class VerifyFriendship : IVerifyFriendship private readonly ILogger _logger; private const string FriendshipNotAcceptedError = "Friendship between user {0} and friend {1} does not exist or is not accepted."; - public VerifyFriendship(IFriendshipsRepository friendshipsRepository, ILogger logger = null) + public VerifyFriendship(IFriendshipsRepository friendshipsRepository, ILogger logger) { _friendshipsRepository = friendshipsRepository ?? throw new ArgumentNullException(nameof(friendshipsRepository)); _logger = logger; @@ -42,14 +42,16 @@ public class VerifyFriendship : IVerifyFriendship throw new FriendshipException(errorMessage); } - _logger?.LogInformation( + _logger.LogInformation("hello"); + + _logger.LogInformation( "Friendship verified successfully for targetUserId={TargetUserId}, friendUserId={FriendUserId}", targetUserId, friendUserId); } catch (NotFoundByKeyException ex) { var errorMessage = string.Format(FriendshipNotAcceptedError, targetUserId, friendUserId); - _logger?.LogError(errorMessage); + _logger.LogError(errorMessage); throw new FriendshipException(errorMessage); } diff --git a/Govor.Contracts/DTOs/UserProfileDto.cs b/Govor.Contracts/DTOs/UserProfileDto.cs new file mode 100644 index 0000000..6d8acfd --- /dev/null +++ b/Govor.Contracts/DTOs/UserProfileDto.cs @@ -0,0 +1,9 @@ +namespace Govor.Contracts.DTOs; + +public class UserProfileDto +{ + public Guid Id { get; set; } + public string Username { get; set; } = string.Empty; + public string? Description { get; set; } + public Guid? IconId { get; set; } +} diff --git a/Govor.Contracts/Requests/MediaUploadRequest.cs b/Govor.Contracts/Requests/MediaUploadRequest.cs index 4c3a74b..ec62161 100644 --- a/Govor.Contracts/Requests/MediaUploadRequest.cs +++ b/Govor.Contracts/Requests/MediaUploadRequest.cs @@ -1,6 +1,7 @@ -using System.ComponentModel.DataAnnotations; +using Govor.Core.Models; using Govor.Core.Models.Messages; using Microsoft.AspNetCore.Http; +using System.ComponentModel.DataAnnotations; namespace Govor.Contracts.Requests; @@ -14,4 +15,6 @@ public class MediaUploadRequest public string MimeType { get; set; } = string.Empty; [Required] public string EncryptedKey { get; set; } = string.Empty; + [Required] + public MediaOwnerType OwnerType { get; set; } = MediaOwnerType.Message; } \ No newline at end of file diff --git a/Govor.Contracts/Requests/SignalR/MessageRequest.cs b/Govor.Contracts/Requests/SignalR/MessageRequest.cs index a29e3fb..d2dabaf 100644 --- a/Govor.Contracts/Requests/SignalR/MessageRequest.cs +++ b/Govor.Contracts/Requests/SignalR/MessageRequest.cs @@ -1,4 +1,5 @@ using Govor.Core.Models.Messages; +using System.ComponentModel.DataAnnotations; namespace Govor.Contracts.Requests.SignalR; @@ -6,6 +7,8 @@ public record MessageRequest { public Guid RecipientId { get; init; } public RecipientType RecipientType { get; init; } + [Required] + [MaxLength(100_000, ErrorMessage = "EncryptedContent cannot exceed 100,000 characters.")] public string EncryptedContent { get; init; } = string.Empty; public Guid? ReplyToMessageId { get; set; } public List MediaAttachments { get; set; } = new(); diff --git a/Govor.Core/Models/MediaFile.cs b/Govor.Core/Models/MediaFile.cs index 50d8e59..940dbd5 100644 --- a/Govor.Core/Models/MediaFile.cs +++ b/Govor.Core/Models/MediaFile.cs @@ -10,4 +10,15 @@ public class MediaFile public MediaType MediaType { get; set; } public string MineType { get; set; } public DateTime DateCreated { get; set; } + + public MediaOwnerType OwnerType { get; set; } = MediaOwnerType.Message; + public Guid? OwnerId { get; set; } +} + +public enum MediaOwnerType +{ + Message = 0, + Avatar = 1, + GroupAvatar = 2, + System = 3 // (Emoge, icons e.t.c) } \ No newline at end of file diff --git a/Govor.Core/Models/Users/UserSession.cs b/Govor.Core/Models/Users/UserSession.cs index da4728e..23a85a5 100644 --- a/Govor.Core/Models/Users/UserSession.cs +++ b/Govor.Core/Models/Users/UserSession.cs @@ -11,7 +11,8 @@ public class UserSession public DateTime CreatedAt { get; set; } = DateTime.UtcNow; public DateTime ExpiresAt { get; set; } public bool IsRevoked { get; set; } = false; - + // public DateTime? RevokedAt { get; set; } TODO: Clear old UserSessions + public UserCryptoSession CryptoSession { get; set; } public override bool Equals(object? obj) diff --git a/Govor.Data/Migrations/20250707142430_InitialCreate.Designer.cs b/Govor.Data/Migrations/20250707142430_InitialCreate.Designer.cs deleted file mode 100644 index 219d0c6..0000000 --- a/Govor.Data/Migrations/20250707142430_InitialCreate.Designer.cs +++ /dev/null @@ -1,582 +0,0 @@ -// -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("20250707142430_InitialCreate")] - partial class InitialCreate - { - /// - 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.Admin", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("UserId"); - - b.ToTable("Admins"); - }); - - modelBuilder.Entity("Govor.Core.Models.ChatGroup", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("Description") - .IsRequired() - .HasMaxLength(500) - .HasColumnType("varchar(500)"); - - b.Property("ImageId") - .HasColumnType("char(36)"); - - b.Property("IsChannel") - .HasColumnType("tinyint(1)"); - - b.Property("IsPrivate") - .HasColumnType("tinyint(1)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("varchar(100)"); - - b.HasKey("Id"); - - b.ToTable("ChatGroups"); - }); - - modelBuilder.Entity("Govor.Core.Models.Friendship", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AddresseeId") - .HasColumnType("char(36)"); - - b.Property("RequesterId") - .HasColumnType("char(36)"); - - b.Property("Status") - .HasColumnType("int"); - - b.HasKey("Id"); - - b.HasIndex("AddresseeId"); - - b.HasIndex("RequesterId"); - - b.ToTable("Friendships"); - }); - - modelBuilder.Entity("Govor.Core.Models.GroupAdmins", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("GroupId") - .HasColumnType("char(36)"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("GroupId"); - - b.ToTable("GroupAdmins"); - }); - - modelBuilder.Entity("Govor.Core.Models.GroupInvitation", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("Description") - .IsRequired() - .HasMaxLength(500) - .HasColumnType("varchar(500)"); - - b.Property("EndDate") - .HasColumnType("datetime(6)"); - - b.Property("GroupId") - .HasColumnType("char(36)"); - - b.Property("InvitationCode") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("varchar(200)"); - - b.Property("MaxParticipants") - .HasColumnType("int"); - - b.Property("UserMakerId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("GroupId"); - - b.HasIndex("UserMakerId"); - - b.ToTable("GroupInvitations"); - }); - - modelBuilder.Entity("Govor.Core.Models.GroupMembership", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("GroupId") - .HasColumnType("char(36)"); - - b.Property("InvitationId") - .IsRequired() - .HasColumnType("char(36)"); - - b.Property("IsBanned") - .HasColumnType("tinyint(1)"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("GroupId"); - - b.HasIndex("InvitationId"); - - b.ToTable("GroupMemberships"); - }); - - modelBuilder.Entity("Govor.Core.Models.Invitation", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("Code") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("DateCreated") - .HasColumnType("datetime(6)"); - - b.Property("Description") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("EndDate") - .HasColumnType("datetime(6)"); - - b.Property("IsActive") - .HasColumnType("tinyint(1)"); - - b.Property("IsAdmin") - .HasColumnType("tinyint(1)"); - - b.Property("MaxParticipants") - .HasColumnType("int"); - - b.HasKey("Id"); - - b.ToTable("Invitations"); - }); - - modelBuilder.Entity("Govor.Core.Models.MediaAttachments", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("MediaFileId") - .HasColumnType("char(36)"); - - b.Property("MessageId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("MediaFileId"); - - b.HasIndex("MessageId"); - - b.ToTable("MediaAttachments"); - }); - - modelBuilder.Entity("Govor.Core.Models.MediaFile", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("DateCreated") - .HasColumnType("datetime(6)"); - - b.Property("MediaType") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("MineType") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("varchar(128)"); - - b.Property("Url") - .IsRequired() - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.ToTable("MediaFiles"); - }); - - modelBuilder.Entity("Govor.Core.Models.Message", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("EditedAt") - .HasColumnType("datetime(6)"); - - b.Property("EncryptedContent") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("IsEdited") - .HasColumnType("tinyint(1)"); - - b.Property("PrivateChatId") - .HasColumnType("char(36)"); - - b.Property("RecipientId") - .HasColumnType("char(36)"); - - b.Property("RecipientType") - .HasColumnType("int"); - - b.Property("ReplyToMessageId") - .HasColumnType("char(36)"); - - b.Property("SenderId") - .HasColumnType("char(36)"); - - b.Property("SentAt") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("PrivateChatId"); - - b.ToTable("Messages"); - }); - - modelBuilder.Entity("Govor.Core.Models.MessageReaction", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("MessageId") - .HasColumnType("char(36)"); - - b.Property("ReactedAt") - .HasColumnType("datetime(6)"); - - b.Property("ReactionCode") - .IsRequired() - .HasMaxLength(64) - .HasColumnType("varchar(64)"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.HasIndex("MessageId", "UserId") - .IsUnique(); - - b.ToTable("MessageReactions"); - }); - - modelBuilder.Entity("Govor.Core.Models.MessageView", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("MessageId") - .HasColumnType("char(36)"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("ViewedAt") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("MessageId", "UserId") - .IsUnique(); - - b.ToTable("MessageViews"); - }); - - modelBuilder.Entity("Govor.Core.Models.PrivateChat", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("UserAId") - .HasColumnType("char(36)"); - - b.Property("UserBId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.ToTable("PrivateChats"); - }); - - modelBuilder.Entity("Govor.Core.Models.User", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedOn") - .HasColumnType("date"); - - b.Property("Description") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("IconId") - .HasColumnType("char(36)"); - - b.Property("InviteId") - .HasColumnType("char(36)"); - - b.Property("PasswordHash") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("Username") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("WasOnline") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("InviteId"); - - b.ToTable("Users"); - }); - - modelBuilder.Entity("Govor.Core.Models.Admin", b => - { - b.HasOne("Govor.Core.Models.User", "User") - .WithOne() - .HasForeignKey("Govor.Core.Models.Admin", "UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Govor.Core.Models.Friendship", b => - { - b.HasOne("Govor.Core.Models.User", "Addressee") - .WithMany("ReceivedFriendRequests") - .HasForeignKey("AddresseeId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired(); - - b.HasOne("Govor.Core.Models.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.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.MediaAttachments", b => - { - b.HasOne("Govor.Core.Models.MediaFile", "MediaFile") - .WithMany() - .HasForeignKey("MediaFileId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired(); - - b.HasOne("Govor.Core.Models.Message", "Message") - .WithMany("MediaAttachments") - .HasForeignKey("MessageId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("MediaFile"); - - b.Navigation("Message"); - }); - - modelBuilder.Entity("Govor.Core.Models.Message", b => - { - b.HasOne("Govor.Core.Models.PrivateChat", null) - .WithMany("Messages") - .HasForeignKey("PrivateChatId"); - }); - - modelBuilder.Entity("Govor.Core.Models.MessageReaction", b => - { - b.HasOne("Govor.Core.Models.Message", "Message") - .WithMany("Reactions") - .HasForeignKey("MessageId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Govor.Core.Models.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Message"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Govor.Core.Models.MessageView", b => - { - b.HasOne("Govor.Core.Models.Message", null) - .WithMany("MessageViews") - .HasForeignKey("MessageId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Govor.Core.Models.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"); - }); - - modelBuilder.Entity("Govor.Core.Models.Invitation", b => - { - b.Navigation("Users"); - }); - - modelBuilder.Entity("Govor.Core.Models.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.User", b => - { - b.Navigation("ReceivedFriendRequests"); - - b.Navigation("SentFriendRequests"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Govor.Data/Migrations/20250712090733_AddChatGroupIdToMessage.Designer.cs b/Govor.Data/Migrations/20250712090733_AddChatGroupIdToMessage.Designer.cs deleted file mode 100644 index a320abd..0000000 --- a/Govor.Data/Migrations/20250712090733_AddChatGroupIdToMessage.Designer.cs +++ /dev/null @@ -1,593 +0,0 @@ -// -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("20250712090733_AddChatGroupIdToMessage")] - partial class AddChatGroupIdToMessage - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "8.0.6") - .HasAnnotation("Relational:MaxIdentifierLength", 64); - - MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); - - modelBuilder.Entity("Govor.Core.Models.ChatGroup", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("Description") - .IsRequired() - .HasMaxLength(500) - .HasColumnType("varchar(500)"); - - b.Property("ImageId") - .HasColumnType("char(36)"); - - b.Property("IsChannel") - .HasColumnType("tinyint(1)"); - - b.Property("IsPrivate") - .HasColumnType("tinyint(1)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("varchar(100)"); - - b.HasKey("Id"); - - b.ToTable("ChatGroups"); - }); - - modelBuilder.Entity("Govor.Core.Models.Friendship", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AddresseeId") - .HasColumnType("char(36)"); - - b.Property("RequesterId") - .HasColumnType("char(36)"); - - b.Property("Status") - .HasColumnType("int"); - - b.HasKey("Id"); - - b.HasIndex("AddresseeId"); - - b.HasIndex("RequesterId"); - - b.ToTable("Friendships"); - }); - - modelBuilder.Entity("Govor.Core.Models.GroupAdmins", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("GroupId") - .HasColumnType("char(36)"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("GroupId"); - - b.ToTable("GroupAdmins"); - }); - - modelBuilder.Entity("Govor.Core.Models.GroupInvitation", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("Description") - .IsRequired() - .HasMaxLength(500) - .HasColumnType("varchar(500)"); - - b.Property("EndDate") - .HasColumnType("datetime(6)"); - - b.Property("GroupId") - .HasColumnType("char(36)"); - - b.Property("InvitationCode") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("varchar(200)"); - - b.Property("MaxParticipants") - .HasColumnType("int"); - - b.Property("UserMakerId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("GroupId"); - - b.HasIndex("UserMakerId"); - - b.ToTable("GroupInvitations"); - }); - - modelBuilder.Entity("Govor.Core.Models.GroupMembership", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("GroupId") - .HasColumnType("char(36)"); - - b.Property("InvitationId") - .IsRequired() - .HasColumnType("char(36)"); - - b.Property("IsBanned") - .HasColumnType("tinyint(1)"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("GroupId"); - - b.HasIndex("InvitationId"); - - b.ToTable("GroupMemberships"); - }); - - modelBuilder.Entity("Govor.Core.Models.Invitation", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("Code") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("DateCreated") - .HasColumnType("datetime(6)"); - - b.Property("Description") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("EndDate") - .HasColumnType("datetime(6)"); - - b.Property("IsActive") - .HasColumnType("tinyint(1)"); - - b.Property("IsAdmin") - .HasColumnType("tinyint(1)"); - - b.Property("MaxParticipants") - .HasColumnType("int"); - - b.HasKey("Id"); - - b.ToTable("Invitations"); - }); - - modelBuilder.Entity("Govor.Core.Models.MediaFile", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("DateCreated") - .HasColumnType("datetime(6)"); - - b.Property("MediaType") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("MineType") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("varchar(128)"); - - b.Property("Url") - .IsRequired() - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.ToTable("MediaFiles"); - }); - - modelBuilder.Entity("Govor.Core.Models.Messages.MediaAttachments", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("MediaFileId") - .HasColumnType("char(36)"); - - b.Property("MessageId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("MediaFileId"); - - b.HasIndex("MessageId"); - - b.ToTable("MediaAttachments"); - }); - - modelBuilder.Entity("Govor.Core.Models.Messages.Message", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ChatGroupId") - .HasColumnType("char(36)"); - - b.Property("EditedAt") - .HasColumnType("datetime(6)"); - - b.Property("EncryptedContent") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("IsEdited") - .HasColumnType("tinyint(1)"); - - b.Property("PrivateChatId") - .HasColumnType("char(36)"); - - b.Property("RecipientId") - .HasColumnType("char(36)"); - - b.Property("RecipientType") - .HasColumnType("int"); - - b.Property("ReplyToMessageId") - .HasColumnType("char(36)"); - - b.Property("SenderId") - .HasColumnType("char(36)"); - - b.Property("SentAt") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("ChatGroupId"); - - b.HasIndex("PrivateChatId"); - - b.ToTable("Messages"); - }); - - modelBuilder.Entity("Govor.Core.Models.Messages.MessageReaction", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("MessageId") - .HasColumnType("char(36)"); - - b.Property("ReactedAt") - .HasColumnType("datetime(6)"); - - b.Property("ReactionCode") - .IsRequired() - .HasMaxLength(64) - .HasColumnType("varchar(64)"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.HasIndex("MessageId", "UserId") - .IsUnique(); - - b.ToTable("MessageReactions"); - }); - - modelBuilder.Entity("Govor.Core.Models.Messages.MessageView", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("MessageId") - .HasColumnType("char(36)"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("ViewedAt") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("MessageId", "UserId") - .IsUnique(); - - b.ToTable("MessageViews"); - }); - - modelBuilder.Entity("Govor.Core.Models.PrivateChat", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("UserAId") - .HasColumnType("char(36)"); - - b.Property("UserBId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.ToTable("PrivateChats"); - }); - - modelBuilder.Entity("Govor.Core.Models.Users.Admin", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("UserId"); - - b.ToTable("Admins"); - }); - - modelBuilder.Entity("Govor.Core.Models.Users.User", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedOn") - .HasColumnType("date"); - - b.Property("Description") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("IconId") - .HasColumnType("char(36)"); - - b.Property("InviteId") - .HasColumnType("char(36)"); - - b.Property("PasswordHash") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("Username") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("WasOnline") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("InviteId"); - - b.ToTable("Users"); - }); - - modelBuilder.Entity("Govor.Core.Models.Friendship", b => - { - b.HasOne("Govor.Core.Models.Users.User", "Addressee") - .WithMany("ReceivedFriendRequests") - .HasForeignKey("AddresseeId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired(); - - b.HasOne("Govor.Core.Models.Users.User", "Requester") - .WithMany("SentFriendRequests") - .HasForeignKey("RequesterId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired(); - - b.Navigation("Addressee"); - - b.Navigation("Requester"); - }); - - modelBuilder.Entity("Govor.Core.Models.GroupAdmins", b => - { - b.HasOne("Govor.Core.Models.ChatGroup", null) - .WithMany("Admins") - .HasForeignKey("GroupId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Govor.Core.Models.GroupInvitation", b => - { - b.HasOne("Govor.Core.Models.ChatGroup", null) - .WithMany("InviteCodes") - .HasForeignKey("GroupId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Govor.Core.Models.Users.User", "UserMaker") - .WithMany() - .HasForeignKey("UserMakerId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired(); - - b.Navigation("UserMaker"); - }); - - modelBuilder.Entity("Govor.Core.Models.GroupMembership", b => - { - b.HasOne("Govor.Core.Models.ChatGroup", null) - .WithMany("Members") - .HasForeignKey("GroupId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Govor.Core.Models.GroupInvitation", null) - .WithMany() - .HasForeignKey("InvitationId") - .OnDelete(DeleteBehavior.SetNull) - .IsRequired(); - }); - - modelBuilder.Entity("Govor.Core.Models.Messages.MediaAttachments", b => - { - b.HasOne("Govor.Core.Models.MediaFile", "MediaFile") - .WithMany() - .HasForeignKey("MediaFileId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired(); - - b.HasOne("Govor.Core.Models.Messages.Message", "Message") - .WithMany("MediaAttachments") - .HasForeignKey("MessageId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("MediaFile"); - - b.Navigation("Message"); - }); - - modelBuilder.Entity("Govor.Core.Models.Messages.Message", b => - { - b.HasOne("Govor.Core.Models.ChatGroup", null) - .WithMany("Messages") - .HasForeignKey("ChatGroupId"); - - b.HasOne("Govor.Core.Models.PrivateChat", null) - .WithMany("Messages") - .HasForeignKey("PrivateChatId"); - }); - - modelBuilder.Entity("Govor.Core.Models.Messages.MessageReaction", b => - { - b.HasOne("Govor.Core.Models.Messages.Message", "Message") - .WithMany("Reactions") - .HasForeignKey("MessageId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Govor.Core.Models.Users.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Message"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Govor.Core.Models.Messages.MessageView", b => - { - b.HasOne("Govor.Core.Models.Messages.Message", null) - .WithMany("MessageViews") - .HasForeignKey("MessageId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Govor.Core.Models.Users.Admin", b => - { - b.HasOne("Govor.Core.Models.Users.User", "User") - .WithOne() - .HasForeignKey("Govor.Core.Models.Users.Admin", "UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Govor.Core.Models.Users.User", b => - { - b.HasOne("Govor.Core.Models.Invitation", "Invite") - .WithMany("Users") - .HasForeignKey("InviteId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Invite"); - }); - - modelBuilder.Entity("Govor.Core.Models.ChatGroup", b => - { - b.Navigation("Admins"); - - b.Navigation("InviteCodes"); - - b.Navigation("Members"); - - b.Navigation("Messages"); - }); - - modelBuilder.Entity("Govor.Core.Models.Invitation", b => - { - b.Navigation("Users"); - }); - - modelBuilder.Entity("Govor.Core.Models.Messages.Message", b => - { - b.Navigation("MediaAttachments"); - - b.Navigation("MessageViews"); - - b.Navigation("Reactions"); - }); - - modelBuilder.Entity("Govor.Core.Models.PrivateChat", b => - { - b.Navigation("Messages"); - }); - - modelBuilder.Entity("Govor.Core.Models.Users.User", b => - { - b.Navigation("ReceivedFriendRequests"); - - b.Navigation("SentFriendRequests"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Govor.Data/Migrations/20250712090733_AddChatGroupIdToMessage.cs b/Govor.Data/Migrations/20250712090733_AddChatGroupIdToMessage.cs deleted file mode 100644 index 6eaab86..0000000 --- a/Govor.Data/Migrations/20250712090733_AddChatGroupIdToMessage.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Govor.Data.Migrations -{ - /// - public partial class AddChatGroupIdToMessage : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AddColumn( - name: "ChatGroupId", - table: "Messages", - type: "char(36)", - nullable: true, - collation: "ascii_general_ci"); - - migrationBuilder.CreateIndex( - name: "IX_Messages_ChatGroupId", - table: "Messages", - column: "ChatGroupId"); - - migrationBuilder.AddForeignKey( - name: "FK_Messages_ChatGroups_ChatGroupId", - table: "Messages", - column: "ChatGroupId", - principalTable: "ChatGroups", - principalColumn: "Id"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropForeignKey( - name: "FK_Messages_ChatGroups_ChatGroupId", - table: "Messages"); - - migrationBuilder.DropIndex( - name: "IX_Messages_ChatGroupId", - table: "Messages"); - - migrationBuilder.DropColumn( - name: "ChatGroupId", - table: "Messages"); - } - } -} diff --git a/Govor.Data/Migrations/20250713101943_AddUploaderIdToMediaFile.Designer.cs b/Govor.Data/Migrations/20250713101943_AddUploaderIdToMediaFile.Designer.cs deleted file mode 100644 index 69fad3a..0000000 --- a/Govor.Data/Migrations/20250713101943_AddUploaderIdToMediaFile.Designer.cs +++ /dev/null @@ -1,596 +0,0 @@ -// -using System; -using Govor.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace Govor.Data.Migrations -{ - [DbContext(typeof(GovorDbContext))] - [Migration("20250713101943_AddUploaderIdToMediaFile")] - partial class AddUploaderIdToMediaFile - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "8.0.6") - .HasAnnotation("Relational:MaxIdentifierLength", 64); - - MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); - - modelBuilder.Entity("Govor.Core.Models.ChatGroup", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("Description") - .IsRequired() - .HasMaxLength(500) - .HasColumnType("varchar(500)"); - - b.Property("ImageId") - .HasColumnType("char(36)"); - - b.Property("IsChannel") - .HasColumnType("tinyint(1)"); - - b.Property("IsPrivate") - .HasColumnType("tinyint(1)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("varchar(100)"); - - b.HasKey("Id"); - - b.ToTable("ChatGroups"); - }); - - modelBuilder.Entity("Govor.Core.Models.Friendship", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AddresseeId") - .HasColumnType("char(36)"); - - b.Property("RequesterId") - .HasColumnType("char(36)"); - - b.Property("Status") - .HasColumnType("int"); - - b.HasKey("Id"); - - b.HasIndex("AddresseeId"); - - b.HasIndex("RequesterId"); - - b.ToTable("Friendships"); - }); - - modelBuilder.Entity("Govor.Core.Models.GroupAdmins", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("GroupId") - .HasColumnType("char(36)"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("GroupId"); - - b.ToTable("GroupAdmins"); - }); - - modelBuilder.Entity("Govor.Core.Models.GroupInvitation", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("Description") - .IsRequired() - .HasMaxLength(500) - .HasColumnType("varchar(500)"); - - b.Property("EndDate") - .HasColumnType("datetime(6)"); - - b.Property("GroupId") - .HasColumnType("char(36)"); - - b.Property("InvitationCode") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("varchar(200)"); - - b.Property("MaxParticipants") - .HasColumnType("int"); - - b.Property("UserMakerId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("GroupId"); - - b.HasIndex("UserMakerId"); - - b.ToTable("GroupInvitations"); - }); - - modelBuilder.Entity("Govor.Core.Models.GroupMembership", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("GroupId") - .HasColumnType("char(36)"); - - b.Property("InvitationId") - .IsRequired() - .HasColumnType("char(36)"); - - b.Property("IsBanned") - .HasColumnType("tinyint(1)"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("GroupId"); - - b.HasIndex("InvitationId"); - - b.ToTable("GroupMemberships"); - }); - - modelBuilder.Entity("Govor.Core.Models.Invitation", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("Code") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("DateCreated") - .HasColumnType("datetime(6)"); - - b.Property("Description") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("EndDate") - .HasColumnType("datetime(6)"); - - b.Property("IsActive") - .HasColumnType("tinyint(1)"); - - b.Property("IsAdmin") - .HasColumnType("tinyint(1)"); - - b.Property("MaxParticipants") - .HasColumnType("int"); - - b.HasKey("Id"); - - b.ToTable("Invitations"); - }); - - modelBuilder.Entity("Govor.Core.Models.MediaFile", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("DateCreated") - .HasColumnType("datetime(6)"); - - b.Property("MediaType") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("MineType") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("varchar(128)"); - - b.Property("UploaderId") - .HasColumnType("char(36)"); - - b.Property("Url") - .IsRequired() - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.ToTable("MediaFiles"); - }); - - modelBuilder.Entity("Govor.Core.Models.Messages.MediaAttachments", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("MediaFileId") - .HasColumnType("char(36)"); - - b.Property("MessageId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("MediaFileId"); - - b.HasIndex("MessageId"); - - b.ToTable("MediaAttachments"); - }); - - modelBuilder.Entity("Govor.Core.Models.Messages.Message", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ChatGroupId") - .HasColumnType("char(36)"); - - b.Property("EditedAt") - .HasColumnType("datetime(6)"); - - b.Property("EncryptedContent") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("IsEdited") - .HasColumnType("tinyint(1)"); - - b.Property("PrivateChatId") - .HasColumnType("char(36)"); - - b.Property("RecipientId") - .HasColumnType("char(36)"); - - b.Property("RecipientType") - .HasColumnType("int"); - - b.Property("ReplyToMessageId") - .HasColumnType("char(36)"); - - b.Property("SenderId") - .HasColumnType("char(36)"); - - b.Property("SentAt") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("ChatGroupId"); - - b.HasIndex("PrivateChatId"); - - b.ToTable("Messages"); - }); - - modelBuilder.Entity("Govor.Core.Models.Messages.MessageReaction", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("MessageId") - .HasColumnType("char(36)"); - - b.Property("ReactedAt") - .HasColumnType("datetime(6)"); - - b.Property("ReactionCode") - .IsRequired() - .HasMaxLength(64) - .HasColumnType("varchar(64)"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.HasIndex("MessageId", "UserId") - .IsUnique(); - - b.ToTable("MessageReactions"); - }); - - modelBuilder.Entity("Govor.Core.Models.Messages.MessageView", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("MessageId") - .HasColumnType("char(36)"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("ViewedAt") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("MessageId", "UserId") - .IsUnique(); - - b.ToTable("MessageViews"); - }); - - modelBuilder.Entity("Govor.Core.Models.PrivateChat", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("UserAId") - .HasColumnType("char(36)"); - - b.Property("UserBId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.ToTable("PrivateChats"); - }); - - modelBuilder.Entity("Govor.Core.Models.Users.Admin", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("UserId"); - - b.ToTable("Admins"); - }); - - modelBuilder.Entity("Govor.Core.Models.Users.User", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedOn") - .HasColumnType("date"); - - b.Property("Description") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("IconId") - .HasColumnType("char(36)"); - - b.Property("InviteId") - .HasColumnType("char(36)"); - - b.Property("PasswordHash") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("Username") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("WasOnline") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("InviteId"); - - b.ToTable("Users"); - }); - - modelBuilder.Entity("Govor.Core.Models.Friendship", b => - { - b.HasOne("Govor.Core.Models.Users.User", "Addressee") - .WithMany("ReceivedFriendRequests") - .HasForeignKey("AddresseeId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired(); - - b.HasOne("Govor.Core.Models.Users.User", "Requester") - .WithMany("SentFriendRequests") - .HasForeignKey("RequesterId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired(); - - b.Navigation("Addressee"); - - b.Navigation("Requester"); - }); - - modelBuilder.Entity("Govor.Core.Models.GroupAdmins", b => - { - b.HasOne("Govor.Core.Models.ChatGroup", null) - .WithMany("Admins") - .HasForeignKey("GroupId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Govor.Core.Models.GroupInvitation", b => - { - b.HasOne("Govor.Core.Models.ChatGroup", null) - .WithMany("InviteCodes") - .HasForeignKey("GroupId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Govor.Core.Models.Users.User", "UserMaker") - .WithMany() - .HasForeignKey("UserMakerId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired(); - - b.Navigation("UserMaker"); - }); - - modelBuilder.Entity("Govor.Core.Models.GroupMembership", b => - { - b.HasOne("Govor.Core.Models.ChatGroup", null) - .WithMany("Members") - .HasForeignKey("GroupId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Govor.Core.Models.GroupInvitation", null) - .WithMany() - .HasForeignKey("InvitationId") - .OnDelete(DeleteBehavior.SetNull) - .IsRequired(); - }); - - modelBuilder.Entity("Govor.Core.Models.Messages.MediaAttachments", b => - { - b.HasOne("Govor.Core.Models.MediaFile", "MediaFile") - .WithMany() - .HasForeignKey("MediaFileId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired(); - - b.HasOne("Govor.Core.Models.Messages.Message", "Message") - .WithMany("MediaAttachments") - .HasForeignKey("MessageId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("MediaFile"); - - b.Navigation("Message"); - }); - - modelBuilder.Entity("Govor.Core.Models.Messages.Message", b => - { - b.HasOne("Govor.Core.Models.ChatGroup", null) - .WithMany("Messages") - .HasForeignKey("ChatGroupId"); - - b.HasOne("Govor.Core.Models.PrivateChat", null) - .WithMany("Messages") - .HasForeignKey("PrivateChatId"); - }); - - modelBuilder.Entity("Govor.Core.Models.Messages.MessageReaction", b => - { - b.HasOne("Govor.Core.Models.Messages.Message", "Message") - .WithMany("Reactions") - .HasForeignKey("MessageId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Govor.Core.Models.Users.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Message"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Govor.Core.Models.Messages.MessageView", b => - { - b.HasOne("Govor.Core.Models.Messages.Message", null) - .WithMany("MessageViews") - .HasForeignKey("MessageId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Govor.Core.Models.Users.Admin", b => - { - b.HasOne("Govor.Core.Models.Users.User", "User") - .WithOne() - .HasForeignKey("Govor.Core.Models.Users.Admin", "UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Govor.Core.Models.Users.User", b => - { - b.HasOne("Govor.Core.Models.Invitation", "Invite") - .WithMany("Users") - .HasForeignKey("InviteId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Invite"); - }); - - modelBuilder.Entity("Govor.Core.Models.ChatGroup", b => - { - b.Navigation("Admins"); - - b.Navigation("InviteCodes"); - - b.Navigation("Members"); - - b.Navigation("Messages"); - }); - - modelBuilder.Entity("Govor.Core.Models.Invitation", b => - { - b.Navigation("Users"); - }); - - modelBuilder.Entity("Govor.Core.Models.Messages.Message", b => - { - b.Navigation("MediaAttachments"); - - b.Navigation("MessageViews"); - - b.Navigation("Reactions"); - }); - - modelBuilder.Entity("Govor.Core.Models.PrivateChat", b => - { - b.Navigation("Messages"); - }); - - modelBuilder.Entity("Govor.Core.Models.Users.User", b => - { - b.Navigation("ReceivedFriendRequests"); - - b.Navigation("SentFriendRequests"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Govor.Data/Migrations/20250718130008_usersessions.cs b/Govor.Data/Migrations/20250718130008_usersessions.cs deleted file mode 100644 index a9f01c0..0000000 --- a/Govor.Data/Migrations/20250718130008_usersessions.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Govor.Data.Migrations -{ - /// - public partial class usersessions : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "UserSessions", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - RefreshToken = table.Column(type: "longtext", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - DeviceInfo = table.Column(type: "varchar(200)", maxLength: 200, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - CreatedAt = table.Column(type: "datetime(6)", nullable: false), - ExpiresAt = table.Column(type: "datetime(6)", nullable: false), - IsRevoked = table.Column(type: "tinyint(1)", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_UserSessions", x => x.Id); - table.ForeignKey( - name: "FK_UserSessions_Users_UserId", - column: x => x.UserId, - principalTable: "Users", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateIndex( - name: "IX_UserSessions_UserId", - table: "UserSessions", - column: "UserId"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "UserSessions"); - } - } -} diff --git a/Govor.Data/Migrations/20250730142221_CryptKeys.cs b/Govor.Data/Migrations/20250730142221_CryptKeys.cs deleted file mode 100644 index 035402f..0000000 --- a/Govor.Data/Migrations/20250730142221_CryptKeys.cs +++ /dev/null @@ -1,170 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Govor.Data.Migrations -{ - /// - public partial class CryptKeys : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterColumn( - name: "DeviceInfo", - table: "UserSessions", - type: "varchar(256)", - maxLength: 256, - nullable: false, - oldClrType: typeof(string), - oldType: "varchar(200)", - oldMaxLength: 200) - .Annotation("MySql:CharSet", "utf8mb4") - .OldAnnotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.AlterColumn( - name: "InvitationId", - table: "GroupMemberships", - type: "char(36)", - nullable: true, - collation: "ascii_general_ci", - oldClrType: typeof(Guid), - oldType: "char(36)") - .OldAnnotation("Relational:Collation", "ascii_general_ci"); - - migrationBuilder.AddColumn( - name: "MemberSince", - table: "GroupMemberships", - type: "datetime(6)", - nullable: false, - defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); - - migrationBuilder.CreateTable( - name: "UserCryptoSessions", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - UserSessionId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - PublicIdentityKey = table.Column(type: "longblob", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_UserCryptoSessions", x => x.Id); - table.ForeignKey( - name: "FK_UserCryptoSessions_UserSessions_UserSessionId", - column: x => x.UserSessionId, - principalTable: "UserSessions", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "OneTimePreKeys", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - UserCryptoSessionId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - PublicKey = table.Column(type: "longblob", nullable: false), - IsUsed = table.Column(type: "tinyint(1)", nullable: false, defaultValue: false), - UploadedAt = table.Column(type: "datetime(6)", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_OneTimePreKeys", x => x.Id); - table.ForeignKey( - name: "FK_OneTimePreKeys_UserCryptoSessions_UserCryptoSessionId", - column: x => x.UserCryptoSessionId, - principalTable: "UserCryptoSessions", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "SignedPreKeys", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - UserCryptoSessionId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - PublicSignedPreKey = table.Column(type: "longblob", nullable: false), - SignedPreKeySignature = table.Column(type: "longblob", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_SignedPreKeys", x => x.Id); - table.ForeignKey( - name: "FK_SignedPreKeys_UserCryptoSessions_UserCryptoSessionId", - column: x => x.UserCryptoSessionId, - principalTable: "UserCryptoSessions", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateIndex( - name: "IX_OneTimePreKeys_UploadedAt", - table: "OneTimePreKeys", - column: "UploadedAt"); - - migrationBuilder.CreateIndex( - name: "IX_OneTimePreKeys_UserCryptoSessionId_IsUsed", - table: "OneTimePreKeys", - columns: new[] { "UserCryptoSessionId", "IsUsed" }); - - migrationBuilder.CreateIndex( - name: "IX_SignedPreKeys_UserCryptoSessionId", - table: "SignedPreKeys", - column: "UserCryptoSessionId", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX_UserCryptoSessions_UserSessionId", - table: "UserCryptoSessions", - column: "UserSessionId", - unique: true); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "OneTimePreKeys"); - - migrationBuilder.DropTable( - name: "SignedPreKeys"); - - migrationBuilder.DropTable( - name: "UserCryptoSessions"); - - migrationBuilder.DropColumn( - name: "MemberSince", - table: "GroupMemberships"); - - migrationBuilder.AlterColumn( - name: "DeviceInfo", - table: "UserSessions", - type: "varchar(200)", - maxLength: 200, - nullable: false, - oldClrType: typeof(string), - oldType: "varchar(256)", - oldMaxLength: 256) - .Annotation("MySql:CharSet", "utf8mb4") - .OldAnnotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.AlterColumn( - name: "InvitationId", - table: "GroupMemberships", - type: "char(36)", - nullable: false, - defaultValue: new Guid("00000000-0000-0000-0000-000000000000"), - collation: "ascii_general_ci", - oldClrType: typeof(Guid), - oldType: "char(36)", - oldNullable: true) - .OldAnnotation("Relational:Collation", "ascii_general_ci"); - } - } -} diff --git a/Govor.Data/Migrations/20250730142221_CryptKeys.Designer.cs b/Govor.Data/Migrations/20251019125424_InitialCreate.Designer.cs similarity index 80% rename from Govor.Data/Migrations/20250730142221_CryptKeys.Designer.cs rename to Govor.Data/Migrations/20251019125424_InitialCreate.Designer.cs index e2a9184..fa346ae 100644 --- a/Govor.Data/Migrations/20250730142221_CryptKeys.Designer.cs +++ b/Govor.Data/Migrations/20251019125424_InitialCreate.Designer.cs @@ -3,17 +3,17 @@ 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; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; #nullable disable namespace Govor.Data.Migrations { [DbContext(typeof(GovorDbContext))] - [Migration("20250730142221_CryptKeys")] - partial class CryptKeys + [Migration("20251019125424_InitialCreate")] + partial class InitialCreate { /// protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -21,34 +21,34 @@ namespace Govor.Data.Migrations #pragma warning disable 612, 618 modelBuilder .HasAnnotation("ProductVersion", "8.0.6") - .HasAnnotation("Relational:MaxIdentifierLength", 64); + .HasAnnotation("Relational:MaxIdentifierLength", 63); - MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); modelBuilder.Entity("Govor.Core.Models.ChatGroup", b => { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("Description") .IsRequired() .HasMaxLength(500) - .HasColumnType("varchar(500)"); + .HasColumnType("character varying(500)"); b.Property("ImageId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("IsChannel") - .HasColumnType("tinyint(1)"); + .HasColumnType("boolean"); b.Property("IsPrivate") - .HasColumnType("tinyint(1)"); + .HasColumnType("boolean"); b.Property("Name") .IsRequired() .HasMaxLength(100) - .HasColumnType("varchar(100)"); + .HasColumnType("character varying(100)"); b.HasKey("Id"); @@ -59,16 +59,16 @@ namespace Govor.Data.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("AddresseeId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("RequesterId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("Status") - .HasColumnType("int"); + .HasColumnType("integer"); b.HasKey("Id"); @@ -83,13 +83,13 @@ namespace Govor.Data.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("GroupId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("UserId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.HasKey("Id"); @@ -102,32 +102,32 @@ namespace Govor.Data.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("CreatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("timestamp with time zone"); b.Property("Description") .IsRequired() .HasMaxLength(500) - .HasColumnType("varchar(500)"); + .HasColumnType("character varying(500)"); b.Property("EndDate") - .HasColumnType("datetime(6)"); + .HasColumnType("timestamp with time zone"); b.Property("GroupId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("InvitationCode") .IsRequired() .HasMaxLength(200) - .HasColumnType("varchar(200)"); + .HasColumnType("character varying(200)"); b.Property("MaxParticipants") - .HasColumnType("int"); + .HasColumnType("integer"); b.Property("UserMakerId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.HasKey("Id"); @@ -142,22 +142,22 @@ namespace Govor.Data.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("GroupId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("InvitationId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("IsBanned") - .HasColumnType("tinyint(1)"); + .HasColumnType("boolean"); b.Property("MemberSince") - .HasColumnType("datetime(6)"); + .HasColumnType("timestamp with time zone"); b.Property("UserId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.HasKey("Id"); @@ -172,30 +172,30 @@ namespace Govor.Data.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("Code") .IsRequired() - .HasColumnType("longtext"); + .HasColumnType("text"); b.Property("DateCreated") - .HasColumnType("datetime(6)"); + .HasColumnType("timestamp with time zone"); b.Property("Description") .IsRequired() - .HasColumnType("longtext"); + .HasColumnType("text"); b.Property("EndDate") - .HasColumnType("datetime(6)"); + .HasColumnType("timestamp with time zone"); b.Property("IsActive") - .HasColumnType("tinyint(1)"); + .HasColumnType("boolean"); b.Property("IsAdmin") - .HasColumnType("tinyint(1)"); + .HasColumnType("boolean"); b.Property("MaxParticipants") - .HasColumnType("int"); + .HasColumnType("integer"); b.HasKey("Id"); @@ -206,26 +206,26 @@ namespace Govor.Data.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("DateCreated") - .HasColumnType("datetime(6)"); + .HasColumnType("timestamp with time zone"); b.Property("MediaType") .IsRequired() - .HasColumnType("longtext"); + .HasColumnType("text"); b.Property("MineType") .IsRequired() .HasMaxLength(128) - .HasColumnType("varchar(128)"); + .HasColumnType("character varying(128)"); b.Property("UploaderId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("Url") .IsRequired() - .HasColumnType("longtext"); + .HasColumnType("text"); b.HasKey("Id"); @@ -236,13 +236,13 @@ namespace Govor.Data.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("MediaFileId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("MessageId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.HasKey("Id"); @@ -257,38 +257,38 @@ namespace Govor.Data.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("ChatGroupId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("EditedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("timestamp with time zone"); b.Property("EncryptedContent") .IsRequired() - .HasColumnType("longtext"); + .HasColumnType("text"); b.Property("IsEdited") - .HasColumnType("tinyint(1)"); + .HasColumnType("boolean"); b.Property("PrivateChatId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("RecipientId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("RecipientType") - .HasColumnType("int"); + .HasColumnType("integer"); b.Property("ReplyToMessageId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("SenderId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("SentAt") - .HasColumnType("datetime(6)"); + .HasColumnType("timestamp with time zone"); b.HasKey("Id"); @@ -303,21 +303,21 @@ namespace Govor.Data.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("MessageId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("ReactedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("timestamp with time zone"); b.Property("ReactionCode") .IsRequired() .HasMaxLength(64) - .HasColumnType("varchar(64)"); + .HasColumnType("character varying(64)"); b.Property("UserId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.HasKey("Id"); @@ -333,16 +333,16 @@ namespace Govor.Data.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("MessageId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("UserId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("ViewedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("timestamp with time zone"); b.HasKey("Id"); @@ -356,13 +356,13 @@ namespace Govor.Data.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("UserAId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("UserBId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.HasKey("Id"); @@ -372,7 +372,7 @@ namespace Govor.Data.Migrations modelBuilder.Entity("Govor.Core.Models.Users.Admin", b => { b.Property("UserId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.HasKey("UserId"); @@ -383,22 +383,22 @@ namespace Govor.Data.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("IsUsed") .ValueGeneratedOnAdd() - .HasColumnType("tinyint(1)") + .HasColumnType("boolean") .HasDefaultValue(false); b.Property("PublicKey") .IsRequired() - .HasColumnType("longblob"); + .HasColumnType("bytea"); b.Property("UploadedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("timestamp with time zone"); b.Property("UserCryptoSessionId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.HasKey("Id"); @@ -413,18 +413,18 @@ namespace Govor.Data.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("PublicSignedPreKey") .IsRequired() - .HasColumnType("longblob"); + .HasColumnType("bytea"); b.Property("SignedPreKeySignature") .IsRequired() - .HasColumnType("longblob"); + .HasColumnType("bytea"); b.Property("UserCryptoSessionId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.HasKey("Id"); @@ -438,14 +438,14 @@ namespace Govor.Data.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("PublicIdentityKey") .IsRequired() - .HasColumnType("longblob"); + .HasColumnType("bytea"); b.Property("UserSessionId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.HasKey("Id"); @@ -459,31 +459,31 @@ namespace Govor.Data.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("CreatedOn") .HasColumnType("date"); b.Property("Description") .IsRequired() - .HasColumnType("longtext"); + .HasColumnType("text"); b.Property("IconId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("InviteId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("PasswordHash") .IsRequired() - .HasColumnType("longtext"); + .HasColumnType("text"); b.Property("Username") .IsRequired() - .HasColumnType("longtext"); + .HasColumnType("text"); b.Property("WasOnline") - .HasColumnType("datetime(6)"); + .HasColumnType("timestamp with time zone"); b.HasKey("Id"); @@ -496,28 +496,28 @@ namespace Govor.Data.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("CreatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("timestamp with time zone"); b.Property("DeviceInfo") .IsRequired() .HasMaxLength(256) - .HasColumnType("varchar(256)"); + .HasColumnType("character varying(256)"); b.Property("ExpiresAt") - .HasColumnType("datetime(6)"); + .HasColumnType("timestamp with time zone"); b.Property("IsRevoked") - .HasColumnType("tinyint(1)"); + .HasColumnType("boolean"); b.Property("RefreshToken") .IsRequired() - .HasColumnType("longtext"); + .HasColumnType("text"); b.Property("UserId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.HasKey("Id"); diff --git a/Govor.Data/Migrations/20250707142430_InitialCreate.cs b/Govor.Data/Migrations/20251019125424_InitialCreate.cs similarity index 51% rename from Govor.Data/Migrations/20250707142430_InitialCreate.cs rename to Govor.Data/Migrations/20251019125424_InitialCreate.cs index 081bcc6..d77b539 100644 --- a/Govor.Data/Migrations/20250707142430_InitialCreate.cs +++ b/Govor.Data/Migrations/20251019125424_InitialCreate.cs @@ -11,89 +11,76 @@ namespace Govor.Data.Migrations /// protected override void Up(MigrationBuilder migrationBuilder) { - migrationBuilder.AlterDatabase() - .Annotation("MySql:CharSet", "utf8mb4"); - migrationBuilder.CreateTable( name: "ChatGroups", columns: table => new { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Name = table.Column(type: "varchar(100)", maxLength: 100, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Description = table.Column(type: "varchar(500)", maxLength: 500, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ImageId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IsChannel = table.Column(type: "tinyint(1)", nullable: false), - IsPrivate = table.Column(type: "tinyint(1)", nullable: false) + Id = table.Column(type: "uuid", nullable: false), + Name = table.Column(type: "character varying(100)", maxLength: 100, nullable: false), + Description = table.Column(type: "character varying(500)", maxLength: 500, nullable: false), + ImageId = table.Column(type: "uuid", nullable: false), + IsChannel = table.Column(type: "boolean", nullable: false), + IsPrivate = table.Column(type: "boolean", nullable: false) }, constraints: table => { table.PrimaryKey("PK_ChatGroups", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); + }); migrationBuilder.CreateTable( name: "Invitations", columns: table => new { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IsAdmin = table.Column(type: "tinyint(1)", nullable: false), - IsActive = table.Column(type: "tinyint(1)", nullable: false), - Code = table.Column(type: "longtext", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Description = table.Column(type: "longtext", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - DateCreated = table.Column(type: "datetime(6)", nullable: false), - EndDate = table.Column(type: "datetime(6)", nullable: false), - MaxParticipants = table.Column(type: "int", nullable: false) + Id = table.Column(type: "uuid", nullable: false), + IsAdmin = table.Column(type: "boolean", nullable: false), + IsActive = table.Column(type: "boolean", nullable: false), + Code = table.Column(type: "text", nullable: false), + Description = table.Column(type: "text", nullable: false), + DateCreated = table.Column(type: "timestamp with time zone", nullable: false), + EndDate = table.Column(type: "timestamp with time zone", nullable: false), + MaxParticipants = table.Column(type: "integer", nullable: false) }, constraints: table => { table.PrimaryKey("PK_Invitations", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); + }); migrationBuilder.CreateTable( name: "MediaFiles", columns: table => new { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Url = table.Column(type: "longtext", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - MediaType = table.Column(type: "longtext", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - MineType = table.Column(type: "varchar(128)", maxLength: 128, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - DateCreated = table.Column(type: "datetime(6)", nullable: false) + Id = table.Column(type: "uuid", nullable: false), + UploaderId = table.Column(type: "uuid", nullable: false), + Url = table.Column(type: "text", nullable: false), + MediaType = table.Column(type: "text", nullable: false), + MineType = table.Column(type: "character varying(128)", maxLength: 128, nullable: false), + DateCreated = table.Column(type: "timestamp with time zone", nullable: false) }, constraints: table => { table.PrimaryKey("PK_MediaFiles", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); + }); migrationBuilder.CreateTable( name: "PrivateChats", columns: table => new { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - UserAId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - UserBId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + Id = table.Column(type: "uuid", nullable: false), + UserAId = table.Column(type: "uuid", nullable: false), + UserBId = table.Column(type: "uuid", nullable: false) }, constraints: table => { table.PrimaryKey("PK_PrivateChats", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); + }); migrationBuilder.CreateTable( name: "GroupAdmins", columns: table => new { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - GroupId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + Id = table.Column(type: "uuid", nullable: false), + GroupId = table.Column(type: "uuid", nullable: false), + UserId = table.Column(type: "uuid", nullable: false) }, constraints: table => { @@ -104,24 +91,20 @@ namespace Govor.Data.Migrations principalTable: "ChatGroups", principalColumn: "Id", onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); + }); migrationBuilder.CreateTable( name: "Users", columns: table => new { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Username = table.Column(type: "longtext", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Description = table.Column(type: "longtext", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - PasswordHash = table.Column(type: "longtext", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - IconId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Id = table.Column(type: "uuid", nullable: false), + Username = table.Column(type: "text", nullable: false), + Description = table.Column(type: "text", nullable: false), + PasswordHash = table.Column(type: "text", nullable: false), + IconId = table.Column(type: "uuid", nullable: false), CreatedOn = table.Column(type: "date", nullable: false), - WasOnline = table.Column(type: "datetime(6)", nullable: false), - InviteId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + WasOnline = table.Column(type: "timestamp with time zone", nullable: false), + InviteId = table.Column(type: "uuid", nullable: false) }, constraints: table => { @@ -132,41 +115,44 @@ namespace Govor.Data.Migrations principalTable: "Invitations", principalColumn: "Id", onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); + }); migrationBuilder.CreateTable( name: "Messages", columns: table => new { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - SenderId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - RecipientId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - RecipientType = table.Column(type: "int", nullable: false), - EncryptedContent = table.Column(type: "longtext", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - SentAt = table.Column(type: "datetime(6)", nullable: false), - IsEdited = table.Column(type: "tinyint(1)", nullable: false), - EditedAt = table.Column(type: "datetime(6)", nullable: true), - ReplyToMessageId = table.Column(type: "char(36)", nullable: true, collation: "ascii_general_ci"), - PrivateChatId = table.Column(type: "char(36)", nullable: true, collation: "ascii_general_ci") + Id = table.Column(type: "uuid", nullable: false), + SenderId = table.Column(type: "uuid", nullable: false), + RecipientId = table.Column(type: "uuid", nullable: false), + RecipientType = table.Column(type: "integer", nullable: false), + EncryptedContent = table.Column(type: "text", nullable: false), + SentAt = table.Column(type: "timestamp with time zone", nullable: false), + IsEdited = table.Column(type: "boolean", nullable: false), + EditedAt = table.Column(type: "timestamp with time zone", nullable: true), + ReplyToMessageId = table.Column(type: "uuid", nullable: true), + ChatGroupId = table.Column(type: "uuid", nullable: true), + PrivateChatId = table.Column(type: "uuid", nullable: true) }, constraints: table => { table.PrimaryKey("PK_Messages", x => x.Id); + table.ForeignKey( + name: "FK_Messages_ChatGroups_ChatGroupId", + column: x => x.ChatGroupId, + principalTable: "ChatGroups", + principalColumn: "Id"); table.ForeignKey( name: "FK_Messages_PrivateChats_PrivateChatId", column: x => x.PrivateChatId, principalTable: "PrivateChats", principalColumn: "Id"); - }) - .Annotation("MySql:CharSet", "utf8mb4"); + }); migrationBuilder.CreateTable( name: "Admins", columns: table => new { - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + UserId = table.Column(type: "uuid", nullable: false) }, constraints: table => { @@ -177,17 +163,16 @@ namespace Govor.Data.Migrations principalTable: "Users", principalColumn: "Id", onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); + }); migrationBuilder.CreateTable( name: "Friendships", columns: table => new { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - RequesterId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - AddresseeId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Status = table.Column(type: "int", nullable: false) + Id = table.Column(type: "uuid", nullable: false), + RequesterId = table.Column(type: "uuid", nullable: false), + AddresseeId = table.Column(type: "uuid", nullable: false), + Status = table.Column(type: "integer", nullable: false) }, constraints: table => { @@ -204,23 +189,20 @@ namespace Govor.Data.Migrations principalTable: "Users", principalColumn: "Id", onDelete: ReferentialAction.Restrict); - }) - .Annotation("MySql:CharSet", "utf8mb4"); + }); migrationBuilder.CreateTable( name: "GroupInvitations", columns: table => new { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - GroupId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - UserMakerId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - InvitationCode = table.Column(type: "varchar(200)", maxLength: 200, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Description = table.Column(type: "varchar(500)", maxLength: 500, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - EndDate = table.Column(type: "datetime(6)", nullable: false), - CreatedAt = table.Column(type: "datetime(6)", nullable: false), - MaxParticipants = table.Column(type: "int", nullable: false) + Id = table.Column(type: "uuid", nullable: false), + GroupId = table.Column(type: "uuid", nullable: false), + UserMakerId = table.Column(type: "uuid", nullable: false), + InvitationCode = table.Column(type: "character varying(200)", maxLength: 200, nullable: false), + Description = table.Column(type: "character varying(500)", maxLength: 500, nullable: false), + EndDate = table.Column(type: "timestamp with time zone", nullable: false), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), + MaxParticipants = table.Column(type: "integer", nullable: false) }, constraints: table => { @@ -237,16 +219,38 @@ namespace Govor.Data.Migrations principalTable: "Users", principalColumn: "Id", onDelete: ReferentialAction.Restrict); - }) - .Annotation("MySql:CharSet", "utf8mb4"); + }); + + migrationBuilder.CreateTable( + name: "UserSessions", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + UserId = table.Column(type: "uuid", nullable: false), + RefreshToken = table.Column(type: "text", nullable: false), + DeviceInfo = table.Column(type: "character varying(256)", maxLength: 256, nullable: false), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), + ExpiresAt = table.Column(type: "timestamp with time zone", nullable: false), + IsRevoked = table.Column(type: "boolean", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_UserSessions", x => x.Id); + table.ForeignKey( + name: "FK_UserSessions_Users_UserId", + column: x => x.UserId, + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); migrationBuilder.CreateTable( name: "MediaAttachments", columns: table => new { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - MessageId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - MediaFileId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + Id = table.Column(type: "uuid", nullable: false), + MessageId = table.Column(type: "uuid", nullable: false), + MediaFileId = table.Column(type: "uuid", nullable: false) }, constraints: table => { @@ -263,19 +267,17 @@ namespace Govor.Data.Migrations principalTable: "Messages", principalColumn: "Id", onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); + }); migrationBuilder.CreateTable( name: "MessageReactions", columns: table => new { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - MessageId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ReactionCode = table.Column(type: "varchar(64)", maxLength: 64, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ReactedAt = table.Column(type: "datetime(6)", nullable: false) + Id = table.Column(type: "uuid", nullable: false), + MessageId = table.Column(type: "uuid", nullable: false), + UserId = table.Column(type: "uuid", nullable: false), + ReactionCode = table.Column(type: "character varying(64)", maxLength: 64, nullable: false), + ReactedAt = table.Column(type: "timestamp with time zone", nullable: false) }, constraints: table => { @@ -292,17 +294,16 @@ namespace Govor.Data.Migrations principalTable: "Users", principalColumn: "Id", onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); + }); migrationBuilder.CreateTable( name: "MessageViews", columns: table => new { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - MessageId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ViewedAt = table.Column(type: "datetime(6)", nullable: false) + Id = table.Column(type: "uuid", nullable: false), + MessageId = table.Column(type: "uuid", nullable: false), + UserId = table.Column(type: "uuid", nullable: false), + ViewedAt = table.Column(type: "timestamp with time zone", nullable: false) }, constraints: table => { @@ -313,18 +314,18 @@ namespace Govor.Data.Migrations principalTable: "Messages", principalColumn: "Id", onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); + }); migrationBuilder.CreateTable( name: "GroupMemberships", columns: table => new { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - GroupId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - InvitationId = table.Column(type: "char(36)", nullable: true, collation: "ascii_general_ci"), - IsBanned = table.Column(type: "tinyint(1)", nullable: false) + Id = table.Column(type: "uuid", nullable: false), + GroupId = table.Column(type: "uuid", nullable: false), + UserId = table.Column(type: "uuid", nullable: false), + InvitationId = table.Column(type: "uuid", nullable: true), + IsBanned = table.Column(type: "boolean", nullable: false), + MemberSince = table.Column(type: "timestamp with time zone", nullable: false) }, constraints: table => { @@ -341,8 +342,67 @@ namespace Govor.Data.Migrations principalTable: "GroupInvitations", principalColumn: "Id", onDelete: ReferentialAction.SetNull); - }) - .Annotation("MySql:CharSet", "utf8mb4"); + }); + + migrationBuilder.CreateTable( + name: "UserCryptoSessions", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + UserSessionId = table.Column(type: "uuid", nullable: false), + PublicIdentityKey = table.Column(type: "bytea", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_UserCryptoSessions", x => x.Id); + table.ForeignKey( + name: "FK_UserCryptoSessions_UserSessions_UserSessionId", + column: x => x.UserSessionId, + principalTable: "UserSessions", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "OneTimePreKeys", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + UserCryptoSessionId = table.Column(type: "uuid", nullable: false), + PublicKey = table.Column(type: "bytea", nullable: false), + IsUsed = table.Column(type: "boolean", nullable: false, defaultValue: false), + UploadedAt = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_OneTimePreKeys", x => x.Id); + table.ForeignKey( + name: "FK_OneTimePreKeys_UserCryptoSessions_UserCryptoSessionId", + column: x => x.UserCryptoSessionId, + principalTable: "UserCryptoSessions", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "SignedPreKeys", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + UserCryptoSessionId = table.Column(type: "uuid", nullable: false), + PublicSignedPreKey = table.Column(type: "bytea", nullable: false), + SignedPreKeySignature = table.Column(type: "bytea", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_SignedPreKeys", x => x.Id); + table.ForeignKey( + name: "FK_SignedPreKeys_UserCryptoSessions_UserCryptoSessionId", + column: x => x.UserCryptoSessionId, + principalTable: "UserCryptoSessions", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); migrationBuilder.CreateIndex( name: "IX_Friendships_AddresseeId", @@ -400,6 +460,11 @@ namespace Govor.Data.Migrations table: "MessageReactions", column: "UserId"); + migrationBuilder.CreateIndex( + name: "IX_Messages_ChatGroupId", + table: "Messages", + column: "ChatGroupId"); + migrationBuilder.CreateIndex( name: "IX_Messages_PrivateChatId", table: "Messages", @@ -411,10 +476,37 @@ namespace Govor.Data.Migrations columns: new[] { "MessageId", "UserId" }, unique: true); + migrationBuilder.CreateIndex( + name: "IX_OneTimePreKeys_UploadedAt", + table: "OneTimePreKeys", + column: "UploadedAt"); + + migrationBuilder.CreateIndex( + name: "IX_OneTimePreKeys_UserCryptoSessionId_IsUsed", + table: "OneTimePreKeys", + columns: new[] { "UserCryptoSessionId", "IsUsed" }); + + migrationBuilder.CreateIndex( + name: "IX_SignedPreKeys_UserCryptoSessionId", + table: "SignedPreKeys", + column: "UserCryptoSessionId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_UserCryptoSessions_UserSessionId", + table: "UserCryptoSessions", + column: "UserSessionId", + unique: true); + migrationBuilder.CreateIndex( name: "IX_Users_InviteId", table: "Users", column: "InviteId"); + + migrationBuilder.CreateIndex( + name: "IX_UserSessions_UserId", + table: "UserSessions", + column: "UserId"); } /// @@ -441,6 +533,12 @@ namespace Govor.Data.Migrations migrationBuilder.DropTable( name: "MessageViews"); + migrationBuilder.DropTable( + name: "OneTimePreKeys"); + + migrationBuilder.DropTable( + name: "SignedPreKeys"); + migrationBuilder.DropTable( name: "GroupInvitations"); @@ -450,14 +548,20 @@ namespace Govor.Data.Migrations migrationBuilder.DropTable( name: "Messages"); + migrationBuilder.DropTable( + name: "UserCryptoSessions"); + migrationBuilder.DropTable( name: "ChatGroups"); migrationBuilder.DropTable( - name: "Users"); + name: "PrivateChats"); migrationBuilder.DropTable( - name: "PrivateChats"); + name: "UserSessions"); + + migrationBuilder.DropTable( + name: "Users"); migrationBuilder.DropTable( name: "Invitations"); diff --git a/Govor.Data/Migrations/20250718130008_usersessions.Designer.cs b/Govor.Data/Migrations/20251103060801_MediaOwnerTypeAdded.Designer.cs similarity index 64% rename from Govor.Data/Migrations/20250718130008_usersessions.Designer.cs rename to Govor.Data/Migrations/20251103060801_MediaOwnerTypeAdded.Designer.cs index e33657b..93a2188 100644 --- a/Govor.Data/Migrations/20250718130008_usersessions.Designer.cs +++ b/Govor.Data/Migrations/20251103060801_MediaOwnerTypeAdded.Designer.cs @@ -3,17 +3,17 @@ 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; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; #nullable disable namespace Govor.Data.Migrations { [DbContext(typeof(GovorDbContext))] - [Migration("20250718130008_usersessions")] - partial class usersessions + [Migration("20251103060801_MediaOwnerTypeAdded")] + partial class MediaOwnerTypeAdded { /// protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -21,34 +21,34 @@ namespace Govor.Data.Migrations #pragma warning disable 612, 618 modelBuilder .HasAnnotation("ProductVersion", "8.0.6") - .HasAnnotation("Relational:MaxIdentifierLength", 64); + .HasAnnotation("Relational:MaxIdentifierLength", 63); - MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); modelBuilder.Entity("Govor.Core.Models.ChatGroup", b => { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("Description") .IsRequired() .HasMaxLength(500) - .HasColumnType("varchar(500)"); + .HasColumnType("character varying(500)"); b.Property("ImageId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("IsChannel") - .HasColumnType("tinyint(1)"); + .HasColumnType("boolean"); b.Property("IsPrivate") - .HasColumnType("tinyint(1)"); + .HasColumnType("boolean"); b.Property("Name") .IsRequired() .HasMaxLength(100) - .HasColumnType("varchar(100)"); + .HasColumnType("character varying(100)"); b.HasKey("Id"); @@ -59,16 +59,16 @@ namespace Govor.Data.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("AddresseeId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("RequesterId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("Status") - .HasColumnType("int"); + .HasColumnType("integer"); b.HasKey("Id"); @@ -83,13 +83,13 @@ namespace Govor.Data.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("GroupId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("UserId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.HasKey("Id"); @@ -102,32 +102,32 @@ namespace Govor.Data.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("CreatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("timestamp with time zone"); b.Property("Description") .IsRequired() .HasMaxLength(500) - .HasColumnType("varchar(500)"); + .HasColumnType("character varying(500)"); b.Property("EndDate") - .HasColumnType("datetime(6)"); + .HasColumnType("timestamp with time zone"); b.Property("GroupId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("InvitationCode") .IsRequired() .HasMaxLength(200) - .HasColumnType("varchar(200)"); + .HasColumnType("character varying(200)"); b.Property("MaxParticipants") - .HasColumnType("int"); + .HasColumnType("integer"); b.Property("UserMakerId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.HasKey("Id"); @@ -142,20 +142,22 @@ namespace Govor.Data.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("GroupId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("InvitationId") - .IsRequired() - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("IsBanned") - .HasColumnType("tinyint(1)"); + .HasColumnType("boolean"); + + b.Property("MemberSince") + .HasColumnType("timestamp with time zone"); b.Property("UserId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.HasKey("Id"); @@ -170,30 +172,30 @@ namespace Govor.Data.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("Code") .IsRequired() - .HasColumnType("longtext"); + .HasColumnType("text"); b.Property("DateCreated") - .HasColumnType("datetime(6)"); + .HasColumnType("timestamp with time zone"); b.Property("Description") .IsRequired() - .HasColumnType("longtext"); + .HasColumnType("text"); b.Property("EndDate") - .HasColumnType("datetime(6)"); + .HasColumnType("timestamp with time zone"); b.Property("IsActive") - .HasColumnType("tinyint(1)"); + .HasColumnType("boolean"); b.Property("IsAdmin") - .HasColumnType("tinyint(1)"); + .HasColumnType("boolean"); b.Property("MaxParticipants") - .HasColumnType("int"); + .HasColumnType("integer"); b.HasKey("Id"); @@ -204,26 +206,32 @@ namespace Govor.Data.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("DateCreated") - .HasColumnType("datetime(6)"); + .HasColumnType("timestamp with time zone"); b.Property("MediaType") .IsRequired() - .HasColumnType("longtext"); + .HasColumnType("text"); b.Property("MineType") .IsRequired() .HasMaxLength(128) - .HasColumnType("varchar(128)"); + .HasColumnType("character varying(128)"); + + b.Property("OwnerId") + .HasColumnType("uuid"); + + b.Property("OwnerType") + .HasColumnType("integer"); b.Property("UploaderId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("Url") .IsRequired() - .HasColumnType("longtext"); + .HasColumnType("text"); b.HasKey("Id"); @@ -234,13 +242,13 @@ namespace Govor.Data.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("MediaFileId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("MessageId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.HasKey("Id"); @@ -255,38 +263,38 @@ namespace Govor.Data.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("ChatGroupId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("EditedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("timestamp with time zone"); b.Property("EncryptedContent") .IsRequired() - .HasColumnType("longtext"); + .HasColumnType("text"); b.Property("IsEdited") - .HasColumnType("tinyint(1)"); + .HasColumnType("boolean"); b.Property("PrivateChatId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("RecipientId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("RecipientType") - .HasColumnType("int"); + .HasColumnType("integer"); b.Property("ReplyToMessageId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("SenderId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("SentAt") - .HasColumnType("datetime(6)"); + .HasColumnType("timestamp with time zone"); b.HasKey("Id"); @@ -301,21 +309,21 @@ namespace Govor.Data.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("MessageId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("ReactedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("timestamp with time zone"); b.Property("ReactionCode") .IsRequired() .HasMaxLength(64) - .HasColumnType("varchar(64)"); + .HasColumnType("character varying(64)"); b.Property("UserId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.HasKey("Id"); @@ -331,16 +339,16 @@ namespace Govor.Data.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("MessageId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("UserId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("ViewedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("timestamp with time zone"); b.HasKey("Id"); @@ -354,92 +362,134 @@ namespace Govor.Data.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("UserAId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("UserBId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.HasKey("Id"); b.ToTable("PrivateChats"); }); - modelBuilder.Entity("Govor.Core.Models.UserSession", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("DeviceInfo") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("varchar(200)"); - - b.Property("ExpiresAt") - .HasColumnType("datetime(6)"); - - b.Property("IsRevoked") - .HasColumnType("tinyint(1)"); - - b.Property("RefreshToken") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("UserSessions"); - }); - modelBuilder.Entity("Govor.Core.Models.Users.Admin", b => { b.Property("UserId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.HasKey("UserId"); b.ToTable("Admins"); }); + modelBuilder.Entity("Govor.Core.Models.Users.Crypto.OneTimePreKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("IsUsed") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false); + + b.Property("PublicKey") + .IsRequired() + .HasColumnType("bytea"); + + b.Property("UploadedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UserCryptoSessionId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UploadedAt"); + + b.HasIndex("UserCryptoSessionId", "IsUsed"); + + b.ToTable("OneTimePreKeys"); + }); + + modelBuilder.Entity("Govor.Core.Models.Users.Crypto.SignedPreKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("PublicSignedPreKey") + .IsRequired() + .HasColumnType("bytea"); + + b.Property("SignedPreKeySignature") + .IsRequired() + .HasColumnType("bytea"); + + b.Property("UserCryptoSessionId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserCryptoSessionId") + .IsUnique(); + + b.ToTable("SignedPreKeys"); + }); + + modelBuilder.Entity("Govor.Core.Models.Users.Crypto.UserCryptoSession", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("PublicIdentityKey") + .IsRequired() + .HasColumnType("bytea"); + + b.Property("UserSessionId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserSessionId") + .IsUnique(); + + b.ToTable("UserCryptoSessions"); + }); + modelBuilder.Entity("Govor.Core.Models.Users.User", b => { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("CreatedOn") .HasColumnType("date"); b.Property("Description") .IsRequired() - .HasColumnType("longtext"); + .HasColumnType("text"); b.Property("IconId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("InviteId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("PasswordHash") .IsRequired() - .HasColumnType("longtext"); + .HasColumnType("text"); b.Property("Username") .IsRequired() - .HasColumnType("longtext"); + .HasColumnType("text"); b.Property("WasOnline") - .HasColumnType("datetime(6)"); + .HasColumnType("timestamp with time zone"); b.HasKey("Id"); @@ -448,6 +498,40 @@ namespace Govor.Data.Migrations b.ToTable("Users"); }); + modelBuilder.Entity("Govor.Core.Models.Users.UserSession", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeviceInfo") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ExpiresAt") + .HasColumnType("timestamp with time zone"); + + b.Property("IsRevoked") + .HasColumnType("boolean"); + + b.Property("RefreshToken") + .IsRequired() + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserSessions"); + }); + modelBuilder.Entity("Govor.Core.Models.Friendship", b => { b.HasOne("Govor.Core.Models.Users.User", "Addressee") @@ -504,8 +588,7 @@ namespace Govor.Data.Migrations b.HasOne("Govor.Core.Models.GroupInvitation", null) .WithMany() .HasForeignKey("InvitationId") - .OnDelete(DeleteBehavior.SetNull) - .IsRequired(); + .OnDelete(DeleteBehavior.SetNull); }); modelBuilder.Entity("Govor.Core.Models.Messages.MediaAttachments", b => @@ -566,15 +649,6 @@ namespace Govor.Data.Migrations .IsRequired(); }); - modelBuilder.Entity("Govor.Core.Models.UserSession", b => - { - b.HasOne("Govor.Core.Models.Users.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - modelBuilder.Entity("Govor.Core.Models.Users.Admin", b => { b.HasOne("Govor.Core.Models.Users.User", "User") @@ -586,6 +660,39 @@ namespace Govor.Data.Migrations b.Navigation("User"); }); + modelBuilder.Entity("Govor.Core.Models.Users.Crypto.OneTimePreKey", b => + { + b.HasOne("Govor.Core.Models.Users.Crypto.UserCryptoSession", "UserCryptoSession") + .WithMany("OneTimePreKeys") + .HasForeignKey("UserCryptoSessionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("UserCryptoSession"); + }); + + modelBuilder.Entity("Govor.Core.Models.Users.Crypto.SignedPreKey", b => + { + b.HasOne("Govor.Core.Models.Users.Crypto.UserCryptoSession", "UserCryptoSession") + .WithOne("SignedPreKey") + .HasForeignKey("Govor.Core.Models.Users.Crypto.SignedPreKey", "UserCryptoSessionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("UserCryptoSession"); + }); + + modelBuilder.Entity("Govor.Core.Models.Users.Crypto.UserCryptoSession", b => + { + b.HasOne("Govor.Core.Models.Users.UserSession", "UserSession") + .WithOne("CryptoSession") + .HasForeignKey("Govor.Core.Models.Users.Crypto.UserCryptoSession", "UserSessionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("UserSession"); + }); + modelBuilder.Entity("Govor.Core.Models.Users.User", b => { b.HasOne("Govor.Core.Models.Invitation", "Invite") @@ -597,6 +704,15 @@ namespace Govor.Data.Migrations b.Navigation("Invite"); }); + modelBuilder.Entity("Govor.Core.Models.Users.UserSession", b => + { + b.HasOne("Govor.Core.Models.Users.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + modelBuilder.Entity("Govor.Core.Models.ChatGroup", b => { b.Navigation("Admins"); @@ -627,12 +743,26 @@ namespace Govor.Data.Migrations b.Navigation("Messages"); }); + modelBuilder.Entity("Govor.Core.Models.Users.Crypto.UserCryptoSession", b => + { + b.Navigation("OneTimePreKeys"); + + b.Navigation("SignedPreKey") + .IsRequired(); + }); + modelBuilder.Entity("Govor.Core.Models.Users.User", b => { b.Navigation("ReceivedFriendRequests"); b.Navigation("SentFriendRequests"); }); + + modelBuilder.Entity("Govor.Core.Models.Users.UserSession", b => + { + b.Navigation("CryptoSession") + .IsRequired(); + }); #pragma warning restore 612, 618 } } diff --git a/Govor.Data/Migrations/20250713101943_AddUploaderIdToMediaFile.cs b/Govor.Data/Migrations/20251103060801_MediaOwnerTypeAdded.cs similarity index 54% rename from Govor.Data/Migrations/20250713101943_AddUploaderIdToMediaFile.cs rename to Govor.Data/Migrations/20251103060801_MediaOwnerTypeAdded.cs index db02898..c9bce75 100644 --- a/Govor.Data/Migrations/20250713101943_AddUploaderIdToMediaFile.cs +++ b/Govor.Data/Migrations/20251103060801_MediaOwnerTypeAdded.cs @@ -6,25 +6,34 @@ using Microsoft.EntityFrameworkCore.Migrations; namespace Govor.Data.Migrations { /// - public partial class AddUploaderIdToMediaFile : Migration + public partial class MediaOwnerTypeAdded : Migration { /// protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.AddColumn( - name: "UploaderId", + name: "OwnerId", table: "MediaFiles", - type: "char(36)", + type: "uuid", + nullable: true); + + migrationBuilder.AddColumn( + name: "OwnerType", + table: "MediaFiles", + type: "integer", nullable: false, - defaultValue: new Guid("00000000-0000-0000-0000-000000000000"), - collation: "ascii_general_ci"); + defaultValue: 0); } /// protected override void Down(MigrationBuilder migrationBuilder) { migrationBuilder.DropColumn( - name: "UploaderId", + name: "OwnerId", + table: "MediaFiles"); + + migrationBuilder.DropColumn( + name: "OwnerType", table: "MediaFiles"); } } diff --git a/Govor.Data/Migrations/GovorDbContextModelSnapshot.cs b/Govor.Data/Migrations/GovorDbContextModelSnapshot.cs index 084ac34..0b916df 100644 --- a/Govor.Data/Migrations/GovorDbContextModelSnapshot.cs +++ b/Govor.Data/Migrations/GovorDbContextModelSnapshot.cs @@ -3,8 +3,8 @@ using System; using Govor.Data; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; #nullable disable @@ -18,34 +18,34 @@ namespace Govor.Data.Migrations #pragma warning disable 612, 618 modelBuilder .HasAnnotation("ProductVersion", "8.0.6") - .HasAnnotation("Relational:MaxIdentifierLength", 64); + .HasAnnotation("Relational:MaxIdentifierLength", 63); - MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); modelBuilder.Entity("Govor.Core.Models.ChatGroup", b => { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("Description") .IsRequired() .HasMaxLength(500) - .HasColumnType("varchar(500)"); + .HasColumnType("character varying(500)"); b.Property("ImageId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("IsChannel") - .HasColumnType("tinyint(1)"); + .HasColumnType("boolean"); b.Property("IsPrivate") - .HasColumnType("tinyint(1)"); + .HasColumnType("boolean"); b.Property("Name") .IsRequired() .HasMaxLength(100) - .HasColumnType("varchar(100)"); + .HasColumnType("character varying(100)"); b.HasKey("Id"); @@ -56,16 +56,16 @@ namespace Govor.Data.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("AddresseeId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("RequesterId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("Status") - .HasColumnType("int"); + .HasColumnType("integer"); b.HasKey("Id"); @@ -80,13 +80,13 @@ namespace Govor.Data.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("GroupId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("UserId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.HasKey("Id"); @@ -99,32 +99,32 @@ namespace Govor.Data.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("CreatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("timestamp with time zone"); b.Property("Description") .IsRequired() .HasMaxLength(500) - .HasColumnType("varchar(500)"); + .HasColumnType("character varying(500)"); b.Property("EndDate") - .HasColumnType("datetime(6)"); + .HasColumnType("timestamp with time zone"); b.Property("GroupId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("InvitationCode") .IsRequired() .HasMaxLength(200) - .HasColumnType("varchar(200)"); + .HasColumnType("character varying(200)"); b.Property("MaxParticipants") - .HasColumnType("int"); + .HasColumnType("integer"); b.Property("UserMakerId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.HasKey("Id"); @@ -139,22 +139,22 @@ namespace Govor.Data.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("GroupId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("InvitationId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("IsBanned") - .HasColumnType("tinyint(1)"); + .HasColumnType("boolean"); b.Property("MemberSince") - .HasColumnType("datetime(6)"); + .HasColumnType("timestamp with time zone"); b.Property("UserId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.HasKey("Id"); @@ -169,30 +169,30 @@ namespace Govor.Data.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("Code") .IsRequired() - .HasColumnType("longtext"); + .HasColumnType("text"); b.Property("DateCreated") - .HasColumnType("datetime(6)"); + .HasColumnType("timestamp with time zone"); b.Property("Description") .IsRequired() - .HasColumnType("longtext"); + .HasColumnType("text"); b.Property("EndDate") - .HasColumnType("datetime(6)"); + .HasColumnType("timestamp with time zone"); b.Property("IsActive") - .HasColumnType("tinyint(1)"); + .HasColumnType("boolean"); b.Property("IsAdmin") - .HasColumnType("tinyint(1)"); + .HasColumnType("boolean"); b.Property("MaxParticipants") - .HasColumnType("int"); + .HasColumnType("integer"); b.HasKey("Id"); @@ -203,26 +203,32 @@ namespace Govor.Data.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("DateCreated") - .HasColumnType("datetime(6)"); + .HasColumnType("timestamp with time zone"); b.Property("MediaType") .IsRequired() - .HasColumnType("longtext"); + .HasColumnType("text"); b.Property("MineType") .IsRequired() .HasMaxLength(128) - .HasColumnType("varchar(128)"); + .HasColumnType("character varying(128)"); + + b.Property("OwnerId") + .HasColumnType("uuid"); + + b.Property("OwnerType") + .HasColumnType("integer"); b.Property("UploaderId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("Url") .IsRequired() - .HasColumnType("longtext"); + .HasColumnType("text"); b.HasKey("Id"); @@ -233,13 +239,13 @@ namespace Govor.Data.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("MediaFileId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("MessageId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.HasKey("Id"); @@ -254,38 +260,38 @@ namespace Govor.Data.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("ChatGroupId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("EditedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("timestamp with time zone"); b.Property("EncryptedContent") .IsRequired() - .HasColumnType("longtext"); + .HasColumnType("text"); b.Property("IsEdited") - .HasColumnType("tinyint(1)"); + .HasColumnType("boolean"); b.Property("PrivateChatId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("RecipientId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("RecipientType") - .HasColumnType("int"); + .HasColumnType("integer"); b.Property("ReplyToMessageId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("SenderId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("SentAt") - .HasColumnType("datetime(6)"); + .HasColumnType("timestamp with time zone"); b.HasKey("Id"); @@ -300,21 +306,21 @@ namespace Govor.Data.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("MessageId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("ReactedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("timestamp with time zone"); b.Property("ReactionCode") .IsRequired() .HasMaxLength(64) - .HasColumnType("varchar(64)"); + .HasColumnType("character varying(64)"); b.Property("UserId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.HasKey("Id"); @@ -330,16 +336,16 @@ namespace Govor.Data.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("MessageId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("UserId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("ViewedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("timestamp with time zone"); b.HasKey("Id"); @@ -353,13 +359,13 @@ namespace Govor.Data.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("UserAId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("UserBId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.HasKey("Id"); @@ -369,7 +375,7 @@ namespace Govor.Data.Migrations modelBuilder.Entity("Govor.Core.Models.Users.Admin", b => { b.Property("UserId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.HasKey("UserId"); @@ -380,22 +386,22 @@ namespace Govor.Data.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("IsUsed") .ValueGeneratedOnAdd() - .HasColumnType("tinyint(1)") + .HasColumnType("boolean") .HasDefaultValue(false); b.Property("PublicKey") .IsRequired() - .HasColumnType("longblob"); + .HasColumnType("bytea"); b.Property("UploadedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("timestamp with time zone"); b.Property("UserCryptoSessionId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.HasKey("Id"); @@ -410,18 +416,18 @@ namespace Govor.Data.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("PublicSignedPreKey") .IsRequired() - .HasColumnType("longblob"); + .HasColumnType("bytea"); b.Property("SignedPreKeySignature") .IsRequired() - .HasColumnType("longblob"); + .HasColumnType("bytea"); b.Property("UserCryptoSessionId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.HasKey("Id"); @@ -435,14 +441,14 @@ namespace Govor.Data.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("PublicIdentityKey") .IsRequired() - .HasColumnType("longblob"); + .HasColumnType("bytea"); b.Property("UserSessionId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.HasKey("Id"); @@ -456,31 +462,31 @@ namespace Govor.Data.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("CreatedOn") .HasColumnType("date"); b.Property("Description") .IsRequired() - .HasColumnType("longtext"); + .HasColumnType("text"); b.Property("IconId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("InviteId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("PasswordHash") .IsRequired() - .HasColumnType("longtext"); + .HasColumnType("text"); b.Property("Username") .IsRequired() - .HasColumnType("longtext"); + .HasColumnType("text"); b.Property("WasOnline") - .HasColumnType("datetime(6)"); + .HasColumnType("timestamp with time zone"); b.HasKey("Id"); @@ -493,28 +499,28 @@ namespace Govor.Data.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.Property("CreatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("timestamp with time zone"); b.Property("DeviceInfo") .IsRequired() .HasMaxLength(256) - .HasColumnType("varchar(256)"); + .HasColumnType("character varying(256)"); b.Property("ExpiresAt") - .HasColumnType("datetime(6)"); + .HasColumnType("timestamp with time zone"); b.Property("IsRevoked") - .HasColumnType("tinyint(1)"); + .HasColumnType("boolean"); b.Property("RefreshToken") .IsRequired() - .HasColumnType("longtext"); + .HasColumnType("text"); b.Property("UserId") - .HasColumnType("char(36)"); + .HasColumnType("uuid"); b.HasKey("Id"); diff --git a/Govor.Data/Repositories/AdminsRepository.cs b/Govor.Data/Repositories/AdminsRepository.cs index 022c7bb..8e569bc 100644 --- a/Govor.Data/Repositories/AdminsRepository.cs +++ b/Govor.Data/Repositories/AdminsRepository.cs @@ -64,6 +64,8 @@ public class AdminsRepository(GovorDbContext context, IObjectValidator va if (rowsAffected == 0) throw new UpdateException($"Not found admin by given id {admin.UserId}"); + + await _context.SaveChangesAsync(); } catch (Exception ex) { diff --git a/Govor.Data/Repositories/FriendshipsRepository.cs b/Govor.Data/Repositories/FriendshipsRepository.cs index 42d6789..87e98f8 100644 --- a/Govor.Data/Repositories/FriendshipsRepository.cs +++ b/Govor.Data/Repositories/FriendshipsRepository.cs @@ -87,6 +87,8 @@ public class FriendshipsRepository : IFriendshipsRepository if (rowsAffected == 0) throw new UpdateException($"Not found friendship by given id {friendship.Id}"); + + await _context.SaveChangesAsync(); } catch (Exception ex) { diff --git a/Govor.Data/Repositories/GroupRepository.cs b/Govor.Data/Repositories/GroupRepository.cs index 6250600..f3a9b3d 100644 --- a/Govor.Data/Repositories/GroupRepository.cs +++ b/Govor.Data/Repositories/GroupRepository.cs @@ -117,6 +117,8 @@ public class GroupRepository : IGroupsRepository if (rowsAffected == 0) throw new UpdateException($"Not found group by given id {group.Id}"); + + await _context.SaveChangesAsync(); } catch (Exception ex) { diff --git a/Govor.Data/Repositories/InvitesRepository.cs b/Govor.Data/Repositories/InvitesRepository.cs index 86cc7be..ba9050d 100644 --- a/Govor.Data/Repositories/InvitesRepository.cs +++ b/Govor.Data/Repositories/InvitesRepository.cs @@ -100,6 +100,8 @@ public class InvitesRepository : IInvitesRepository if (rowsAffected == 0) throw new UpdateException($"Not found invitation by given id {invitation.Id}"); + + await _context.SaveChangesAsync(); } catch (Exception ex) { diff --git a/Govor.Data/Repositories/MediaAttachmentsRepository.cs b/Govor.Data/Repositories/MediaAttachmentsRepository.cs index 7694691..bb7588d 100644 --- a/Govor.Data/Repositories/MediaAttachmentsRepository.cs +++ b/Govor.Data/Repositories/MediaAttachmentsRepository.cs @@ -82,6 +82,8 @@ public class MediaAttachmentsRepository : IMediaAttachmentsRepository if (rowsAffected == 0) throw new UpdateException($"Not found attachments by given id {attachments.Id}"); + + await _context.SaveChangesAsync(); } catch (Exception ex) { diff --git a/Govor.Data/Repositories/MessagesRepository.cs b/Govor.Data/Repositories/MessagesRepository.cs index 5f34c85..4932297 100644 --- a/Govor.Data/Repositories/MessagesRepository.cs +++ b/Govor.Data/Repositories/MessagesRepository.cs @@ -128,6 +128,8 @@ public class MessagesRepository : IMessagesRepository if (rowsAffected == 0) throw new UpdateException($"Not found message by given id {message.Id}"); + + await _context.SaveChangesAsync(); } catch (Exception ex) { diff --git a/Govor.Data/Repositories/PrivateChatsRepository.cs b/Govor.Data/Repositories/PrivateChatsRepository.cs index 616362c..38a3f58 100644 --- a/Govor.Data/Repositories/PrivateChatsRepository.cs +++ b/Govor.Data/Repositories/PrivateChatsRepository.cs @@ -77,6 +77,8 @@ public class PrivateChatsRepository : IPrivateChatsRepository if (rowsAffected == 0) throw new UpdateException($"Not found private chat by given id {chat.Id}"); + + await _context.SaveChangesAsync(); } catch (Exception ex) { diff --git a/Govor.Data/Repositories/UserSessionsRepository.cs b/Govor.Data/Repositories/UserSessionsRepository.cs index 37cb6eb..897b9bc 100644 --- a/Govor.Data/Repositories/UserSessionsRepository.cs +++ b/Govor.Data/Repositories/UserSessionsRepository.cs @@ -102,6 +102,8 @@ public class UserSessionsRepository : IUserSessionsRepository if (rowsAffected == 0) throw new UpdateException($"Not found user session by given id {userSession.Id}"); + + await _context.SaveChangesAsync(); } catch (Exception ex) { diff --git a/Govor.Data/Repositories/UsersRepository.cs b/Govor.Data/Repositories/UsersRepository.cs index 55b3519..8cbe546 100644 --- a/Govor.Data/Repositories/UsersRepository.cs +++ b/Govor.Data/Repositories/UsersRepository.cs @@ -162,6 +162,8 @@ public class UsersRepository : IUsersRepository if (rowsAffected == 0) throw new UpdateException($"Not found user by given id {user.Id}"); + + await _context.SaveChangesAsync(); } catch (Exception ex) {