diff --git a/backend/Knot.sln b/backend/Knot.sln
index 731bc4e..9597f75 100644
--- a/backend/Knot.sln
+++ b/backend/Knot.sln
@@ -13,19 +13,79 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Knot.Shared.Infrastructure"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Modules", "Modules", "{EC447DCF-ABFA-6E24-52A5-D7FD48A5C558}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Identity", "Identity", "{0EC62A1A-9858-3A60-8A0C-FC9AACFA2EC7}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Knot.Modules.Identity", "src\Modules\Identity\Knot.Modules.Identity.csproj", "{5BB2F421-C4C4-40A1-8D6E-FEBB79AE7C98}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Chats", "Chats", "{B4154CE3-9153-E2B0-0515-138FFCE32938}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Knot.Modules.Chats", "src\Modules\Chats\Knot.Modules.Chats.csproj", "{695CC919-E321-4DC0-8D70-C631C51624E1}"
-EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{0AB3BF05-4346-4AA6-1389-037BE0695223}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Knot.Modules.Identity.UnitTests", "tests\Knot.Modules.Identity.UnitTests\Knot.Modules.Identity.UnitTests.csproj", "{AB2F334E-DD7C-4314-93B9-941AFC6B2ACD}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Profiles", "Profiles", "{CA5186C2-289A-9324-4152-9805A73A9773}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Knot.Modules.Chats.UnitTests", "tests\Knot.Modules.Chats.UnitTests\Knot.Modules.Chats.UnitTests.csproj", "{F48A3E3A-EEBB-43AA-81AB-7DD7FE5F29B4}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Knot.Modules.Profiles", "src\Modules\Profiles\Knot.Modules.Profiles.csproj", "{CD14770B-8BCD-4D93-AD2C-581ED030E209}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Relations", "Relations", "{10980DF6-D81A-2FEE-4ACB-4D508FA38BC6}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Knot.Modules.Relations", "src\Modules\Relations\Knot.Modules.Relations.csproj", "{B97352F3-709A-42CF-A448-07B9996F5FBE}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Stories", "Stories", "{F83653D1-5494-68B8-6444-7DEF58635BF6}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Knot.Modules.Stories", "src\Modules\Stories\Knot.Modules.Stories.csproj", "{BC98E6FC-B46A-4554-B75E-B912714A915C}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Storage", "Storage", "{AF61C489-0B09-54BC-BD16-4353135E9C4A}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Knot.Modules.Storage", "src\Modules\Storage\Knot.Modules.Storage.csproj", "{D052C05A-CDCD-48DF-8F4F-0B10E3719819}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Auth", "Auth", "{E9D55F35-C373-3DD8-6914-3213F2F582A2}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Knot.Modules.Auth", "src\Modules\Auth\Knot.Modules.Auth.csproj", "{CF08AE9C-B337-47BA-B273-4B8E5610D7BD}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Knot.Modules.Auth.UnitTests", "tests\Knot.Modules.Auth.UnitTests\Knot.Modules.Auth.UnitTests.csproj", "{BA37DF67-98D3-4399-BD68-9679396C1547}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Messaging", "Messaging", "{AE120A0E-5815-2D46-946C-DA544028AD88}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Knot.Modules.Messaging", "src\Modules\Messaging\Knot.Modules.Messaging.csproj", "{C0B5496E-F083-413E-90AE-31282A0ACCA5}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TelegramImport", "TelegramImport", "{4D5D7319-366D-1BD8-49C5-A840B487E852}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Knot.Modules.TelegramImport", "src\Modules\TelegramImport\Knot.Modules.TelegramImport.csproj", "{C905C120-DE44-4CBC-8F98-5BE148115487}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Conversations", "Conversations", "{138B168B-2473-B06D-4BD5-1B010DA319A1}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Knot.Modules.Conversations", "src\Modules\Conversations\Knot.Modules.Conversations.csproj", "{F3EFD119-44F7-46BF-A03C-060F30A1A1E0}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Knot.Modules.Admin.UnitTests", "Tests\Knot.Modules.Admin.UnitTests\Knot.Modules.Admin.UnitTests.csproj", "{489D5878-3945-4F8E-AD13-E5499A18DFCC}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Admin", "Admin", "{E9E1155A-A339-766C-C185-6698FF423EBC}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Knot.Modules.Admin", "src\Modules\Admin\Knot.Modules.Admin.csproj", "{FFC12440-F128-48F5-9BE5-E5263930F876}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Knot.Modules.Federation.UnitTests", "Tests\Knot.Modules.Federation.UnitTests\Knot.Modules.Federation.UnitTests.csproj", "{7A402C99-2E66-4EBE-A257-0D7923A96DFD}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Federation", "Federation", "{837D2CF4-7AEA-BF76-6362-47ECD7058EEC}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Knot.Modules.Federation", "src\Modules\Federation\Knot.Modules.Federation.csproj", "{716E089D-0661-4D39-813B-063DF9A4C0E4}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Knot.Modules.Klipy.UnitTests", "Tests\Knot.Modules.Klipy.UnitTests\Knot.Modules.Klipy.UnitTests.csproj", "{43E6E19C-39EC-49C8-91EE-49586A4B9D03}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Klipy", "Klipy", "{4BC86D3F-8D7B-ACB9-EA51-9374BC9D421A}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Knot.Modules.Klipy", "src\Modules\Klipy\Knot.Modules.Klipy.csproj", "{2498C58C-53B0-4DF2-B4EA-BA69B35A7488}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Knot.Modules.Stories.UnitTests", "Tests\Knot.Modules.Stories.UnitTests\Knot.Modules.Stories.UnitTests.csproj", "{4394178B-1ADD-478E-BF73-574B31C11292}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Knot.Modules.Relations.UnitTests", "Tests\Knot.Modules.Relations.UnitTests\Knot.Modules.Relations.UnitTests.csproj", "{2F2C7E01-5791-4A6D-9A80-C71CAD846FA8}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Knot.Modules.Settings.UnitTests", "Tests\Knot.Modules.Settings.UnitTests\Knot.Modules.Settings.UnitTests.csproj", "{76706BA9-11E0-463A-AF8E-18DFBD9E95A2}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Settings", "Settings", "{0587C5BB-3015-204D-19B6-EB01E03E48B6}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Knot.Modules.Settings", "src\Modules\Settings\Knot.Modules.Settings.csproj", "{AEFD93B4-59B8-4B02-A694-CA15E4EC1C08}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Knot.Modules.Storage.UnitTests", "Tests\Knot.Modules.Storage.UnitTests\Knot.Modules.Storage.UnitTests.csproj", "{C11BFE93-017D-4538-A61F-A8D98F8D2D75}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Knot.Modules.TelegramImport.UnitTests", "Tests\Knot.Modules.TelegramImport.UnitTests\Knot.Modules.TelegramImport.UnitTests.csproj", "{38276FFF-05D8-4005-8863-018CEE833D66}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Knot.Modules.WebRtc.UnitTests", "Tests\Knot.Modules.WebRtc.UnitTests\Knot.Modules.WebRtc.UnitTests.csproj", "{32AC7906-B197-4628-9C47-9337BB5A53E2}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WebRtc", "WebRtc", "{DD6FAF9E-10A0-95E7-0C06-698A6F70AFEE}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Knot.Modules.WebRtc", "src\Modules\WebRtc\Knot.Modules.WebRtc.csproj", "{03BE4EB1-FD38-445A-9A4B-7B481560E9A2}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -61,54 +121,282 @@ Global
{6C13E9A4-234C-4BF1-A145-F1BBA2AC56CB}.Release|x64.Build.0 = Release|Any CPU
{6C13E9A4-234C-4BF1-A145-F1BBA2AC56CB}.Release|x86.ActiveCfg = Release|Any CPU
{6C13E9A4-234C-4BF1-A145-F1BBA2AC56CB}.Release|x86.Build.0 = Release|Any CPU
- {5BB2F421-C4C4-40A1-8D6E-FEBB79AE7C98}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {5BB2F421-C4C4-40A1-8D6E-FEBB79AE7C98}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {5BB2F421-C4C4-40A1-8D6E-FEBB79AE7C98}.Debug|x64.ActiveCfg = Debug|Any CPU
- {5BB2F421-C4C4-40A1-8D6E-FEBB79AE7C98}.Debug|x64.Build.0 = Debug|Any CPU
- {5BB2F421-C4C4-40A1-8D6E-FEBB79AE7C98}.Debug|x86.ActiveCfg = Debug|Any CPU
- {5BB2F421-C4C4-40A1-8D6E-FEBB79AE7C98}.Debug|x86.Build.0 = Debug|Any CPU
- {5BB2F421-C4C4-40A1-8D6E-FEBB79AE7C98}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {5BB2F421-C4C4-40A1-8D6E-FEBB79AE7C98}.Release|Any CPU.Build.0 = Release|Any CPU
- {5BB2F421-C4C4-40A1-8D6E-FEBB79AE7C98}.Release|x64.ActiveCfg = Release|Any CPU
- {5BB2F421-C4C4-40A1-8D6E-FEBB79AE7C98}.Release|x64.Build.0 = Release|Any CPU
- {5BB2F421-C4C4-40A1-8D6E-FEBB79AE7C98}.Release|x86.ActiveCfg = Release|Any CPU
- {5BB2F421-C4C4-40A1-8D6E-FEBB79AE7C98}.Release|x86.Build.0 = Release|Any CPU
- {695CC919-E321-4DC0-8D70-C631C51624E1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {695CC919-E321-4DC0-8D70-C631C51624E1}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {695CC919-E321-4DC0-8D70-C631C51624E1}.Debug|x64.ActiveCfg = Debug|Any CPU
- {695CC919-E321-4DC0-8D70-C631C51624E1}.Debug|x64.Build.0 = Debug|Any CPU
- {695CC919-E321-4DC0-8D70-C631C51624E1}.Debug|x86.ActiveCfg = Debug|Any CPU
- {695CC919-E321-4DC0-8D70-C631C51624E1}.Debug|x86.Build.0 = Debug|Any CPU
- {695CC919-E321-4DC0-8D70-C631C51624E1}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {695CC919-E321-4DC0-8D70-C631C51624E1}.Release|Any CPU.Build.0 = Release|Any CPU
- {695CC919-E321-4DC0-8D70-C631C51624E1}.Release|x64.ActiveCfg = Release|Any CPU
- {695CC919-E321-4DC0-8D70-C631C51624E1}.Release|x64.Build.0 = Release|Any CPU
- {695CC919-E321-4DC0-8D70-C631C51624E1}.Release|x86.ActiveCfg = Release|Any CPU
- {695CC919-E321-4DC0-8D70-C631C51624E1}.Release|x86.Build.0 = Release|Any CPU
- {AB2F334E-DD7C-4314-93B9-941AFC6B2ACD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {AB2F334E-DD7C-4314-93B9-941AFC6B2ACD}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {AB2F334E-DD7C-4314-93B9-941AFC6B2ACD}.Debug|x64.ActiveCfg = Debug|Any CPU
- {AB2F334E-DD7C-4314-93B9-941AFC6B2ACD}.Debug|x64.Build.0 = Debug|Any CPU
- {AB2F334E-DD7C-4314-93B9-941AFC6B2ACD}.Debug|x86.ActiveCfg = Debug|Any CPU
- {AB2F334E-DD7C-4314-93B9-941AFC6B2ACD}.Debug|x86.Build.0 = Debug|Any CPU
- {AB2F334E-DD7C-4314-93B9-941AFC6B2ACD}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {AB2F334E-DD7C-4314-93B9-941AFC6B2ACD}.Release|Any CPU.Build.0 = Release|Any CPU
- {AB2F334E-DD7C-4314-93B9-941AFC6B2ACD}.Release|x64.ActiveCfg = Release|Any CPU
- {AB2F334E-DD7C-4314-93B9-941AFC6B2ACD}.Release|x64.Build.0 = Release|Any CPU
- {AB2F334E-DD7C-4314-93B9-941AFC6B2ACD}.Release|x86.ActiveCfg = Release|Any CPU
- {AB2F334E-DD7C-4314-93B9-941AFC6B2ACD}.Release|x86.Build.0 = Release|Any CPU
- {F48A3E3A-EEBB-43AA-81AB-7DD7FE5F29B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {F48A3E3A-EEBB-43AA-81AB-7DD7FE5F29B4}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {F48A3E3A-EEBB-43AA-81AB-7DD7FE5F29B4}.Debug|x64.ActiveCfg = Debug|Any CPU
- {F48A3E3A-EEBB-43AA-81AB-7DD7FE5F29B4}.Debug|x64.Build.0 = Debug|Any CPU
- {F48A3E3A-EEBB-43AA-81AB-7DD7FE5F29B4}.Debug|x86.ActiveCfg = Debug|Any CPU
- {F48A3E3A-EEBB-43AA-81AB-7DD7FE5F29B4}.Debug|x86.Build.0 = Debug|Any CPU
- {F48A3E3A-EEBB-43AA-81AB-7DD7FE5F29B4}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {F48A3E3A-EEBB-43AA-81AB-7DD7FE5F29B4}.Release|Any CPU.Build.0 = Release|Any CPU
- {F48A3E3A-EEBB-43AA-81AB-7DD7FE5F29B4}.Release|x64.ActiveCfg = Release|Any CPU
- {F48A3E3A-EEBB-43AA-81AB-7DD7FE5F29B4}.Release|x64.Build.0 = Release|Any CPU
- {F48A3E3A-EEBB-43AA-81AB-7DD7FE5F29B4}.Release|x86.ActiveCfg = Release|Any CPU
- {F48A3E3A-EEBB-43AA-81AB-7DD7FE5F29B4}.Release|x86.Build.0 = Release|Any CPU
+ {CD14770B-8BCD-4D93-AD2C-581ED030E209}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CD14770B-8BCD-4D93-AD2C-581ED030E209}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CD14770B-8BCD-4D93-AD2C-581ED030E209}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {CD14770B-8BCD-4D93-AD2C-581ED030E209}.Debug|x64.Build.0 = Debug|Any CPU
+ {CD14770B-8BCD-4D93-AD2C-581ED030E209}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {CD14770B-8BCD-4D93-AD2C-581ED030E209}.Debug|x86.Build.0 = Debug|Any CPU
+ {CD14770B-8BCD-4D93-AD2C-581ED030E209}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CD14770B-8BCD-4D93-AD2C-581ED030E209}.Release|Any CPU.Build.0 = Release|Any CPU
+ {CD14770B-8BCD-4D93-AD2C-581ED030E209}.Release|x64.ActiveCfg = Release|Any CPU
+ {CD14770B-8BCD-4D93-AD2C-581ED030E209}.Release|x64.Build.0 = Release|Any CPU
+ {CD14770B-8BCD-4D93-AD2C-581ED030E209}.Release|x86.ActiveCfg = Release|Any CPU
+ {CD14770B-8BCD-4D93-AD2C-581ED030E209}.Release|x86.Build.0 = Release|Any CPU
+ {B97352F3-709A-42CF-A448-07B9996F5FBE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B97352F3-709A-42CF-A448-07B9996F5FBE}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B97352F3-709A-42CF-A448-07B9996F5FBE}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {B97352F3-709A-42CF-A448-07B9996F5FBE}.Debug|x64.Build.0 = Debug|Any CPU
+ {B97352F3-709A-42CF-A448-07B9996F5FBE}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {B97352F3-709A-42CF-A448-07B9996F5FBE}.Debug|x86.Build.0 = Debug|Any CPU
+ {B97352F3-709A-42CF-A448-07B9996F5FBE}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B97352F3-709A-42CF-A448-07B9996F5FBE}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B97352F3-709A-42CF-A448-07B9996F5FBE}.Release|x64.ActiveCfg = Release|Any CPU
+ {B97352F3-709A-42CF-A448-07B9996F5FBE}.Release|x64.Build.0 = Release|Any CPU
+ {B97352F3-709A-42CF-A448-07B9996F5FBE}.Release|x86.ActiveCfg = Release|Any CPU
+ {B97352F3-709A-42CF-A448-07B9996F5FBE}.Release|x86.Build.0 = Release|Any CPU
+ {BC98E6FC-B46A-4554-B75E-B912714A915C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {BC98E6FC-B46A-4554-B75E-B912714A915C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {BC98E6FC-B46A-4554-B75E-B912714A915C}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {BC98E6FC-B46A-4554-B75E-B912714A915C}.Debug|x64.Build.0 = Debug|Any CPU
+ {BC98E6FC-B46A-4554-B75E-B912714A915C}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {BC98E6FC-B46A-4554-B75E-B912714A915C}.Debug|x86.Build.0 = Debug|Any CPU
+ {BC98E6FC-B46A-4554-B75E-B912714A915C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {BC98E6FC-B46A-4554-B75E-B912714A915C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {BC98E6FC-B46A-4554-B75E-B912714A915C}.Release|x64.ActiveCfg = Release|Any CPU
+ {BC98E6FC-B46A-4554-B75E-B912714A915C}.Release|x64.Build.0 = Release|Any CPU
+ {BC98E6FC-B46A-4554-B75E-B912714A915C}.Release|x86.ActiveCfg = Release|Any CPU
+ {BC98E6FC-B46A-4554-B75E-B912714A915C}.Release|x86.Build.0 = Release|Any CPU
+ {D052C05A-CDCD-48DF-8F4F-0B10E3719819}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D052C05A-CDCD-48DF-8F4F-0B10E3719819}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D052C05A-CDCD-48DF-8F4F-0B10E3719819}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {D052C05A-CDCD-48DF-8F4F-0B10E3719819}.Debug|x64.Build.0 = Debug|Any CPU
+ {D052C05A-CDCD-48DF-8F4F-0B10E3719819}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {D052C05A-CDCD-48DF-8F4F-0B10E3719819}.Debug|x86.Build.0 = Debug|Any CPU
+ {D052C05A-CDCD-48DF-8F4F-0B10E3719819}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D052C05A-CDCD-48DF-8F4F-0B10E3719819}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D052C05A-CDCD-48DF-8F4F-0B10E3719819}.Release|x64.ActiveCfg = Release|Any CPU
+ {D052C05A-CDCD-48DF-8F4F-0B10E3719819}.Release|x64.Build.0 = Release|Any CPU
+ {D052C05A-CDCD-48DF-8F4F-0B10E3719819}.Release|x86.ActiveCfg = Release|Any CPU
+ {D052C05A-CDCD-48DF-8F4F-0B10E3719819}.Release|x86.Build.0 = Release|Any CPU
+ {CF08AE9C-B337-47BA-B273-4B8E5610D7BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CF08AE9C-B337-47BA-B273-4B8E5610D7BD}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CF08AE9C-B337-47BA-B273-4B8E5610D7BD}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {CF08AE9C-B337-47BA-B273-4B8E5610D7BD}.Debug|x64.Build.0 = Debug|Any CPU
+ {CF08AE9C-B337-47BA-B273-4B8E5610D7BD}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {CF08AE9C-B337-47BA-B273-4B8E5610D7BD}.Debug|x86.Build.0 = Debug|Any CPU
+ {CF08AE9C-B337-47BA-B273-4B8E5610D7BD}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CF08AE9C-B337-47BA-B273-4B8E5610D7BD}.Release|Any CPU.Build.0 = Release|Any CPU
+ {CF08AE9C-B337-47BA-B273-4B8E5610D7BD}.Release|x64.ActiveCfg = Release|Any CPU
+ {CF08AE9C-B337-47BA-B273-4B8E5610D7BD}.Release|x64.Build.0 = Release|Any CPU
+ {CF08AE9C-B337-47BA-B273-4B8E5610D7BD}.Release|x86.ActiveCfg = Release|Any CPU
+ {CF08AE9C-B337-47BA-B273-4B8E5610D7BD}.Release|x86.Build.0 = Release|Any CPU
+ {BA37DF67-98D3-4399-BD68-9679396C1547}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {BA37DF67-98D3-4399-BD68-9679396C1547}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {BA37DF67-98D3-4399-BD68-9679396C1547}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {BA37DF67-98D3-4399-BD68-9679396C1547}.Debug|x64.Build.0 = Debug|Any CPU
+ {BA37DF67-98D3-4399-BD68-9679396C1547}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {BA37DF67-98D3-4399-BD68-9679396C1547}.Debug|x86.Build.0 = Debug|Any CPU
+ {BA37DF67-98D3-4399-BD68-9679396C1547}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {BA37DF67-98D3-4399-BD68-9679396C1547}.Release|Any CPU.Build.0 = Release|Any CPU
+ {BA37DF67-98D3-4399-BD68-9679396C1547}.Release|x64.ActiveCfg = Release|Any CPU
+ {BA37DF67-98D3-4399-BD68-9679396C1547}.Release|x64.Build.0 = Release|Any CPU
+ {BA37DF67-98D3-4399-BD68-9679396C1547}.Release|x86.ActiveCfg = Release|Any CPU
+ {BA37DF67-98D3-4399-BD68-9679396C1547}.Release|x86.Build.0 = Release|Any CPU
+ {C0B5496E-F083-413E-90AE-31282A0ACCA5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C0B5496E-F083-413E-90AE-31282A0ACCA5}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C0B5496E-F083-413E-90AE-31282A0ACCA5}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {C0B5496E-F083-413E-90AE-31282A0ACCA5}.Debug|x64.Build.0 = Debug|Any CPU
+ {C0B5496E-F083-413E-90AE-31282A0ACCA5}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {C0B5496E-F083-413E-90AE-31282A0ACCA5}.Debug|x86.Build.0 = Debug|Any CPU
+ {C0B5496E-F083-413E-90AE-31282A0ACCA5}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C0B5496E-F083-413E-90AE-31282A0ACCA5}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C0B5496E-F083-413E-90AE-31282A0ACCA5}.Release|x64.ActiveCfg = Release|Any CPU
+ {C0B5496E-F083-413E-90AE-31282A0ACCA5}.Release|x64.Build.0 = Release|Any CPU
+ {C0B5496E-F083-413E-90AE-31282A0ACCA5}.Release|x86.ActiveCfg = Release|Any CPU
+ {C0B5496E-F083-413E-90AE-31282A0ACCA5}.Release|x86.Build.0 = Release|Any CPU
+ {C905C120-DE44-4CBC-8F98-5BE148115487}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C905C120-DE44-4CBC-8F98-5BE148115487}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C905C120-DE44-4CBC-8F98-5BE148115487}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {C905C120-DE44-4CBC-8F98-5BE148115487}.Debug|x64.Build.0 = Debug|Any CPU
+ {C905C120-DE44-4CBC-8F98-5BE148115487}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {C905C120-DE44-4CBC-8F98-5BE148115487}.Debug|x86.Build.0 = Debug|Any CPU
+ {C905C120-DE44-4CBC-8F98-5BE148115487}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C905C120-DE44-4CBC-8F98-5BE148115487}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C905C120-DE44-4CBC-8F98-5BE148115487}.Release|x64.ActiveCfg = Release|Any CPU
+ {C905C120-DE44-4CBC-8F98-5BE148115487}.Release|x64.Build.0 = Release|Any CPU
+ {C905C120-DE44-4CBC-8F98-5BE148115487}.Release|x86.ActiveCfg = Release|Any CPU
+ {C905C120-DE44-4CBC-8F98-5BE148115487}.Release|x86.Build.0 = Release|Any CPU
+ {F3EFD119-44F7-46BF-A03C-060F30A1A1E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F3EFD119-44F7-46BF-A03C-060F30A1A1E0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F3EFD119-44F7-46BF-A03C-060F30A1A1E0}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {F3EFD119-44F7-46BF-A03C-060F30A1A1E0}.Debug|x64.Build.0 = Debug|Any CPU
+ {F3EFD119-44F7-46BF-A03C-060F30A1A1E0}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {F3EFD119-44F7-46BF-A03C-060F30A1A1E0}.Debug|x86.Build.0 = Debug|Any CPU
+ {F3EFD119-44F7-46BF-A03C-060F30A1A1E0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F3EFD119-44F7-46BF-A03C-060F30A1A1E0}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F3EFD119-44F7-46BF-A03C-060F30A1A1E0}.Release|x64.ActiveCfg = Release|Any CPU
+ {F3EFD119-44F7-46BF-A03C-060F30A1A1E0}.Release|x64.Build.0 = Release|Any CPU
+ {F3EFD119-44F7-46BF-A03C-060F30A1A1E0}.Release|x86.ActiveCfg = Release|Any CPU
+ {F3EFD119-44F7-46BF-A03C-060F30A1A1E0}.Release|x86.Build.0 = Release|Any CPU
+ {489D5878-3945-4F8E-AD13-E5499A18DFCC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {489D5878-3945-4F8E-AD13-E5499A18DFCC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {489D5878-3945-4F8E-AD13-E5499A18DFCC}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {489D5878-3945-4F8E-AD13-E5499A18DFCC}.Debug|x64.Build.0 = Debug|Any CPU
+ {489D5878-3945-4F8E-AD13-E5499A18DFCC}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {489D5878-3945-4F8E-AD13-E5499A18DFCC}.Debug|x86.Build.0 = Debug|Any CPU
+ {489D5878-3945-4F8E-AD13-E5499A18DFCC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {489D5878-3945-4F8E-AD13-E5499A18DFCC}.Release|Any CPU.Build.0 = Release|Any CPU
+ {489D5878-3945-4F8E-AD13-E5499A18DFCC}.Release|x64.ActiveCfg = Release|Any CPU
+ {489D5878-3945-4F8E-AD13-E5499A18DFCC}.Release|x64.Build.0 = Release|Any CPU
+ {489D5878-3945-4F8E-AD13-E5499A18DFCC}.Release|x86.ActiveCfg = Release|Any CPU
+ {489D5878-3945-4F8E-AD13-E5499A18DFCC}.Release|x86.Build.0 = Release|Any CPU
+ {FFC12440-F128-48F5-9BE5-E5263930F876}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {FFC12440-F128-48F5-9BE5-E5263930F876}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {FFC12440-F128-48F5-9BE5-E5263930F876}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {FFC12440-F128-48F5-9BE5-E5263930F876}.Debug|x64.Build.0 = Debug|Any CPU
+ {FFC12440-F128-48F5-9BE5-E5263930F876}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {FFC12440-F128-48F5-9BE5-E5263930F876}.Debug|x86.Build.0 = Debug|Any CPU
+ {FFC12440-F128-48F5-9BE5-E5263930F876}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {FFC12440-F128-48F5-9BE5-E5263930F876}.Release|Any CPU.Build.0 = Release|Any CPU
+ {FFC12440-F128-48F5-9BE5-E5263930F876}.Release|x64.ActiveCfg = Release|Any CPU
+ {FFC12440-F128-48F5-9BE5-E5263930F876}.Release|x64.Build.0 = Release|Any CPU
+ {FFC12440-F128-48F5-9BE5-E5263930F876}.Release|x86.ActiveCfg = Release|Any CPU
+ {FFC12440-F128-48F5-9BE5-E5263930F876}.Release|x86.Build.0 = Release|Any CPU
+ {7A402C99-2E66-4EBE-A257-0D7923A96DFD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7A402C99-2E66-4EBE-A257-0D7923A96DFD}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7A402C99-2E66-4EBE-A257-0D7923A96DFD}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {7A402C99-2E66-4EBE-A257-0D7923A96DFD}.Debug|x64.Build.0 = Debug|Any CPU
+ {7A402C99-2E66-4EBE-A257-0D7923A96DFD}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {7A402C99-2E66-4EBE-A257-0D7923A96DFD}.Debug|x86.Build.0 = Debug|Any CPU
+ {7A402C99-2E66-4EBE-A257-0D7923A96DFD}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7A402C99-2E66-4EBE-A257-0D7923A96DFD}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7A402C99-2E66-4EBE-A257-0D7923A96DFD}.Release|x64.ActiveCfg = Release|Any CPU
+ {7A402C99-2E66-4EBE-A257-0D7923A96DFD}.Release|x64.Build.0 = Release|Any CPU
+ {7A402C99-2E66-4EBE-A257-0D7923A96DFD}.Release|x86.ActiveCfg = Release|Any CPU
+ {7A402C99-2E66-4EBE-A257-0D7923A96DFD}.Release|x86.Build.0 = Release|Any CPU
+ {716E089D-0661-4D39-813B-063DF9A4C0E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {716E089D-0661-4D39-813B-063DF9A4C0E4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {716E089D-0661-4D39-813B-063DF9A4C0E4}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {716E089D-0661-4D39-813B-063DF9A4C0E4}.Debug|x64.Build.0 = Debug|Any CPU
+ {716E089D-0661-4D39-813B-063DF9A4C0E4}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {716E089D-0661-4D39-813B-063DF9A4C0E4}.Debug|x86.Build.0 = Debug|Any CPU
+ {716E089D-0661-4D39-813B-063DF9A4C0E4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {716E089D-0661-4D39-813B-063DF9A4C0E4}.Release|Any CPU.Build.0 = Release|Any CPU
+ {716E089D-0661-4D39-813B-063DF9A4C0E4}.Release|x64.ActiveCfg = Release|Any CPU
+ {716E089D-0661-4D39-813B-063DF9A4C0E4}.Release|x64.Build.0 = Release|Any CPU
+ {716E089D-0661-4D39-813B-063DF9A4C0E4}.Release|x86.ActiveCfg = Release|Any CPU
+ {716E089D-0661-4D39-813B-063DF9A4C0E4}.Release|x86.Build.0 = Release|Any CPU
+ {43E6E19C-39EC-49C8-91EE-49586A4B9D03}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {43E6E19C-39EC-49C8-91EE-49586A4B9D03}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {43E6E19C-39EC-49C8-91EE-49586A4B9D03}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {43E6E19C-39EC-49C8-91EE-49586A4B9D03}.Debug|x64.Build.0 = Debug|Any CPU
+ {43E6E19C-39EC-49C8-91EE-49586A4B9D03}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {43E6E19C-39EC-49C8-91EE-49586A4B9D03}.Debug|x86.Build.0 = Debug|Any CPU
+ {43E6E19C-39EC-49C8-91EE-49586A4B9D03}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {43E6E19C-39EC-49C8-91EE-49586A4B9D03}.Release|Any CPU.Build.0 = Release|Any CPU
+ {43E6E19C-39EC-49C8-91EE-49586A4B9D03}.Release|x64.ActiveCfg = Release|Any CPU
+ {43E6E19C-39EC-49C8-91EE-49586A4B9D03}.Release|x64.Build.0 = Release|Any CPU
+ {43E6E19C-39EC-49C8-91EE-49586A4B9D03}.Release|x86.ActiveCfg = Release|Any CPU
+ {43E6E19C-39EC-49C8-91EE-49586A4B9D03}.Release|x86.Build.0 = Release|Any CPU
+ {2498C58C-53B0-4DF2-B4EA-BA69B35A7488}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2498C58C-53B0-4DF2-B4EA-BA69B35A7488}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2498C58C-53B0-4DF2-B4EA-BA69B35A7488}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {2498C58C-53B0-4DF2-B4EA-BA69B35A7488}.Debug|x64.Build.0 = Debug|Any CPU
+ {2498C58C-53B0-4DF2-B4EA-BA69B35A7488}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {2498C58C-53B0-4DF2-B4EA-BA69B35A7488}.Debug|x86.Build.0 = Debug|Any CPU
+ {2498C58C-53B0-4DF2-B4EA-BA69B35A7488}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2498C58C-53B0-4DF2-B4EA-BA69B35A7488}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2498C58C-53B0-4DF2-B4EA-BA69B35A7488}.Release|x64.ActiveCfg = Release|Any CPU
+ {2498C58C-53B0-4DF2-B4EA-BA69B35A7488}.Release|x64.Build.0 = Release|Any CPU
+ {2498C58C-53B0-4DF2-B4EA-BA69B35A7488}.Release|x86.ActiveCfg = Release|Any CPU
+ {2498C58C-53B0-4DF2-B4EA-BA69B35A7488}.Release|x86.Build.0 = Release|Any CPU
+ {4394178B-1ADD-478E-BF73-574B31C11292}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4394178B-1ADD-478E-BF73-574B31C11292}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4394178B-1ADD-478E-BF73-574B31C11292}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {4394178B-1ADD-478E-BF73-574B31C11292}.Debug|x64.Build.0 = Debug|Any CPU
+ {4394178B-1ADD-478E-BF73-574B31C11292}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {4394178B-1ADD-478E-BF73-574B31C11292}.Debug|x86.Build.0 = Debug|Any CPU
+ {4394178B-1ADD-478E-BF73-574B31C11292}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4394178B-1ADD-478E-BF73-574B31C11292}.Release|Any CPU.Build.0 = Release|Any CPU
+ {4394178B-1ADD-478E-BF73-574B31C11292}.Release|x64.ActiveCfg = Release|Any CPU
+ {4394178B-1ADD-478E-BF73-574B31C11292}.Release|x64.Build.0 = Release|Any CPU
+ {4394178B-1ADD-478E-BF73-574B31C11292}.Release|x86.ActiveCfg = Release|Any CPU
+ {4394178B-1ADD-478E-BF73-574B31C11292}.Release|x86.Build.0 = Release|Any CPU
+ {2F2C7E01-5791-4A6D-9A80-C71CAD846FA8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2F2C7E01-5791-4A6D-9A80-C71CAD846FA8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2F2C7E01-5791-4A6D-9A80-C71CAD846FA8}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {2F2C7E01-5791-4A6D-9A80-C71CAD846FA8}.Debug|x64.Build.0 = Debug|Any CPU
+ {2F2C7E01-5791-4A6D-9A80-C71CAD846FA8}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {2F2C7E01-5791-4A6D-9A80-C71CAD846FA8}.Debug|x86.Build.0 = Debug|Any CPU
+ {2F2C7E01-5791-4A6D-9A80-C71CAD846FA8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2F2C7E01-5791-4A6D-9A80-C71CAD846FA8}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2F2C7E01-5791-4A6D-9A80-C71CAD846FA8}.Release|x64.ActiveCfg = Release|Any CPU
+ {2F2C7E01-5791-4A6D-9A80-C71CAD846FA8}.Release|x64.Build.0 = Release|Any CPU
+ {2F2C7E01-5791-4A6D-9A80-C71CAD846FA8}.Release|x86.ActiveCfg = Release|Any CPU
+ {2F2C7E01-5791-4A6D-9A80-C71CAD846FA8}.Release|x86.Build.0 = Release|Any CPU
+ {76706BA9-11E0-463A-AF8E-18DFBD9E95A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {76706BA9-11E0-463A-AF8E-18DFBD9E95A2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {76706BA9-11E0-463A-AF8E-18DFBD9E95A2}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {76706BA9-11E0-463A-AF8E-18DFBD9E95A2}.Debug|x64.Build.0 = Debug|Any CPU
+ {76706BA9-11E0-463A-AF8E-18DFBD9E95A2}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {76706BA9-11E0-463A-AF8E-18DFBD9E95A2}.Debug|x86.Build.0 = Debug|Any CPU
+ {76706BA9-11E0-463A-AF8E-18DFBD9E95A2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {76706BA9-11E0-463A-AF8E-18DFBD9E95A2}.Release|Any CPU.Build.0 = Release|Any CPU
+ {76706BA9-11E0-463A-AF8E-18DFBD9E95A2}.Release|x64.ActiveCfg = Release|Any CPU
+ {76706BA9-11E0-463A-AF8E-18DFBD9E95A2}.Release|x64.Build.0 = Release|Any CPU
+ {76706BA9-11E0-463A-AF8E-18DFBD9E95A2}.Release|x86.ActiveCfg = Release|Any CPU
+ {76706BA9-11E0-463A-AF8E-18DFBD9E95A2}.Release|x86.Build.0 = Release|Any CPU
+ {AEFD93B4-59B8-4B02-A694-CA15E4EC1C08}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {AEFD93B4-59B8-4B02-A694-CA15E4EC1C08}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {AEFD93B4-59B8-4B02-A694-CA15E4EC1C08}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {AEFD93B4-59B8-4B02-A694-CA15E4EC1C08}.Debug|x64.Build.0 = Debug|Any CPU
+ {AEFD93B4-59B8-4B02-A694-CA15E4EC1C08}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {AEFD93B4-59B8-4B02-A694-CA15E4EC1C08}.Debug|x86.Build.0 = Debug|Any CPU
+ {AEFD93B4-59B8-4B02-A694-CA15E4EC1C08}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {AEFD93B4-59B8-4B02-A694-CA15E4EC1C08}.Release|Any CPU.Build.0 = Release|Any CPU
+ {AEFD93B4-59B8-4B02-A694-CA15E4EC1C08}.Release|x64.ActiveCfg = Release|Any CPU
+ {AEFD93B4-59B8-4B02-A694-CA15E4EC1C08}.Release|x64.Build.0 = Release|Any CPU
+ {AEFD93B4-59B8-4B02-A694-CA15E4EC1C08}.Release|x86.ActiveCfg = Release|Any CPU
+ {AEFD93B4-59B8-4B02-A694-CA15E4EC1C08}.Release|x86.Build.0 = Release|Any CPU
+ {C11BFE93-017D-4538-A61F-A8D98F8D2D75}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C11BFE93-017D-4538-A61F-A8D98F8D2D75}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C11BFE93-017D-4538-A61F-A8D98F8D2D75}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {C11BFE93-017D-4538-A61F-A8D98F8D2D75}.Debug|x64.Build.0 = Debug|Any CPU
+ {C11BFE93-017D-4538-A61F-A8D98F8D2D75}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {C11BFE93-017D-4538-A61F-A8D98F8D2D75}.Debug|x86.Build.0 = Debug|Any CPU
+ {C11BFE93-017D-4538-A61F-A8D98F8D2D75}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C11BFE93-017D-4538-A61F-A8D98F8D2D75}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C11BFE93-017D-4538-A61F-A8D98F8D2D75}.Release|x64.ActiveCfg = Release|Any CPU
+ {C11BFE93-017D-4538-A61F-A8D98F8D2D75}.Release|x64.Build.0 = Release|Any CPU
+ {C11BFE93-017D-4538-A61F-A8D98F8D2D75}.Release|x86.ActiveCfg = Release|Any CPU
+ {C11BFE93-017D-4538-A61F-A8D98F8D2D75}.Release|x86.Build.0 = Release|Any CPU
+ {38276FFF-05D8-4005-8863-018CEE833D66}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {38276FFF-05D8-4005-8863-018CEE833D66}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {38276FFF-05D8-4005-8863-018CEE833D66}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {38276FFF-05D8-4005-8863-018CEE833D66}.Debug|x64.Build.0 = Debug|Any CPU
+ {38276FFF-05D8-4005-8863-018CEE833D66}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {38276FFF-05D8-4005-8863-018CEE833D66}.Debug|x86.Build.0 = Debug|Any CPU
+ {38276FFF-05D8-4005-8863-018CEE833D66}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {38276FFF-05D8-4005-8863-018CEE833D66}.Release|Any CPU.Build.0 = Release|Any CPU
+ {38276FFF-05D8-4005-8863-018CEE833D66}.Release|x64.ActiveCfg = Release|Any CPU
+ {38276FFF-05D8-4005-8863-018CEE833D66}.Release|x64.Build.0 = Release|Any CPU
+ {38276FFF-05D8-4005-8863-018CEE833D66}.Release|x86.ActiveCfg = Release|Any CPU
+ {38276FFF-05D8-4005-8863-018CEE833D66}.Release|x86.Build.0 = Release|Any CPU
+ {32AC7906-B197-4628-9C47-9337BB5A53E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {32AC7906-B197-4628-9C47-9337BB5A53E2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {32AC7906-B197-4628-9C47-9337BB5A53E2}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {32AC7906-B197-4628-9C47-9337BB5A53E2}.Debug|x64.Build.0 = Debug|Any CPU
+ {32AC7906-B197-4628-9C47-9337BB5A53E2}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {32AC7906-B197-4628-9C47-9337BB5A53E2}.Debug|x86.Build.0 = Debug|Any CPU
+ {32AC7906-B197-4628-9C47-9337BB5A53E2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {32AC7906-B197-4628-9C47-9337BB5A53E2}.Release|Any CPU.Build.0 = Release|Any CPU
+ {32AC7906-B197-4628-9C47-9337BB5A53E2}.Release|x64.ActiveCfg = Release|Any CPU
+ {32AC7906-B197-4628-9C47-9337BB5A53E2}.Release|x64.Build.0 = Release|Any CPU
+ {32AC7906-B197-4628-9C47-9337BB5A53E2}.Release|x86.ActiveCfg = Release|Any CPU
+ {32AC7906-B197-4628-9C47-9337BB5A53E2}.Release|x86.Build.0 = Release|Any CPU
+ {03BE4EB1-FD38-445A-9A4B-7B481560E9A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {03BE4EB1-FD38-445A-9A4B-7B481560E9A2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {03BE4EB1-FD38-445A-9A4B-7B481560E9A2}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {03BE4EB1-FD38-445A-9A4B-7B481560E9A2}.Debug|x64.Build.0 = Debug|Any CPU
+ {03BE4EB1-FD38-445A-9A4B-7B481560E9A2}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {03BE4EB1-FD38-445A-9A4B-7B481560E9A2}.Debug|x86.Build.0 = Debug|Any CPU
+ {03BE4EB1-FD38-445A-9A4B-7B481560E9A2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {03BE4EB1-FD38-445A-9A4B-7B481560E9A2}.Release|Any CPU.Build.0 = Release|Any CPU
+ {03BE4EB1-FD38-445A-9A4B-7B481560E9A2}.Release|x64.ActiveCfg = Release|Any CPU
+ {03BE4EB1-FD38-445A-9A4B-7B481560E9A2}.Release|x64.Build.0 = Release|Any CPU
+ {03BE4EB1-FD38-445A-9A4B-7B481560E9A2}.Release|x86.ActiveCfg = Release|Any CPU
+ {03BE4EB1-FD38-445A-9A4B-7B481560E9A2}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -118,11 +406,41 @@ Global
{C829F859-FF48-4025-BF22-8E63672D113C} = {C8E42992-5E42-0C2B-DBFE-AA848D06431C}
{6C13E9A4-234C-4BF1-A145-F1BBA2AC56CB} = {C8E42992-5E42-0C2B-DBFE-AA848D06431C}
{EC447DCF-ABFA-6E24-52A5-D7FD48A5C558} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
- {0EC62A1A-9858-3A60-8A0C-FC9AACFA2EC7} = {EC447DCF-ABFA-6E24-52A5-D7FD48A5C558}
- {5BB2F421-C4C4-40A1-8D6E-FEBB79AE7C98} = {0EC62A1A-9858-3A60-8A0C-FC9AACFA2EC7}
- {B4154CE3-9153-E2B0-0515-138FFCE32938} = {EC447DCF-ABFA-6E24-52A5-D7FD48A5C558}
- {695CC919-E321-4DC0-8D70-C631C51624E1} = {B4154CE3-9153-E2B0-0515-138FFCE32938}
- {AB2F334E-DD7C-4314-93B9-941AFC6B2ACD} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
- {F48A3E3A-EEBB-43AA-81AB-7DD7FE5F29B4} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
+ {CA5186C2-289A-9324-4152-9805A73A9773} = {EC447DCF-ABFA-6E24-52A5-D7FD48A5C558}
+ {CD14770B-8BCD-4D93-AD2C-581ED030E209} = {CA5186C2-289A-9324-4152-9805A73A9773}
+ {10980DF6-D81A-2FEE-4ACB-4D508FA38BC6} = {EC447DCF-ABFA-6E24-52A5-D7FD48A5C558}
+ {B97352F3-709A-42CF-A448-07B9996F5FBE} = {10980DF6-D81A-2FEE-4ACB-4D508FA38BC6}
+ {F83653D1-5494-68B8-6444-7DEF58635BF6} = {EC447DCF-ABFA-6E24-52A5-D7FD48A5C558}
+ {BC98E6FC-B46A-4554-B75E-B912714A915C} = {F83653D1-5494-68B8-6444-7DEF58635BF6}
+ {AF61C489-0B09-54BC-BD16-4353135E9C4A} = {EC447DCF-ABFA-6E24-52A5-D7FD48A5C558}
+ {D052C05A-CDCD-48DF-8F4F-0B10E3719819} = {AF61C489-0B09-54BC-BD16-4353135E9C4A}
+ {E9D55F35-C373-3DD8-6914-3213F2F582A2} = {EC447DCF-ABFA-6E24-52A5-D7FD48A5C558}
+ {CF08AE9C-B337-47BA-B273-4B8E5610D7BD} = {E9D55F35-C373-3DD8-6914-3213F2F582A2}
+ {BA37DF67-98D3-4399-BD68-9679396C1547} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
+ {AE120A0E-5815-2D46-946C-DA544028AD88} = {EC447DCF-ABFA-6E24-52A5-D7FD48A5C558}
+ {C0B5496E-F083-413E-90AE-31282A0ACCA5} = {AE120A0E-5815-2D46-946C-DA544028AD88}
+ {4D5D7319-366D-1BD8-49C5-A840B487E852} = {EC447DCF-ABFA-6E24-52A5-D7FD48A5C558}
+ {C905C120-DE44-4CBC-8F98-5BE148115487} = {4D5D7319-366D-1BD8-49C5-A840B487E852}
+ {138B168B-2473-B06D-4BD5-1B010DA319A1} = {EC447DCF-ABFA-6E24-52A5-D7FD48A5C558}
+ {F3EFD119-44F7-46BF-A03C-060F30A1A1E0} = {138B168B-2473-B06D-4BD5-1B010DA319A1}
+ {489D5878-3945-4F8E-AD13-E5499A18DFCC} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
+ {E9E1155A-A339-766C-C185-6698FF423EBC} = {EC447DCF-ABFA-6E24-52A5-D7FD48A5C558}
+ {FFC12440-F128-48F5-9BE5-E5263930F876} = {E9E1155A-A339-766C-C185-6698FF423EBC}
+ {7A402C99-2E66-4EBE-A257-0D7923A96DFD} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
+ {837D2CF4-7AEA-BF76-6362-47ECD7058EEC} = {EC447DCF-ABFA-6E24-52A5-D7FD48A5C558}
+ {716E089D-0661-4D39-813B-063DF9A4C0E4} = {837D2CF4-7AEA-BF76-6362-47ECD7058EEC}
+ {43E6E19C-39EC-49C8-91EE-49586A4B9D03} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
+ {4BC86D3F-8D7B-ACB9-EA51-9374BC9D421A} = {EC447DCF-ABFA-6E24-52A5-D7FD48A5C558}
+ {2498C58C-53B0-4DF2-B4EA-BA69B35A7488} = {4BC86D3F-8D7B-ACB9-EA51-9374BC9D421A}
+ {4394178B-1ADD-478E-BF73-574B31C11292} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
+ {2F2C7E01-5791-4A6D-9A80-C71CAD846FA8} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
+ {76706BA9-11E0-463A-AF8E-18DFBD9E95A2} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
+ {0587C5BB-3015-204D-19B6-EB01E03E48B6} = {EC447DCF-ABFA-6E24-52A5-D7FD48A5C558}
+ {AEFD93B4-59B8-4B02-A694-CA15E4EC1C08} = {0587C5BB-3015-204D-19B6-EB01E03E48B6}
+ {C11BFE93-017D-4538-A61F-A8D98F8D2D75} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
+ {38276FFF-05D8-4005-8863-018CEE833D66} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
+ {32AC7906-B197-4628-9C47-9337BB5A53E2} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
+ {DD6FAF9E-10A0-95E7-0C06-698A6F70AFEE} = {EC447DCF-ABFA-6E24-52A5-D7FD48A5C558}
+ {03BE4EB1-FD38-445A-9A4B-7B481560E9A2} = {DD6FAF9E-10A0-95E7-0C06-698A6F70AFEE}
EndGlobalSection
EndGlobal
diff --git a/backend/Tests/Knot.Modules.Admin.UnitTests/Knot.Modules.Admin.UnitTests.csproj b/backend/Tests/Knot.Modules.Admin.UnitTests/Knot.Modules.Admin.UnitTests.csproj
new file mode 100644
index 0000000..d15a597
--- /dev/null
+++ b/backend/Tests/Knot.Modules.Admin.UnitTests/Knot.Modules.Admin.UnitTests.csproj
@@ -0,0 +1,28 @@
+
+
+
+ net10.0
+ enable
+ enable
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/backend/Tests/Knot.Modules.Admin.UnitTests/ResetUserPasswordCommandHandlerTests.cs b/backend/Tests/Knot.Modules.Admin.UnitTests/ResetUserPasswordCommandHandlerTests.cs
new file mode 100644
index 0000000..c98b46e
--- /dev/null
+++ b/backend/Tests/Knot.Modules.Admin.UnitTests/ResetUserPasswordCommandHandlerTests.cs
@@ -0,0 +1,58 @@
+using FluentAssertions;
+using Host.Application.Admin.Commands;
+using Knot.Modules.Auth.Application.Abstractions;
+using Knot.Modules.Auth.Domain;
+using Knot.Shared.Kernel;
+using NSubstitute;
+using Xunit;
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Knot.Modules.Admin.UnitTests;
+
+public class ResetUserPasswordCommandHandlerTests
+{
+ private readonly IAuthUnitOfWork _authUnitOfWork;
+ private readonly IUserRepository _userRepository;
+ private readonly ResetUserPasswordCommandHandler _handler;
+
+ public ResetUserPasswordCommandHandlerTests()
+ {
+ _authUnitOfWork = Substitute.For();
+ _userRepository = Substitute.For();
+ _handler = new ResetUserPasswordCommandHandler(_userRepository, _authUnitOfWork);
+ }
+
+ [Fact]
+ public async Task Handle_ShouldReturnError_WhenUserNotFound()
+ {
+ // Arrange
+ var command = new ResetUserPasswordCommand(Guid.NewGuid(), "NewP@ssw0rd");
+ _userRepository.GetByIdAsync(command.UserId, Arg.Any()).Returns((User?)null);
+
+ // Act
+ var result = await _handler.Handle(command, CancellationToken.None);
+
+ // Assert
+ result.IsFailure.Should().BeTrue();
+ result.Error.Should().Be(AuthErrors.UserNotFound);
+ }
+
+ [Fact]
+ public async Task Handle_ShouldSucceed_WhenUserFound()
+ {
+ // Arrange
+ var user = User.Create("User", "pass", "salt", "admin");
+ var command = new ResetUserPasswordCommand(user.Id, "NewP@ssw0rd");
+
+ _userRepository.GetByIdAsync(command.UserId, Arg.Any()).Returns(user);
+
+ // Act
+ var result = await _handler.Handle(command, CancellationToken.None);
+
+ // Assert
+ result.IsSuccess.Should().BeTrue();
+ await _authUnitOfWork.Received(1).SaveChangesAsync(Arg.Any());
+ }
+}
diff --git a/backend/tests/Knot.Modules.Identity.UnitTests/Knot.Modules.Identity.UnitTests.csproj b/backend/Tests/Knot.Modules.Auth.UnitTests/Knot.Modules.Auth.UnitTests.csproj
similarity index 91%
rename from backend/tests/Knot.Modules.Identity.UnitTests/Knot.Modules.Identity.UnitTests.csproj
rename to backend/Tests/Knot.Modules.Auth.UnitTests/Knot.Modules.Auth.UnitTests.csproj
index b6d5144..aea9f1e 100644
--- a/backend/tests/Knot.Modules.Identity.UnitTests/Knot.Modules.Identity.UnitTests.csproj
+++ b/backend/Tests/Knot.Modules.Auth.UnitTests/Knot.Modules.Auth.UnitTests.csproj
@@ -23,7 +23,9 @@
-
+
+
+
diff --git a/backend/tests/Knot.Modules.Identity.UnitTests/LoginUserCommandHandlerTests.cs b/backend/Tests/Knot.Modules.Auth.UnitTests/LoginUserCommandHandlerTests.cs
similarity index 86%
rename from backend/tests/Knot.Modules.Identity.UnitTests/LoginUserCommandHandlerTests.cs
rename to backend/Tests/Knot.Modules.Auth.UnitTests/LoginUserCommandHandlerTests.cs
index e634e8c..518e53d 100644
--- a/backend/tests/Knot.Modules.Identity.UnitTests/LoginUserCommandHandlerTests.cs
+++ b/backend/Tests/Knot.Modules.Auth.UnitTests/LoginUserCommandHandlerTests.cs
@@ -1,15 +1,15 @@
using FluentAssertions;
using NSubstitute;
-using Knot.Modules.Identity.Application.Abstractions;
-using Knot.Modules.Identity.Application.Users.Login;
-using Knot.Modules.Identity.Domain;
+using Knot.Modules.Auth.Application.Abstractions;
+using Knot.Modules.Auth.Application.Users.Login;
+using Knot.Modules.Auth.Domain;
using Knot.Shared.Kernel;
-using Knot.Modules.Identity.Application.Users.Auth;
+using Knot.Modules.Auth.Application.Users.Auth;
using Xunit;
using System.Threading;
using System.Threading.Tasks;
-namespace Knot.Modules.Identity.UnitTests;
+namespace Knot.Modules.Auth.UnitTests;
public class LoginUserCommandHandlerTests
{
@@ -61,7 +61,7 @@ public class LoginUserCommandHandlerTests
// Assert
result.IsFailure.Should().BeTrue();
- result.Error.Code.Should().Be(IdentityErrors.IdentityInvalidCredentials.Code);
+ result.Error.Code.Should().Be(AuthErrors.IdentityInvalidCredentials.Code);
}
[Fact]
@@ -82,6 +82,7 @@ public class LoginUserCommandHandlerTests
// Assert
result.IsFailure.Should().BeTrue();
- result.Error.Code.Should().Be(IdentityErrors.IdentityInvalidCredentials.Code);
+ result.Error.Code.Should().Be(AuthErrors.IdentityInvalidCredentials.Code);
}
}
+
diff --git a/backend/tests/Knot.Modules.Chats.UnitTests/Chats/CreateChatCommandHandlerTests.cs b/backend/Tests/Knot.Modules.Conversations.UnitTests/Chats/CreateChatCommandHandlerTests.cs
similarity index 85%
rename from backend/tests/Knot.Modules.Chats.UnitTests/Chats/CreateChatCommandHandlerTests.cs
rename to backend/Tests/Knot.Modules.Conversations.UnitTests/Chats/CreateChatCommandHandlerTests.cs
index 0c56093..4de4ea6 100644
--- a/backend/tests/Knot.Modules.Chats.UnitTests/Chats/CreateChatCommandHandlerTests.cs
+++ b/backend/Tests/Knot.Modules.Conversations.UnitTests/Chats/CreateChatCommandHandlerTests.cs
@@ -7,11 +7,12 @@ using FluentAssertions;
using NSubstitute;
using Xunit;
using Knot.Shared.Kernel;
-using Knot.Modules.Chats.Domain;
-using Knot.Modules.Chats.Application.Abstractions;
-using Knot.Modules.Chats.Application.Chats.Create;
+using Knot.Modules.Conversations.Domain;
+using Knot.Modules.Messaging.Domain;
+using Knot.Modules.Conversations.Application.Abstractions;
+using Knot.Modules.Conversations.Application.Chats.Create;
-namespace Knot.Modules.Chats.UnitTests.Chats;
+namespace Knot.Modules.Conversations.UnitTests.Chats;
public class CreateChatCommandHandlerTests
{
@@ -49,3 +50,5 @@ public class CreateChatCommandHandlerTests
await _unitOfWork.Received(1).SaveChangesAsync(Arg.Any());
}
}
+
+
diff --git a/backend/tests/Knot.Modules.Chats.UnitTests/Chats/GetChatsQueryHandlerTests.cs b/backend/Tests/Knot.Modules.Conversations.UnitTests/Chats/GetChatsQueryHandlerTests.cs
similarity index 89%
rename from backend/tests/Knot.Modules.Chats.UnitTests/Chats/GetChatsQueryHandlerTests.cs
rename to backend/Tests/Knot.Modules.Conversations.UnitTests/Chats/GetChatsQueryHandlerTests.cs
index aafa2db..a009178 100644
--- a/backend/tests/Knot.Modules.Chats.UnitTests/Chats/GetChatsQueryHandlerTests.cs
+++ b/backend/Tests/Knot.Modules.Conversations.UnitTests/Chats/GetChatsQueryHandlerTests.cs
@@ -7,12 +7,13 @@ using FluentAssertions;
using NSubstitute;
using Xunit;
using Knot.Shared.Kernel;
-using Knot.Modules.Chats.Domain;
-using Knot.Modules.Chats.Application.Abstractions;
-using Knot.Modules.Chats.Application.Chats.GetChats;
-using Knot.Modules.Chats.Application.DTOs;
+using Knot.Modules.Conversations.Domain;
+using Knot.Modules.Messaging.Domain;
+using Knot.Modules.Conversations.Application.Abstractions;
+using Knot.Modules.Conversations.Application.Chats.GetChats;
+using Knot.Modules.Conversations.Application.DTOs;
-namespace Knot.Modules.Chats.UnitTests.Chats;
+namespace Knot.Modules.Conversations.UnitTests.Chats;
public class GetChatsQueryHandlerTests
{
@@ -69,3 +70,5 @@ public class GetChatsQueryHandlerTests
result.Value.Any(c => c.Type == "favorites").Should().BeTrue();
}
}
+
+
diff --git a/backend/Tests/Knot.Modules.Conversations.UnitTests/CreatePersonalChatCommandHandlerTests.cs b/backend/Tests/Knot.Modules.Conversations.UnitTests/CreatePersonalChatCommandHandlerTests.cs
new file mode 100644
index 0000000..a54275d
--- /dev/null
+++ b/backend/Tests/Knot.Modules.Conversations.UnitTests/CreatePersonalChatCommandHandlerTests.cs
@@ -0,0 +1,42 @@
+using FluentAssertions;
+using Knot.Modules.Conversations.Application.Chats.Commands.CreatePersonalChat;
+using Knot.Modules.Conversations.Domain;
+using Knot.Modules.Conversations.Application.Abstractions;
+using Knot.Shared.Kernel;
+using NSubstitute;
+using Xunit;
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Knot.Modules.Conversations.UnitTests;
+
+public class CreatePersonalChatCommandHandlerTests
+{
+ private readonly IChatRepository _chatRepository;
+ private readonly IChatsUnitOfWork _unitOfWork;
+ private readonly CreatePersonalChatCommandHandler _handler;
+
+ public CreatePersonalChatCommandHandlerTests()
+ {
+ _chatRepository = Substitute.For();
+ _unitOfWork = Substitute.For();
+ _handler = new CreatePersonalChatCommandHandler(_chatRepository, _unitOfWork);
+ }
+
+ [Fact]
+ public async Task Handle_ShouldReturnError_WhenChatAlreadyExists()
+ {
+ // Arrange
+ var command = new CreatePersonalChatCommand(Guid.NewGuid(), Guid.NewGuid());
+ _chatRepository.GetPersonalChatAsync(command.InitiatorId, command.TargetUserId, Arg.Any())
+ .Returns(PersonalChat.Create(command.InitiatorId, command.TargetUserId));
+
+ // Act
+ var result = await _handler.Handle(command, CancellationToken.None);
+
+ // Assert
+ result.IsFailure.Should().BeTrue();
+ result.Error.Should().Be(ChatErrors.PersonalChatAlreadyExists);
+ }
+}
diff --git a/backend/tests/Knot.Modules.Chats.UnitTests/GetOrCreateFavoritesCommandHandlerTests.cs b/backend/Tests/Knot.Modules.Conversations.UnitTests/GetOrCreateFavoritesCommandHandlerTests.cs
similarity index 89%
rename from backend/tests/Knot.Modules.Chats.UnitTests/GetOrCreateFavoritesCommandHandlerTests.cs
rename to backend/Tests/Knot.Modules.Conversations.UnitTests/GetOrCreateFavoritesCommandHandlerTests.cs
index 9b007da..0368f51 100644
--- a/backend/tests/Knot.Modules.Chats.UnitTests/GetOrCreateFavoritesCommandHandlerTests.cs
+++ b/backend/Tests/Knot.Modules.Conversations.UnitTests/GetOrCreateFavoritesCommandHandlerTests.cs
@@ -1,12 +1,13 @@
using FluentAssertions;
using NSubstitute;
-using Knot.Modules.Chats.Application.Abstractions;
-using Knot.Modules.Chats.Application.Chats.GetOrCreateFavorites;
-using Knot.Modules.Chats.Domain;
+using Knot.Modules.Conversations.Application.Abstractions;
+using Knot.Modules.Conversations.Application.Chats.GetOrCreateFavorites;
+using Knot.Modules.Conversations.Domain;
+using Knot.Modules.Messaging.Domain;
using Knot.Shared.Kernel;
using Xunit;
-namespace Knot.Modules.Chats.UnitTests;
+namespace Knot.Modules.Conversations.UnitTests;
public class GetOrCreateFavoritesCommandHandlerTests
{
@@ -69,3 +70,5 @@ public class GetOrCreateFavoritesCommandHandlerTests
await _unitOfWork.Received(1).SaveChangesAsync(Arg.Any());
}
}
+
+
diff --git a/backend/tests/Knot.Modules.Chats.UnitTests/Knot.Modules.Chats.UnitTests.csproj b/backend/Tests/Knot.Modules.Conversations.UnitTests/Knot.Modules.Conversations.UnitTests.csproj
similarity index 83%
rename from backend/tests/Knot.Modules.Chats.UnitTests/Knot.Modules.Chats.UnitTests.csproj
rename to backend/Tests/Knot.Modules.Conversations.UnitTests/Knot.Modules.Conversations.UnitTests.csproj
index 60263e1..4939487 100644
--- a/backend/tests/Knot.Modules.Chats.UnitTests/Knot.Modules.Chats.UnitTests.csproj
+++ b/backend/Tests/Knot.Modules.Conversations.UnitTests/Knot.Modules.Conversations.UnitTests.csproj
@@ -23,7 +23,9 @@
-
+
+
+
diff --git a/backend/tests/Knot.Modules.Chats.UnitTests/Messages/SendMessageCommandHandlerTests.cs b/backend/Tests/Knot.Modules.Conversations.UnitTests/Messages/SendMessageCommandHandlerTests.cs
similarity index 92%
rename from backend/tests/Knot.Modules.Chats.UnitTests/Messages/SendMessageCommandHandlerTests.cs
rename to backend/Tests/Knot.Modules.Conversations.UnitTests/Messages/SendMessageCommandHandlerTests.cs
index ca25045..c40e483 100644
--- a/backend/tests/Knot.Modules.Chats.UnitTests/Messages/SendMessageCommandHandlerTests.cs
+++ b/backend/Tests/Knot.Modules.Conversations.UnitTests/Messages/SendMessageCommandHandlerTests.cs
@@ -8,11 +8,12 @@ using MediatR;
using NSubstitute;
using Xunit;
using Knot.Shared.Kernel;
-using Knot.Modules.Chats.Domain;
-using Knot.Modules.Chats.Application.Abstractions;
-using Knot.Modules.Chats.Application.Messages.Send;
+using Knot.Modules.Conversations.Domain;
+using Knot.Modules.Messaging.Domain;
+using Knot.Modules.Conversations.Application.Abstractions;
+using Knot.Modules.Conversations.Application.Messages.Send;
-namespace Knot.Modules.Chats.UnitTests.Messages;
+namespace Knot.Modules.Conversations.UnitTests.Messages;
public class SendMessageCommandHandlerTests
{
@@ -95,3 +96,5 @@ public class SendMessageCommandHandlerTests
await _unitOfWork.Received(1).SaveChangesAsync(Arg.Any());
}
}
+
+
diff --git a/backend/Tests/Knot.Modules.Federation.UnitTests/Knot.Modules.Federation.UnitTests.csproj b/backend/Tests/Knot.Modules.Federation.UnitTests/Knot.Modules.Federation.UnitTests.csproj
new file mode 100644
index 0000000..3b7ca74
--- /dev/null
+++ b/backend/Tests/Knot.Modules.Federation.UnitTests/Knot.Modules.Federation.UnitTests.csproj
@@ -0,0 +1,28 @@
+
+
+
+ net10.0
+ enable
+ enable
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/backend/Tests/Knot.Modules.Klipy.UnitTests/Knot.Modules.Klipy.UnitTests.csproj b/backend/Tests/Knot.Modules.Klipy.UnitTests/Knot.Modules.Klipy.UnitTests.csproj
new file mode 100644
index 0000000..824f599
--- /dev/null
+++ b/backend/Tests/Knot.Modules.Klipy.UnitTests/Knot.Modules.Klipy.UnitTests.csproj
@@ -0,0 +1,28 @@
+
+
+
+ net10.0
+ enable
+ enable
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/backend/Tests/Knot.Modules.Messaging.UnitTests/Knot.Modules.Messaging.UnitTests.csproj b/backend/Tests/Knot.Modules.Messaging.UnitTests/Knot.Modules.Messaging.UnitTests.csproj
new file mode 100644
index 0000000..6a0be6a
--- /dev/null
+++ b/backend/Tests/Knot.Modules.Messaging.UnitTests/Knot.Modules.Messaging.UnitTests.csproj
@@ -0,0 +1,22 @@
+
+
+
+ net10.0
+ enable
+ enable
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/backend/Tests/Knot.Modules.Profiles.UnitTests/Knot.Modules.Profiles.UnitTests.csproj b/backend/Tests/Knot.Modules.Profiles.UnitTests/Knot.Modules.Profiles.UnitTests.csproj
new file mode 100644
index 0000000..6a0be6a
--- /dev/null
+++ b/backend/Tests/Knot.Modules.Profiles.UnitTests/Knot.Modules.Profiles.UnitTests.csproj
@@ -0,0 +1,22 @@
+
+
+
+ net10.0
+ enable
+ enable
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/backend/Tests/Knot.Modules.Profiles.UnitTests/UpdateProfileCommandHandlerTests.cs b/backend/Tests/Knot.Modules.Profiles.UnitTests/UpdateProfileCommandHandlerTests.cs
new file mode 100644
index 0000000..ef99d04
--- /dev/null
+++ b/backend/Tests/Knot.Modules.Profiles.UnitTests/UpdateProfileCommandHandlerTests.cs
@@ -0,0 +1,41 @@
+using FluentAssertions;
+using Knot.Modules.Profiles.Application.Profiles.Commands.UpdateProfile;
+using Knot.Modules.Profiles.Domain;
+using Knot.Modules.Profiles.Application.Abstractions;
+using Knot.Shared.Kernel;
+using NSubstitute;
+using Xunit;
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Knot.Modules.Profiles.UnitTests;
+
+public class UpdateProfileCommandHandlerTests
+{
+ private readonly IProfileRepository _profileRepository;
+ private readonly IProfilesUnitOfWork _unitOfWork;
+ private readonly UpdateProfileCommandHandler _handler;
+
+ public UpdateProfileCommandHandlerTests()
+ {
+ _profileRepository = Substitute.For();
+ _unitOfWork = Substitute.For();
+ _handler = new UpdateProfileCommandHandler(_profileRepository, _unitOfWork);
+ }
+
+ [Fact]
+ public async Task Handle_ShouldReturnError_WhenProfileNotFound()
+ {
+ // Arrange
+ var command = new UpdateProfileCommand(Guid.NewGuid(), "FirstName", "LastName", "Bio");
+ _profileRepository.GetByIdAsync(command.UserId, Arg.Any()).Returns((UserProfile?)null);
+
+ // Act
+ var result = await _handler.Handle(command, CancellationToken.None);
+
+ // Assert
+ result.IsFailure.Should().BeTrue();
+ result.Error.Should().Be(ProfileErrors.NotFound);
+ }
+}
diff --git a/backend/Tests/Knot.Modules.Relations.UnitTests/Knot.Modules.Relations.UnitTests.csproj b/backend/Tests/Knot.Modules.Relations.UnitTests/Knot.Modules.Relations.UnitTests.csproj
new file mode 100644
index 0000000..f9ca8bf
--- /dev/null
+++ b/backend/Tests/Knot.Modules.Relations.UnitTests/Knot.Modules.Relations.UnitTests.csproj
@@ -0,0 +1,28 @@
+
+
+
+ net10.0
+ enable
+ enable
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/backend/Tests/Knot.Modules.Relations.UnitTests/UnitTest1.cs b/backend/Tests/Knot.Modules.Relations.UnitTests/UnitTest1.cs
new file mode 100644
index 0000000..e4a5c48
--- /dev/null
+++ b/backend/Tests/Knot.Modules.Relations.UnitTests/UnitTest1.cs
@@ -0,0 +1,10 @@
+namespace Knot.Modules.Relations.UnitTests;
+
+public class UnitTest1
+{
+ [Fact]
+ public void Test1()
+ {
+
+ }
+}
diff --git a/backend/Tests/Knot.Modules.Settings.UnitTests/Knot.Modules.Settings.UnitTests.csproj b/backend/Tests/Knot.Modules.Settings.UnitTests/Knot.Modules.Settings.UnitTests.csproj
new file mode 100644
index 0000000..319b9b1
--- /dev/null
+++ b/backend/Tests/Knot.Modules.Settings.UnitTests/Knot.Modules.Settings.UnitTests.csproj
@@ -0,0 +1,28 @@
+
+
+
+ net10.0
+ enable
+ enable
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/backend/Tests/Knot.Modules.Settings.UnitTests/UnitTest1.cs b/backend/Tests/Knot.Modules.Settings.UnitTests/UnitTest1.cs
new file mode 100644
index 0000000..853b349
--- /dev/null
+++ b/backend/Tests/Knot.Modules.Settings.UnitTests/UnitTest1.cs
@@ -0,0 +1,10 @@
+namespace Knot.Modules.Settings.UnitTests;
+
+public class UnitTest1
+{
+ [Fact]
+ public void Test1()
+ {
+
+ }
+}
diff --git a/backend/Tests/Knot.Modules.Storage.UnitTests/Knot.Modules.Storage.UnitTests.csproj b/backend/Tests/Knot.Modules.Storage.UnitTests/Knot.Modules.Storage.UnitTests.csproj
new file mode 100644
index 0000000..bba2b3d
--- /dev/null
+++ b/backend/Tests/Knot.Modules.Storage.UnitTests/Knot.Modules.Storage.UnitTests.csproj
@@ -0,0 +1,28 @@
+
+
+
+ net10.0
+ enable
+ enable
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/backend/Tests/Knot.Modules.Storage.UnitTests/UnitTest1.cs b/backend/Tests/Knot.Modules.Storage.UnitTests/UnitTest1.cs
new file mode 100644
index 0000000..ce94531
--- /dev/null
+++ b/backend/Tests/Knot.Modules.Storage.UnitTests/UnitTest1.cs
@@ -0,0 +1,10 @@
+namespace Knot.Modules.Storage.UnitTests;
+
+public class UnitTest1
+{
+ [Fact]
+ public void Test1()
+ {
+
+ }
+}
diff --git a/backend/Tests/Knot.Modules.Stories.UnitTests/Knot.Modules.Stories.UnitTests.csproj b/backend/Tests/Knot.Modules.Stories.UnitTests/Knot.Modules.Stories.UnitTests.csproj
new file mode 100644
index 0000000..45223ef
--- /dev/null
+++ b/backend/Tests/Knot.Modules.Stories.UnitTests/Knot.Modules.Stories.UnitTests.csproj
@@ -0,0 +1,28 @@
+
+
+
+ net10.0
+ enable
+ enable
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/backend/Tests/Knot.Modules.Stories.UnitTests/UnitTest1.cs b/backend/Tests/Knot.Modules.Stories.UnitTests/UnitTest1.cs
new file mode 100644
index 0000000..1e2093d
--- /dev/null
+++ b/backend/Tests/Knot.Modules.Stories.UnitTests/UnitTest1.cs
@@ -0,0 +1,10 @@
+namespace Knot.Modules.Stories.UnitTests;
+
+public class UnitTest1
+{
+ [Fact]
+ public void Test1()
+ {
+
+ }
+}
diff --git a/backend/Tests/Knot.Modules.TelegramImport.UnitTests/Knot.Modules.TelegramImport.UnitTests.csproj b/backend/Tests/Knot.Modules.TelegramImport.UnitTests/Knot.Modules.TelegramImport.UnitTests.csproj
new file mode 100644
index 0000000..7ab8a19
--- /dev/null
+++ b/backend/Tests/Knot.Modules.TelegramImport.UnitTests/Knot.Modules.TelegramImport.UnitTests.csproj
@@ -0,0 +1,28 @@
+
+
+
+ net10.0
+ enable
+ enable
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/backend/Tests/Knot.Modules.TelegramImport.UnitTests/UnitTest1.cs b/backend/Tests/Knot.Modules.TelegramImport.UnitTests/UnitTest1.cs
new file mode 100644
index 0000000..e316532
--- /dev/null
+++ b/backend/Tests/Knot.Modules.TelegramImport.UnitTests/UnitTest1.cs
@@ -0,0 +1,10 @@
+namespace Knot.Modules.TelegramImport.UnitTests;
+
+public class UnitTest1
+{
+ [Fact]
+ public void Test1()
+ {
+
+ }
+}
diff --git a/backend/Tests/Knot.Modules.WebRtc.UnitTests/Knot.Modules.WebRtc.UnitTests.csproj b/backend/Tests/Knot.Modules.WebRtc.UnitTests/Knot.Modules.WebRtc.UnitTests.csproj
new file mode 100644
index 0000000..fdc5794
--- /dev/null
+++ b/backend/Tests/Knot.Modules.WebRtc.UnitTests/Knot.Modules.WebRtc.UnitTests.csproj
@@ -0,0 +1,28 @@
+
+
+
+ net10.0
+ enable
+ enable
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/backend/Tests/Knot.Modules.WebRtc.UnitTests/UnitTest1.cs b/backend/Tests/Knot.Modules.WebRtc.UnitTests/UnitTest1.cs
new file mode 100644
index 0000000..c4d7b7a
--- /dev/null
+++ b/backend/Tests/Knot.Modules.WebRtc.UnitTests/UnitTest1.cs
@@ -0,0 +1,10 @@
+namespace Knot.Modules.WebRtc.UnitTests;
+
+public class UnitTest1
+{
+ [Fact]
+ public void Test1()
+ {
+
+ }
+}
diff --git a/backend/src/Host/Controllers/AdminController.cs b/backend/src/Host/Controllers/AdminController.cs
deleted file mode 100644
index 73c76a4..0000000
--- a/backend/src/Host/Controllers/AdminController.cs
+++ /dev/null
@@ -1,110 +0,0 @@
-using Microsoft.AspNetCore.Mvc;
-using Host.Models;
-using Knot.Shared.Kernel.Configuration;
-using Knot.Modules.Identity.Application.Users.Register;
-using Knot.Shared.Kernel.Storage;
-using Knot.Modules.Identity.Infrastructure.Persistence;
-using System.Threading;
-using System.Threading.Tasks;
-using System;
-using MediatR;
-using Host.Application.Admin.Queries;
-using Host.Application.Admin.Commands;
-
-using Microsoft.AspNetCore.Authorization;
-
-namespace Host.Controllers;
-
-[AllowAnonymous]
-[ApiController]
-[Route("api/[controller]")]
-public class AdminController : ControllerBase
-{
- private readonly ISender _sender;
-
- public AdminController(ISender sender)
- {
- _sender = sender;
- }
-
- [HttpGet("settings")]
- public async Task GetSettings(CancellationToken ct)
- {
- var result = await _sender.Send(new GetSettingsQuery(), ct);
- return Ok(result.Value);
- }
-
- [HttpPut("settings")]
- public async Task UpdateSettings([FromBody] SystemSettingsDto settings, CancellationToken ct)
- {
- var result = await _sender.Send(new UpdateSettingsCommand(settings), ct);
- return Ok(result.Value);
- }
-
- [HttpGet("dashboard")]
- public async Task GetDashboardStats(CancellationToken ct)
- {
- var result = await _sender.Send(new GetDashboardStatsQuery(), ct);
- return Ok(result.Value);
- }
-
- [HttpPost("users")]
- public async Task CreateUser([FromBody] RegisterUserCommand command, CancellationToken ct)
- {
- var result = await _sender.Send(command, ct);
- if (result.IsFailure) return BadRequest(new { error = result.Error.Description });
-
- var userDetails = await _sender.Send(new GetUserDetailsQuery(result.Value.User.Id), ct);
- return Ok(userDetails.Value);
- }
-
- [HttpPost("users/{userId:guid}/reset-password")]
- public async Task ResetUserPassword(Guid userId, [FromBody] ResetPasswordDto dto, CancellationToken ct)
- {
- var result = await _sender.Send(new ResetUserPasswordCommand(userId, dto.NewPassword), ct);
- if (result.IsFailure)
- {
- if (result.Error.Code == "User.NotFound") return NotFound(new { error = "User not found" });
- return BadRequest(new { error = result.Error.Description });
- }
- return Ok(result.Value);
- }
-
- [HttpGet("users")]
- public async Task SearchUsers([FromQuery] string query = "", CancellationToken ct = default)
- {
- var result = await _sender.Send(new SearchUsersQuery(query), ct);
- return Ok(result.Value);
- }
-
- [HttpGet("users/{id:guid}")]
- public async Task GetUserDetails(Guid id, CancellationToken ct)
- {
- var result = await _sender.Send(new GetUserDetailsQuery(id), ct);
- if (result.IsFailure)
- {
- return NotFound("User not found");
- }
- return Ok(result.Value);
- }
-
- [HttpGet("clean/dry-run")]
- public async Task CleanDryRun(
- [FromServices] IFileStorageService fileStorage,
- [FromServices] IdentityDbContext identityDb,
- CancellationToken ct)
- {
- var result = await _sender.Send(new CleanDryRunQuery(fileStorage, identityDb), ct);
- return Ok(result.Value);
- }
-
- [HttpPost("clean/run")]
- public async Task CleanRun(
- [FromServices] IFileStorageService fileStorage,
- [FromServices] IdentityDbContext identityDb,
- CancellationToken ct)
- {
- var result = await _sender.Send(new CleanRunCommand(fileStorage, identityDb), ct);
- return Ok(result.Value);
- }
-}
\ No newline at end of file
diff --git a/backend/src/Host/Controllers/AuthController.cs b/backend/src/Host/Controllers/AuthController.cs
deleted file mode 100644
index b1fbe48..0000000
--- a/backend/src/Host/Controllers/AuthController.cs
+++ /dev/null
@@ -1,50 +0,0 @@
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.AspNetCore.Authorization;
-using Knot.Shared.Kernel;
-using Knot.Modules.Identity.Application.Users.Auth;
-using Knot.Modules.Identity.Application.Users.Register;
-using Knot.Modules.Identity.Application.Users.Login;
-using Knot.Modules.Identity.Application.Users.GetMe;
-using MediatR;
-
-namespace Host.Controllers;
-
-[ApiController]
-[Route("api/[controller]")]
-public sealed class AuthController : ControllerBase
-{
- private readonly ISender _sender;
-
- public AuthController(ISender sender)
- {
- _sender = sender;
- }
-
- [HttpPost("register")]
- public async Task Register([FromBody] RegisterUserCommand command, CancellationToken ct)
- {
- var result = await _sender.Send(command, ct);
- if (result.IsFailure) return BadRequest(new { error = result.Error.Code ?? result.Error.Description });
- return Ok(result.Value);
- }
-
- [HttpGet("me")]
- [Authorize]
- public async Task GetMe([FromServices] IUserContext userContext, CancellationToken ct)
- {
- var result = await _sender.Send(new GetMeQuery(userContext.UserId), ct);
- if (result.IsFailure)
- {
- return NotFound();
- }
- return Ok(new { User = result.Value.User });
- }
-
- [HttpPost("login")]
- public async Task Login([FromBody] LoginUserCommand command, CancellationToken ct)
- {
- var result = await _sender.Send(command, ct);
- if (result.IsFailure) return Unauthorized(new { error = result.Error.Code ?? result.Error.Description });
- return Ok(result.Value);
- }
-}
\ No newline at end of file
diff --git a/backend/src/Host/Controllers/ChatsController.cs b/backend/src/Host/Controllers/ChatsController.cs
deleted file mode 100644
index a7aadc5..0000000
--- a/backend/src/Host/Controllers/ChatsController.cs
+++ /dev/null
@@ -1,241 +0,0 @@
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.AspNetCore.Authorization;
-using Knot.Shared.Kernel;
-using Knot.Modules.Chats.Application.DTOs;
-using Knot.Shared.Kernel.Storage;
-using Knot.Modules.Chats.Application.Chats.GetChats;
-using Knot.Modules.Chats.Application.Chats.GetChatById;
-using Knot.Modules.Chats.Application.Chats.Create;
-using Knot.Modules.Chats.Application.Chats.GetOrCreateFavorites;
-using Knot.Modules.Chats.Application.Chats.Update;
-using Knot.Modules.Chats.Application.Chats.LeaveOrDelete;
-using Knot.Modules.Chats.Application.Chats.Clear;
-using Knot.Modules.Chats.Application.Chats.TogglePin;
-using Knot.Modules.Chats.Application.Chats.Members;
-using Knot.Modules.Chats.Application.Chats.Avatar;
-using Knot.Modules.Chats.Domain;
-using MediatR;
-using System;
-using System.Linq;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace Host.Controllers;
-
-[Authorize]
-[ApiController]
-[Route("api/chats")]
-public sealed class ChatsController : ControllerBase
-{
- private readonly ISender _sender;
- private readonly IUserContext _userContext;
-
- public ChatsController(ISender sender, IUserContext userContext)
- {
- _sender = sender;
- _userContext = userContext;
- }
-
- [HttpGet]
- public async Task GetChats(CancellationToken ct)
- {
- var result = await _sender.Send(new GetChatsQuery(_userContext.UserId), ct);
- if (result.IsFailure)
- {
- return BadRequest(result.Error.Description);
- }
- return Ok(result.Value);
- }
-
- [HttpPost]
- public async Task Create([FromBody] CreateChatRequest request, CancellationToken ct)
- {
- var command = new CreateChatCommand(request.Name, request.Type, request.MemberIds);
- var result = await _sender.Send(command, ct);
- if (result.IsFailure)
- {
- return BadRequest(result.Error.Description);
- }
-
- var chatResult = await _sender.Send(new GetChatByIdQuery(_userContext.UserId, result.Value), ct);
- return Ok(chatResult.Value);
- }
-
- [HttpPost("personal")]
- public async Task CreatePersonal([FromBody] CreatePersonalChatRequest request, CancellationToken ct)
- {
- var command = new CreateChatCommand(string.Empty, ChatType.Personal, new List { _userContext.UserId, request.UserId });
- var result = await _sender.Send(command, ct);
- if (result.IsFailure)
- {
- return BadRequest(result.Error.Description);
- }
-
- var chatResult = await _sender.Send(new GetChatByIdQuery(_userContext.UserId, result.Value), ct);
- return Ok(chatResult.Value);
- }
-
- [HttpPost("group")]
- public async Task CreateGroup([FromBody] CreateGroupChatRequest request, CancellationToken ct)
- {
- var memberIds = request.MemberIds.ToList();
- if (memberIds.Contains(_userContext.UserId))
- {
- memberIds.Remove(_userContext.UserId);
- }
- memberIds.Insert(0, _userContext.UserId);
-
- var command = new CreateChatCommand(request.Name, ChatType.Group, memberIds);
- var result = await _sender.Send(command, ct);
- if (result.IsFailure)
- {
- return BadRequest(result.Error.Description);
- }
-
- var chatResult = await _sender.Send(new GetChatByIdQuery(_userContext.UserId, result.Value), ct);
- return Ok(chatResult.Value);
- }
-
- [HttpPost("favorites")]
- public async Task GetOrCreateFavorites(CancellationToken ct)
- {
- var result = await _sender.Send(new GetOrCreateFavoritesCommand(_userContext.UserId), ct);
- if (result.IsFailure)
- {
- return BadRequest(result.Error.Description);
- }
-
- var chatResult = await _sender.Send(new GetChatByIdQuery(_userContext.UserId, result.Value), ct);
- return Ok(chatResult.Value);
- }
-
- [HttpPut("{id:guid}")]
- public async Task UpdateChat(Guid id, [FromBody] UpdateChatRequest request, CancellationToken ct)
- {
- var result = await _sender.Send(new UpdateChatCommand(id, _userContext.UserId, request.Name, request.Description), ct);
- if (result.IsFailure)
- {
- return NotFound();
- }
-
- var chatResult = await _sender.Send(new GetChatByIdQuery(_userContext.UserId, result.Value), ct);
- return Ok(chatResult.Value);
- }
-
- [HttpDelete("{id:guid}")]
- public async Task LeaveOrDeleteChat(Guid id, CancellationToken ct)
- {
- var result = await _sender.Send(new LeaveOrDeleteChatCommand(id, _userContext.UserId), ct);
- if (result.IsFailure)
- {
- if (result.Error.Code == "Unauthorized")
- {
- return Forbid();
- }
- return NotFound();
- }
- return Ok(result.Value);
- }
-
- [HttpPost("{id:guid}/clear")]
- public async Task ClearChat(Guid id, CancellationToken ct)
- {
- var result = await _sender.Send(new ClearChatCommand(id, _userContext.UserId), ct);
- if (result.IsFailure)
- {
- return NotFound();
- }
- return Ok(result.Value);
- }
-
- [HttpPost("{id:guid}/pin")]
- public async Task TogglePin(Guid id, CancellationToken ct)
- {
- var result = await _sender.Send(new TogglePinCommand(id, _userContext.UserId), ct);
- if (result.IsFailure)
- {
- return NotFound();
- }
- return Ok(result.Value);
- }
-
- [HttpPost("{id:guid}/members")]
- public async Task AddMembers(Guid id, [FromBody] AddMembersRequest request, CancellationToken ct)
- {
- var result = await _sender.Send(new AddMembersCommand(id, _userContext.UserId, request.UserIds.ToList()), ct);
- if (result.IsFailure)
- {
- return NotFound();
- }
-
- var chatResult = await _sender.Send(new GetChatByIdQuery(_userContext.UserId, result.Value), ct);
- return Ok(chatResult.Value);
- }
-
- [HttpDelete("{id:guid}/members/{userId:guid}")]
- public async Task RemoveMember(Guid id, Guid userId, CancellationToken ct)
- {
- var result = await _sender.Send(new RemoveMemberCommand(id, _userContext.UserId, userId), ct);
- if (result.IsFailure)
- {
- return NotFound();
- }
-
- var chatResult = await _sender.Send(new GetChatByIdQuery(_userContext.UserId, result.Value), ct);
- return Ok(chatResult.Value);
- }
-
- [HttpPost("{id:guid}/avatar")]
- public async Task UploadGroupAvatar(Guid id, Microsoft.AspNetCore.Http.IFormFile avatar, CancellationToken ct)
- {
- if (avatar == null || avatar.Length == 0)
- {
- return BadRequest("No file");
- }
-
- using var stream = avatar.OpenReadStream();
- var result = await _sender.Send(new UploadGroupAvatarCommand(id, _userContext.UserId, avatar.FileName, avatar.ContentType, stream), ct);
-
- if (result.IsFailure)
- {
- return NotFound();
- }
-
- var chatResult = await _sender.Send(new GetChatByIdQuery(_userContext.UserId, result.Value), ct);
- return Ok(chatResult.Value);
- }
-
- [HttpPost("{id:guid}/avatar/crop")]
- public async Task CropGroupAvatar(Guid id, [FromForm] Microsoft.AspNetCore.Http.IFormFile avatar, [FromForm] int x, [FromForm] int y, [FromForm] int width, [FromForm] int height, CancellationToken ct)
- {
- if (avatar == null || avatar.Length == 0)
- {
- return BadRequest("No file");
- }
-
- using var stream = avatar.OpenReadStream();
- var result = await _sender.Send(new CropGroupAvatarCommand(id, _userContext.UserId, avatar.FileName, avatar.ContentType, stream, x, y, width, height), ct);
-
- if (result.IsFailure)
- {
- return NotFound();
- }
-
- var chatResult = await _sender.Send(new GetChatByIdQuery(_userContext.UserId, result.Value), ct);
- return Ok(chatResult.Value);
- }
-
- [HttpDelete("{id:guid}/avatar")]
- public async Task RemoveGroupAvatar(Guid id, CancellationToken ct)
- {
- var result = await _sender.Send(new RemoveGroupAvatarCommand(id, _userContext.UserId), ct);
- if (result.IsFailure)
- {
- return NotFound();
- }
-
- var chatResult = await _sender.Send(new GetChatByIdQuery(_userContext.UserId, result.Value), ct);
- return Ok(chatResult.Value);
- }
-}
\ No newline at end of file
diff --git a/backend/src/Host/Controllers/FilesController.cs b/backend/src/Host/Controllers/FilesController.cs
deleted file mode 100644
index 3f808c4..0000000
--- a/backend/src/Host/Controllers/FilesController.cs
+++ /dev/null
@@ -1,38 +0,0 @@
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.AspNetCore.Authorization;
-using Knot.Shared.Kernel.Storage;
-
-namespace Host.Controllers;
-
-[Authorize]
-[ApiController]
-[Route("api/files")]
-public sealed class FilesController : ControllerBase
-{
- private readonly IFileStorageService _fileStorage;
-
- public FilesController(IFileStorageService fileStorage)
- {
- _fileStorage = fileStorage;
- }
-
- [HttpGet("{id}")]
- [AllowAnonymous]
- public async Task DownloadFile(string id, [FromQuery] bool download = false)
- {
- try
- {
- var result = await _fileStorage.DownloadFileAsync(id);
- if (download && !string.IsNullOrEmpty(result.FileName))
- {
- return File(result.Stream, result.ContentType, result.FileName, enableRangeProcessing: true);
- }
-
- return File(result.Stream, result.ContentType, enableRangeProcessing: true);
- }
- catch (Exception ex)
- {
- return NotFound(new { error = "Файл не найден или ошибка доступа.", message = ex.Message });
- }
- }
-}
diff --git a/backend/src/Host/Controllers/FriendsController.cs b/backend/src/Host/Controllers/FriendsController.cs
deleted file mode 100644
index af0da69..0000000
--- a/backend/src/Host/Controllers/FriendsController.cs
+++ /dev/null
@@ -1,113 +0,0 @@
-using System;
-using System.Threading;
-using System.Threading.Tasks;
-using Knot.Modules.Identity.Application.Friends;
-using Knot.Shared.Kernel;
-using MediatR;
-using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Mvc;
-
-namespace Host.Controllers;
-
-[Authorize]
-[ApiController]
-[Route("api/friends")]
-public sealed class FriendsController : ControllerBase
-{
- private readonly ISender _sender;
- private readonly IUserContext _userContext;
-
- public FriendsController(ISender sender, IUserContext userContext)
- {
- _sender = sender;
- _userContext = userContext;
- }
-
- [HttpGet]
- public async Task GetFriends(CancellationToken ct)
- {
- var result = await _sender.Send(new GetFriendsQuery(_userContext.UserId), ct);
- if (result.IsFailure)
- {
- return BadRequest(result.Error);
- }
- return Ok(result.Value);
- }
-
- [HttpGet("requests")]
- public async Task GetRequests(CancellationToken ct)
- {
- var result = await _sender.Send(new GetIncomingRequestsQuery(_userContext.UserId), ct);
- if (result.IsFailure)
- {
- return BadRequest(result.Error);
- }
- return Ok(result.Value);
- }
-
- [HttpGet("outgoing")]
- public async Task GetOutgoingRequests(CancellationToken ct)
- {
- var result = await _sender.Send(new GetOutgoingRequestsQuery(_userContext.UserId), ct);
- if (result.IsFailure)
- {
- return BadRequest(result.Error);
- }
- return Ok(result.Value);
- }
-
- [HttpPost("request")]
- public async Task SendRequest([FromBody] Host.Models.SendFriendRequest request, CancellationToken ct)
- {
- var result = await _sender.Send(new SendFriendRequestCommand(_userContext.UserId, request.FriendId), ct);
- if (result.IsFailure)
- {
- return BadRequest(result.Error.Description);
- }
- return Ok(new { status = "pending" });
- }
-
- [HttpPost("{id:guid}/accept")]
- public async Task AcceptRequest(Guid id, CancellationToken ct)
- {
- var result = await _sender.Send(new AcceptFriendRequestCommand(_userContext.UserId, id), ct);
- if (result.IsFailure)
- {
- return NotFound(result.Error.Description);
- }
- return Ok(new { id = result.Value });
- }
-
- [HttpPost("{id:guid}/decline")]
- public async Task DeclineRequest(Guid id, CancellationToken ct)
- {
- var result = await _sender.Send(new DeclineFriendRequestCommand(_userContext.UserId, id), ct);
- if (result.IsFailure)
- {
- return NotFound(result.Error.Description);
- }
- return Ok(new Knot.Shared.Kernel.SuccessResponse(true));
- }
-
- [HttpGet("status/{userId:guid}")]
- public async Task GetStatus(Guid userId, CancellationToken ct)
- {
- var result = await _sender.Send(new GetFriendshipStatusQuery(_userContext.UserId, userId), ct);
- if (result.IsFailure)
- {
- return BadRequest(result.Error);
- }
- return Ok(result.Value);
- }
-
- [HttpDelete("{id:guid}")]
- public async Task RemoveFriend(Guid id, CancellationToken ct)
- {
- var result = await _sender.Send(new RemoveFriendCommand(_userContext.UserId, id), ct);
- if (result.IsFailure)
- {
- return NotFound(result.Error.Description);
- }
- return Ok(new Knot.Shared.Kernel.SuccessResponse(true));
- }
-}
\ No newline at end of file
diff --git a/backend/src/Host/Controllers/MessagesController.cs b/backend/src/Host/Controllers/MessagesController.cs
deleted file mode 100644
index 466c776..0000000
--- a/backend/src/Host/Controllers/MessagesController.cs
+++ /dev/null
@@ -1,116 +0,0 @@
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.AspNetCore.Authorization;
-using Knot.Modules.Chats.Application.DTOs;
-using Knot.Shared.Kernel;
-using System.Threading;
-using System.Threading.Tasks;
-using System;
-using Microsoft.AspNetCore.Http;
-using MediatR;
-using Knot.Modules.Chats.Application.Messages.GetMessages;
-using Knot.Modules.Chats.Application.Messages.SearchMessages;
-using Knot.Modules.Chats.Application.Messages.UploadFile;
-using Knot.Modules.Chats.Application.Messages.GetSharedMedia;
-using Knot.Modules.Chats.Application.Messages.Send;
-using System.Linq;
-
-namespace Host.Controllers;
-
-[Authorize]
-[ApiController]
-[Route("api/messages")]
-public sealed class MessagesController : ControllerBase
-{
- private readonly ISender _sender;
- private readonly IUserContext _userContext;
-
- public MessagesController(ISender sender, IUserContext userContext)
- {
- _sender = sender;
- _userContext = userContext;
- }
-
- [HttpGet("chat/{chatId:guid}")]
- public async Task GetMessages(Guid chatId, [FromQuery] string? cursor, CancellationToken ct = default)
- {
- var result = await _sender.Send(new GetMessagesQuery(_userContext.UserId, chatId, cursor), ct);
- if (result.IsFailure)
- {
- return BadRequest(result.Error.Description);
- }
- return Ok(result.Value);
- }
-
- [HttpGet("search")]
- public async Task GetSearch([FromQuery] string q, [FromQuery] Guid? chatId, CancellationToken ct)
- {
- var result = await _sender.Send(new SearchMessagesQuery(_userContext.UserId, q, chatId), ct);
- if (result.IsFailure)
- {
- return BadRequest(result.Error.Description);
- }
- return Ok(result.Value);
- }
-
- [HttpPost("upload")]
- [DisableRequestSizeLimit]
- [RequestFormLimits(MultipartBodyLengthLimit = 10L * 1024 * 1024 * 1024)] // 10 GB limit for form body
- public async Task UploadFile(IFormFile file, CancellationToken ct)
- {
- if (file == null || file.Length == 0)
- {
- return BadRequest("No file uploaded");
- }
-
- using var stream = file.OpenReadStream();
- var result = await _sender.Send(new UploadFileCommand(file.FileName, file.ContentType, file.Length, stream), ct);
-
- if (result.IsFailure)
- {
- if (result.Error.Code == "File.TooLarge")
- {
- return StatusCode(413, result.Error.Description);
- }
- return BadRequest(result.Error.Description);
- }
-
- return Ok(result.Value);
- }
-
- [HttpGet("chat/{chatId:guid}/shared")]
- public async Task GetSharedMedia(Guid chatId, [FromQuery] string? type, CancellationToken ct)
- {
- var result = await _sender.Send(new GetSharedMediaQuery(_userContext.UserId, chatId, type), ct);
- if (result.IsFailure)
- {
- return BadRequest(result.Error.Description);
- }
- return Ok(result.Value);
- }
-
- [HttpPost("chat/{chatId:guid}")]
- public async Task SendMessage(Guid chatId, [FromBody] SendMessageRequest request, CancellationToken ct)
- {
- var attachments = request.Attachments?.Select(a =>
- new AttachmentRequest(a.Type, a.Url, a.FileName, a.FileSize)).ToList();
-
- var command = new SendMessageCommand(
- chatId,
- _userContext.UserId,
- request.Content,
- request.Type,
- attachments,
- request.ReplyToId,
- request.Quote,
- request.ForwardedFromId);
-
- var result = await _sender.Send(command, ct);
-
- if (result.IsFailure)
- {
- return BadRequest(result.Error.Description);
- }
-
- return Ok(result.Value);
- }
-}
\ No newline at end of file
diff --git a/backend/src/Host/Controllers/StoriesController.cs b/backend/src/Host/Controllers/StoriesController.cs
deleted file mode 100644
index 3ea3174..0000000
--- a/backend/src/Host/Controllers/StoriesController.cs
+++ /dev/null
@@ -1,149 +0,0 @@
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.AspNetCore.Authorization;
-using Host.Models;
-using Knot.Shared.Kernel;
-using System;
-using System.Threading;
-using System.Threading.Tasks;
-using MediatR;
-using Host.Application.Stories.Queries.GetStories;
-using Host.Application.Stories.Commands.CreateStory;
-using Host.Application.Stories.Queries.GetUserStories;
-using Host.Application.Stories.Commands.ViewStory;
-using Host.Application.Stories.Queries.GetStoryViewers;
-using Host.Application.Stories.Commands.AddStoryReaction;
-using Host.Application.Stories.Commands.RemoveStoryReaction;
-using Host.Application.Stories.Commands.AddStoryReply;
-using Host.Application.Stories.Queries.GetStoryReplies;
-using Host.Application.Stories.Commands.DeleteStory;
-
-namespace Host.Controllers;
-
-[Authorize]
-[ApiController]
-[Route("api/stories")]
-public sealed class StoriesController : ControllerBase
-{
- private readonly ISender _sender;
- private readonly IUserContext _userContext;
-
- public StoriesController(ISender sender, IUserContext userContext)
- {
- _sender = sender;
- _userContext = userContext;
- }
-
- [HttpGet]
- public async Task GetStories(CancellationToken ct)
- {
- var result = await _sender.Send(new GetStoriesQuery(_userContext.UserId), ct);
- return Ok(result.Value);
- }
-
- [HttpPost]
- public async Task CreateStory([FromBody] CreateStoryRequest request, CancellationToken ct)
- {
- var result = await _sender.Send(new CreateStoryCommand(_userContext.UserId, request.Type, request.MediaUrl, request.Content, request.BgColor), ct);
- return Ok(new { id = result.Value });
- }
-
- [HttpGet("user/{userId}")]
- public async Task GetUserStories(Guid userId, CancellationToken ct)
- {
- var result = await _sender.Send(new GetUserStoriesQuery(_userContext.UserId, userId), ct);
- if (result.IsFailure)
- {
- return NotFound();
- }
- return Ok(result.Value);
- }
-
- [HttpPost("{id}/view")]
- public async Task ViewStory(Guid id, CancellationToken ct)
- {
- var result = await _sender.Send(new ViewStoryCommand(_userContext.UserId, id), ct);
- if (result.IsFailure)
- {
- return NotFound();
- }
- return Ok(result.Value);
- }
-
- [HttpGet("{id}/viewers")]
- public async Task GetStoryViewers(Guid id, CancellationToken ct)
- {
- var result = await _sender.Send(new GetStoryViewersQuery(_userContext.UserId, id), ct);
- if (result.IsFailure)
- {
- if (result.Error.Code == "Unauthorized")
- {
- return Forbid();
- }
- return NotFound();
- }
- return Ok(result.Value);
- }
-
- [HttpPost("{id}/reaction")]
- public async Task AddReaction(Guid id, [FromBody] AddStoryReactionRequest request, CancellationToken ct)
- {
- var result = await _sender.Send(new AddStoryReactionCommand(_userContext.UserId, id, request.Emoji), ct);
- if (result.IsFailure)
- {
- return NotFound();
- }
- return Ok(result.Value);
- }
-
- [HttpDelete("{id}/reaction")]
- public async Task RemoveReaction(Guid id, [FromBody] RemoveStoryReactionRequest request, CancellationToken ct)
- {
- var result = await _sender.Send(new RemoveStoryReactionCommand(_userContext.UserId, id, request.Emoji), ct);
- if (result.IsFailure)
- {
- return NotFound();
- }
- return Ok(result.Value);
- }
-
- [HttpPost("{id}/reply")]
- public async Task AddReply(Guid id, [FromBody] AddStoryReplyRequest request, CancellationToken ct)
- {
- var result = await _sender.Send(new AddStoryReplyCommand(_userContext.UserId, id, request.Content), ct);
- if (result.IsFailure)
- {
- return NotFound();
- }
- return Ok(result.Value);
- }
-
- [HttpGet("{id}/replies")]
- public async Task GetReplies(Guid id, CancellationToken ct)
- {
- var result = await _sender.Send(new GetStoryRepliesQuery(_userContext.UserId, id), ct);
- if (result.IsFailure)
- {
- if (result.Error.Code == "Unauthorized")
- {
- return Forbid();
- }
- return NotFound();
- }
- return Ok(result.Value);
- }
-
- [HttpDelete("{id}")]
- public async Task DeleteStory(Guid id, CancellationToken ct)
- {
- var result = await _sender.Send(new DeleteStoryCommand(_userContext.UserId, id), ct);
- if (result.IsFailure)
- {
- if (result.Error.Code == "Unauthorized")
- {
- return Forbid();
- }
- return NotFound();
- }
- return Ok(result.Value);
- }
-}
\ No newline at end of file
diff --git a/backend/src/Host/Controllers/TelegramImportController.cs b/backend/src/Host/Controllers/TelegramImportController.cs
deleted file mode 100644
index defcf1b..0000000
--- a/backend/src/Host/Controllers/TelegramImportController.cs
+++ /dev/null
@@ -1,64 +0,0 @@
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Http;
-using MediatR;
-using Host.Models;
-using Knot.Shared.Kernel;
-using Knot.Modules.Chats.Application.TelegramImport;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace Host.Controllers;
-
-[Authorize]
-[ApiController]
-[Route("api/import/telegram")]
-public sealed class TelegramImportController : ControllerBase
-{
- private readonly ISender _sender;
- private readonly IUserContext _userContext;
-
- public TelegramImportController(ISender sender, IUserContext userContext)
- {
- _sender = sender;
- _userContext = userContext;
- }
-
- [HttpPost("analyze")]
- [DisableRequestSizeLimit]
- [RequestFormLimits(MultipartBodyLengthLimit = 10L * 1024 * 1024 * 1024)] // 10GB for big exports
- public async Task Analyze(IFormFile file, CancellationToken ct)
- {
- if (file == null)
- {
- return BadRequest("No file uploaded.");
- }
-
- using var stream = file.OpenReadStream();
- var command = new AnalyzeImportCommand(stream, file.FileName);
-
- var result = await _sender.Send(command, ct);
-
- if (result.IsFailure)
- {
- return BadRequest(result.Error.Description ?? result.Error.Code);
- }
-
- return Ok(result.Value);
- }
-
- [HttpPost("execute")]
- public async Task Execute([FromBody] ExecuteImportRequest req, CancellationToken ct)
- {
- var command = new ExecuteImportCommand(_userContext.UserId, req.Token, req.Mapping, req.GroupName);
-
- var result = await _sender.Send(command, ct);
-
- if (result.IsFailure)
- {
- return BadRequest(result.Error.Description ?? result.Error.Code);
- }
-
- return Ok(result.Value);
- }
-}
diff --git a/backend/src/Host/Controllers/UsersController.cs b/backend/src/Host/Controllers/UsersController.cs
deleted file mode 100644
index ac68fd8..0000000
--- a/backend/src/Host/Controllers/UsersController.cs
+++ /dev/null
@@ -1,134 +0,0 @@
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.AspNetCore.Authorization;
-using Knot.Shared.Kernel;
-using Host.Models;
-using Knot.Shared.Kernel.Storage;
-using MediatR;
-using Knot.Modules.Identity.Application.Users;
-using Knot.Modules.Identity.Application.Users.Avatar;
-using Knot.Modules.Identity.Application.Users.GetUser;
-using Knot.Modules.Identity.Application.Users.Search;
-using Knot.Modules.Identity.Application.Users.UpdateProfile;
-using Knot.Modules.Identity.Application.Users.UpdateSettings;
-
-namespace Host.Controllers;
-
-[Authorize]
-[ApiController]
-[Route("api/users")]
-public sealed class UsersController : ControllerBase
-{
- private readonly ISender _sender;
- private readonly IUserContext _userContext;
-
- public UsersController(ISender sender, IUserContext userContext)
- {
- _sender = sender;
- _userContext = userContext;
- }
-
- [HttpGet("search")]
- public async Task Search([FromQuery] string q, CancellationToken ct)
- {
- var result = await _sender.Send(new SearchUsersQuery(q), ct);
- if (result.IsFailure)
- {
- return BadRequest(result.Error);
- }
- return Ok(result.Value);
- }
-
- [HttpPut("settings")]
- public async Task UpdateSettings([FromBody] UpdateSettingsRequest request, CancellationToken ct)
- {
- var result = await _sender.Send(new UpdateSettingsCommand(_userContext.UserId, request.HideStoryViews), ct);
- if (result.IsFailure)
- {
- return NotFound(result.Error);
- }
- return Ok(result.Value);
- }
-
- [HttpPost("avatar")]
- public async Task UploadAvatar(IFormFile avatar, CancellationToken ct)
- {
- var fileToUpload = avatar ?? Request.Form.Files.FirstOrDefault();
- if (fileToUpload == null || fileToUpload.Length == 0)
- {
- return BadRequest("No file uploaded");
- }
-
- using var stream = fileToUpload.OpenReadStream();
- var command = new UploadAvatarCommand(
- _userContext.UserId,
- stream,
- fileToUpload.FileName,
- fileToUpload.ContentType
- );
-
- var result = await _sender.Send(command, ct);
- if (result.IsFailure)
- {
- return NotFound(result.Error);
- }
- return Ok(result.Value);
- }
-
- [HttpPost("avatar/crop")]
- public async Task CropAvatar([FromForm] IFormFile avatar, [FromForm] int x, [FromForm] int y, [FromForm] int width, [FromForm] int height, CancellationToken ct)
- {
- if (avatar == null || avatar.Length == 0)
- {
- return BadRequest("No file uploaded");
- }
-
- using var stream = avatar.OpenReadStream();
- var command = new CropAvatarCommand(
- _userContext.UserId,
- stream,
- avatar.FileName ?? "avatar.jpg",
- avatar.ContentType,
- x, y, width, height
- );
-
- var result = await _sender.Send(command, ct);
- if (result.IsFailure)
- {
- return NotFound(result.Error);
- }
- return Ok(result.Value);
- }
-
- [HttpDelete("avatar")]
- public async Task DeleteAvatar(CancellationToken ct)
- {
- var result = await _sender.Send(new DeleteAvatarCommand(_userContext.UserId), ct);
- if (result.IsFailure)
- {
- return NotFound(result.Error);
- }
- return Ok(result.Value);
- }
-
- [HttpPut("profile")]
- public async Task UpdateProfile([FromBody] UpdateProfileRequest request, CancellationToken ct)
- {
- var result = await _sender.Send(new UpdateProfileCommand(_userContext.UserId, request.DisplayName, request.Bio, request.Birthday), ct);
- if (result.IsFailure)
- {
- return NotFound(result.Error);
- }
- return Ok(result.Value);
- }
-
- [HttpGet("{id:guid}")]
- public async Task GetUser(Guid id, CancellationToken ct)
- {
- var result = await _sender.Send(new GetUserQuery(id), ct);
- if (result.IsFailure)
- {
- return NotFound(result.Error);
- }
- return Ok(result.Value);
- }
-}
\ No newline at end of file
diff --git a/backend/src/Host/Endpoints/ConfigEndpoints.cs b/backend/src/Host/Endpoints/ConfigEndpoints.cs
deleted file mode 100644
index c00df8b..0000000
--- a/backend/src/Host/Endpoints/ConfigEndpoints.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-using Carter;
-using MediatR;
-using Knot.Shared.Kernel.Constants;
-using Microsoft.AspNetCore.Builder;
-using Microsoft.AspNetCore.Http;
-using Microsoft.AspNetCore.Routing;
-
-namespace Host.Endpoints;
-
-///
-/// Регистрация эндпоинтов для конфигурации приложения.
-///
-public sealed class ConfigEndpoints : ICarterModule
-{
- public void AddRoutes(IEndpointRouteBuilder app)
- {
- var group = app.MapGroup(Routes.ApiConfig);
-
- group.MapGet("/", async (ISender sender, CancellationToken ct) =>
- {
- var result = await sender.Send(new Host.Application.Config.Queries.GetPublicConfigQuery(), ct);
- return result.IsSuccess ? Results.Ok(result.Value) : Results.BadRequest(result.Error);
- });
- }
-}
diff --git a/backend/src/Host/Endpoints/FederationEndpoints.cs b/backend/src/Host/Endpoints/FederationEndpoints.cs
deleted file mode 100644
index b8e8276..0000000
--- a/backend/src/Host/Endpoints/FederationEndpoints.cs
+++ /dev/null
@@ -1,41 +0,0 @@
-using Carter;
-using MediatR;
-using Knot.Shared.Kernel.Constants;
-using Microsoft.AspNetCore.Builder;
-using Microsoft.AspNetCore.Http;
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.AspNetCore.Routing;
-using System;
-
-namespace Host.Endpoints;
-
-///
-/// Регистрация эндпоинтов федерации.
-///
-public sealed class FederationEndpoints : ICarterModule
-{
- public void AddRoutes(IEndpointRouteBuilder app)
- {
- var group = app.MapGroup(Routes.ApiFederation);
-
- group.MapPost("/handshake", async ([FromBody] Host.Application.Federation.Commands.HandshakeRequest request, ISender sender, CancellationToken ct) =>
- {
- var result = await sender.Send(new Host.Application.Federation.Commands.HandshakeFederationCommand(request), ct);
- if (result.IsSuccess)
- {
- return Results.Ok(result.Value);
- }
-
- if (result.Error.Code == "Unauthorized")
- {
- return Results.Forbid();
- }
- if (result.Error.Code == Knot.Shared.Kernel.Constants.Errors.DisabledByAdmin)
- {
- return Results.StatusCode(503);
- }
-
- return Results.BadRequest(new { error = result.Error.Description });
- });
- }
-}
\ No newline at end of file
diff --git a/backend/src/Host/Endpoints/WebRtcEndpoints.cs b/backend/src/Host/Endpoints/WebRtcEndpoints.cs
deleted file mode 100644
index f121727..0000000
--- a/backend/src/Host/Endpoints/WebRtcEndpoints.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-using Carter;
-using MediatR;
-using Knot.Shared.Kernel;
-using Microsoft.AspNetCore.Builder;
-using Microsoft.AspNetCore.Http;
-using Microsoft.AspNetCore.Routing;
-
-namespace Host.Endpoints;
-
-///
-/// Регистрация эндпоинтов WebRTC
-///
-public sealed class WebRtcEndpoints : ICarterModule
-{
- public void AddRoutes(IEndpointRouteBuilder app)
- {
- var group = app.MapGroup(Knot.Shared.Kernel.Constants.Routes.ApiWebRtc)
- .RequireAuthorization();
-
- group.MapGet("/ice-servers", async (ISender sender, CancellationToken ct) =>
- {
- var result = await sender.Send(new Host.Application.WebRtc.Queries.GetIceServersQuery(), ct);
- return result.IsSuccess ? Results.Ok(result.Value) : Results.BadRequest(result.Error);
- })
- .WithName("GetIceServers")
- .WithOpenApi();
- }
-}
diff --git a/backend/src/Host/Host.csproj b/backend/src/Host/Host.csproj
index d753cb1..cf96201 100644
--- a/backend/src/Host/Host.csproj
+++ b/backend/src/Host/Host.csproj
@@ -28,9 +28,18 @@
-
-
+
+
+
+
+
+
+
+
+
+
+
diff --git a/backend/src/Host/Models/Friends/SendFriendRequest.cs b/backend/src/Host/Models/Friends/SendFriendRequest.cs
deleted file mode 100644
index 382ea0e..0000000
--- a/backend/src/Host/Models/Friends/SendFriendRequest.cs
+++ /dev/null
@@ -1,5 +0,0 @@
-using System;
-
-namespace Host.Models;
-
-public record SendFriendRequest(Guid FriendId);
diff --git a/backend/src/Host/Models/Stories/AddStoryReactionRequest.cs b/backend/src/Host/Models/Stories/AddStoryReactionRequest.cs
deleted file mode 100644
index 0ff9548..0000000
--- a/backend/src/Host/Models/Stories/AddStoryReactionRequest.cs
+++ /dev/null
@@ -1,5 +0,0 @@
-using System;
-
-namespace Host.Models;
-
-public record AddStoryReactionRequest(string Emoji);
diff --git a/backend/src/Host/Models/Stories/AddStoryReplyRequest.cs b/backend/src/Host/Models/Stories/AddStoryReplyRequest.cs
deleted file mode 100644
index 5c4d2cc..0000000
--- a/backend/src/Host/Models/Stories/AddStoryReplyRequest.cs
+++ /dev/null
@@ -1,5 +0,0 @@
-using System;
-
-namespace Host.Models;
-
-public record AddStoryReplyRequest(string Content);
diff --git a/backend/src/Host/Models/Stories/RemoveStoryReactionRequest.cs b/backend/src/Host/Models/Stories/RemoveStoryReactionRequest.cs
deleted file mode 100644
index f011f5e..0000000
--- a/backend/src/Host/Models/Stories/RemoveStoryReactionRequest.cs
+++ /dev/null
@@ -1,5 +0,0 @@
-using System;
-
-namespace Host.Models;
-
-public record RemoveStoryReactionRequest(string Emoji);
diff --git a/backend/src/Host/Models/Users/UpdateProfileRequest.cs b/backend/src/Host/Models/Users/UpdateProfileRequest.cs
deleted file mode 100644
index 160eb9c..0000000
--- a/backend/src/Host/Models/Users/UpdateProfileRequest.cs
+++ /dev/null
@@ -1,5 +0,0 @@
-using System;
-
-namespace Host.Models;
-
-public sealed record UpdateProfileRequest(string? DisplayName, string? Bio, DateTime? Birthday);
diff --git a/backend/src/Host/Models/Users/UpdateSettingsRequest.cs b/backend/src/Host/Models/Users/UpdateSettingsRequest.cs
deleted file mode 100644
index 188ab7a..0000000
--- a/backend/src/Host/Models/Users/UpdateSettingsRequest.cs
+++ /dev/null
@@ -1,3 +0,0 @@
-namespace Host.Models;
-
-public sealed record UpdateSettingsRequest(bool? HideStoryViews);
diff --git a/backend/src/Host/Models/Users/UserDto.cs b/backend/src/Host/Models/Users/UserDto.cs
deleted file mode 100644
index b0c62a7..0000000
--- a/backend/src/Host/Models/Users/UserDto.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using System;
-
-namespace Host.Models;
-
-public record UserDto(
- Guid Id,
- string Username,
- string DisplayName,
- string? Avatar,
- bool IsOnline,
- DateTime LastSeen
-);
diff --git a/backend/src/Host/Models/Users/UserProfileDto.cs b/backend/src/Host/Models/Users/UserProfileDto.cs
deleted file mode 100644
index 7a1682b..0000000
--- a/backend/src/Host/Models/Users/UserProfileDto.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using System;
-
-namespace Host.Models;
-
-public record UserProfileDto(
- Guid Id,
- string Username,
- string DisplayName,
- string? Avatar,
- string? Bio,
- DateTime? Birthday,
- DateTime CreatedAt,
- bool? HideStoryViews = null,
- bool IsOnline = false,
- DateTime? LastSeen = null
-);
diff --git a/backend/src/Host/Program.cs b/backend/src/Host/Program.cs
index 6ae34a4..71f7da1 100644
--- a/backend/src/Host/Program.cs
+++ b/backend/src/Host/Program.cs
@@ -1,10 +1,11 @@
+using Knot.Modules.Messaging;
using Carter;
using Knot.Shared.Infrastructure;
-using Knot.Modules.Identity;
-using Knot.Modules.Chats;
-using Knot.Modules.Chats.Infrastructure.SignalR;
-using Knot.Modules.Identity.Infrastructure.Persistence;
-using Knot.Modules.Chats.Infrastructure.Persistence;
+using Knot.Modules.Auth;
+using Knot.Modules.Conversations;
+using Knot.Modules.Conversations.Infrastructure.SignalR;
+using Knot.Modules.Auth.Infrastructure.Persistence;
+using Knot.Modules.Conversations.Infrastructure.Persistence;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
@@ -26,7 +27,7 @@ var builder = WebApplication.CreateBuilder(args);
// Маппинг стандартных переменных окружения в иерархию .NET
var envMappings = new Dictionary
{
- ["ConnectionStrings:DefaultConnection"] = builder.Configuration["DATABASE_URL"],
+ ["ConnectionStrings:DefaultConnection"] = builder.Configuration["DATABASE_URL"] ?? "Host=localhost;Database=knot;Username=postgres;Password=postgres",
["ConnectionStrings:MongoConnection"] = builder.Configuration["MONGO_CONNECTION"],
["Jwt:Secret"] = builder.Configuration["JWT_SECRET"],
["Jwt:Issuer"] = builder.Configuration["JWT_ISSUER"],
@@ -48,8 +49,8 @@ builder.Configuration.AddInMemoryCollection(
envMappings.Where(kv => !string.IsNullOrEmpty(kv.Value))
.ToDictionary(kv => kv.Key, kv => kv.Value));
-builder.Services.AddIdentityModule(builder.Configuration);
-builder.Services.AddChatsModule(builder.Configuration);
+builder.Services.AddAuthModule(builder.Configuration);
+builder.Services.AddConversationsModule(builder.Configuration);
builder.Services.AddSharedInfrastructure(builder.Configuration);
// CQRS / MediatR для команд в Host (например, AdminController)
@@ -98,12 +99,7 @@ builder.Services.AddRouting(options =>
builder.Services.AddMemoryCache();
builder.Services.AddHttpClient();
-builder.Services.AddControllers()
- .AddJsonOptions(options =>
- {
- options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
- options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
- });
+
builder.Services.AddSignalR()
.AddJsonProtocol(options =>
@@ -152,10 +148,10 @@ var app = builder.Build();
// Применяем миграции при старте
using (var scope = app.Services.CreateScope())
{
- var identityDb = scope.ServiceProvider.GetRequiredService();
+ var identityDb = scope.ServiceProvider.GetRequiredService();
await identityDb.Database.MigrateAsync();
- var chatsDb = scope.ServiceProvider.GetRequiredService();
+ var chatsDb = scope.ServiceProvider.GetRequiredService();
await chatsDb.Database.MigrateAsync();
var systemDb = scope.ServiceProvider.GetRequiredService();
@@ -163,7 +159,7 @@ using (var scope = app.Services.CreateScope())
// Set Encryption Service for MongoDB serializers
var encryptionService = scope.ServiceProvider.GetRequiredService();
- Knot.Modules.Chats.Infrastructure.Persistence.Mongo.EncryptedStringSerializer.EncryptionService = encryptionService;
+ Knot.Modules.Messaging.Infrastructure.Persistence.Mongo.EncryptedStringSerializer.EncryptionService = encryptionService;
// Initialize Global Settings Cache
var settingsService = scope.ServiceProvider.GetRequiredService();
@@ -193,7 +189,6 @@ app.UseAuthentication();
app.UseAuthorization();
// Добавляем контроллеры и Carter (Minimal APIs)
-app.MapControllers();
app.MapCarter();
// Добавляем SignalR хабы
diff --git a/backend/src/Host/Application/Admin/Commands/CleanRun/CleanRunCommand.cs b/backend/src/Modules/Admin/Application/Admin/Commands/CleanRun/CleanRunCommand.cs
similarity index 89%
rename from backend/src/Host/Application/Admin/Commands/CleanRun/CleanRunCommand.cs
rename to backend/src/Modules/Admin/Application/Admin/Commands/CleanRun/CleanRunCommand.cs
index 0705732..394cb52 100644
--- a/backend/src/Host/Application/Admin/Commands/CleanRun/CleanRunCommand.cs
+++ b/backend/src/Modules/Admin/Application/Admin/Commands/CleanRun/CleanRunCommand.cs
@@ -1,3 +1,5 @@
+using Knot.Modules.Admin.Application.Admin.DTOs;
+using Knot.Modules.Messaging.Domain;
using System;
using System.Linq;
using System.Collections.Generic;
@@ -6,15 +8,15 @@ using System.Threading.Tasks;
using MediatR;
using Knot.Shared.Kernel;
using Knot.Shared.Kernel.Storage;
-using Knot.Modules.Identity.Infrastructure.Persistence;
-using Knot.Modules.Chats.Infrastructure.Persistence;
-using Knot.Modules.Chats.Domain;
+using Knot.Modules.Auth.Infrastructure.Persistence;
+using Knot.Modules.Conversations.Infrastructure.Persistence;
+using Knot.Modules.Conversations.Domain;
using MongoDB.Driver;
using Microsoft.EntityFrameworkCore;
namespace Host.Application.Admin.Commands;
-public record CleanRunCommand(IFileStorageService FileStorage, IdentityDbContext IdentityDb) : ICommand;
+public record CleanRunCommand(IFileStorageService FileStorage, AuthDbContext IdentityDb) : ICommand;
internal sealed class CleanRunCommandHandler : ICommandHandler
{
diff --git a/backend/src/Host/Application/Admin/Commands/ResetUserPassword/ResetUserPasswordCommand.cs b/backend/src/Modules/Admin/Application/Admin/Commands/ResetUserPassword/ResetUserPasswordCommand.cs
similarity index 71%
rename from backend/src/Host/Application/Admin/Commands/ResetUserPassword/ResetUserPasswordCommand.cs
rename to backend/src/Modules/Admin/Application/Admin/Commands/ResetUserPassword/ResetUserPasswordCommand.cs
index e6078c8..b4dd5f2 100644
--- a/backend/src/Host/Application/Admin/Commands/ResetUserPassword/ResetUserPasswordCommand.cs
+++ b/backend/src/Modules/Admin/Application/Admin/Commands/ResetUserPassword/ResetUserPasswordCommand.cs
@@ -1,12 +1,16 @@
+using Knot.Modules.Auth.Domain;
+using Knot.Modules.Admin.Application.Admin.DTOs;
using System;
using System.Threading;
using System.Threading.Tasks;
using MediatR;
using Knot.Shared.Kernel;
-using Host.Models;
-using Knot.Modules.Identity.Domain;
-using Knot.Modules.Identity.Infrastructure.Persistence;
-using Knot.Modules.Identity.Application.Abstractions;
+using Knot.Modules.Auth.Application.Auth.DTOs;
+
+using Knot.Modules.Auth.Application.Users;
+using Knot.Modules.Auth.Domain;
+using Knot.Modules.Auth.Infrastructure.Persistence;
+using Knot.Modules.Auth.Application.Abstractions;
namespace Host.Application.Admin.Commands;
@@ -15,9 +19,9 @@ public record ResetUserPasswordCommand(Guid UserId, string NewPassword) : IComma
internal sealed class ResetUserPasswordCommandHandler : ICommandHandler
{
private readonly IUserRepository _userRepository;
- private readonly IIdentityUnitOfWork _identityUnitOfWork;
+ private readonly IAuthUnitOfWork _identityUnitOfWork;
- public ResetUserPasswordCommandHandler(IUserRepository userRepository, IIdentityUnitOfWork identityUnitOfWork)
+ public ResetUserPasswordCommandHandler(IUserRepository userRepository, IAuthUnitOfWork identityUnitOfWork)
{
_userRepository = userRepository;
_identityUnitOfWork = identityUnitOfWork;
@@ -28,7 +32,7 @@ internal sealed class ResetUserPasswordCommandHandler : ICommandHandler(IdentityErrors.UserNotFound);
+ return Result.Failure(AuthErrors.UserNotFound);
}
if (string.IsNullOrWhiteSpace(request.NewPassword))
diff --git a/backend/src/Host/Application/Admin/Commands/UpdateSettings/UpdateSettingsCommand.cs b/backend/src/Modules/Admin/Application/Admin/Commands/UpdateSettings/UpdateSettingsCommand.cs
similarity index 93%
rename from backend/src/Host/Application/Admin/Commands/UpdateSettings/UpdateSettingsCommand.cs
rename to backend/src/Modules/Admin/Application/Admin/Commands/UpdateSettings/UpdateSettingsCommand.cs
index adf3863..6fe3717 100644
--- a/backend/src/Host/Application/Admin/Commands/UpdateSettings/UpdateSettingsCommand.cs
+++ b/backend/src/Modules/Admin/Application/Admin/Commands/UpdateSettings/UpdateSettingsCommand.cs
@@ -1,4 +1,5 @@
-using System;
+using Knot.Modules.Admin.Application.Admin.DTOs;
+using System;
using System.Threading;
using System.Threading.Tasks;
using MediatR;
@@ -23,4 +24,4 @@ internal sealed class UpdateSettingsCommandHandler : ICommandHandler;
+public record CleanDryRunQuery(IFileStorageService FileStorage, AuthDbContext IdentityDb) : IQuery;
internal sealed class CleanDryRunQueryHandler : IQueryHandler
{
diff --git a/backend/src/Host/Application/Admin/Queries/GetDashboardStats/GetDashboardStatsQuery.cs b/backend/src/Modules/Admin/Application/Admin/Queries/GetDashboardStats/GetDashboardStatsQuery.cs
similarity index 91%
rename from backend/src/Host/Application/Admin/Queries/GetDashboardStats/GetDashboardStatsQuery.cs
rename to backend/src/Modules/Admin/Application/Admin/Queries/GetDashboardStats/GetDashboardStatsQuery.cs
index bcb82d2..24d5ec0 100644
--- a/backend/src/Host/Application/Admin/Queries/GetDashboardStats/GetDashboardStatsQuery.cs
+++ b/backend/src/Modules/Admin/Application/Admin/Queries/GetDashboardStats/GetDashboardStatsQuery.cs
@@ -1,4 +1,5 @@
-using System.Threading;
+using Knot.Modules.Admin.Application.Admin.DTOs;
+using System.Threading;
using System.Threading.Tasks;
using MediatR;
using Knot.Shared.Kernel;
@@ -22,4 +23,4 @@ internal sealed class GetDashboardStatsQueryHandler : IQueryHandler(IdentityErrors.UserNotFound);
+ return Result.Failure(AuthErrors.UserNotFound);
}
var filter = Builders.Filter.Eq(m => m.SenderId, request.UserId);
@@ -56,8 +61,8 @@ internal sealed class GetUserDetailsQueryHandler : IQueryHandler
+
+ net10.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <_Parameter1>Knot.Modules.Admin.UnitTests
+
+
+
\ No newline at end of file
diff --git a/backend/src/Modules/Admin/Presentation/Endpoints/AdminEndpoints.cs b/backend/src/Modules/Admin/Presentation/Endpoints/AdminEndpoints.cs
new file mode 100644
index 0000000..c7c0848
--- /dev/null
+++ b/backend/src/Modules/Admin/Presentation/Endpoints/AdminEndpoints.cs
@@ -0,0 +1,86 @@
+using Carter;
+using Knot.Modules.Auth.Application.Auth.DTOs;
+using Knot.Shared.Kernel;
+using Knot.Shared.Kernel.Configuration;
+using Knot.Modules.Auth.Application.Users.Register;
+using Knot.Shared.Kernel.Storage;
+using Knot.Modules.Auth.Infrastructure.Persistence;
+using MediatR;
+using Host.Application.Admin.Queries;
+using Host.Application.Admin.Commands;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Routing;
+using Microsoft.AspNetCore.Mvc;
+
+namespace Knot.Host.Presentation.Endpoints;
+
+public sealed class AdminEndpoints : ICarterModule
+{
+ public void AddRoutes(IEndpointRouteBuilder app)
+ {
+ var group = app.MapGroup("api/admin"); // Middleware handles auth
+
+ group.MapGet("settings", async (ISender sender, CancellationToken ct) =>
+ {
+ var result = await sender.Send(new GetSettingsQuery(), ct);
+ return Results.Ok(result.Value);
+ });
+
+ group.MapPut("settings", async ([FromBody] SystemSettingsDto settings, ISender sender, CancellationToken ct) =>
+ {
+ var result = await sender.Send(new UpdateSettingsCommand(settings), ct);
+ return Results.Ok(result.Value);
+ });
+
+ group.MapGet("dashboard", async (ISender sender, CancellationToken ct) =>
+ {
+ var result = await sender.Send(new GetDashboardStatsQuery(), ct);
+ return Results.Ok(result.Value);
+ });
+
+ group.MapPost("users", async ([FromBody] RegisterUserCommand command, ISender sender, CancellationToken ct) =>
+ {
+ var result = await sender.Send(command, ct);
+ if (result.IsFailure) return Results.BadRequest(new { error = result.Error.Description });
+
+ var userDetails = await sender.Send(new GetUserDetailsQuery(result.Value.User.Id), ct);
+ return Results.Ok(userDetails.Value);
+ });
+
+ group.MapPost("users/{userId:guid}/reset-password", async (Guid userId, [FromBody] ResetPasswordDto dto, ISender sender, CancellationToken ct) =>
+ {
+ var result = await sender.Send(new ResetUserPasswordCommand(userId, dto.NewPassword), ct);
+ if (result.IsFailure)
+ {
+ if (result.Error.Code == "User.NotFound") return Results.NotFound(new { error = "User not found" });
+ return Results.BadRequest(new { error = result.Error.Description });
+ }
+ return Results.Ok(result.Value);
+ });
+
+ group.MapGet("users", async (ISender sender, [FromQuery] string query = "", CancellationToken ct = default) =>
+ {
+ var result = await sender.Send(new SearchUsersQuery(query), ct);
+ return Results.Ok(result.Value);
+ });
+
+ group.MapGet("users/{id:guid}", async (Guid id, ISender sender, CancellationToken ct) =>
+ {
+ var result = await sender.Send(new GetUserDetailsQuery(id), ct);
+ return result.IsSuccess ? Results.Ok(result.Value) : Results.NotFound("User not found");
+ });
+
+ group.MapGet("clean/dry-run", async (IFileStorageService fileStorage, AuthDbContext identityDb, ISender sender, CancellationToken ct) =>
+ {
+ var result = await sender.Send(new CleanDryRunQuery(fileStorage, identityDb), ct);
+ return Results.Ok(result.Value);
+ });
+
+ group.MapPost("clean/run", async (IFileStorageService fileStorage, AuthDbContext identityDb, ISender sender, CancellationToken ct) =>
+ {
+ var result = await sender.Send(new CleanRunCommand(fileStorage, identityDb), ct);
+ return Results.Ok(result.Value);
+ });
+ }
+}
\ No newline at end of file
diff --git a/backend/src/Modules/Identity/Application/Abstractions/IIdentityDbContext.cs b/backend/src/Modules/Auth/Application/Abstractions/IIdentityDbContext.cs
similarity index 59%
rename from backend/src/Modules/Identity/Application/Abstractions/IIdentityDbContext.cs
rename to backend/src/Modules/Auth/Application/Abstractions/IIdentityDbContext.cs
index c02d5e2..a323aa8 100644
--- a/backend/src/Modules/Identity/Application/Abstractions/IIdentityDbContext.cs
+++ b/backend/src/Modules/Auth/Application/Abstractions/IIdentityDbContext.cs
@@ -1,14 +1,14 @@
using System.Threading;
using System.Threading.Tasks;
-using Knot.Modules.Identity.Domain;
+using Knot.Modules.Auth.Domain;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
-namespace Knot.Modules.Identity.Application.Abstractions;
+namespace Knot.Modules.Auth.Application.Abstractions;
-public interface IIdentityDbContext
+public interface IAuthDbContext
{
DbSet Users { get; }
- DbSet Friendships { get; }
Task SaveChangesAsync(CancellationToken cancellationToken);
}
+
diff --git a/backend/src/Modules/Identity/Application/Abstractions/IIdentityUnitOfWork.cs b/backend/src/Modules/Auth/Application/Abstractions/IIdentityUnitOfWork.cs
similarity index 54%
rename from backend/src/Modules/Identity/Application/Abstractions/IIdentityUnitOfWork.cs
rename to backend/src/Modules/Auth/Application/Abstractions/IIdentityUnitOfWork.cs
index ca4361c..11f260e 100644
--- a/backend/src/Modules/Identity/Application/Abstractions/IIdentityUnitOfWork.cs
+++ b/backend/src/Modules/Auth/Application/Abstractions/IIdentityUnitOfWork.cs
@@ -1,10 +1,11 @@
using Knot.Shared.Kernel;
-namespace Knot.Modules.Identity.Application.Abstractions;
+namespace Knot.Modules.Auth.Application.Abstractions;
///
/// Unit of Work специфичный для модуля Identity.
///
-public interface IIdentityUnitOfWork : IUnitOfWork
+public interface IAuthUnitOfWork : IUnitOfWork
{
}
+
diff --git a/backend/src/Modules/Auth/Application/Abstractions/IJwtTokenProvider.cs b/backend/src/Modules/Auth/Application/Abstractions/IJwtTokenProvider.cs
new file mode 100644
index 0000000..4cf048e
--- /dev/null
+++ b/backend/src/Modules/Auth/Application/Abstractions/IJwtTokenProvider.cs
@@ -0,0 +1,9 @@
+using Knot.Modules.Auth.Domain;
+
+namespace Knot.Modules.Auth.Application.Abstractions;
+
+public interface IJwtTokenProvider
+{
+ string Generate(User user);
+}
+
diff --git a/backend/src/Host/Models/Auth/AuthResponseDto.cs b/backend/src/Modules/Auth/Application/Auth/DTOs/AuthResponseDto.cs
similarity index 85%
rename from backend/src/Host/Models/Auth/AuthResponseDto.cs
rename to backend/src/Modules/Auth/Application/Auth/DTOs/AuthResponseDto.cs
index 31a503a..4e28fd4 100644
--- a/backend/src/Host/Models/Auth/AuthResponseDto.cs
+++ b/backend/src/Modules/Auth/Application/Auth/DTOs/AuthResponseDto.cs
@@ -1,6 +1,6 @@
using System;
-namespace Host.Models;
+namespace Knot.Modules.Auth.Application.Auth.DTOs;
public record AuthResponseDto(
string Token,
@@ -18,3 +18,4 @@ public record AuthUserDto(
bool IsOnline,
DateTime CreatedAt
);
+
diff --git a/backend/src/Host/Models/Auth/ResetPasswordDto.cs b/backend/src/Modules/Auth/Application/Auth/DTOs/ResetPasswordDto.cs
similarity index 64%
rename from backend/src/Host/Models/Auth/ResetPasswordDto.cs
rename to backend/src/Modules/Auth/Application/Auth/DTOs/ResetPasswordDto.cs
index feeb162..d2a1d2a 100644
--- a/backend/src/Host/Models/Auth/ResetPasswordDto.cs
+++ b/backend/src/Modules/Auth/Application/Auth/DTOs/ResetPasswordDto.cs
@@ -1,6 +1,7 @@
-namespace Host.Models;
+namespace Knot.Modules.Auth.Application.Auth.DTOs;
public class ResetPasswordDto
{
public string NewPassword { get; set; } = string.Empty;
}
+
diff --git a/backend/src/Modules/Identity/Application/Users/Auth/AuthResponseDto.cs b/backend/src/Modules/Auth/Application/Users/Auth/AuthResponseDto.cs
similarity index 84%
rename from backend/src/Modules/Identity/Application/Users/Auth/AuthResponseDto.cs
rename to backend/src/Modules/Auth/Application/Users/Auth/AuthResponseDto.cs
index fdedad6..17f2752 100644
--- a/backend/src/Modules/Identity/Application/Users/Auth/AuthResponseDto.cs
+++ b/backend/src/Modules/Auth/Application/Users/Auth/AuthResponseDto.cs
@@ -1,6 +1,6 @@
using System;
-namespace Knot.Modules.Identity.Application.Users.Auth;
+namespace Knot.Modules.Auth.Application.Users.Auth;
public record AuthResponseDto(
string Token,
@@ -18,3 +18,4 @@ public record AuthUserDto(
bool IsOnline,
DateTime CreatedAt
);
+
diff --git a/backend/src/Modules/Identity/Application/Users/GetMe/GetMe.cs b/backend/src/Modules/Auth/Application/Users/GetMe/GetMe.cs
similarity index 78%
rename from backend/src/Modules/Identity/Application/Users/GetMe/GetMe.cs
rename to backend/src/Modules/Auth/Application/Users/GetMe/GetMe.cs
index 2db2b19..7384a81 100644
--- a/backend/src/Modules/Identity/Application/Users/GetMe/GetMe.cs
+++ b/backend/src/Modules/Auth/Application/Users/GetMe/GetMe.cs
@@ -1,9 +1,9 @@
using Knot.Shared.Kernel;
-using Knot.Modules.Identity.Domain;
-using Knot.Modules.Identity.Application.Abstractions;
-using Knot.Modules.Identity.Application.Users.Auth;
+using Knot.Modules.Auth.Domain;
-namespace Knot.Modules.Identity.Application.Users.GetMe;
+using Knot.Modules.Auth.Application.Users.Auth;
+
+namespace Knot.Modules.Auth.Application.Users.GetMe;
public sealed record GetMeQuery(Guid UserId) : IQuery;
@@ -21,7 +21,7 @@ internal sealed class GetMeQueryHandler : IQueryHandler(IdentityErrors.UserNotFound);
+ return Result.Failure(AuthErrors.UserNotFound);
}
var response = new AuthResponseDto(
@@ -42,3 +42,4 @@ internal sealed class GetMeQueryHandler : IQueryHandler
/// Команда для входа пользователя. Возвращает AuthResponseDto.
@@ -28,7 +29,7 @@ public sealed class LoginUserCommandHandler : ICommandHandler(IdentityErrors.IdentityInvalidCredentials);
+ return Result.Failure(AuthErrors.IdentityInvalidCredentials);
}
string token = _tokenProvider.Generate(user);
@@ -49,3 +50,4 @@ public sealed class LoginUserCommandHandler : ICommandHandler
/// Команда для регистрации нового пользователя.
@@ -23,13 +24,13 @@ public sealed record RegisterUserCommand(
public sealed class RegisterUserCommandHandler : ICommandHandler
{
private readonly IUserRepository _userRepository;
- private readonly IIdentityUnitOfWork _unitOfWork;
+ private readonly IAuthUnitOfWork _unitOfWork;
private readonly ISettingsService _settings;
private readonly IJwtTokenProvider _tokenProvider;
public RegisterUserCommandHandler(
IUserRepository userRepository,
- IIdentityUnitOfWork unitOfWork,
+ IAuthUnitOfWork unitOfWork,
ISettingsService settings,
IJwtTokenProvider tokenProvider)
{
@@ -43,13 +44,13 @@ public sealed class RegisterUserCommandHandler : ICommandHandler(IdentityErrors.IdentityRegistrationDisabled);
+ return Result.Failure(AuthErrors.IdentityRegistrationDisabled);
}
// 1. Проверка уникальности username
if (!await _userRepository.IsUsernameUniqueAsync(request.Username, cancellationToken))
{
- return Result.Failure(IdentityErrors.IdentityUsernameNotUnique);
+ return Result.Failure(AuthErrors.IdentityUsernameNotUnique);
}
// 2. Хеширование пароля (здесь будет вызов сервиса, пока заглушка)
@@ -85,3 +86,4 @@ public sealed class RegisterUserCommandHandler : ICommandHandler
/// Регистрация сервисов модуля Identity.
///
- public static IServiceCollection AddIdentityModule(
+ public static IServiceCollection AddAuthModule(
this IServiceCollection services,
IConfiguration configuration)
{
// Настройка базы данных
string connectionString = configuration.GetConnectionString("DefaultConnection")!;
- services.AddDbContext(options =>
+ services.AddDbContext(options =>
options.UseNpgsql(connectionString));
// Регистрация Unit of Work и Репозиториев
- services.AddScoped(sp => sp.GetRequiredService());
- services.AddScoped(sp => sp.GetRequiredService());
+ services.AddScoped(sp => sp.GetRequiredService());
+ services.AddScoped(sp => sp.GetRequiredService());
services.AddScoped();
services.AddScoped();
- services.AddScoped();
+ services.AddScoped();
// Регистрация MediatR для этого модуля
services.AddMediatR(config =>
@@ -38,3 +38,4 @@ public static class DependencyInjection
return services;
}
}
+
diff --git a/backend/src/Modules/Identity/Domain/IdentityErrors.cs b/backend/src/Modules/Auth/Domain/AuthErrors.cs
similarity index 92%
rename from backend/src/Modules/Identity/Domain/IdentityErrors.cs
rename to backend/src/Modules/Auth/Domain/AuthErrors.cs
index 5e317da..8361373 100644
--- a/backend/src/Modules/Identity/Domain/IdentityErrors.cs
+++ b/backend/src/Modules/Auth/Domain/AuthErrors.cs
@@ -1,8 +1,8 @@
using Knot.Shared.Kernel;
-namespace Knot.Modules.Identity.Domain;
+namespace Knot.Modules.Auth.Domain;
-public static class IdentityErrors
+public static class AuthErrors
{
public static readonly Error FriendsNotFound = new Error("Friends.NotFound", "Friendship not found");
public static readonly Error FriendsSelf = new Error("Friends.Self", "Cannot add yourself");
@@ -12,3 +12,4 @@ public static class IdentityErrors
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/backend/src/Modules/Identity/Domain/IUserRepository.cs b/backend/src/Modules/Auth/Domain/IUserRepository.cs
similarity index 90%
rename from backend/src/Modules/Identity/Domain/IUserRepository.cs
rename to backend/src/Modules/Auth/Domain/IUserRepository.cs
index cdf77d6..287e2cc 100644
--- a/backend/src/Modules/Identity/Domain/IUserRepository.cs
+++ b/backend/src/Modules/Auth/Domain/IUserRepository.cs
@@ -1,6 +1,6 @@
-using Knot.Modules.Identity.Domain;
+using Knot.Modules.Auth.Domain;
-namespace Knot.Modules.Identity.Domain;
+namespace Knot.Modules.Auth.Domain;
///
/// Интерфейс репозитория для работы с пользователями.
@@ -15,3 +15,4 @@ public interface IUserRepository
void Add(User user);
void Update(User user);
}
+
diff --git a/backend/src/Modules/Identity/Domain/User.cs b/backend/src/Modules/Auth/Domain/User.cs
similarity index 97%
rename from backend/src/Modules/Identity/Domain/User.cs
rename to backend/src/Modules/Auth/Domain/User.cs
index 1744493..0820d44 100644
--- a/backend/src/Modules/Identity/Domain/User.cs
+++ b/backend/src/Modules/Auth/Domain/User.cs
@@ -1,6 +1,6 @@
using Knot.Shared.Kernel;
-namespace Knot.Modules.Identity.Domain;
+namespace Knot.Modules.Auth.Domain;
///
/// Сущность пользователя в контексте идентификации (Identity).
@@ -58,3 +58,4 @@ public sealed class User : AggregateRoot
PasswordHash = newPasswordHash;
}
}
+
diff --git a/backend/src/Modules/Identity/Infrastructure/Authentication/JwtTokenProvider.cs b/backend/src/Modules/Auth/Infrastructure/Authentication/JwtTokenProvider.cs
similarity index 89%
rename from backend/src/Modules/Identity/Infrastructure/Authentication/JwtTokenProvider.cs
rename to backend/src/Modules/Auth/Infrastructure/Authentication/JwtTokenProvider.cs
index 7ce5b4f..35037eb 100644
--- a/backend/src/Modules/Identity/Infrastructure/Authentication/JwtTokenProvider.cs
+++ b/backend/src/Modules/Auth/Infrastructure/Authentication/JwtTokenProvider.cs
@@ -3,10 +3,10 @@ using System.Security.Claims;
using System.Text;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
-using Knot.Modules.Identity.Application.Abstractions;
-using Knot.Modules.Identity.Domain;
+using Knot.Modules.Auth.Application.Abstractions;
+using Knot.Modules.Auth.Domain;
-namespace Knot.Modules.Identity.Infrastructure.Authentication;
+namespace Knot.Modules.Auth.Infrastructure.Authentication;
public sealed class JwtTokenProvider : IJwtTokenProvider
{
@@ -43,3 +43,4 @@ public sealed class JwtTokenProvider : IJwtTokenProvider
return new JwtSecurityTokenHandler().WriteToken(token);
}
}
+
diff --git a/backend/src/Modules/Auth/Infrastructure/Persistence/AuthDbContext.cs b/backend/src/Modules/Auth/Infrastructure/Persistence/AuthDbContext.cs
new file mode 100644
index 0000000..ce03a31
--- /dev/null
+++ b/backend/src/Modules/Auth/Infrastructure/Persistence/AuthDbContext.cs
@@ -0,0 +1,81 @@
+using MediatR;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Diagnostics;
+using Microsoft.EntityFrameworkCore.Metadata;
+using Knot.Modules.Auth.Application.Abstractions;
+using Knot.Modules.Auth.Domain;
+using Knot.Shared.Kernel;
+using Knot.Shared.Kernel.Security;
+
+namespace Knot.Modules.Auth.Infrastructure.Persistence;
+
+///
+/// Контекст базы данных для модуля Identity.
+///
+public sealed class AuthDbContext : DbContext, IAuthUnitOfWork, Knot.Modules.Auth.Application.Abstractions.IAuthDbContext
+{
+ private readonly IMediator _mediator;
+ private readonly IEncryptionService _encryptionService;
+
+ public AuthDbContext(DbContextOptions options, IMediator mediator, IEncryptionService encryptionService)
+ : base(options)
+ {
+ _mediator = mediator;
+ _encryptionService = encryptionService;
+ }
+
+ public DbSet Users => Set();
+
+
+
+ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
+ {
+ base.OnConfiguring(optionsBuilder);
+ optionsBuilder.ConfigureWarnings(w => w.Ignore(RelationalEventId.PendingModelChangesWarning));
+ }
+
+ protected override void OnModelCreating(ModelBuilder modelBuilder)
+ {
+ modelBuilder.HasDefaultSchema("identity");
+
+
+
+ modelBuilder.Entity(builder =>
+ {
+ builder.ToTable("Users");
+ builder.HasKey(u => u.Id);
+ builder.Property(u => u.Username).IsRequired().HasMaxLength(50);
+ builder.HasIndex(u => u.Username).IsUnique();
+ builder.Property(u => u.PasswordHash).IsRequired();
+
+ });
+ }
+
+ public override async Task SaveChangesAsync(CancellationToken cancellationToken = default)
+ {
+ var domainEvents = ChangeTracker
+ .Entries()
+ .SelectMany(x =>
+
+ {
+ if (x.Entity is AggregateRoot root)
+ {
+ var events = root.GetDomainEvents().ToList();
+ root.ClearDomainEvents();
+ return events;
+ }
+ return Enumerable.Empty();
+ })
+ .ToList();
+
+ int result = await base.SaveChangesAsync(cancellationToken);
+
+ foreach (var domainEvent in domainEvents)
+ {
+ await _mediator.Publish(domainEvent, cancellationToken);
+ }
+
+ return result;
+ }
+}
+
diff --git a/backend/src/Modules/Identity/Infrastructure/Persistence/UserRepository.cs b/backend/src/Modules/Auth/Infrastructure/Persistence/UserRepository.cs
similarity index 89%
rename from backend/src/Modules/Identity/Infrastructure/Persistence/UserRepository.cs
rename to backend/src/Modules/Auth/Infrastructure/Persistence/UserRepository.cs
index c81efc9..91835b8 100644
--- a/backend/src/Modules/Identity/Infrastructure/Persistence/UserRepository.cs
+++ b/backend/src/Modules/Auth/Infrastructure/Persistence/UserRepository.cs
@@ -1,16 +1,16 @@
using Microsoft.EntityFrameworkCore;
-using Knot.Modules.Identity.Domain;
+using Knot.Modules.Auth.Domain;
-namespace Knot.Modules.Identity.Infrastructure.Persistence;
+namespace Knot.Modules.Auth.Infrastructure.Persistence;
///
/// Реализация репозитория пользователей с использованием EF Core.
///
public sealed class UserRepository : IUserRepository
{
- private readonly IdentityDbContext _context;
+ private readonly AuthDbContext _context;
- public UserRepository(IdentityDbContext context)
+ public UserRepository(AuthDbContext context)
{
_context = context;
}
@@ -54,3 +54,4 @@ public sealed class UserRepository : IUserRepository
_context.Users.Update(user);
}
}
+
diff --git a/backend/src/Modules/Identity/Infrastructure/Services/UserDisplayNameProvider.cs b/backend/src/Modules/Auth/Infrastructure/Services/UserDisplayNameProvider.cs
similarity index 93%
rename from backend/src/Modules/Identity/Infrastructure/Services/UserDisplayNameProvider.cs
rename to backend/src/Modules/Auth/Infrastructure/Services/UserDisplayNameProvider.cs
index 04b9af8..94b4822 100644
--- a/backend/src/Modules/Identity/Infrastructure/Services/UserDisplayNameProvider.cs
+++ b/backend/src/Modules/Auth/Infrastructure/Services/UserDisplayNameProvider.cs
@@ -1,7 +1,7 @@
using Knot.Shared.Kernel;
-using Knot.Modules.Identity.Domain;
+using Knot.Modules.Auth.Domain;
-namespace Knot.Modules.Identity.Infrastructure.Services;
+namespace Knot.Modules.Auth.Infrastructure.Services;
public sealed class UserDisplayNameProvider : IUserDisplayNameProvider
{
@@ -40,3 +40,4 @@ public sealed class UserDisplayNameProvider : IUserDisplayNameProvider
return result;
}
}
+
diff --git a/backend/src/Modules/Identity/Knot.Modules.Identity.csproj b/backend/src/Modules/Auth/Knot.Modules.Auth.csproj
similarity index 91%
rename from backend/src/Modules/Identity/Knot.Modules.Identity.csproj
rename to backend/src/Modules/Auth/Knot.Modules.Auth.csproj
index f2abb95..887161b 100644
--- a/backend/src/Modules/Identity/Knot.Modules.Identity.csproj
+++ b/backend/src/Modules/Auth/Knot.Modules.Auth.csproj
@@ -8,10 +8,12 @@
+
+
diff --git a/backend/src/Modules/Identity/Infrastructure/Persistence/Migrations/20260311180816_InitialIdentity.Designer.cs b/backend/src/Modules/Auth/Migrations/20260322191927_InitialAuth.Designer.cs
similarity index 62%
rename from backend/src/Modules/Identity/Infrastructure/Persistence/Migrations/20260311180816_InitialIdentity.Designer.cs
rename to backend/src/Modules/Auth/Migrations/20260322191927_InitialAuth.Designer.cs
index ac05526..0c967f8 100644
--- a/backend/src/Modules/Identity/Infrastructure/Persistence/Migrations/20260311180816_InitialIdentity.Designer.cs
+++ b/backend/src/Modules/Auth/Migrations/20260322191927_InitialAuth.Designer.cs
@@ -1,19 +1,19 @@
-//
+//
using System;
+using Knot.Modules.Auth.Infrastructure.Persistence;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
-using Knot.Modules.Identity.Infrastructure.Persistence;
#nullable disable
-namespace Knot.Modules.Identity.Infrastructure.Persistence.Migrations
+namespace Knot.Modules.Auth.Migrations
{
- [DbContext(typeof(IdentityDbContext))]
- [Migration("20260311180816_InitialIdentity")]
- partial class InitialIdentity
+ [DbContext(typeof(AuthDbContext))]
+ [Migration("20260322191927_InitialAuth")]
+ partial class InitialAuth
{
///
protected override void BuildTargetModel(ModelBuilder modelBuilder)
@@ -26,20 +26,33 @@ namespace Knot.Modules.Identity.Infrastructure.Persistence.Migrations
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
- modelBuilder.Entity("Knot.Modules.Identity.Domain.User", b =>
+ modelBuilder.Entity("Knot.Modules.Auth.Domain.User", b =>
{
b.Property("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
+ b.Property("Avatar")
+ .HasColumnType("text");
+
+ b.Property("Bio")
+ .HasColumnType("text");
+
+ b.Property("Birthday")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone");
+
b.Property("DisplayName")
.IsRequired()
- .HasMaxLength(100)
- .HasColumnType("character varying(100)");
+ .HasColumnType("text");
b.Property("Email")
- .HasMaxLength(255)
- .HasColumnType("character varying(255)");
+ .HasColumnType("text");
+
+ b.Property("HideStoryViews")
+ .HasColumnType("boolean");
b.Property("PasswordHash")
.IsRequired()
diff --git a/backend/src/Modules/Identity/Infrastructure/Persistence/Migrations/20260311180816_InitialIdentity.cs b/backend/src/Modules/Auth/Migrations/20260322191927_InitialAuth.cs
similarity index 64%
rename from backend/src/Modules/Identity/Infrastructure/Persistence/Migrations/20260311180816_InitialIdentity.cs
rename to backend/src/Modules/Auth/Migrations/20260322191927_InitialAuth.cs
index 7086701..2f03e9a 100644
--- a/backend/src/Modules/Identity/Infrastructure/Persistence/Migrations/20260311180816_InitialIdentity.cs
+++ b/backend/src/Modules/Auth/Migrations/20260322191927_InitialAuth.cs
@@ -1,12 +1,12 @@
-using System;
+using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
-namespace Knot.Modules.Identity.Infrastructure.Persistence.Migrations
+namespace Knot.Modules.Auth.Migrations
{
///
- public partial class InitialIdentity : Migration
+ public partial class InitialAuth : Migration
{
///
protected override void Up(MigrationBuilder migrationBuilder)
@@ -22,8 +22,13 @@ namespace Knot.Modules.Identity.Infrastructure.Persistence.Migrations
Id = table.Column(type: "uuid", nullable: false),
Username = table.Column(type: "character varying(50)", maxLength: 50, nullable: false),
PasswordHash = table.Column(type: "text", nullable: false),
- DisplayName = table.Column(type: "character varying(100)", maxLength: 100, nullable: false),
- Email = table.Column(type: "character varying(255)", maxLength: 255, nullable: true)
+ DisplayName = table.Column(type: "text", nullable: false),
+ Email = table.Column(type: "text", nullable: true),
+ Bio = table.Column(type: "text", nullable: true),
+ Avatar = table.Column(type: "text", nullable: true),
+ Birthday = table.Column(type: "timestamp with time zone", nullable: true),
+ CreatedAt = table.Column(type: "timestamp with time zone", nullable: false),
+ HideStoryViews = table.Column(type: "boolean", nullable: false)
},
constraints: table =>
{
diff --git a/backend/src/Modules/Auth/Migrations/AuthDbContextModelSnapshot.cs b/backend/src/Modules/Auth/Migrations/AuthDbContextModelSnapshot.cs
new file mode 100644
index 0000000..6bdbe0f
--- /dev/null
+++ b/backend/src/Modules/Auth/Migrations/AuthDbContextModelSnapshot.cs
@@ -0,0 +1,73 @@
+//
+using System;
+using Knot.Modules.Auth.Infrastructure.Persistence;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
+
+#nullable disable
+
+namespace Knot.Modules.Auth.Migrations
+{
+ [DbContext(typeof(AuthDbContext))]
+ partial class AuthDbContextModelSnapshot : ModelSnapshot
+ {
+ protected override void BuildModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasDefaultSchema("identity")
+ .HasAnnotation("ProductVersion", "10.0.4")
+ .HasAnnotation("Relational:MaxIdentifierLength", 63);
+
+ NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
+
+ modelBuilder.Entity("Knot.Modules.Auth.Domain.User", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("Avatar")
+ .HasColumnType("text");
+
+ b.Property("Bio")
+ .HasColumnType("text");
+
+ b.Property("Birthday")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DisplayName")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Email")
+ .HasColumnType("text");
+
+ b.Property("HideStoryViews")
+ .HasColumnType("boolean");
+
+ b.Property("PasswordHash")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Username")
+ .IsRequired()
+ .HasMaxLength(50)
+ .HasColumnType("character varying(50)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Username")
+ .IsUnique();
+
+ b.ToTable("Users", "identity");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/backend/src/Modules/Auth/Presentation/Endpoints/AuthEndpoints.cs b/backend/src/Modules/Auth/Presentation/Endpoints/AuthEndpoints.cs
new file mode 100644
index 0000000..d522a27
--- /dev/null
+++ b/backend/src/Modules/Auth/Presentation/Endpoints/AuthEndpoints.cs
@@ -0,0 +1,39 @@
+using Carter;
+using Knot.Shared.Kernel;
+using Knot.Modules.Auth.Application.Users.Login;
+using Knot.Modules.Auth.Application.Users.Register;
+using Knot.Modules.Auth.Application.Users.GetMe;
+using MediatR;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Routing;
+using Microsoft.AspNetCore.Mvc;
+
+namespace Knot.Modules.Auth.Presentation.Endpoints;
+
+public sealed class AuthEndpoints : ICarterModule
+{
+ public void AddRoutes(IEndpointRouteBuilder app)
+ {
+ var group = app.MapGroup("api/auth");
+
+ group.MapPost("register", async ([FromBody] RegisterUserCommand command, ISender sender, CancellationToken ct) =>
+ {
+ var result = await sender.Send(command, ct);
+ return result.IsSuccess ? Results.Ok(result.Value) : Results.BadRequest(new { error = result.Error.Code ?? result.Error.Description });
+ });
+
+ group.MapPost("login", async ([FromBody] LoginUserCommand command, ISender sender, CancellationToken ct) =>
+ {
+ var result = await sender.Send(command, ct);
+ return result.IsSuccess ? Results.Ok(result.Value) : Results.Unauthorized();
+ });
+
+ group.MapGet("me", async (ISender sender, IUserContext userContext, CancellationToken ct) =>
+ {
+ var result = await sender.Send(new GetMeQuery(userContext.UserId), ct);
+ return result.IsSuccess ? Results.Ok(new { User = result.Value.User }) : Results.NotFound();
+ }).RequireAuthorization();
+ }
+}
+
diff --git a/backend/src/Modules/Identity/migrations.sql b/backend/src/Modules/Auth/migrations.sql
similarity index 100%
rename from backend/src/Modules/Identity/migrations.sql
rename to backend/src/Modules/Auth/migrations.sql
diff --git a/backend/src/Modules/Chats/Application/DTOs/CreatePersonalChatRequest.cs b/backend/src/Modules/Chats/Application/DTOs/CreatePersonalChatRequest.cs
deleted file mode 100644
index 8683e77..0000000
--- a/backend/src/Modules/Chats/Application/DTOs/CreatePersonalChatRequest.cs
+++ /dev/null
@@ -1,5 +0,0 @@
-using System;
-
-namespace Knot.Modules.Chats.Application.DTOs;
-
-public sealed record CreatePersonalChatRequest(Guid UserId);
diff --git a/backend/src/Modules/Chats/Application/DTOs/TogglePinResponse.cs b/backend/src/Modules/Chats/Application/DTOs/TogglePinResponse.cs
deleted file mode 100644
index 57c2ce0..0000000
--- a/backend/src/Modules/Chats/Application/DTOs/TogglePinResponse.cs
+++ /dev/null
@@ -1,3 +0,0 @@
-namespace Knot.Modules.Chats.Application.DTOs;
-
-public record TogglePinResponse(bool IsPinned);
diff --git a/backend/src/Modules/Chats/Migrations/20260319124845_InitialChats.Designer.cs b/backend/src/Modules/Chats/Migrations/20260319124845_InitialChats.Designer.cs
deleted file mode 100644
index 4aeba6c..0000000
--- a/backend/src/Modules/Chats/Migrations/20260319124845_InitialChats.Designer.cs
+++ /dev/null
@@ -1,99 +0,0 @@
-//
-using System;
-using Knot.Modules.Chats.Infrastructure.Persistence;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Migrations;
-using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
-using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
-
-#nullable disable
-
-namespace Knot.Modules.Chats.Migrations
-{
- [DbContext(typeof(ChatsDbContext))]
- [Migration("20260319124845_InitialChats")]
- partial class InitialChats
- {
- ///
- protected override void BuildTargetModel(ModelBuilder modelBuilder)
- {
-#pragma warning disable 612, 618
- modelBuilder
- .HasDefaultSchema("chats")
- .HasAnnotation("ProductVersion", "10.0.4")
- .HasAnnotation("Relational:MaxIdentifierLength", 63);
-
- NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
-
- modelBuilder.Entity("Knot.Modules.Chats.Domain.Chat", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("uuid");
-
- b.Property("Avatar")
- .HasColumnType("text");
-
- b.Property("CreatedAt")
- .HasColumnType("timestamp with time zone");
-
- b.Property("Description")
- .HasColumnType("text");
-
- b.Property("Name")
- .HasColumnType("text");
-
- b.Property("Type")
- .IsRequired()
- .HasColumnType("text");
-
- b.HasKey("Id");
-
- b.ToTable("Chats", "chats");
- });
-
- modelBuilder.Entity("Knot.Modules.Chats.Domain.Chat", b =>
- {
- b.OwnsMany("Knot.Modules.Chats.Domain.ChatMember", "Members", b1 =>
- {
- b1.Property("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("uuid");
-
- b1.Property("ChatId")
- .HasColumnType("uuid");
-
- b1.Property("IsMuted")
- .HasColumnType("boolean");
-
- b1.Property("IsPinned")
- .HasColumnType("boolean");
-
- b1.Property("JoinedAt")
- .HasColumnType("timestamp with time zone");
-
- b1.Property("Role")
- .IsRequired()
- .HasColumnType("text");
-
- b1.Property("UserId")
- .HasColumnType("uuid");
-
- b1.HasKey("Id");
-
- b1.HasIndex("ChatId", "UserId")
- .IsUnique();
-
- b1.ToTable("ChatMembers", "chats");
-
- b1.WithOwner()
- .HasForeignKey("ChatId");
- });
-
- b.Navigation("Members");
- });
-#pragma warning restore 612, 618
- }
- }
-}
diff --git a/backend/src/Modules/Chats/Migrations/20260320185319_AddHighWaterMark.cs b/backend/src/Modules/Chats/Migrations/20260320185319_AddHighWaterMark.cs
deleted file mode 100644
index a9da1fc..0000000
--- a/backend/src/Modules/Chats/Migrations/20260320185319_AddHighWaterMark.cs
+++ /dev/null
@@ -1,69 +0,0 @@
-using System;
-using Microsoft.EntityFrameworkCore.Migrations;
-
-#nullable disable
-
-namespace Knot.Modules.Chats.Migrations
-{
- ///
- public partial class AddHighWaterMark : Migration
- {
- ///
- protected override void Up(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.AddColumn(
- name: "LastMessageSequenceId",
- schema: "chats",
- table: "Chats",
- type: "bigint",
- nullable: false,
- defaultValue: 0L);
-
- migrationBuilder.AddColumn(
- name: "LastDeliveredMessageId",
- schema: "chats",
- table: "ChatMembers",
- type: "uuid",
- nullable: true);
-
- migrationBuilder.AddColumn(
- name: "LastReadMessageId",
- schema: "chats",
- table: "ChatMembers",
- type: "uuid",
- nullable: true);
-
- migrationBuilder.AddColumn(
- name: "LastReadSequenceId",
- schema: "chats",
- table: "ChatMembers",
- type: "bigint",
- nullable: false,
- defaultValue: 0L);
- }
-
- ///
- protected override void Down(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.DropColumn(
- name: "LastMessageSequenceId",
- schema: "chats",
- table: "Chats");
-
- migrationBuilder.DropColumn(
- name: "LastDeliveredMessageId",
- schema: "chats",
- table: "ChatMembers");
-
- migrationBuilder.DropColumn(
- name: "LastReadMessageId",
- schema: "chats",
- table: "ChatMembers");
-
- migrationBuilder.DropColumn(
- name: "LastReadSequenceId",
- schema: "chats",
- table: "ChatMembers");
- }
- }
-}
diff --git a/backend/src/Modules/Chats/Application/Abstractions/IChatsUnitOfWork.cs b/backend/src/Modules/Conversations/Application/Abstractions/IChatsUnitOfWork.cs
similarity index 73%
rename from backend/src/Modules/Chats/Application/Abstractions/IChatsUnitOfWork.cs
rename to backend/src/Modules/Conversations/Application/Abstractions/IChatsUnitOfWork.cs
index 5a52644..6c9bdb2 100644
--- a/backend/src/Modules/Chats/Application/Abstractions/IChatsUnitOfWork.cs
+++ b/backend/src/Modules/Conversations/Application/Abstractions/IChatsUnitOfWork.cs
@@ -1,6 +1,6 @@
using Knot.Shared.Kernel;
-namespace Knot.Modules.Chats.Application.Abstractions;
+namespace Knot.Modules.Conversations.Application.Abstractions;
///
/// Unit of Work специфичный для модуля Chats.
@@ -8,3 +8,4 @@ namespace Knot.Modules.Chats.Application.Abstractions;
public interface IChatsUnitOfWork : IUnitOfWork
{
}
+
diff --git a/backend/src/Modules/Chats/Application/Chats/Avatar/Avatar.cs b/backend/src/Modules/Conversations/Application/Chats/Avatar/Avatar.cs
similarity index 96%
rename from backend/src/Modules/Chats/Application/Chats/Avatar/Avatar.cs
rename to backend/src/Modules/Conversations/Application/Chats/Avatar/Avatar.cs
index 52168d0..64189be 100644
--- a/backend/src/Modules/Chats/Application/Chats/Avatar/Avatar.cs
+++ b/backend/src/Modules/Conversations/Application/Chats/Avatar/Avatar.cs
@@ -4,14 +4,14 @@ using System.Threading;
using System.Threading.Tasks;
using Knot.Shared.Kernel;
using Knot.Shared.Kernel.Storage;
-using Knot.Modules.Chats.Domain;
-using Knot.Modules.Chats.Application.Abstractions;
+using Knot.Modules.Conversations.Domain;
+using Knot.Modules.Conversations.Application.Abstractions;
using MediatR;
using System.Linq;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Processing;
-namespace Knot.Modules.Chats.Application.Chats.Avatar;
+namespace Knot.Modules.Conversations.Application.Chats.Avatar;
public record UploadGroupAvatarCommand(Guid ChatId, Guid UserId, string FileName, string ContentType, Stream FileStream) : ICommand;
@@ -127,3 +127,4 @@ internal sealed class RemoveGroupAvatarCommandHandler : ICommandHandler;
@@ -32,3 +32,4 @@ internal sealed class ClearChatCommandHandler : ICommandHandler
/// Команда для создания чата.
@@ -41,3 +41,4 @@ public sealed class CreateChatCommandHandler : ICommandHandler;
@@ -153,3 +155,5 @@ internal sealed class GetChatByIdQueryHandler : IQueryHandler>;
@@ -147,3 +149,5 @@ internal sealed class GetChatsQueryHandler : IQueryHandler;
@@ -36,3 +36,4 @@ public sealed class GetOrCreateFavoritesCommandHandler : ICommandHandler;
@@ -51,3 +51,4 @@ internal sealed class LeaveOrDeleteChatCommandHandler : ICommandHandler UserIdsToAdd) : ICommand;
@@ -70,3 +70,4 @@ internal sealed class RemoveMemberCommandHandler : ICommandHandler;
@@ -44,3 +44,4 @@ internal sealed class TogglePinCommandHandler : ICommandHandler;
@@ -46,3 +46,4 @@ internal sealed class UpdateChatCommandHandler : ICommandHandler UserIds);
+
diff --git a/backend/src/Modules/Chats/Application/DTOs/ChatDto.cs b/backend/src/Modules/Conversations/Application/DTOs/ChatDto.cs
similarity index 83%
rename from backend/src/Modules/Chats/Application/DTOs/ChatDto.cs
rename to backend/src/Modules/Conversations/Application/DTOs/ChatDto.cs
index 8664e61..de4a3c6 100644
--- a/backend/src/Modules/Chats/Application/DTOs/ChatDto.cs
+++ b/backend/src/Modules/Conversations/Application/DTOs/ChatDto.cs
@@ -1,7 +1,7 @@
using System;
using System.Collections.Generic;
-namespace Knot.Modules.Chats.Application.DTOs;
+namespace Knot.Modules.Conversations.Application.DTOs;
public record ChatDto(
Guid Id,
@@ -14,3 +14,4 @@ public record ChatDto(
List Messages,
int UnreadCount
);
+
diff --git a/backend/src/Modules/Chats/Application/DTOs/ChatMemberDto.cs b/backend/src/Modules/Conversations/Application/DTOs/ChatMemberDto.cs
similarity index 58%
rename from backend/src/Modules/Chats/Application/DTOs/ChatMemberDto.cs
rename to backend/src/Modules/Conversations/Application/DTOs/ChatMemberDto.cs
index 7798a15..70a9024 100644
--- a/backend/src/Modules/Chats/Application/DTOs/ChatMemberDto.cs
+++ b/backend/src/Modules/Conversations/Application/DTOs/ChatMemberDto.cs
@@ -1,7 +1,7 @@
-using Knot.Modules.Chats.Domain;
+using Knot.Modules.Conversations.Domain;
using System;
-namespace Knot.Modules.Chats.Application.DTOs;
+namespace Knot.Modules.Conversations.Application.DTOs;
public record ChatMemberDto(
Guid Id,
@@ -10,3 +10,4 @@ public record ChatMemberDto(
bool IsPinned,
ChatUserDto? User
);
+
diff --git a/backend/src/Modules/Chats/Application/DTOs/ChatMessageDto.cs b/backend/src/Modules/Conversations/Application/DTOs/ChatMessageDto.cs
similarity index 89%
rename from backend/src/Modules/Chats/Application/DTOs/ChatMessageDto.cs
rename to backend/src/Modules/Conversations/Application/DTOs/ChatMessageDto.cs
index 2589fc5..1e16e6e 100644
--- a/backend/src/Modules/Chats/Application/DTOs/ChatMessageDto.cs
+++ b/backend/src/Modules/Conversations/Application/DTOs/ChatMessageDto.cs
@@ -1,7 +1,7 @@
using System;
using System.Collections.Generic;
-namespace Knot.Modules.Chats.Application.DTOs;
+namespace Knot.Modules.Conversations.Application.DTOs;
public record ChatMessageDto(
Guid Id,
@@ -23,3 +23,4 @@ public record ChatMessageDto(
List Reactions,
List ReadBy
);
+
diff --git a/backend/src/Modules/Chats/Application/DTOs/ChatUserDto.cs b/backend/src/Modules/Conversations/Application/DTOs/ChatUserDto.cs
similarity index 74%
rename from backend/src/Modules/Chats/Application/DTOs/ChatUserDto.cs
rename to backend/src/Modules/Conversations/Application/DTOs/ChatUserDto.cs
index 22cd440..cba1c13 100644
--- a/backend/src/Modules/Chats/Application/DTOs/ChatUserDto.cs
+++ b/backend/src/Modules/Conversations/Application/DTOs/ChatUserDto.cs
@@ -1,6 +1,6 @@
using System;
-namespace Knot.Modules.Chats.Application.DTOs;
+namespace Knot.Modules.Conversations.Application.DTOs;
public record ChatUserDto(
Guid Id,
@@ -10,3 +10,4 @@ public record ChatUserDto(
bool IsOnline,
DateTime LastSeen
);
+
diff --git a/backend/src/Modules/Chats/Application/DTOs/CreateChatRequest.cs b/backend/src/Modules/Conversations/Application/DTOs/CreateChatRequest.cs
similarity index 59%
rename from backend/src/Modules/Chats/Application/DTOs/CreateChatRequest.cs
rename to backend/src/Modules/Conversations/Application/DTOs/CreateChatRequest.cs
index 9446c22..638431b 100644
--- a/backend/src/Modules/Chats/Application/DTOs/CreateChatRequest.cs
+++ b/backend/src/Modules/Conversations/Application/DTOs/CreateChatRequest.cs
@@ -1,7 +1,8 @@
using System;
using System.Collections.Generic;
-using Knot.Modules.Chats.Domain;
+using Knot.Modules.Conversations.Domain;
-namespace Knot.Modules.Chats.Application.DTOs;
+namespace Knot.Modules.Conversations.Application.DTOs;
public sealed record CreateChatRequest(string Name, ChatType Type, List MemberIds);
+
diff --git a/backend/src/Modules/Chats/Application/DTOs/CreateGroupChatRequest.cs b/backend/src/Modules/Conversations/Application/DTOs/CreateGroupChatRequest.cs
similarity index 69%
rename from backend/src/Modules/Chats/Application/DTOs/CreateGroupChatRequest.cs
rename to backend/src/Modules/Conversations/Application/DTOs/CreateGroupChatRequest.cs
index 122feee..8357fc1 100644
--- a/backend/src/Modules/Chats/Application/DTOs/CreateGroupChatRequest.cs
+++ b/backend/src/Modules/Conversations/Application/DTOs/CreateGroupChatRequest.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
-namespace Knot.Modules.Chats.Application.DTOs;
+namespace Knot.Modules.Conversations.Application.DTOs;
public sealed record CreateGroupChatRequest(string Name, List MemberIds);
+
diff --git a/backend/src/Modules/Conversations/Application/DTOs/CreatePersonalChatRequest.cs b/backend/src/Modules/Conversations/Application/DTOs/CreatePersonalChatRequest.cs
new file mode 100644
index 0000000..8878ab5
--- /dev/null
+++ b/backend/src/Modules/Conversations/Application/DTOs/CreatePersonalChatRequest.cs
@@ -0,0 +1,4 @@
+namespace Knot.Modules.Conversations.Application.DTOs;
+using System;
+
+public record CreatePersonalChatRequest(Guid UserId);
diff --git a/backend/src/Modules/Chats/Application/DTOs/MediaDto.cs b/backend/src/Modules/Conversations/Application/DTOs/MediaDto.cs
similarity index 69%
rename from backend/src/Modules/Chats/Application/DTOs/MediaDto.cs
rename to backend/src/Modules/Conversations/Application/DTOs/MediaDto.cs
index bf2e24d..c6dc45f 100644
--- a/backend/src/Modules/Chats/Application/DTOs/MediaDto.cs
+++ b/backend/src/Modules/Conversations/Application/DTOs/MediaDto.cs
@@ -1,6 +1,6 @@
using System;
-namespace Knot.Modules.Chats.Application.DTOs;
+namespace Knot.Modules.Conversations.Application.DTOs;
public record MediaDto(
Guid Id,
@@ -9,3 +9,4 @@ public record MediaDto(
string? Filename,
long? Size
);
+
diff --git a/backend/src/Modules/Chats/Application/DTOs/MessageDetailDto.cs b/backend/src/Modules/Conversations/Application/DTOs/MessageDetailDto.cs
similarity index 93%
rename from backend/src/Modules/Chats/Application/DTOs/MessageDetailDto.cs
rename to backend/src/Modules/Conversations/Application/DTOs/MessageDetailDto.cs
index 302662d..686350f 100644
--- a/backend/src/Modules/Chats/Application/DTOs/MessageDetailDto.cs
+++ b/backend/src/Modules/Conversations/Application/DTOs/MessageDetailDto.cs
@@ -1,7 +1,7 @@
using System;
using System.Collections.Generic;
-namespace Knot.Modules.Chats.Application.DTOs;
+namespace Knot.Modules.Conversations.Application.DTOs;
public record MessageDetailDto(
Guid Id,
@@ -41,3 +41,4 @@ public record MessageReactionDto(
Guid UserId,
MessageSenderDto? User
);
+
diff --git a/backend/src/Modules/Chats/Application/DTOs/MessageSenderDto.cs b/backend/src/Modules/Conversations/Application/DTOs/MessageSenderDto.cs
similarity index 69%
rename from backend/src/Modules/Chats/Application/DTOs/MessageSenderDto.cs
rename to backend/src/Modules/Conversations/Application/DTOs/MessageSenderDto.cs
index 55c31ae..c1c996e 100644
--- a/backend/src/Modules/Chats/Application/DTOs/MessageSenderDto.cs
+++ b/backend/src/Modules/Conversations/Application/DTOs/MessageSenderDto.cs
@@ -1,6 +1,6 @@
using System;
-namespace Knot.Modules.Chats.Application.DTOs;
+namespace Knot.Modules.Conversations.Application.DTOs;
public record MessageSenderDto(
Guid Id,
@@ -8,3 +8,4 @@ public record MessageSenderDto(
string DisplayName,
string? Avatar
);
+
diff --git a/backend/src/Modules/Chats/Application/DTOs/ReactionDto.cs b/backend/src/Modules/Conversations/Application/DTOs/ReactionDto.cs
similarity index 68%
rename from backend/src/Modules/Chats/Application/DTOs/ReactionDto.cs
rename to backend/src/Modules/Conversations/Application/DTOs/ReactionDto.cs
index 988bf86..d427841 100644
--- a/backend/src/Modules/Chats/Application/DTOs/ReactionDto.cs
+++ b/backend/src/Modules/Conversations/Application/DTOs/ReactionDto.cs
@@ -1,6 +1,6 @@
using System;
-namespace Knot.Modules.Chats.Application.DTOs;
+namespace Knot.Modules.Conversations.Application.DTOs;
public record ReactionDto(
Guid Id,
@@ -8,3 +8,4 @@ public record ReactionDto(
Guid UserId,
MessageSenderDto User
);
+
diff --git a/backend/src/Modules/Chats/Application/DTOs/ReadByDto.cs b/backend/src/Modules/Conversations/Application/DTOs/ReadByDto.cs
similarity index 51%
rename from backend/src/Modules/Chats/Application/DTOs/ReadByDto.cs
rename to backend/src/Modules/Conversations/Application/DTOs/ReadByDto.cs
index 0f853d0..2443891 100644
--- a/backend/src/Modules/Chats/Application/DTOs/ReadByDto.cs
+++ b/backend/src/Modules/Conversations/Application/DTOs/ReadByDto.cs
@@ -1,7 +1,8 @@
using System;
-namespace Knot.Modules.Chats.Application.DTOs;
+namespace Knot.Modules.Conversations.Application.DTOs;
public record ReadByDto(
Guid UserId
);
+
diff --git a/backend/src/Modules/Chats/Application/DTOs/SearchMessageDto.cs b/backend/src/Modules/Conversations/Application/DTOs/SearchMessageDto.cs
similarity index 91%
rename from backend/src/Modules/Chats/Application/DTOs/SearchMessageDto.cs
rename to backend/src/Modules/Conversations/Application/DTOs/SearchMessageDto.cs
index 82758a7..cc4744d 100644
--- a/backend/src/Modules/Chats/Application/DTOs/SearchMessageDto.cs
+++ b/backend/src/Modules/Conversations/Application/DTOs/SearchMessageDto.cs
@@ -1,7 +1,7 @@
using System;
using System.Collections.Generic;
-namespace Knot.Modules.Chats.Application.DTOs;
+namespace Knot.Modules.Conversations.Application.DTOs;
public record SearchMessageDto(
Guid Id,
@@ -30,3 +30,4 @@ public record SimpleReactionDto(
Guid UserId,
string Emoji
);
+
diff --git a/backend/src/Modules/Chats/Application/DTOs/SendMessageRequest.cs b/backend/src/Modules/Conversations/Application/DTOs/SendMessageRequest.cs
similarity index 86%
rename from backend/src/Modules/Chats/Application/DTOs/SendMessageRequest.cs
rename to backend/src/Modules/Conversations/Application/DTOs/SendMessageRequest.cs
index 7cbfa31..7e93202 100644
--- a/backend/src/Modules/Chats/Application/DTOs/SendMessageRequest.cs
+++ b/backend/src/Modules/Conversations/Application/DTOs/SendMessageRequest.cs
@@ -1,7 +1,7 @@
using System;
using System.Collections.Generic;
-namespace Knot.Modules.Chats.Application.DTOs;
+namespace Knot.Modules.Conversations.Application.DTOs;
public sealed record SendMessageRequest(
string? Content,
@@ -12,3 +12,4 @@ public sealed record SendMessageRequest(
Guid? ForwardedFromId = null);
public sealed record AttachmentDto(string Type, string Url, string? FileName, long? FileSize);
+
diff --git a/backend/src/Modules/Chats/Application/DTOs/SharedMediaDto.cs b/backend/src/Modules/Conversations/Application/DTOs/SharedMediaDto.cs
similarity index 86%
rename from backend/src/Modules/Chats/Application/DTOs/SharedMediaDto.cs
rename to backend/src/Modules/Conversations/Application/DTOs/SharedMediaDto.cs
index 8914fd5..917406b 100644
--- a/backend/src/Modules/Chats/Application/DTOs/SharedMediaDto.cs
+++ b/backend/src/Modules/Conversations/Application/DTOs/SharedMediaDto.cs
@@ -1,7 +1,7 @@
using System;
using System.Collections.Generic;
-namespace Knot.Modules.Chats.Application.DTOs;
+namespace Knot.Modules.Conversations.Application.DTOs;
public record SharedMediaDto(
Guid Id,
@@ -18,3 +18,4 @@ public record SharedMediaDto(
string? Type,
List? Media
);
+
diff --git a/backend/src/Modules/Conversations/Application/DTOs/TogglePinResponse.cs b/backend/src/Modules/Conversations/Application/DTOs/TogglePinResponse.cs
new file mode 100644
index 0000000..173e97d
--- /dev/null
+++ b/backend/src/Modules/Conversations/Application/DTOs/TogglePinResponse.cs
@@ -0,0 +1,4 @@
+namespace Knot.Modules.Conversations.Application.DTOs;
+
+public record TogglePinResponse(bool IsPinned);
+
diff --git a/backend/src/Modules/Chats/Application/DTOs/UpdateChatRequest.cs b/backend/src/Modules/Conversations/Application/DTOs/UpdateChatRequest.cs
similarity index 57%
rename from backend/src/Modules/Chats/Application/DTOs/UpdateChatRequest.cs
rename to backend/src/Modules/Conversations/Application/DTOs/UpdateChatRequest.cs
index 2d95905..e361993 100644
--- a/backend/src/Modules/Chats/Application/DTOs/UpdateChatRequest.cs
+++ b/backend/src/Modules/Conversations/Application/DTOs/UpdateChatRequest.cs
@@ -1,3 +1,4 @@
-namespace Knot.Modules.Chats.Application.DTOs;
+namespace Knot.Modules.Conversations.Application.DTOs;
public sealed record UpdateChatRequest(string? Name, string? Description);
+
diff --git a/backend/src/Modules/Chats/Application/DTOs/UploadFileResponseDto.cs b/backend/src/Modules/Conversations/Application/DTOs/UploadFileResponseDto.cs
similarity index 71%
rename from backend/src/Modules/Chats/Application/DTOs/UploadFileResponseDto.cs
rename to backend/src/Modules/Conversations/Application/DTOs/UploadFileResponseDto.cs
index 1ee6f95..fb5b995 100644
--- a/backend/src/Modules/Chats/Application/DTOs/UploadFileResponseDto.cs
+++ b/backend/src/Modules/Conversations/Application/DTOs/UploadFileResponseDto.cs
@@ -1,10 +1,11 @@
using System;
using System.Collections.Generic;
-namespace Knot.Modules.Chats.Application.DTOs;
+namespace Knot.Modules.Conversations.Application.DTOs;
public record UploadFileResponseDto(
string Url,
string Filename,
long Size
);
+
diff --git a/backend/src/Modules/Chats/Application/Messages/Delete/DeleteMessagesCommand.cs b/backend/src/Modules/Conversations/Application/Messages/Delete/DeleteMessagesCommand.cs
similarity index 87%
rename from backend/src/Modules/Chats/Application/Messages/Delete/DeleteMessagesCommand.cs
rename to backend/src/Modules/Conversations/Application/Messages/Delete/DeleteMessagesCommand.cs
index 1106119..b0a2563 100644
--- a/backend/src/Modules/Chats/Application/Messages/Delete/DeleteMessagesCommand.cs
+++ b/backend/src/Modules/Conversations/Application/Messages/Delete/DeleteMessagesCommand.cs
@@ -1,11 +1,13 @@
+using Knot.Modules.Messaging.Application.Abstractions;
+using Knot.Modules.Messaging.Domain;
using MediatR;
using Microsoft.AspNetCore.SignalR;
-using global::Knot.Modules.Chats.Domain;
-using global::Knot.Modules.Chats.Infrastructure.SignalR;
+using global::Knot.Modules.Conversations.Domain;
+using global::Knot.Modules.Conversations.Infrastructure.SignalR;
using global::Knot.Shared.Kernel;
-using global::Knot.Modules.Chats.Application.Abstractions;
+using global::Knot.Modules.Conversations.Application.Abstractions;
-namespace Knot.Modules.Chats.Application.Messages.Delete;
+namespace Knot.Modules.Conversations.Application.Messages.Delete;
public sealed record DeleteMessagesCommand(
Guid ChatId,
@@ -77,3 +79,5 @@ public sealed class DeleteMessagesCommandHandler : ICommandHandler>;
@@ -145,3 +147,5 @@ internal sealed class GetMessagesQueryHandler : IQueryHandler>;
@@ -125,3 +127,5 @@ internal sealed class GetSharedMediaQueryHandler : IQueryHandler>;
@@ -66,3 +68,5 @@ internal sealed class SearchMessagesQueryHandler : IQueryHandler
/// Команда для отправки сообщения в чат.
@@ -133,3 +135,5 @@ public sealed class SendMessageCommandHandler : ICommandHandler;
@@ -42,3 +42,4 @@ internal sealed class UploadFileCommandHandler : ICommandHandler
+ /// ╨а╨╡╨│╨╕╤Б╤В╤А╨░╤Ж╨╕╤П ╤Б╨╡╤А╨▓╨╕╤Б╨╛╨▓ ╨╝╨╛╨┤╤Г╨╗╤П Chats.
+ ///
+ public static IServiceCollection AddConversationsModule(
+ this IServiceCollection services,
+ IConfiguration configuration)
+ {
+ // в•ЁР╨░╤Б╤В╤А╨╛╨╣╨║╨░ ╨▒╨░╨╖╤Л ╨┤╨░╨╜╨╜╤Л╤Е
+ string connectionString = configuration.GetConnectionString("DefaultConnection")!;
+
+ services.AddDbContext(options =>
+ options.UseNpgsql(connectionString));
+
+ // MongoDB Setup for Messages
+
+ var mongoConnectionString = configuration.GetConnectionString("MongoConnection") ?? "mongodb://localhost:27017";
+
+ // Registration
+ services.AddScoped(sp => sp.GetRequiredService());
+ services.AddScoped();
+
+ // MediatR
+ services.AddMediatR(config =>
+ config.RegisterServicesFromAssembly(typeof(DependencyInjection).Assembly));
+
+ services.AddScoped();
+ services.AddScoped();
+ return services;
+ }
+}
\ No newline at end of file
diff --git a/backend/src/Modules/Chats/Domain/Chat.cs b/backend/src/Modules/Conversations/Domain/Chat.cs
similarity index 98%
rename from backend/src/Modules/Chats/Domain/Chat.cs
rename to backend/src/Modules/Conversations/Domain/Chat.cs
index 1e8cc24..73417e9 100644
--- a/backend/src/Modules/Chats/Domain/Chat.cs
+++ b/backend/src/Modules/Conversations/Domain/Chat.cs
@@ -1,6 +1,6 @@
using Knot.Shared.Kernel;
-namespace Knot.Modules.Chats.Domain;
+namespace Knot.Modules.Conversations.Domain;
public sealed record ChatCreatedDomainEvent(Chat Chat) : IDomainEvent;
public sealed record ChatMemberAddedDomainEvent(Guid ChatId, Guid UserId) : IDomainEvent;
@@ -154,3 +154,4 @@ public sealed class ChatMember : Entity
LastDeliveredMessageId = messageId;
}
}
+
diff --git a/backend/src/Modules/Chats/Domain/ChatConstants.cs b/backend/src/Modules/Conversations/Domain/ChatConstants.cs
similarity index 83%
rename from backend/src/Modules/Chats/Domain/ChatConstants.cs
rename to backend/src/Modules/Conversations/Domain/ChatConstants.cs
index 3faae01..33e0890 100644
--- a/backend/src/Modules/Chats/Domain/ChatConstants.cs
+++ b/backend/src/Modules/Conversations/Domain/ChatConstants.cs
@@ -1,4 +1,4 @@
-namespace Knot.Modules.Chats.Domain;
+namespace Knot.Modules.Conversations.Domain;
public static class ChatConstants
{
@@ -7,3 +7,4 @@ public static class ChatConstants
public const int SearchMessagesLimit = 50;
public const int MaxFileUploadSizeMb = 50;
}
+
diff --git a/backend/src/Modules/Chats/Domain/ChatErrors.cs b/backend/src/Modules/Conversations/Domain/ChatErrors.cs
similarity index 97%
rename from backend/src/Modules/Chats/Domain/ChatErrors.cs
rename to backend/src/Modules/Conversations/Domain/ChatErrors.cs
index b48bd62..0877f6b 100644
--- a/backend/src/Modules/Chats/Domain/ChatErrors.cs
+++ b/backend/src/Modules/Conversations/Domain/ChatErrors.cs
@@ -1,6 +1,6 @@
using Knot.Shared.Kernel;
-namespace Knot.Modules.Chats.Domain;
+namespace Knot.Modules.Conversations.Domain;
public static class ChatErrors
{
@@ -19,3 +19,4 @@ public static class ChatErrors
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/backend/src/Modules/Chats/Domain/IChatRepository.cs b/backend/src/Modules/Conversations/Domain/IChatRepository.cs
similarity index 81%
rename from backend/src/Modules/Chats/Domain/IChatRepository.cs
rename to backend/src/Modules/Conversations/Domain/IChatRepository.cs
index 97c9597..4eb7698 100644
--- a/backend/src/Modules/Chats/Domain/IChatRepository.cs
+++ b/backend/src/Modules/Conversations/Domain/IChatRepository.cs
@@ -1,6 +1,6 @@
-using Knot.Modules.Chats.Domain;
+using Knot.Modules.Conversations.Domain;
-namespace Knot.Modules.Chats.Domain;
+namespace Knot.Modules.Conversations.Domain;
public interface IChatRepository
{
@@ -11,3 +11,4 @@ public interface IChatRepository
Task GetFavoritesAsync(Guid userId, CancellationToken cancellationToken);
Task> GetUserChatsAsync(Guid userId, CancellationToken cancellationToken);
}
+
diff --git a/backend/src/Modules/Chats/Infrastructure/Handlers/ChatCreatedDomainEventHandler.cs b/backend/src/Modules/Conversations/Infrastructure/Handlers/ChatCreatedDomainEventHandler.cs
similarity index 96%
rename from backend/src/Modules/Chats/Infrastructure/Handlers/ChatCreatedDomainEventHandler.cs
rename to backend/src/Modules/Conversations/Infrastructure/Handlers/ChatCreatedDomainEventHandler.cs
index 27aa7a5..4dd2403 100644
--- a/backend/src/Modules/Chats/Infrastructure/Handlers/ChatCreatedDomainEventHandler.cs
+++ b/backend/src/Modules/Conversations/Infrastructure/Handlers/ChatCreatedDomainEventHandler.cs
@@ -1,10 +1,10 @@
using Knot.Shared.Kernel;
-using Knot.Modules.Chats.Domain;
-using Knot.Modules.Chats.Infrastructure.SignalR;
+using Knot.Modules.Conversations.Domain;
+using Knot.Modules.Conversations.Infrastructure.SignalR;
using MediatR;
using Microsoft.AspNetCore.SignalR;
-namespace Knot.Modules.Chats.Infrastructure.Handlers;
+namespace Knot.Modules.Conversations.Infrastructure.Handlers;
///
/// Обработчик доменного события создания чата.
@@ -89,3 +89,4 @@ public sealed class ChatCreatedDomainEventHandler : INotificationHandler
/// Контекст базы данных для модуля чатов.
@@ -85,3 +85,4 @@ public sealed class ChatsDbContext : DbContext, IChatsUnitOfWork
return result;
}
}
+
diff --git a/backend/src/Modules/Conversations/Infrastructure/Services/ChatAccessProvider.cs b/backend/src/Modules/Conversations/Infrastructure/Services/ChatAccessProvider.cs
new file mode 100644
index 0000000..b7f1a7e
--- /dev/null
+++ b/backend/src/Modules/Conversations/Infrastructure/Services/ChatAccessProvider.cs
@@ -0,0 +1,11 @@
+namespace Knot.Modules.Conversations.Infrastructure.Services;
+using Knot.Modules.Messaging.Application.Abstractions;
+using Knot.Modules.Conversations.Infrastructure.Persistence;
+using Microsoft.EntityFrameworkCore;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+public class ChatAccessProvider : IChatAccessProvider { private readonly ChatsDbContext _db; public ChatAccessProvider(ChatsDbContext db) { _db = db; } public Task> GetValidChatIdsForUserAsync(Guid userId, CancellationToken ct) { return _db.Chats.Where(c => c.Members.Any(m => m.UserId == userId)).Select(c => c.Id).ToListAsync(ct); } }
+
diff --git a/backend/src/Modules/Chats/Infrastructure/SignalR/ChatHub.cs b/backend/src/Modules/Conversations/Infrastructure/SignalR/ChatHub.cs
similarity index 98%
rename from backend/src/Modules/Chats/Infrastructure/SignalR/ChatHub.cs
rename to backend/src/Modules/Conversations/Infrastructure/SignalR/ChatHub.cs
index 31b1e38..7de3736 100644
--- a/backend/src/Modules/Chats/Infrastructure/SignalR/ChatHub.cs
+++ b/backend/src/Modules/Conversations/Infrastructure/SignalR/ChatHub.cs
@@ -4,15 +4,15 @@ using MediatR;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.Logging;
-using Knot.Modules.Chats.Application.Messages.Send;
-using Knot.Modules.Chats.Application.Messages.Read;
-using Knot.Modules.Chats.Application.Messages.Delete;
-using Knot.Modules.Chats.Application.Messages.React;
-using Knot.Modules.Chats.Domain;
+using Knot.Modules.Conversations.Application.Messages.Send;
+using Knot.Modules.Conversations.Application.Messages.Read;
+using Knot.Modules.Conversations.Application.Messages.Delete;
+using Knot.Modules.Conversations.Application.Messages.React;
+using Knot.Modules.Conversations.Domain;
using Knot.Shared.Kernel;
using Microsoft.Extensions.Caching.Memory;
-namespace Knot.Modules.Chats.Infrastructure.SignalR;
+namespace Knot.Modules.Conversations.Infrastructure.SignalR;
///
/// Хаб SignalR для обработки сообщений и WebRTC сигналинга в реальном времени.
@@ -661,3 +661,4 @@ public sealed class ChatHub : Hub
public record GroupRenegotiateAnswerRequest(string ChatId, string TargetUserId, object Answer);
public record FriendSignalRequest(string FriendId);
}
+
diff --git a/backend/src/Modules/Conversations/Infrastructure/SignalR/MessageNotifier.cs b/backend/src/Modules/Conversations/Infrastructure/SignalR/MessageNotifier.cs
new file mode 100644
index 0000000..e55d921
--- /dev/null
+++ b/backend/src/Modules/Conversations/Infrastructure/SignalR/MessageNotifier.cs
@@ -0,0 +1,7 @@
+namespace Knot.Modules.Conversations.Infrastructure.SignalR;
+using Knot.Modules.Messaging.Application.Abstractions;
+using Microsoft.AspNetCore.SignalR;
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+public class MessageNotifier : IMessageNotifier { private readonly IHubContext _hubContext; public MessageNotifier(IHubContext hubContext) { _hubContext = hubContext; } public Task NotifyNewMessageAsync(Guid chatId, object messagePayload, CancellationToken cancellationToken) { return _hubContext.Clients.Group(chatId.ToString()).SendAsync("new_message", messagePayload, cancellationToken); } }
\ No newline at end of file
diff --git a/backend/src/Modules/Chats/Knot.Modules.Chats.csproj b/backend/src/Modules/Conversations/Knot.Modules.Conversations.csproj
similarity index 91%
rename from backend/src/Modules/Chats/Knot.Modules.Chats.csproj
rename to backend/src/Modules/Conversations/Knot.Modules.Conversations.csproj
index 71d2902..8d94180 100644
--- a/backend/src/Modules/Chats/Knot.Modules.Chats.csproj
+++ b/backend/src/Modules/Conversations/Knot.Modules.Conversations.csproj
@@ -2,10 +2,12 @@
+
+
@@ -33,3 +35,4 @@
+
diff --git a/backend/src/Modules/Chats/Migrations/20260320185319_AddHighWaterMark.Designer.cs b/backend/src/Modules/Conversations/Migrations/20260322191932_InitialConversations.Designer.cs
similarity index 88%
rename from backend/src/Modules/Chats/Migrations/20260320185319_AddHighWaterMark.Designer.cs
rename to backend/src/Modules/Conversations/Migrations/20260322191932_InitialConversations.Designer.cs
index 530e2ff..3ae1c4d 100644
--- a/backend/src/Modules/Chats/Migrations/20260320185319_AddHighWaterMark.Designer.cs
+++ b/backend/src/Modules/Conversations/Migrations/20260322191932_InitialConversations.Designer.cs
@@ -1,6 +1,6 @@
//
using System;
-using Knot.Modules.Chats.Infrastructure.Persistence;
+using Knot.Modules.Conversations.Infrastructure.Persistence;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
@@ -9,11 +9,11 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
-namespace Knot.Modules.Chats.Migrations
+namespace Knot.Modules.Conversations.Migrations
{
[DbContext(typeof(ChatsDbContext))]
- [Migration("20260320185319_AddHighWaterMark")]
- partial class AddHighWaterMark
+ [Migration("20260322191932_InitialConversations")]
+ partial class InitialConversations
{
///
protected override void BuildTargetModel(ModelBuilder modelBuilder)
@@ -26,7 +26,7 @@ namespace Knot.Modules.Chats.Migrations
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
- modelBuilder.Entity("Knot.Modules.Chats.Domain.Chat", b =>
+ modelBuilder.Entity("Knot.Modules.Conversations.Domain.Chat", b =>
{
b.Property("Id")
.ValueGeneratedOnAdd()
@@ -56,9 +56,9 @@ namespace Knot.Modules.Chats.Migrations
b.ToTable("Chats", "chats");
});
- modelBuilder.Entity("Knot.Modules.Chats.Domain.Chat", b =>
+ modelBuilder.Entity("Knot.Modules.Conversations.Domain.Chat", b =>
{
- b.OwnsMany("Knot.Modules.Chats.Domain.ChatMember", "Members", b1 =>
+ b.OwnsMany("Knot.Modules.Conversations.Domain.ChatMember", "Members", b1 =>
{
b1.Property("Id")
.ValueGeneratedOnAdd()
diff --git a/backend/src/Modules/Chats/Migrations/20260319124845_InitialChats.cs b/backend/src/Modules/Conversations/Migrations/20260322191932_InitialConversations.cs
similarity index 84%
rename from backend/src/Modules/Chats/Migrations/20260319124845_InitialChats.cs
rename to backend/src/Modules/Conversations/Migrations/20260322191932_InitialConversations.cs
index d32bc50..9969cfe 100644
--- a/backend/src/Modules/Chats/Migrations/20260319124845_InitialChats.cs
+++ b/backend/src/Modules/Conversations/Migrations/20260322191932_InitialConversations.cs
@@ -3,10 +3,10 @@ using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
-namespace Knot.Modules.Chats.Migrations
+namespace Knot.Modules.Conversations.Migrations
{
///
- public partial class InitialChats : Migration
+ public partial class InitialConversations : Migration
{
///
protected override void Up(MigrationBuilder migrationBuilder)
@@ -24,7 +24,8 @@ namespace Knot.Modules.Chats.Migrations
Name = table.Column(type: "text", nullable: true),
Description = table.Column(type: "text", nullable: true),
Avatar = table.Column(type: "text", nullable: true),
- CreatedAt = table.Column(type: "timestamp with time zone", nullable: false)
+ CreatedAt = table.Column(type: "timestamp with time zone", nullable: false),
+ LastMessageSequenceId = table.Column(type: "bigint", nullable: false)
},
constraints: table =>
{
@@ -42,7 +43,10 @@ namespace Knot.Modules.Chats.Migrations
Role = table.Column(type: "text", nullable: false),
JoinedAt = table.Column(type: "timestamp with time zone", nullable: false),
IsPinned = table.Column(type: "boolean", nullable: false),
- IsMuted = table.Column(type: "boolean", nullable: false)
+ IsMuted = table.Column(type: "boolean", nullable: false),
+ LastReadMessageId = table.Column(type: "uuid", nullable: true),
+ LastReadSequenceId = table.Column(type: "bigint", nullable: false),
+ LastDeliveredMessageId = table.Column(type: "uuid", nullable: true)
},
constraints: table =>
{
diff --git a/backend/src/Modules/Chats/Migrations/ChatsDbContextModelSnapshot.cs b/backend/src/Modules/Conversations/Migrations/ChatsDbContextModelSnapshot.cs
similarity index 90%
rename from backend/src/Modules/Chats/Migrations/ChatsDbContextModelSnapshot.cs
rename to backend/src/Modules/Conversations/Migrations/ChatsDbContextModelSnapshot.cs
index 3d5500d..b0df6c5 100644
--- a/backend/src/Modules/Chats/Migrations/ChatsDbContextModelSnapshot.cs
+++ b/backend/src/Modules/Conversations/Migrations/ChatsDbContextModelSnapshot.cs
@@ -1,6 +1,6 @@
//
using System;
-using Knot.Modules.Chats.Infrastructure.Persistence;
+using Knot.Modules.Conversations.Infrastructure.Persistence;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
@@ -8,7 +8,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
-namespace Knot.Modules.Chats.Migrations
+namespace Knot.Modules.Conversations.Migrations
{
[DbContext(typeof(ChatsDbContext))]
partial class ChatsDbContextModelSnapshot : ModelSnapshot
@@ -23,7 +23,7 @@ namespace Knot.Modules.Chats.Migrations
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
- modelBuilder.Entity("Knot.Modules.Chats.Domain.Chat", b =>
+ modelBuilder.Entity("Knot.Modules.Conversations.Domain.Chat", b =>
{
b.Property("Id")
.ValueGeneratedOnAdd()
@@ -53,9 +53,9 @@ namespace Knot.Modules.Chats.Migrations
b.ToTable("Chats", "chats");
});
- modelBuilder.Entity("Knot.Modules.Chats.Domain.Chat", b =>
+ modelBuilder.Entity("Knot.Modules.Conversations.Domain.Chat", b =>
{
- b.OwnsMany("Knot.Modules.Chats.Domain.ChatMember", "Members", b1 =>
+ b.OwnsMany("Knot.Modules.Conversations.Domain.ChatMember", "Members", b1 =>
{
b1.Property("Id")
.ValueGeneratedOnAdd()
diff --git a/backend/src/Modules/Conversations/Presentation/Endpoints/ChatsEndpoints.cs b/backend/src/Modules/Conversations/Presentation/Endpoints/ChatsEndpoints.cs
new file mode 100644
index 0000000..0665923
--- /dev/null
+++ b/backend/src/Modules/Conversations/Presentation/Endpoints/ChatsEndpoints.cs
@@ -0,0 +1,174 @@
+using Carter;
+using Knot.Shared.Kernel;
+using Knot.Modules.Conversations.Application.DTOs;
+using Knot.Modules.Conversations.Application.Chats.GetChats;
+using Knot.Modules.Conversations.Application.Chats.GetChatById;
+using Knot.Modules.Conversations.Application.Chats.Create;
+using Knot.Modules.Conversations.Application.Chats.GetOrCreateFavorites;
+using Knot.Modules.Conversations.Application.Chats.Update;
+using Knot.Modules.Conversations.Application.Chats.LeaveOrDelete;
+using Knot.Modules.Conversations.Application.Chats.Clear;
+using Knot.Modules.Conversations.Application.Chats.TogglePin;
+using Knot.Modules.Conversations.Application.Chats.Members;
+using Knot.Modules.Conversations.Application.Chats.Avatar;
+using Knot.Modules.Conversations.Domain;
+using MediatR;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Routing;
+using Microsoft.AspNetCore.Mvc;
+
+namespace Knot.Modules.Conversations.Presentation.Endpoints;
+
+public sealed class ChatsEndpoints : ICarterModule
+{
+ public void AddRoutes(IEndpointRouteBuilder app)
+ {
+ var group = app.MapGroup("api/chats").RequireAuthorization();
+
+ group.MapGet("", async (ISender sender, IUserContext userContext, CancellationToken ct) =>
+ {
+ var result = await sender.Send(new GetChatsQuery(userContext.UserId), ct);
+ return result.IsSuccess ? Results.Ok(result.Value) : Results.BadRequest(result.Error.Description);
+ });
+
+ group.MapPost("", async ([FromBody] CreateChatRequest request, ISender sender, IUserContext userContext, CancellationToken ct) =>
+ {
+ var command = new CreateChatCommand(request.Name, request.Type, request.MemberIds);
+ var result = await sender.Send(command, ct);
+ if (result.IsFailure) return Results.BadRequest(result.Error.Description);
+
+ var chatResult = await sender.Send(new GetChatByIdQuery(userContext.UserId, result.Value), ct);
+ return Results.Ok(chatResult.Value);
+ });
+
+ group.MapPost("personal", async ([FromBody] CreatePersonalChatRequest request, ISender sender, IUserContext userContext, CancellationToken ct) =>
+ {
+ var command = new CreateChatCommand(string.Empty, ChatType.Personal, new List { userContext.UserId, request.UserId });
+ var result = await sender.Send(command, ct);
+ if (result.IsFailure) return Results.BadRequest(result.Error.Description);
+
+ var chatResult = await sender.Send(new GetChatByIdQuery(userContext.UserId, result.Value), ct);
+ return Results.Ok(chatResult.Value);
+ });
+
+ group.MapPost("group", async ([FromBody] CreateGroupChatRequest request, ISender sender, IUserContext userContext, CancellationToken ct) =>
+ {
+ var memberIds = request.MemberIds.ToList();
+ if (memberIds.Contains(userContext.UserId)) memberIds.Remove(userContext.UserId);
+ memberIds.Insert(0, userContext.UserId);
+
+ var command = new CreateChatCommand(request.Name, ChatType.Group, memberIds);
+ var result = await sender.Send(command, ct);
+ if (result.IsFailure) return Results.BadRequest(result.Error.Description);
+
+ var chatResult = await sender.Send(new GetChatByIdQuery(userContext.UserId, result.Value), ct);
+ return Results.Ok(chatResult.Value);
+ });
+
+ group.MapPost("favorites", async (ISender sender, IUserContext userContext, CancellationToken ct) =>
+ {
+ var result = await sender.Send(new GetOrCreateFavoritesCommand(userContext.UserId), ct);
+ if (result.IsFailure) return Results.BadRequest(result.Error.Description);
+
+ var chatResult = await sender.Send(new GetChatByIdQuery(userContext.UserId, result.Value), ct);
+ return Results.Ok(chatResult.Value);
+ });
+
+ group.MapPut("{id:guid}", async (Guid id, [FromBody] UpdateChatRequest request, ISender sender, IUserContext userContext, CancellationToken ct) =>
+ {
+ var result = await sender.Send(new UpdateChatCommand(id, userContext.UserId, request.Name, request.Description), ct);
+ if (result.IsFailure) return Results.NotFound();
+
+ var chatResult = await sender.Send(new GetChatByIdQuery(userContext.UserId, result.Value), ct);
+ return Results.Ok(chatResult.Value);
+ });
+
+ group.MapDelete("{id:guid}", async (Guid id, ISender sender, IUserContext userContext, CancellationToken ct) =>
+ {
+ var result = await sender.Send(new LeaveOrDeleteChatCommand(id, userContext.UserId), ct);
+ if (result.IsFailure)
+ {
+ if (result.Error.Code == "Unauthorized") return Results.Forbid();
+ return Results.NotFound();
+ }
+ return Results.Ok(result.Value);
+ });
+
+ group.MapPost("{id:guid}/clear", async (Guid id, ISender sender, IUserContext userContext, CancellationToken ct) =>
+ {
+ var result = await sender.Send(new ClearChatCommand(id, userContext.UserId), ct);
+ return result.IsSuccess ? Results.Ok(result.Value) : Results.NotFound();
+ });
+
+ group.MapPost("{id:guid}/pin", async (Guid id, ISender sender, IUserContext userContext, CancellationToken ct) =>
+ {
+ var result = await sender.Send(new TogglePinCommand(id, userContext.UserId), ct);
+ return result.IsSuccess ? Results.Ok(result.Value) : Results.NotFound();
+ });
+
+ group.MapPost("{id:guid}/members", async (Guid id, [FromBody] AddMembersRequest request, ISender sender, IUserContext userContext, CancellationToken ct) =>
+ {
+ var result = await sender.Send(new AddMembersCommand(id, userContext.UserId, request.UserIds.ToList()), ct);
+ if (result.IsFailure) return Results.NotFound();
+
+ var chatResult = await sender.Send(new GetChatByIdQuery(userContext.UserId, result.Value), ct);
+ return Results.Ok(chatResult.Value);
+ });
+
+ group.MapDelete("{id:guid}/members/{userId:guid}", async (Guid id, Guid userId, ISender sender, IUserContext userContext, CancellationToken ct) =>
+ {
+ var result = await sender.Send(new RemoveMemberCommand(id, userContext.UserId, userId), ct);
+ if (result.IsFailure) return Results.NotFound();
+
+ var chatResult = await sender.Send(new GetChatByIdQuery(userContext.UserId, result.Value), ct);
+ return Results.Ok(chatResult.Value);
+ });
+
+ group.MapPost("{id:guid}/avatar", async (Guid id, HttpRequest req, ISender sender, IUserContext userContext, CancellationToken ct) =>
+ {
+ if (!req.HasFormContentType) return Results.BadRequest("No file");
+ var form = await req.ReadFormAsync(ct);
+ var avatar = form.Files.FirstOrDefault();
+ if (avatar == null || avatar.Length == 0) return Results.BadRequest("No file");
+
+ using var stream = avatar.OpenReadStream();
+ var result = await sender.Send(new UploadGroupAvatarCommand(id, userContext.UserId, avatar.FileName, avatar.ContentType, stream), ct);
+ if (result.IsFailure) return Results.NotFound();
+
+ var chatResult = await sender.Send(new GetChatByIdQuery(userContext.UserId, result.Value), ct);
+ return Results.Ok(chatResult.Value);
+ }).DisableAntiforgery();
+
+ group.MapPost("{id:guid}/avatar/crop", async (Guid id, HttpRequest req, ISender sender, IUserContext userContext, CancellationToken ct) =>
+ {
+ if (!req.HasFormContentType) return Results.BadRequest("No file");
+ var form = await req.ReadFormAsync(ct);
+ var avatar = form.Files.FirstOrDefault();
+ if (avatar == null || avatar.Length == 0) return Results.BadRequest("No file");
+
+ int.TryParse(form["x"], out int x);
+ int.TryParse(form["y"], out int y);
+ int.TryParse(form["width"], out int width);
+ int.TryParse(form["height"], out int height);
+
+ using var stream = avatar.OpenReadStream();
+ var result = await sender.Send(new CropGroupAvatarCommand(id, userContext.UserId, avatar.FileName ?? "avatar.jpg", avatar.ContentType, stream, x, y, width, height), ct);
+ if (result.IsFailure) return Results.NotFound();
+
+ var chatResult = await sender.Send(new GetChatByIdQuery(userContext.UserId, result.Value), ct);
+ return Results.Ok(chatResult.Value);
+ }).DisableAntiforgery();
+
+ group.MapDelete("{id:guid}/avatar", async (Guid id, ISender sender, IUserContext userContext, CancellationToken ct) =>
+ {
+ var result = await sender.Send(new RemoveGroupAvatarCommand(id, userContext.UserId), ct);
+ if (result.IsFailure) return Results.NotFound();
+
+ var chatResult = await sender.Send(new GetChatByIdQuery(userContext.UserId, result.Value), ct);
+ return Results.Ok(chatResult.Value);
+ });
+ }
+}
+
+
diff --git a/backend/src/Modules/Conversations/Presentation/Endpoints/MessagesEndpoints.cs b/backend/src/Modules/Conversations/Presentation/Endpoints/MessagesEndpoints.cs
new file mode 100644
index 0000000..9d3bf53
--- /dev/null
+++ b/backend/src/Modules/Conversations/Presentation/Endpoints/MessagesEndpoints.cs
@@ -0,0 +1,79 @@
+using Carter;
+using Knot.Shared.Kernel;
+using Knot.Modules.Conversations.Application.DTOs;
+using Knot.Modules.Conversations.Application.Messages.GetMessages;
+using Knot.Modules.Conversations.Application.Messages.SearchMessages;
+using Knot.Modules.Conversations.Application.Messages.UploadFile;
+using Knot.Modules.Conversations.Application.Messages.GetSharedMedia;
+using Knot.Modules.Conversations.Application.Messages.Send;
+using MediatR;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Routing;
+using Microsoft.AspNetCore.Mvc;
+
+namespace Knot.Modules.Conversations.Presentation.Endpoints;
+
+public sealed class MessagesEndpoints : ICarterModule
+{
+ public void AddRoutes(IEndpointRouteBuilder app)
+ {
+ var group = app.MapGroup("api/messages").RequireAuthorization();
+
+ group.MapGet("chat/{chatId:guid}", async (Guid chatId, [FromQuery] string? cursor, ISender sender, IUserContext userContext, CancellationToken ct) =>
+ {
+ var result = await sender.Send(new GetMessagesQuery(userContext.UserId, chatId, cursor), ct);
+ return result.IsSuccess ? Results.Ok(result.Value) : Results.BadRequest(result.Error.Description);
+ });
+
+ group.MapGet("search", async ([FromQuery] string q, [FromQuery] Guid? chatId, ISender sender, IUserContext userContext, CancellationToken ct) =>
+ {
+ var result = await sender.Send(new SearchMessagesQuery(userContext.UserId, q, chatId), ct);
+ return result.IsSuccess ? Results.Ok(result.Value) : Results.BadRequest(result.Error.Description);
+ });
+
+ group.MapPost("upload", async (HttpRequest req, ISender sender, CancellationToken ct) =>
+ {
+ if (!req.HasFormContentType) return Results.BadRequest("No file uploaded");
+ var form = await req.ReadFormAsync(ct);
+ var file = form.Files.FirstOrDefault();
+ if (file == null || file.Length == 0) return Results.BadRequest("No file uploaded");
+
+ using var stream = file.OpenReadStream();
+ var result = await sender.Send(new UploadFileCommand(file.FileName, file.ContentType, file.Length, stream), ct);
+
+ if (result.IsFailure)
+ {
+ if (result.Error.Code == "File.TooLarge") return Results.StatusCode(413);
+ return Results.BadRequest(result.Error.Description);
+ }
+ return Results.Ok(result.Value);
+ }).DisableAntiforgery();
+
+ group.MapGet("chat/{chatId:guid}/shared", async (Guid chatId, [FromQuery] string? type, ISender sender, IUserContext userContext, CancellationToken ct) =>
+ {
+ var result = await sender.Send(new GetSharedMediaQuery(userContext.UserId, chatId, type), ct);
+ return result.IsSuccess ? Results.Ok(result.Value) : Results.BadRequest(result.Error.Description);
+ });
+
+ group.MapPost("chat/{chatId:guid}", async (Guid chatId, [FromBody] SendMessageRequest request, ISender sender, IUserContext userContext, CancellationToken ct) =>
+ {
+ var attachments = request.Attachments?.Select(a =>
+ new AttachmentRequest(a.Type, a.Url, a.FileName, a.FileSize)).ToList();
+
+ var command = new SendMessageCommand(
+ chatId,
+ userContext.UserId,
+ request.Content,
+ request.Type,
+ attachments,
+ request.ReplyToId,
+ request.Quote,
+ request.ForwardedFromId);
+
+ var result = await sender.Send(command, ct);
+ return result.IsSuccess ? Results.Ok(result.Value) : Results.BadRequest(result.Error.Description);
+ });
+ }
+}
+
diff --git a/backend/src/Host/Application/Federation/Commands/HandshakeFederationCommand.cs b/backend/src/Modules/Federation/Application/Federation/Commands/HandshakeFederationCommand.cs
similarity index 98%
rename from backend/src/Host/Application/Federation/Commands/HandshakeFederationCommand.cs
rename to backend/src/Modules/Federation/Application/Federation/Commands/HandshakeFederationCommand.cs
index fe2d38c..ee9f336 100644
--- a/backend/src/Host/Application/Federation/Commands/HandshakeFederationCommand.cs
+++ b/backend/src/Modules/Federation/Application/Federation/Commands/HandshakeFederationCommand.cs
@@ -1,4 +1,6 @@
-using Host.Application.Stories;
+
+using Knot.Modules.Stories.Domain;
+
using Knot.Shared.Kernel;
using Knot.Shared.Kernel.Configuration;
using Knot.Shared.Kernel.Constants;
@@ -49,4 +51,4 @@ internal sealed class HandshakeFederationCommandHandler : ICommandHandler
+
+ net10.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/backend/src/Modules/Federation/Presentation/Endpoints/FederationEndpoints.cs b/backend/src/Modules/Federation/Presentation/Endpoints/FederationEndpoints.cs
new file mode 100644
index 0000000..a976e15
--- /dev/null
+++ b/backend/src/Modules/Federation/Presentation/Endpoints/FederationEndpoints.cs
@@ -0,0 +1,41 @@
+using Carter;
+using MediatR;
+using Knot.Shared.Kernel.Constants;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Routing;
+using System;
+
+namespace Host.Endpoints;
+
+///
+/// РегРСвЂВВстрацРСвЂВВР РЋР РЏ РЎРЊР Р…Р ТвЂВВРїРѕРСвЂВВнтовфеРТвЂВВерацРСвЂВВР В Р’В Р РЋРІР‚ВВ.
+///
+public sealed class FederationEndpoints : ICarterModule
+{
+ public void AddRoutes(IEndpointRouteBuilder app)
+ {
+ var group = app.MapGroup(Routes.ApiFederation);
+
+ group.MapPost("/handshake", async ([FromBody] Host.Application.Federation.Commands.HandshakeRequest request, ISender sender, CancellationToken ct) =>
+ {
+ var result = await sender.Send(new Host.Application.Federation.Commands.HandshakeFederationCommand(request), ct);
+ if (result.IsSuccess)
+ {
+ return Results.Ok(result.Value);
+ }
+
+ if (result.Error.Code == "Unauthorized")
+ {
+ return Results.Forbid();
+ }
+ if (result.Error.Code == Knot.Shared.Kernel.Constants.Errors.DisabledByAdmin)
+ {
+ return Results.StatusCode(503);
+ }
+
+ return Results.BadRequest(new { error = result.Error.Description });
+ });
+ }
+}
\ No newline at end of file
diff --git a/backend/src/Modules/Identity/Application/Abstractions/IJwtTokenProvider.cs b/backend/src/Modules/Identity/Application/Abstractions/IJwtTokenProvider.cs
deleted file mode 100644
index edaba4a..0000000
--- a/backend/src/Modules/Identity/Application/Abstractions/IJwtTokenProvider.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-using Knot.Modules.Identity.Domain;
-
-namespace Knot.Modules.Identity.Application.Abstractions;
-
-public interface IJwtTokenProvider
-{
- string Generate(User user);
-}
diff --git a/backend/src/Modules/Identity/Application/Users/Avatar/DeleteAvatar.cs b/backend/src/Modules/Identity/Application/Users/Avatar/DeleteAvatar.cs
deleted file mode 100644
index 785a622..0000000
--- a/backend/src/Modules/Identity/Application/Users/Avatar/DeleteAvatar.cs
+++ /dev/null
@@ -1,43 +0,0 @@
-using Knot.Shared.Kernel;
-using Knot.Modules.Identity.Domain;
-using Knot.Modules.Identity.Application.Abstractions;
-
-namespace Knot.Modules.Identity.Application.Users.Avatar;
-
-public sealed record DeleteAvatarCommand(Guid UserId) : ICommand;
-
-internal sealed class DeleteAvatarCommandHandler : ICommandHandler
-{
- private readonly IUserRepository _userRepository;
- private readonly IIdentityUnitOfWork _unitOfWork;
-
- public DeleteAvatarCommandHandler(IUserRepository userRepository, IIdentityUnitOfWork unitOfWork)
- {
- _userRepository = userRepository;
- _unitOfWork = unitOfWork;
- }
-
- public async Task> Handle(DeleteAvatarCommand request, CancellationToken cancellationToken)
- {
- var user = await _userRepository.GetByIdAsync(request.UserId, cancellationToken);
- if (user == null)
- {
- return Result.Failure(IdentityErrors.UserNotFound);
- }
-
- user.UpdateAvatar(null);
- await _unitOfWork.SaveChangesAsync(cancellationToken);
-
- var dto = new UserProfileDto(
- user.Id,
- user.Username,
- user.DisplayName,
- user.Avatar,
- user.Bio,
- user.Birthday,
- user.CreatedAt
- );
-
- return Result.Success(dto);
- }
-}
diff --git a/backend/src/Modules/Identity/Application/Users/Avatar/UploadAvatar.cs b/backend/src/Modules/Identity/Application/Users/Avatar/UploadAvatar.cs
deleted file mode 100644
index 7f428cb..0000000
--- a/backend/src/Modules/Identity/Application/Users/Avatar/UploadAvatar.cs
+++ /dev/null
@@ -1,49 +0,0 @@
-using Knot.Shared.Kernel;
-using Knot.Modules.Identity.Domain;
-using Knot.Modules.Identity.Application.Abstractions;
-using Knot.Shared.Kernel.Storage;
-
-namespace Knot.Modules.Identity.Application.Users.Avatar;
-
-public sealed record UploadAvatarCommand(Guid UserId, Stream FileStream, string FileName, string ContentType) : ICommand;
-
-internal sealed class UploadAvatarCommandHandler : ICommandHandler
-{
- private readonly IUserRepository _userRepository;
- private readonly IIdentityUnitOfWork _unitOfWork;
- private readonly IFileStorageService _fileStorage;
-
- public UploadAvatarCommandHandler(IUserRepository userRepository, IIdentityUnitOfWork unitOfWork, IFileStorageService fileStorage)
- {
- _userRepository = userRepository;
- _unitOfWork = unitOfWork;
- _fileStorage = fileStorage;
- }
-
- public async Task> Handle(UploadAvatarCommand request, CancellationToken cancellationToken)
- {
- var user = await _userRepository.GetByIdAsync(request.UserId, cancellationToken);
- if (user == null)
- {
- return Result.Failure(IdentityErrors.UserNotFound);
- }
-
- var fileId = await _fileStorage.UploadFileAsync(request.FileStream, request.FileName, request.ContentType);
- var avatarUrl = $"/api/files/{fileId}";
-
- user.UpdateAvatar(avatarUrl);
- await _unitOfWork.SaveChangesAsync(cancellationToken);
-
- var dto = new UserProfileDto(
- user.Id,
- user.Username,
- user.DisplayName,
- user.Avatar,
- user.Bio,
- user.Birthday,
- user.CreatedAt
- );
-
- return Result.Success(dto);
- }
-}
diff --git a/backend/src/Modules/Identity/Application/Users/GetUser/GetUser.cs b/backend/src/Modules/Identity/Application/Users/GetUser/GetUser.cs
deleted file mode 100644
index 2ba6c38..0000000
--- a/backend/src/Modules/Identity/Application/Users/GetUser/GetUser.cs
+++ /dev/null
@@ -1,41 +0,0 @@
-using Knot.Shared.Kernel;
-using Knot.Modules.Identity.Domain;
-using Knot.Modules.Identity.Application.Abstractions;
-
-namespace Knot.Modules.Identity.Application.Users.GetUser;
-
-public sealed record GetUserQuery(Guid Id) : IQuery;
-
-internal sealed class GetUserQueryHandler : IQueryHandler
-{
- private readonly IUserRepository _userRepository;
-
- public GetUserQueryHandler(IUserRepository userRepository)
- {
- _userRepository = userRepository;
- }
-
- public async Task> Handle(GetUserQuery request, CancellationToken cancellationToken)
- {
- var user = await _userRepository.GetByIdAsync(request.Id, cancellationToken);
- if (user == null)
- {
- return Result.Failure(IdentityErrors.UserNotFound);
- }
-
- var dto = new UserProfileDto(
- user.Id,
- user.Username,
- user.DisplayName,
- user.Avatar,
- user.Bio,
- user.Birthday,
- user.CreatedAt,
- null,
- false,
- DateTime.UtcNow
- );
-
- return Result.Success(dto);
- }
-}
diff --git a/backend/src/Modules/Identity/Application/Users/Search/SearchUsers.cs b/backend/src/Modules/Identity/Application/Users/Search/SearchUsers.cs
deleted file mode 100644
index ea526d4..0000000
--- a/backend/src/Modules/Identity/Application/Users/Search/SearchUsers.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using Knot.Shared.Kernel;
-using Knot.Modules.Identity.Domain;
-using Knot.Modules.Identity.Application.Abstractions;
-
-namespace Knot.Modules.Identity.Application.Users.Search;
-
-public sealed record SearchUsersQuery(string Query) : IQuery>;
-
-internal sealed class SearchUsersQueryHandler : IQueryHandler>
-{
- private readonly IUserRepository _userRepository;
-
- public SearchUsersQueryHandler(IUserRepository userRepository)
- {
- _userRepository = userRepository;
- }
-
- public async Task>> Handle(SearchUsersQuery request, CancellationToken cancellationToken)
- {
- var users = await _userRepository.SearchUsersAsync(request.Query, cancellationToken);
-
- var result = users.Select(user => new UserDto(
- user.Id,
- user.Username,
- user.DisplayName,
- user.Avatar,
- false,
- DateTime.UtcNow
- )).ToList();
-
- return Result.Success(result);
- }
-}
diff --git a/backend/src/Modules/Identity/Application/Users/UpdateSettings/UpdateSettings.cs b/backend/src/Modules/Identity/Application/Users/UpdateSettings/UpdateSettings.cs
deleted file mode 100644
index 16c4ae4..0000000
--- a/backend/src/Modules/Identity/Application/Users/UpdateSettings/UpdateSettings.cs
+++ /dev/null
@@ -1,44 +0,0 @@
-using Knot.Shared.Kernel;
-using Knot.Modules.Identity.Domain;
-using Knot.Modules.Identity.Application.Abstractions;
-
-namespace Knot.Modules.Identity.Application.Users.UpdateSettings;
-
-public sealed record UpdateSettingsCommand(Guid UserId, bool? HideStoryViews) : ICommand;
-
-internal sealed class UpdateSettingsCommandHandler : ICommandHandler
-{
- private readonly IUserRepository _userRepository;
- private readonly IIdentityUnitOfWork _unitOfWork;
-
- public UpdateSettingsCommandHandler(IUserRepository userRepository, IIdentityUnitOfWork unitOfWork)
- {
- _userRepository = userRepository;
- _unitOfWork = unitOfWork;
- }
-
- public async Task> Handle(UpdateSettingsCommand request, CancellationToken cancellationToken)
- {
- var user = await _userRepository.GetByIdAsync(request.UserId, cancellationToken);
- if (user == null)
- {
- return Result.Failure(IdentityErrors.UserNotFound);
- }
-
- user.UpdateSettings(request.HideStoryViews ?? user.HideStoryViews);
- await _unitOfWork.SaveChangesAsync(cancellationToken);
-
- var dto = new UserProfileDto(
- user.Id,
- user.Username,
- user.DisplayName,
- user.Avatar,
- user.Bio,
- user.Birthday,
- user.CreatedAt,
- user.HideStoryViews
- );
-
- return Result.Success(dto);
- }
-}
diff --git a/backend/src/Modules/Identity/Application/Users/UserDto.cs b/backend/src/Modules/Identity/Application/Users/UserDto.cs
deleted file mode 100644
index 66169a2..0000000
--- a/backend/src/Modules/Identity/Application/Users/UserDto.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using System;
-
-namespace Knot.Modules.Identity.Application.Users;
-
-public record UserDto(
- Guid Id,
- string Username,
- string DisplayName,
- string? Avatar,
- bool IsOnline,
- DateTime LastSeen
-);
diff --git a/backend/src/Modules/Identity/Infrastructure/Persistence/IdentityDbContext.cs b/backend/src/Modules/Identity/Infrastructure/Persistence/IdentityDbContext.cs
deleted file mode 100644
index 7d566e9..0000000
--- a/backend/src/Modules/Identity/Infrastructure/Persistence/IdentityDbContext.cs
+++ /dev/null
@@ -1,157 +0,0 @@
-using MediatR;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Diagnostics;
-using Microsoft.EntityFrameworkCore.Metadata;
-using Knot.Modules.Identity.Application.Abstractions;
-using Knot.Modules.Identity.Domain;
-using Knot.Shared.Kernel;
-using Knot.Shared.Kernel.Security;
-
-namespace Knot.Modules.Identity.Infrastructure.Persistence;
-
-///
-/// Контекст базы данных для модуля Identity.
-///
-public sealed class IdentityDbContext : DbContext, IIdentityUnitOfWork, Knot.Modules.Identity.Application.Abstractions.IIdentityDbContext
-{
- private readonly IMediator _mediator;
- private readonly IEncryptionService _encryptionService;
-
- public IdentityDbContext(DbContextOptions options, IMediator mediator, IEncryptionService encryptionService)
- : base(options)
- {
- _mediator = mediator;
- _encryptionService = encryptionService;
- }
-
- public DbSet Users => Set();
- public DbSet Stories => Set();
- public DbSet StoryViewers => Set();
- public DbSet StoryReactions => Set();
- public DbSet StoryReplies => Set();
- public DbSet Friendships => Set();
-
-
- protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
- {
- base.OnConfiguring(optionsBuilder);
- optionsBuilder.ConfigureWarnings(w => w.Ignore(RelationalEventId.PendingModelChangesWarning));
- }
-
- protected override void OnModelCreating(ModelBuilder modelBuilder)
- {
- modelBuilder.HasDefaultSchema("identity");
-
- modelBuilder.Entity(builder =>
- {
- builder.ToTable("Friendships");
- builder.HasKey(f => f.Id);
- builder.HasIndex(f => new { f.UserId, f.FriendId }).IsUnique();
- });
-
- modelBuilder.Entity