From 4410f2d98bfe97213dff7c987f3b8b827f76496f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A5=D0=B0=D0=BB=D0=B8=D0=BC=D0=BE=D0=B2=20=D0=A0=D1=83?= =?UTF-8?q?=D1=81=D1=82=D0=B0=D0=BC?= Date: Thu, 19 Mar 2026 01:49:55 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9C=D0=B5=D0=BB=D0=BA=D0=B8=D0=B9=20=D1=80?= =?UTF-8?q?=D0=B5=D1=84=D0=B0=D0=BA=D1=82=D0=BE=D1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/server-net/.editorconfig | 6 ------ .../ResetUserPasswordCommand.cs | 4 ++-- .../GetUserDetails/GetUserDetailsQuery.cs | 2 +- .../Commands/HandshakeFederationCommand.cs | 5 +++-- .../AddStoryReactionCommand.cs | 3 ++- .../AddStoryReply/AddStoryReplyCommand.cs | 3 ++- .../DeleteStory/DeleteStoryCommand.cs | 5 +++-- .../Commands/ViewStory/ViewStoryCommand.cs | 3 ++- .../GetStoryReplies/GetStoryRepliesQuery.cs | 5 +++-- .../GetStoryViewers/GetStoryViewersQuery.cs | 5 +++-- .../GetUserStories/GetUserStoriesQuery.cs | 2 +- .../Host/Application/Stories/StoryErrors.cs | 9 +++++++++ .../Chats/Application/Chats/Avatar/Avatar.cs | 6 +++--- .../Application/Chats/Clear/ClearChat.cs | 2 +- .../Chats/GetChatById/GetChatById.cs | 2 +- .../Chats/LeaveOrDelete/LeaveOrDeleteChat.cs | 3 ++- .../Application/Chats/Members/Members.cs | 4 ++-- .../Application/Chats/TogglePin/TogglePin.cs | 4 ++-- .../Application/Chats/Update/UpdateChat.cs | 2 +- .../Messages/GetMessages/GetMessagesQuery.cs | 4 ++-- .../GetSharedMedia/GetSharedMediaQuery.cs | 4 ++-- .../Messages/React/AddReactionCommand.cs | 2 +- .../Send/SendMessageCommandHandler.cs | 4 ++-- .../Messages/UploadFile/UploadFileCommand.cs | 7 ++++--- .../TelegramImport/AnalyzeImportCommand.cs | 5 +++-- .../TelegramImport/ExecuteImportCommand.cs | 8 ++++---- .../src/Modules/Chats/Domain/ChatConstants.cs | 9 +++++++++ .../src/Modules/Chats/Domain/ChatErrors.cs | 19 +++++++++++++++++++ .../Persistence/MessageRepository.cs | 2 +- .../Friends/AcceptFriendRequest.cs | 3 ++- .../Friends/DeclineFriendRequest.cs | 3 ++- .../Application/Friends/RemoveFriend.cs | 3 ++- .../Application/Friends/SendFriendRequest.cs | 4 ++-- .../Application/Users/Avatar/CropAvatar.cs | 2 +- .../Application/Users/Avatar/DeleteAvatar.cs | 2 +- .../Application/Users/Avatar/UploadAvatar.cs | 2 +- .../Identity/Application/Users/GetMe/GetMe.cs | 2 +- .../Application/Users/GetUser/GetUser.cs | 2 +- .../Users/Login/LoginUserCommandHandler.cs | 2 +- .../Register/RegisterUserCommandHandler.cs | 4 ++-- .../Users/UpdateProfile/UpdateProfile.cs | 2 +- .../Users/UpdateSettings/UpdateSettings.cs | 2 +- .../Modules/Identity/Domain/IdentityErrors.cs | 14 ++++++++++++++ .../Shared/Knot.Shared.Kernel/DomainErrors.cs | 7 +++++++ 44 files changed, 129 insertions(+), 64 deletions(-) delete mode 100644 apps/server-net/.editorconfig create mode 100644 apps/server-net/src/Host/Application/Stories/StoryErrors.cs create mode 100644 apps/server-net/src/Modules/Chats/Domain/ChatConstants.cs create mode 100644 apps/server-net/src/Modules/Chats/Domain/ChatErrors.cs create mode 100644 apps/server-net/src/Modules/Identity/Domain/IdentityErrors.cs create mode 100644 apps/server-net/src/Shared/Knot.Shared.Kernel/DomainErrors.cs diff --git a/apps/server-net/.editorconfig b/apps/server-net/.editorconfig deleted file mode 100644 index a078cf2..0000000 --- a/apps/server-net/.editorconfig +++ /dev/null @@ -1,6 +0,0 @@ -root = true - -[*.cs] -csharp_prefer_braces = true:warning -csharp_style_namespace_declarations = file_scoped:warning -dotnet_diagnostic.IDE0011.severity = warning diff --git a/apps/server-net/src/Host/Application/Admin/Commands/ResetUserPassword/ResetUserPasswordCommand.cs b/apps/server-net/src/Host/Application/Admin/Commands/ResetUserPassword/ResetUserPasswordCommand.cs index c2b874d..e6078c8 100644 --- a/apps/server-net/src/Host/Application/Admin/Commands/ResetUserPassword/ResetUserPasswordCommand.cs +++ b/apps/server-net/src/Host/Application/Admin/Commands/ResetUserPassword/ResetUserPasswordCommand.cs @@ -28,11 +28,11 @@ internal sealed class ResetUserPasswordCommandHandler : ICommandHandler(new Error("User.NotFound", "User not found")); + return Result.Failure(IdentityErrors.UserNotFound); } if (string.IsNullOrWhiteSpace(request.NewPassword)) - return Result.Failure(new Error("InvalidPassword", "Password cannot be empty")); + return Result.Failure(DomainErrors.InvalidPassword); var hash = BCrypt.Net.BCrypt.HashPassword(request.NewPassword); user.ChangePassword(hash); diff --git a/apps/server-net/src/Host/Application/Admin/Queries/GetUserDetails/GetUserDetailsQuery.cs b/apps/server-net/src/Host/Application/Admin/Queries/GetUserDetails/GetUserDetailsQuery.cs index 7ff7337..2167f73 100644 --- a/apps/server-net/src/Host/Application/Admin/Queries/GetUserDetails/GetUserDetailsQuery.cs +++ b/apps/server-net/src/Host/Application/Admin/Queries/GetUserDetails/GetUserDetailsQuery.cs @@ -29,7 +29,7 @@ internal sealed class GetUserDetailsQueryHandler : IQueryHandler(new Error("User.NotFound", "User not found")); + return Result.Failure(IdentityErrors.UserNotFound); } var messagesCount = await _chatsDbContext.Messages.CountAsync(m => m.SenderId == request.UserId, cancellationToken); diff --git a/apps/server-net/src/Host/Application/Federation/Commands/HandshakeFederationCommand.cs b/apps/server-net/src/Host/Application/Federation/Commands/HandshakeFederationCommand.cs index 70135ff..fe2d38c 100644 --- a/apps/server-net/src/Host/Application/Federation/Commands/HandshakeFederationCommand.cs +++ b/apps/server-net/src/Host/Application/Federation/Commands/HandshakeFederationCommand.cs @@ -1,3 +1,4 @@ +using Host.Application.Stories; using Knot.Shared.Kernel; using Knot.Shared.Kernel.Configuration; using Knot.Shared.Kernel.Constants; @@ -31,11 +32,11 @@ internal sealed class HandshakeFederationCommandHandler : ICommandHandler(new Error(Errors.DisabledByAdmin, "Federation is disabled"))); if (string.IsNullOrEmpty(request.Request.Domain) || string.IsNullOrEmpty(request.Request.PublicKey)) - return Task.FromResult(Result.Failure(new Error("Request.Invalid", "Domain and PublicKey are required"))); + return Task.FromResult(Result.Failure(DomainErrors.RequestInvalid)); var allowedList = conf.AllowedDomains?.Select(d => d.Trim().ToLower()).ToList() ?? new System.Collections.Generic.List(); if (!allowedList.Contains(request.Request.Domain.ToLowerInvariant())) - return Task.FromResult(Result.Failure(new Error("Unauthorized", "Domain is not in the whitelist"))); + return Task.FromResult(Result.Failure(StoryErrors.Unauthorized)); using var rsa = RSA.Create(2048); var selfPublicKey = Convert.ToBase64String(rsa.ExportRSAPublicKey()); diff --git a/apps/server-net/src/Host/Application/Stories/Commands/AddStoryReaction/AddStoryReactionCommand.cs b/apps/server-net/src/Host/Application/Stories/Commands/AddStoryReaction/AddStoryReactionCommand.cs index 6fa4e6b..5ededd7 100644 --- a/apps/server-net/src/Host/Application/Stories/Commands/AddStoryReaction/AddStoryReactionCommand.cs +++ b/apps/server-net/src/Host/Application/Stories/Commands/AddStoryReaction/AddStoryReactionCommand.cs @@ -1,3 +1,4 @@ +using Host.Application.Stories; using System; using System.Collections.Generic; using System.Linq; @@ -49,7 +50,7 @@ internal sealed class AddStoryReactionCommandHandler : ICommandHandler(new Error("Story.NotFound", "Story not found")); + return Result.Failure(StoryErrors.StoryNotFound); } var existing = await _context.StoryReactions diff --git a/apps/server-net/src/Host/Application/Stories/Commands/AddStoryReply/AddStoryReplyCommand.cs b/apps/server-net/src/Host/Application/Stories/Commands/AddStoryReply/AddStoryReplyCommand.cs index 7e77d23..d57222b 100644 --- a/apps/server-net/src/Host/Application/Stories/Commands/AddStoryReply/AddStoryReplyCommand.cs +++ b/apps/server-net/src/Host/Application/Stories/Commands/AddStoryReply/AddStoryReplyCommand.cs @@ -1,3 +1,4 @@ +using Host.Application.Stories; using System; using System.Collections.Generic; using System.Linq; @@ -49,7 +50,7 @@ internal sealed class AddStoryReplyCommandHandler : ICommandHandler(new Error("Story.NotFound", "Story not found")); + return Result.Failure(StoryErrors.StoryNotFound); } var reply = new StoryReply(request.StoryId, request.UserId, request.Content); diff --git a/apps/server-net/src/Host/Application/Stories/Commands/DeleteStory/DeleteStoryCommand.cs b/apps/server-net/src/Host/Application/Stories/Commands/DeleteStory/DeleteStoryCommand.cs index 2724e5c..a21c957 100644 --- a/apps/server-net/src/Host/Application/Stories/Commands/DeleteStory/DeleteStoryCommand.cs +++ b/apps/server-net/src/Host/Application/Stories/Commands/DeleteStory/DeleteStoryCommand.cs @@ -1,3 +1,4 @@ +using Host.Application.Stories; using System; using System.Threading; using System.Threading.Tasks; @@ -24,11 +25,11 @@ internal sealed class DeleteStoryCommandHandler : ICommandHandler(new Error("Story.NotFound", "Story not found")); + return Result.Failure(StoryErrors.StoryNotFound); } if (story.UserId != request.UserId) { - return Result.Failure(new Error("Unauthorized", "Unauthorized")); + return Result.Failure(StoryErrors.Unauthorized); } _context.Stories.Remove(story); diff --git a/apps/server-net/src/Host/Application/Stories/Commands/ViewStory/ViewStoryCommand.cs b/apps/server-net/src/Host/Application/Stories/Commands/ViewStory/ViewStoryCommand.cs index 05ce334..901b2b6 100644 --- a/apps/server-net/src/Host/Application/Stories/Commands/ViewStory/ViewStoryCommand.cs +++ b/apps/server-net/src/Host/Application/Stories/Commands/ViewStory/ViewStoryCommand.cs @@ -1,3 +1,4 @@ +using Host.Application.Stories; using System; using System.Linq; using System.Threading; @@ -38,7 +39,7 @@ internal sealed class ViewStoryCommandHandler : ICommandHandler(new Error("Story.NotFound", "Story not found")); + return Result.Failure(StoryErrors.StoryNotFound); } if (story.UserId == request.UserId) diff --git a/apps/server-net/src/Host/Application/Stories/Queries/GetStoryReplies/GetStoryRepliesQuery.cs b/apps/server-net/src/Host/Application/Stories/Queries/GetStoryReplies/GetStoryRepliesQuery.cs index 50e9402..d103eab 100644 --- a/apps/server-net/src/Host/Application/Stories/Queries/GetStoryReplies/GetStoryRepliesQuery.cs +++ b/apps/server-net/src/Host/Application/Stories/Queries/GetStoryReplies/GetStoryRepliesQuery.cs @@ -1,3 +1,4 @@ +using Host.Application.Stories; using System; using System.Collections.Generic; using System.Linq; @@ -33,11 +34,11 @@ internal sealed class GetStoryRepliesQueryHandler : IQueryHandler>(new Error("Story.NotFound", "Story not found")); + return Result.Failure>(StoryErrors.StoryNotFound); } if (story.UserId != request.UserId) { - return Result.Failure>(new Error("Unauthorized", "Unauthorized")); + return Result.Failure>(StoryErrors.Unauthorized); } var replies = new List(); diff --git a/apps/server-net/src/Host/Application/Stories/Queries/GetStoryViewers/GetStoryViewersQuery.cs b/apps/server-net/src/Host/Application/Stories/Queries/GetStoryViewers/GetStoryViewersQuery.cs index cc56225..f25e503 100644 --- a/apps/server-net/src/Host/Application/Stories/Queries/GetStoryViewers/GetStoryViewersQuery.cs +++ b/apps/server-net/src/Host/Application/Stories/Queries/GetStoryViewers/GetStoryViewersQuery.cs @@ -1,3 +1,4 @@ +using Host.Application.Stories; using System; using System.Collections.Generic; using System.Linq; @@ -33,11 +34,11 @@ internal sealed class GetStoryViewersQueryHandler : IQueryHandler>(new Error("Story.NotFound", "Story not found")); + return Result.Failure>(StoryErrors.StoryNotFound); } if (story.UserId != request.UserId) { - return Result.Failure>(new Error("Unauthorized", "Unauthorized")); + return Result.Failure>(StoryErrors.Unauthorized); } var viewerIds = story.Viewers.Select(v => v.UserId).ToList(); diff --git a/apps/server-net/src/Host/Application/Stories/Queries/GetUserStories/GetUserStoriesQuery.cs b/apps/server-net/src/Host/Application/Stories/Queries/GetUserStories/GetUserStoriesQuery.cs index e61999d..806f6c0 100644 --- a/apps/server-net/src/Host/Application/Stories/Queries/GetUserStories/GetUserStoriesQuery.cs +++ b/apps/server-net/src/Host/Application/Stories/Queries/GetUserStories/GetUserStoriesQuery.cs @@ -39,7 +39,7 @@ internal sealed class GetUserStoriesQueryHandler : IQueryHandler(new Error("User.NotFound", "User not found")); + return Result.Failure(IdentityErrors.UserNotFound); } var result = new StoryGroupDto( diff --git a/apps/server-net/src/Host/Application/Stories/StoryErrors.cs b/apps/server-net/src/Host/Application/Stories/StoryErrors.cs new file mode 100644 index 0000000..0d05d38 --- /dev/null +++ b/apps/server-net/src/Host/Application/Stories/StoryErrors.cs @@ -0,0 +1,9 @@ +using Knot.Shared.Kernel; + +namespace Host.Application.Stories; + +public static class StoryErrors +{ + public static readonly Error Unauthorized = new Error("Unauthorized", "Access denied"); + public static readonly Error StoryNotFound = new Error("Story.NotFound", "Story not found"); +} diff --git a/apps/server-net/src/Modules/Chats/Application/Chats/Avatar/Avatar.cs b/apps/server-net/src/Modules/Chats/Application/Chats/Avatar/Avatar.cs index 03447ac..52168d0 100644 --- a/apps/server-net/src/Modules/Chats/Application/Chats/Avatar/Avatar.cs +++ b/apps/server-net/src/Modules/Chats/Application/Chats/Avatar/Avatar.cs @@ -33,7 +33,7 @@ internal sealed class UploadGroupAvatarCommandHandler : ICommandHandler m.UserId == request.UserId)) { - return Result.Failure(new Error("Chat.NotFound", "Chat not found or access denied")); + return Result.Failure(ChatErrors.ChatNotFound); } var fileId = await _fileStorage.UploadFileAsync(request.FileStream, request.FileName, request.ContentType); @@ -67,7 +67,7 @@ internal sealed class CropGroupAvatarCommandHandler : ICommandHandler m.UserId == request.UserId)) { - return Result.Failure(new Error("Chat.NotFound", "Chat not found or access denied")); + return Result.Failure(ChatErrors.ChatNotFound); } string url; @@ -117,7 +117,7 @@ internal sealed class RemoveGroupAvatarCommandHandler : ICommandHandler m.UserId == request.UserId)) { - return Result.Failure(new Error("Chat.NotFound", "Chat not found or access denied")); + return Result.Failure(ChatErrors.ChatNotFound); } chat.UpdateAvatar(null); diff --git a/apps/server-net/src/Modules/Chats/Application/Chats/Clear/ClearChat.cs b/apps/server-net/src/Modules/Chats/Application/Chats/Clear/ClearChat.cs index 6463e25..cfe9692 100644 --- a/apps/server-net/src/Modules/Chats/Application/Chats/Clear/ClearChat.cs +++ b/apps/server-net/src/Modules/Chats/Application/Chats/Clear/ClearChat.cs @@ -25,7 +25,7 @@ internal sealed class ClearChatCommandHandler : ICommandHandler m.UserId == request.UserId)) { - return Result.Failure(new Error("Chat.NotFound", "Chat not found or access denied")); + return Result.Failure(ChatErrors.ChatNotFound); } // Currently a placeholder diff --git a/apps/server-net/src/Modules/Chats/Application/Chats/GetChatById/GetChatById.cs b/apps/server-net/src/Modules/Chats/Application/Chats/GetChatById/GetChatById.cs index a8f73db..1d93ab3 100644 --- a/apps/server-net/src/Modules/Chats/Application/Chats/GetChatById/GetChatById.cs +++ b/apps/server-net/src/Modules/Chats/Application/Chats/GetChatById/GetChatById.cs @@ -36,7 +36,7 @@ internal sealed class GetChatByIdQueryHandler : IQueryHandler m.UserId == request.UserId)) { - return Result.Failure(new Error("Chats.Forbidden", "Вы не являетесь участником этого чата.")); + return Result.Failure(ChatErrors.ChatsForbidden); } var userIdsToFetch = new HashSet(); diff --git a/apps/server-net/src/Modules/Chats/Application/Chats/LeaveOrDelete/LeaveOrDeleteChat.cs b/apps/server-net/src/Modules/Chats/Application/Chats/LeaveOrDelete/LeaveOrDeleteChat.cs index 1f0c0c0..c9b8a6b 100644 --- a/apps/server-net/src/Modules/Chats/Application/Chats/LeaveOrDelete/LeaveOrDeleteChat.cs +++ b/apps/server-net/src/Modules/Chats/Application/Chats/LeaveOrDelete/LeaveOrDeleteChat.cs @@ -1,3 +1,4 @@ + using System; using System.Threading; using System.Threading.Tasks; @@ -32,7 +33,7 @@ internal sealed class LeaveOrDeleteChatCommandHandler : ICommandHandler m.UserId == request.UserId)) { - return Result.Failure(new Error("Unauthorized", "Access denied")); + return Result.Failure(ChatErrors.Unauthorized); } if (chat.Type == ChatType.Group) diff --git a/apps/server-net/src/Modules/Chats/Application/Chats/Members/Members.cs b/apps/server-net/src/Modules/Chats/Application/Chats/Members/Members.cs index 51c99f0..8a9e5f3 100644 --- a/apps/server-net/src/Modules/Chats/Application/Chats/Members/Members.cs +++ b/apps/server-net/src/Modules/Chats/Application/Chats/Members/Members.cs @@ -28,7 +28,7 @@ internal sealed class AddMembersCommandHandler : ICommandHandler m.UserId == request.UserId)) { - return Result.Failure(new Error("Chat.NotFound", "Chat not found or access denied")); + return Result.Failure(ChatErrors.ChatNotFound); } foreach (var userId in request.UserIdsToAdd) @@ -60,7 +60,7 @@ internal sealed class RemoveMemberCommandHandler : ICommandHandler m.UserId == request.UserId)) { - return Result.Failure(new Error("Chat.NotFound", "Chat not found or access denied")); + return Result.Failure(ChatErrors.ChatNotFound); } chat.RemoveMember(request.UserIdToRemove); diff --git a/apps/server-net/src/Modules/Chats/Application/Chats/TogglePin/TogglePin.cs b/apps/server-net/src/Modules/Chats/Application/Chats/TogglePin/TogglePin.cs index 22337bd..9b2f91a 100644 --- a/apps/server-net/src/Modules/Chats/Application/Chats/TogglePin/TogglePin.cs +++ b/apps/server-net/src/Modules/Chats/Application/Chats/TogglePin/TogglePin.cs @@ -28,13 +28,13 @@ internal sealed class TogglePinCommandHandler : ICommandHandler(new Error("Chat.NotFound", "Chat not found")); + return Result.Failure(ChatErrors.ChatNotFound); } var member = chat.Members.FirstOrDefault(m => m.UserId == request.UserId); if (member == null) { - return Result.Failure(new Error("Chat.NotFound", "Member not found")); + return Result.Failure(ChatErrors.ChatNotFound); } member.TogglePin(); diff --git a/apps/server-net/src/Modules/Chats/Application/Chats/Update/UpdateChat.cs b/apps/server-net/src/Modules/Chats/Application/Chats/Update/UpdateChat.cs index 4c89b3f..dffb9e6 100644 --- a/apps/server-net/src/Modules/Chats/Application/Chats/Update/UpdateChat.cs +++ b/apps/server-net/src/Modules/Chats/Application/Chats/Update/UpdateChat.cs @@ -27,7 +27,7 @@ internal sealed class UpdateChatCommandHandler : ICommandHandler m.UserId == request.UserId)) { - return Result.Failure(new Error("Chat.NotFound", "Chat not found or access denied")); + return Result.Failure(ChatErrors.ChatNotFound); } if (request.Name != null) diff --git a/apps/server-net/src/Modules/Chats/Application/Messages/GetMessages/GetMessagesQuery.cs b/apps/server-net/src/Modules/Chats/Application/Messages/GetMessages/GetMessagesQuery.cs index 6e05512..52212cb 100644 --- a/apps/server-net/src/Modules/Chats/Application/Messages/GetMessages/GetMessagesQuery.cs +++ b/apps/server-net/src/Modules/Chats/Application/Messages/GetMessages/GetMessagesQuery.cs @@ -32,7 +32,7 @@ internal sealed class GetMessagesQueryHandler : IQueryHandler m.UserId == request.UserId)) { - return Result.Failure>(new Error("Chats.Forbidden", "Вы не являетесь участником этого чата.")); + return Result.Failure>(ChatErrors.ChatsForbidden); } DateTime? cursorDate = null; @@ -41,7 +41,7 @@ internal sealed class GetMessagesQueryHandler : IQueryHandler(); var userIdsToFetch = new HashSet(); diff --git a/apps/server-net/src/Modules/Chats/Application/Messages/GetSharedMedia/GetSharedMediaQuery.cs b/apps/server-net/src/Modules/Chats/Application/Messages/GetSharedMedia/GetSharedMediaQuery.cs index ac99b36..8f00a2e 100644 --- a/apps/server-net/src/Modules/Chats/Application/Messages/GetSharedMedia/GetSharedMediaQuery.cs +++ b/apps/server-net/src/Modules/Chats/Application/Messages/GetSharedMedia/GetSharedMediaQuery.cs @@ -33,10 +33,10 @@ internal sealed class GetSharedMediaQueryHandler : IQueryHandler m.UserId == request.UserId)) { - return Result.Failure>(new Error("Chats.Forbidden", "Вы не являетесь участником этого чата.")); + return Result.Failure>(ChatErrors.ChatsForbidden); } - var messages = await _messageRepository.GetChatMessagesAsync(request.ChatId, 300, 0, cancellationToken); + var messages = await _messageRepository.GetChatMessagesAsync(request.ChatId, ChatConstants.MaxSharedMediaQueryLimit, 0, cancellationToken); messages = messages.Where(m => !m.IsDeleted && !m.DeletedByUsers.Contains(request.UserId)).ToList(); var result = new List(); diff --git a/apps/server-net/src/Modules/Chats/Application/Messages/React/AddReactionCommand.cs b/apps/server-net/src/Modules/Chats/Application/Messages/React/AddReactionCommand.cs index 2463f3d..e530151 100644 --- a/apps/server-net/src/Modules/Chats/Application/Messages/React/AddReactionCommand.cs +++ b/apps/server-net/src/Modules/Chats/Application/Messages/React/AddReactionCommand.cs @@ -57,7 +57,7 @@ public sealed class AddReactionCommandHandler : ICommandHandler(new Error("Chats.NotFound", "Чат не найден.")); + return Result.Failure(ChatErrors.ChatsNotFound); } // 2. Проверяем, является ли отправитель участником if (!chat.Members.Any(m => m.UserId == request.SenderId)) { - return Result.Failure(new Error("Chats.Forbidden", "Вы не являетесь участником этого чата.")); + return Result.Failure(ChatErrors.ChatsForbidden); } // 3. Создаем сообщение diff --git a/apps/server-net/src/Modules/Chats/Application/Messages/UploadFile/UploadFileCommand.cs b/apps/server-net/src/Modules/Chats/Application/Messages/UploadFile/UploadFileCommand.cs index 3bbbd42..a9ec894 100644 --- a/apps/server-net/src/Modules/Chats/Application/Messages/UploadFile/UploadFileCommand.cs +++ b/apps/server-net/src/Modules/Chats/Application/Messages/UploadFile/UploadFileCommand.cs @@ -1,4 +1,5 @@ -using System; +using Knot.Modules.Chats.Domain; +using System; using System.IO; using System.Threading; using System.Threading.Tasks; @@ -27,13 +28,13 @@ internal sealed class UploadFileCommandHandler : ICommandHandler(new Error("File.Empty", "No file uploaded")); + return Result.Failure(ChatErrors.FileEmpty); } var maxMb = _settingsService.Current.MaxFileSizeMb; if (request.Length > maxMb * 1024 * 1024) { - return Result.Failure(new Error("File.TooLarge", $"File exceeds the maximum allowed size of {maxMb}MB.")); + return Result.Failure(ChatErrors.FileTooLarge(maxMb)); } var fileId = await _fileStorage.UploadFileAsync(request.FileStream, request.FileName, request.ContentType); diff --git a/apps/server-net/src/Modules/Chats/Application/TelegramImport/AnalyzeImportCommand.cs b/apps/server-net/src/Modules/Chats/Application/TelegramImport/AnalyzeImportCommand.cs index ed4861a..d507d2a 100644 --- a/apps/server-net/src/Modules/Chats/Application/TelegramImport/AnalyzeImportCommand.cs +++ b/apps/server-net/src/Modules/Chats/Application/TelegramImport/AnalyzeImportCommand.cs @@ -1,3 +1,4 @@ +using Knot.Modules.Chats.Domain; using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -28,12 +29,12 @@ internal sealed class AnalyzeImportCommandHandler : ICommandHandler(new Error("File.Empty", "No file uploaded")); + return Result.Failure(ChatErrors.FileEmpty); } if (!request.FileName.EndsWith(".zip", StringComparison.OrdinalIgnoreCase)) { - return Result.Failure(new Error("File.InvalidExtension", "Must be a ZIP archive")); + return Result.Failure(ChatErrors.FileInvalidExtension); } var token = Guid.NewGuid(); diff --git a/apps/server-net/src/Modules/Chats/Application/TelegramImport/ExecuteImportCommand.cs b/apps/server-net/src/Modules/Chats/Application/TelegramImport/ExecuteImportCommand.cs index 485f032..b1d5638 100644 --- a/apps/server-net/src/Modules/Chats/Application/TelegramImport/ExecuteImportCommand.cs +++ b/apps/server-net/src/Modules/Chats/Application/TelegramImport/ExecuteImportCommand.cs @@ -56,12 +56,12 @@ internal sealed class ExecuteImportCommandHandler : ICommandHandler(new Error("Import.Expired", "Session not found or expired")); + return Result.Failure(ChatErrors.ImportExpired); } if (!System.IO.File.Exists(tempPath)) { - return Result.Failure(new Error("Import.Missing", "ZIP file lost")); + return Result.Failure(ChatErrors.ImportMissing); } var myId = request.CurrentUserId; @@ -95,7 +95,7 @@ internal sealed class ExecuteImportCommandHandler : ICommandHandler(new Error("Import.CreateChatFailed", res.Error.Description ?? res.Error.Code)); + return Result.Failure(ChatErrors.ImportCreateChatFailed(res.Error.Description ?? res.Error.Code)); } chatId = res.Value; @@ -107,7 +107,7 @@ internal sealed class ExecuteImportCommandHandler : ICommandHandler(new Error("Import.CreateChatFailed", res.Error.Description ?? res.Error.Code)); + return Result.Failure(ChatErrors.ImportCreateChatFailed(res.Error.Description ?? res.Error.Code)); } chatId = res.Value; diff --git a/apps/server-net/src/Modules/Chats/Domain/ChatConstants.cs b/apps/server-net/src/Modules/Chats/Domain/ChatConstants.cs new file mode 100644 index 0000000..3faae01 --- /dev/null +++ b/apps/server-net/src/Modules/Chats/Domain/ChatConstants.cs @@ -0,0 +1,9 @@ +namespace Knot.Modules.Chats.Domain; + +public static class ChatConstants +{ + public const int DefaultMessageQueryLimit = 100; + public const int MaxSharedMediaQueryLimit = 300; + public const int SearchMessagesLimit = 50; + public const int MaxFileUploadSizeMb = 50; +} diff --git a/apps/server-net/src/Modules/Chats/Domain/ChatErrors.cs b/apps/server-net/src/Modules/Chats/Domain/ChatErrors.cs new file mode 100644 index 0000000..af11dda --- /dev/null +++ b/apps/server-net/src/Modules/Chats/Domain/ChatErrors.cs @@ -0,0 +1,19 @@ +using Knot.Shared.Kernel; + +namespace Knot.Modules.Chats.Domain; + +public static class ChatErrors +{ + public static readonly Error FileEmpty = new Error("File.Empty", "No file uploaded"); + public static readonly Error FileInvalidExtension = new Error("File.InvalidExtension", "Must be a ZIP archive"); + public static readonly Error ImportExpired = new Error("Import.Expired", "Session not found or expired"); + public static readonly Error ImportMissing = new Error("Import.Missing", "ZIP file lost"); + public static readonly Error ChatNotFound = new Error("Chat.NotFound", "Chat not found or access denied"); + public static readonly Error ChatsForbidden = new Error("Chats.Forbidden", "Вы не являетесь участником этого чата."); + public static readonly Error MessagesNotFound = new Error("Messages.NotFound", "Message not found."); + public static readonly Error ChatsNotFound = new Error("Chats.NotFound", "Чат не найден."); + public static readonly Error Unauthorized = new Error("Chats.Unauthorized", "Access denied"); + + public static Error ImportCreateChatFailed(string msg) => new Error("Import.CreateChatFailed", msg); + public static Error FileTooLarge(int maxMb) => new Error("File.TooLarge", $"File exceeds the maximum allowed size of {maxMb}MB."); +} diff --git a/apps/server-net/src/Modules/Chats/Infrastructure/Persistence/MessageRepository.cs b/apps/server-net/src/Modules/Chats/Infrastructure/Persistence/MessageRepository.cs index 902dc09..9925d96 100644 --- a/apps/server-net/src/Modules/Chats/Infrastructure/Persistence/MessageRepository.cs +++ b/apps/server-net/src/Modules/Chats/Infrastructure/Persistence/MessageRepository.cs @@ -74,7 +74,7 @@ public sealed class MessageRepository : IMessageRepository return await q.Where(m => m.Content != null && m.Content.Contains(query)) .OrderByDescending(m => m.CreatedAt) - .Take(50) + .Take(ChatConstants.SearchMessagesLimit) .Include(m => m.Media) .Include(m => m.ReadBy) .Include(m => m.Reactions) diff --git a/apps/server-net/src/Modules/Identity/Application/Friends/AcceptFriendRequest.cs b/apps/server-net/src/Modules/Identity/Application/Friends/AcceptFriendRequest.cs index 639d6a4..c5977a3 100644 --- a/apps/server-net/src/Modules/Identity/Application/Friends/AcceptFriendRequest.cs +++ b/apps/server-net/src/Modules/Identity/Application/Friends/AcceptFriendRequest.cs @@ -1,3 +1,4 @@ +using Knot.Modules.Identity.Domain; using System; using System.Threading; using System.Threading.Tasks; @@ -24,7 +25,7 @@ internal sealed class AcceptFriendRequestCommandHandler : ICommandHandler(new Error("Friends.NotFound", "Request not found")); + return Result.Failure(IdentityErrors.FriendsNotFound); } friendship.Accept(); diff --git a/apps/server-net/src/Modules/Identity/Application/Friends/DeclineFriendRequest.cs b/apps/server-net/src/Modules/Identity/Application/Friends/DeclineFriendRequest.cs index f6bdc4b..803e0e7 100644 --- a/apps/server-net/src/Modules/Identity/Application/Friends/DeclineFriendRequest.cs +++ b/apps/server-net/src/Modules/Identity/Application/Friends/DeclineFriendRequest.cs @@ -1,3 +1,4 @@ +using Knot.Modules.Identity.Domain; using System; using System.Threading; using System.Threading.Tasks; @@ -24,7 +25,7 @@ internal sealed class DeclineFriendRequestCommandHandler : ICommandHandler(new Error("User.NotFound", "User not found")); + return Result.Failure(IdentityErrors.UserNotFound); } string avatarUrl; diff --git a/apps/server-net/src/Modules/Identity/Application/Users/Avatar/DeleteAvatar.cs b/apps/server-net/src/Modules/Identity/Application/Users/Avatar/DeleteAvatar.cs index c7ddd3f..785a622 100644 --- a/apps/server-net/src/Modules/Identity/Application/Users/Avatar/DeleteAvatar.cs +++ b/apps/server-net/src/Modules/Identity/Application/Users/Avatar/DeleteAvatar.cs @@ -22,7 +22,7 @@ internal sealed class DeleteAvatarCommandHandler : ICommandHandler(new Error("User.NotFound", "User not found")); + return Result.Failure(IdentityErrors.UserNotFound); } user.UpdateAvatar(null); diff --git a/apps/server-net/src/Modules/Identity/Application/Users/Avatar/UploadAvatar.cs b/apps/server-net/src/Modules/Identity/Application/Users/Avatar/UploadAvatar.cs index c87fe71..7f428cb 100644 --- a/apps/server-net/src/Modules/Identity/Application/Users/Avatar/UploadAvatar.cs +++ b/apps/server-net/src/Modules/Identity/Application/Users/Avatar/UploadAvatar.cs @@ -25,7 +25,7 @@ internal sealed class UploadAvatarCommandHandler : ICommandHandler(new Error("User.NotFound", "User not found")); + return Result.Failure(IdentityErrors.UserNotFound); } var fileId = await _fileStorage.UploadFileAsync(request.FileStream, request.FileName, request.ContentType); diff --git a/apps/server-net/src/Modules/Identity/Application/Users/GetMe/GetMe.cs b/apps/server-net/src/Modules/Identity/Application/Users/GetMe/GetMe.cs index b6075dd..2db2b19 100644 --- a/apps/server-net/src/Modules/Identity/Application/Users/GetMe/GetMe.cs +++ b/apps/server-net/src/Modules/Identity/Application/Users/GetMe/GetMe.cs @@ -21,7 +21,7 @@ internal sealed class GetMeQueryHandler : IQueryHandler(new Error("User.NotFound", "User not found")); + return Result.Failure(IdentityErrors.UserNotFound); } var response = new AuthResponseDto( diff --git a/apps/server-net/src/Modules/Identity/Application/Users/GetUser/GetUser.cs b/apps/server-net/src/Modules/Identity/Application/Users/GetUser/GetUser.cs index aa4e5f6..2ba6c38 100644 --- a/apps/server-net/src/Modules/Identity/Application/Users/GetUser/GetUser.cs +++ b/apps/server-net/src/Modules/Identity/Application/Users/GetUser/GetUser.cs @@ -20,7 +20,7 @@ internal sealed class GetUserQueryHandler : IQueryHandler(new Error("User.NotFound", "User not found")); + return Result.Failure(IdentityErrors.UserNotFound); } var dto = new UserProfileDto( diff --git a/apps/server-net/src/Modules/Identity/Application/Users/Login/LoginUserCommandHandler.cs b/apps/server-net/src/Modules/Identity/Application/Users/Login/LoginUserCommandHandler.cs index d58ff6e..21d5652 100644 --- a/apps/server-net/src/Modules/Identity/Application/Users/Login/LoginUserCommandHandler.cs +++ b/apps/server-net/src/Modules/Identity/Application/Users/Login/LoginUserCommandHandler.cs @@ -28,7 +28,7 @@ public sealed class LoginUserCommandHandler : ICommandHandler(new Error("Identity.InvalidCredentials", "Неверное имя пользователя или пароль.")); + return Result.Failure(IdentityErrors.IdentityInvalidCredentials); } string token = _tokenProvider.Generate(user); diff --git a/apps/server-net/src/Modules/Identity/Application/Users/Register/RegisterUserCommandHandler.cs b/apps/server-net/src/Modules/Identity/Application/Users/Register/RegisterUserCommandHandler.cs index 7db1105..c392d23 100644 --- a/apps/server-net/src/Modules/Identity/Application/Users/Register/RegisterUserCommandHandler.cs +++ b/apps/server-net/src/Modules/Identity/Application/Users/Register/RegisterUserCommandHandler.cs @@ -43,13 +43,13 @@ public sealed class RegisterUserCommandHandler : ICommandHandler(new Error("Identity.RegistrationDisabled", "Registration is disabled by the administrator.")); + return Result.Failure(IdentityErrors.IdentityRegistrationDisabled); } // 1. Проверка уникальности username if (!await _userRepository.IsUsernameUniqueAsync(request.Username, cancellationToken)) { - return Result.Failure(new Error("Identity.UsernameNotUnique", "Это имя пользователя уже занято.")); + return Result.Failure(IdentityErrors.IdentityUsernameNotUnique); } // 2. Хеширование пароля (здесь будет вызов сервиса, пока заглушка) diff --git a/apps/server-net/src/Modules/Identity/Application/Users/UpdateProfile/UpdateProfile.cs b/apps/server-net/src/Modules/Identity/Application/Users/UpdateProfile/UpdateProfile.cs index 773d53c..16095ab 100644 --- a/apps/server-net/src/Modules/Identity/Application/Users/UpdateProfile/UpdateProfile.cs +++ b/apps/server-net/src/Modules/Identity/Application/Users/UpdateProfile/UpdateProfile.cs @@ -22,7 +22,7 @@ internal sealed class UpdateProfileCommandHandler : ICommandHandler(new Error("User.NotFound", "User not found")); + return Result.Failure(IdentityErrors.UserNotFound); } user.UpdateProfile(request.DisplayName ?? user.DisplayName, request.Bio, request.Birthday); diff --git a/apps/server-net/src/Modules/Identity/Application/Users/UpdateSettings/UpdateSettings.cs b/apps/server-net/src/Modules/Identity/Application/Users/UpdateSettings/UpdateSettings.cs index 0ec1a4b..16c4ae4 100644 --- a/apps/server-net/src/Modules/Identity/Application/Users/UpdateSettings/UpdateSettings.cs +++ b/apps/server-net/src/Modules/Identity/Application/Users/UpdateSettings/UpdateSettings.cs @@ -22,7 +22,7 @@ internal sealed class UpdateSettingsCommandHandler : ICommandHandler(new Error("User.NotFound", "User not found")); + return Result.Failure(IdentityErrors.UserNotFound); } user.UpdateSettings(request.HideStoryViews ?? user.HideStoryViews); diff --git a/apps/server-net/src/Modules/Identity/Domain/IdentityErrors.cs b/apps/server-net/src/Modules/Identity/Domain/IdentityErrors.cs new file mode 100644 index 0000000..5e317da --- /dev/null +++ b/apps/server-net/src/Modules/Identity/Domain/IdentityErrors.cs @@ -0,0 +1,14 @@ +using Knot.Shared.Kernel; + +namespace Knot.Modules.Identity.Domain; + +public static class IdentityErrors +{ + public static readonly Error FriendsNotFound = new Error("Friends.NotFound", "Friendship not found"); + public static readonly Error FriendsSelf = new Error("Friends.Self", "Cannot add yourself"); + public static readonly Error FriendsExists = new Error("Friends.Exists", "Friendship already exists"); + public static readonly Error UserNotFound = new Error("User.NotFound", "User not found"); + public static readonly Error IdentityInvalidCredentials = new Error("Identity.InvalidCredentials", "Неверное имя пользователя или пароль."); + public static readonly Error IdentityRegistrationDisabled = new Error("Identity.RegistrationDisabled", "Registration is disabled by the administrator."); + public static readonly Error IdentityUsernameNotUnique = new Error("Identity.UsernameNotUnique", "Это имя пользователя уже занято."); +} diff --git a/apps/server-net/src/Shared/Knot.Shared.Kernel/DomainErrors.cs b/apps/server-net/src/Shared/Knot.Shared.Kernel/DomainErrors.cs new file mode 100644 index 0000000..5678916 --- /dev/null +++ b/apps/server-net/src/Shared/Knot.Shared.Kernel/DomainErrors.cs @@ -0,0 +1,7 @@ +namespace Knot.Shared.Kernel; + +public static class DomainErrors +{ + public static readonly Error InvalidPassword = new Error("Admin.InvalidPassword", "Пароль не должен быть пустым."); + public static readonly Error RequestInvalid = new Error("Request.Invalid", "Invalid federation request"); +}