diff --git a/backend/Knot.sln b/backend/Knot.sln
index 9597f75..60ebd47 100644
--- a/backend/Knot.sln
+++ b/backend/Knot.sln
@@ -13,8 +13,6 @@ 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}") = "tests", "tests", "{0AB3BF05-4346-4AA6-1389-037BE0695223}"
-EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Profiles", "Profiles", "{CA5186C2-289A-9324-4152-9805A73A9773}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Knot.Modules.Profiles", "src\Modules\Profiles\Knot.Modules.Profiles.csproj", "{CD14770B-8BCD-4D93-AD2C-581ED030E209}"
@@ -35,8 +33,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Auth", "Auth", "{E9D55F35-C
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}"
@@ -49,44 +45,132 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Conversations", "Conversati
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
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{0AB3BF05-4346-4AA6-1389-037BE0695223}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{D0D6B8B0-AC35-B3FA-0B96-A9B0E4A3253E}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "IntegrationTests.Shared", "IntegrationTests.Shared", "{21C6533F-5B99-F63D-D6A0-0D1E74554637}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Knot.IntegrationTests.Shared", "Tests\Shared\IntegrationTests.Shared\Knot.IntegrationTests.Shared.csproj", "{2BDD861B-44BC-463B-88C4-E8E2B516A7EE}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Host", "src\Host\Host.csproj", "{98ABB710-9106-4E5C-AA04-D19D5E92EDC1}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Stories", "Stories", "{407BCCB7-2AB0-5429-FB5F-A9376121CF06}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "UnitTests", "UnitTests", "{E7E14295-D412-4B43-44D5-0AAD5D27712E}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Knot.Modules.Stories.UnitTests", "Tests\Stories\UnitTests\Knot.Modules.Stories.UnitTests.csproj", "{8865807E-C577-4476-BE33-0EBB4E07CD8B}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "IntegrationTests", "IntegrationTests", "{8CFDC985-5E1F-E566-CA4D-28D1B5AD1B06}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Knot.Modules.Stories.IntegrationTests", "Tests\Stories\IntegrationTests\Knot.Modules.Stories.IntegrationTests.csproj", "{028AF0DA-6D64-435F-B1CA-C14829892C4F}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Admin", "Admin", "{EDEF9ADB-D556-6B20-2E4B-8F977F5C545A}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "UnitTests", "UnitTests", "{AA22B6BC-789B-946B-F9BD-06D6D86B3211}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Knot.Modules.Admin.UnitTests", "Tests\Admin\UnitTests\Knot.Modules.Admin.UnitTests.csproj", "{9DB38617-9738-45C3-A101-8B04869CB07F}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "IntegrationTests", "IntegrationTests", "{1A80A494-6197-707A-BB12-E5CAFEB6DBC3}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Knot.Modules.Admin.IntegrationTests", "Tests\Admin\IntegrationTests\Knot.Modules.Admin.IntegrationTests.csproj", "{ABC9A8FF-0332-4987-A2E8-63108394DA26}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Auth", "Auth", "{EDFE54F1-08A0-5241-003B-94DECB51237C}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "UnitTests", "UnitTests", "{50896B77-11EA-ADED-5FA2-195E6FABE81B}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Knot.Modules.Auth.UnitTests", "Tests\Auth\UnitTests\Knot.Modules.Auth.UnitTests.csproj", "{DD4F5E02-4366-4CA3-8CF1-941C21C27B6E}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "IntegrationTests", "IntegrationTests", "{0BF9C624-18C3-4CFE-0DEF-3D1D0C280507}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Knot.Modules.Auth.IntegrationTests", "Tests\Auth\IntegrationTests\Knot.Modules.Auth.IntegrationTests.csproj", "{776C6198-A229-4C2C-B19E-3F2744BD5279}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Federation", "Federation", "{036B8169-510F-3971-AD3F-66559BF086AA}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "UnitTests", "UnitTests", "{92885E6D-C52A-2FB4-A33D-9A8FE23A87BF}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Knot.Modules.Federation.UnitTests", "Tests\Federation\UnitTests\Knot.Modules.Federation.UnitTests.csproj", "{EA780C77-735F-4FA7-8AE9-78413C928BA6}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "IntegrationTests", "IntegrationTests", "{3794ADC3-BA35-663C-885F-C144081FBEA1}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Knot.Modules.Federation.IntegrationTests", "Tests\Federation\IntegrationTests\Knot.Modules.Federation.IntegrationTests.csproj", "{60326D0F-D9C7-4B8D-B664-BC8DE239AD31}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Messaging", "Messaging", "{3AB41C38-05DB-BFCC-9BA0-9BC97041A508}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "UnitTests", "UnitTests", "{43109C86-4551-F94F-E9C3-994B9C735653}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Knot.Modules.Messaging.UnitTests", "Tests\Messaging\UnitTests\Knot.Modules.Messaging.UnitTests.csproj", "{635D6C93-D9E2-46C2-9294-5C0713CBA245}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "IntegrationTests", "IntegrationTests", "{FE27F79D-3631-2D96-056A-69F8E94CD8E3}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Knot.Modules.Messaging.IntegrationTests", "Tests\Messaging\IntegrationTests\Knot.Modules.Messaging.IntegrationTests.csproj", "{0C37AA6D-37DB-439B-97D5-9E799DDDC831}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Relations", "Relations", "{F29503A5-E7F1-B611-4026-76E83B89B0AB}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "UnitTests", "UnitTests", "{8D009ABD-C820-B7D4-2412-0990C1960013}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Knot.Modules.Relations.UnitTests", "Tests\Relations\UnitTests\Knot.Modules.Relations.UnitTests.csproj", "{89593AC0-B761-400D-8213-F6E728980F59}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "IntegrationTests", "IntegrationTests", "{7C677DC5-D0E9-6A09-3DB0-042075A9E677}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Knot.Modules.Relations.IntegrationTests", "Tests\Relations\IntegrationTests\Knot.Modules.Relations.IntegrationTests.csproj", "{312F4C56-7865-47FD-8170-E6555DBC4884}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Conversations", "Conversations", "{382D38B3-4D74-95AB-947E-DC7B53CBCF32}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "UnitTests", "UnitTests", "{489DA9C1-7DF6-4B43-A5A6-01AD261B3AC8}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Knot.Modules.Conversations.UnitTests", "Tests\Conversations\UnitTests\Knot.Modules.Conversations.UnitTests.csproj", "{5B428B36-6857-47BA-8DE1-A8207A2DF1E8}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TelegramImport", "TelegramImport", "{A3D04C92-6F74-537C-D0F4-BD2C8CD7687D}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "UnitTests", "UnitTests", "{2BF544BD-4498-3DD9-D97E-96A22189C7D8}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Knot.Modules.TelegramImport.UnitTests", "Tests\TelegramImport\UnitTests\Knot.Modules.TelegramImport.UnitTests.csproj", "{568561C3-C4D9-4783-8103-B68C94E2F8D1}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WebRtc", "WebRtc", "{CCF0649B-70C0-BDF5-F1BD-87A1DBED8C5D}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "UnitTests", "UnitTests", "{4FFF0ACB-E93F-597A-5968-6C89F4BB5AC7}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Knot.Modules.WebRtc.UnitTests", "Tests\WebRtc\UnitTests\Knot.Modules.WebRtc.UnitTests.csproj", "{65A65ABB-1F74-471C-84C5-E9CBA6F27B74}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Klipy", "Klipy", "{46A5ACB5-176C-983D-5434-922CD2C33C4A}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "UnitTests", "UnitTests", "{43930E3C-6ABA-C329-E65B-CFA08664DBAF}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Knot.Modules.Klipy.UnitTests", "Tests\Klipy\UnitTests\Knot.Modules.Klipy.UnitTests.csproj", "{71B7CB00-B0B9-4FEB-9E9F-E8B47FAD5408}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Storage", "Storage", "{B6FD6E61-6054-1C88-3299-57EC8B98D824}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "UnitTests", "UnitTests", "{93CD11E9-EE4D-82DD-DD63-4D36831D0976}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Knot.Modules.Storage.UnitTests", "Tests\Storage\UnitTests\Knot.Modules.Storage.UnitTests.csproj", "{418BE048-D472-40C0-917F-060A9E781B82}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Users", "Users", "{553D81D4-666B-BE26-75F9-480CB214ACEE}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "UnitTests", "UnitTests", "{3A48F700-9635-2216-D291-85337CE5D869}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Knot.Modules.Profiles.UnitTests", "Tests\Users\UnitTests\Knot.Modules.Profiles.UnitTests.csproj", "{6BD8DC02-2270-4276-952E-E34E973A1237}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -181,18 +265,6 @@ Global
{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
@@ -229,18 +301,6 @@ Global
{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
@@ -253,18 +313,6 @@ Global
{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
@@ -277,18 +325,6 @@ Global
{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
@@ -301,42 +337,6 @@ Global
{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
@@ -349,42 +349,6 @@ Global
{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
@@ -397,6 +361,246 @@ Global
{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
+ {2BDD861B-44BC-463B-88C4-E8E2B516A7EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2BDD861B-44BC-463B-88C4-E8E2B516A7EE}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2BDD861B-44BC-463B-88C4-E8E2B516A7EE}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {2BDD861B-44BC-463B-88C4-E8E2B516A7EE}.Debug|x64.Build.0 = Debug|Any CPU
+ {2BDD861B-44BC-463B-88C4-E8E2B516A7EE}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {2BDD861B-44BC-463B-88C4-E8E2B516A7EE}.Debug|x86.Build.0 = Debug|Any CPU
+ {2BDD861B-44BC-463B-88C4-E8E2B516A7EE}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2BDD861B-44BC-463B-88C4-E8E2B516A7EE}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2BDD861B-44BC-463B-88C4-E8E2B516A7EE}.Release|x64.ActiveCfg = Release|Any CPU
+ {2BDD861B-44BC-463B-88C4-E8E2B516A7EE}.Release|x64.Build.0 = Release|Any CPU
+ {2BDD861B-44BC-463B-88C4-E8E2B516A7EE}.Release|x86.ActiveCfg = Release|Any CPU
+ {2BDD861B-44BC-463B-88C4-E8E2B516A7EE}.Release|x86.Build.0 = Release|Any CPU
+ {98ABB710-9106-4E5C-AA04-D19D5E92EDC1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {98ABB710-9106-4E5C-AA04-D19D5E92EDC1}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {98ABB710-9106-4E5C-AA04-D19D5E92EDC1}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {98ABB710-9106-4E5C-AA04-D19D5E92EDC1}.Debug|x64.Build.0 = Debug|Any CPU
+ {98ABB710-9106-4E5C-AA04-D19D5E92EDC1}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {98ABB710-9106-4E5C-AA04-D19D5E92EDC1}.Debug|x86.Build.0 = Debug|Any CPU
+ {98ABB710-9106-4E5C-AA04-D19D5E92EDC1}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {98ABB710-9106-4E5C-AA04-D19D5E92EDC1}.Release|Any CPU.Build.0 = Release|Any CPU
+ {98ABB710-9106-4E5C-AA04-D19D5E92EDC1}.Release|x64.ActiveCfg = Release|Any CPU
+ {98ABB710-9106-4E5C-AA04-D19D5E92EDC1}.Release|x64.Build.0 = Release|Any CPU
+ {98ABB710-9106-4E5C-AA04-D19D5E92EDC1}.Release|x86.ActiveCfg = Release|Any CPU
+ {98ABB710-9106-4E5C-AA04-D19D5E92EDC1}.Release|x86.Build.0 = Release|Any CPU
+ {8865807E-C577-4476-BE33-0EBB4E07CD8B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8865807E-C577-4476-BE33-0EBB4E07CD8B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8865807E-C577-4476-BE33-0EBB4E07CD8B}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {8865807E-C577-4476-BE33-0EBB4E07CD8B}.Debug|x64.Build.0 = Debug|Any CPU
+ {8865807E-C577-4476-BE33-0EBB4E07CD8B}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {8865807E-C577-4476-BE33-0EBB4E07CD8B}.Debug|x86.Build.0 = Debug|Any CPU
+ {8865807E-C577-4476-BE33-0EBB4E07CD8B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8865807E-C577-4476-BE33-0EBB4E07CD8B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {8865807E-C577-4476-BE33-0EBB4E07CD8B}.Release|x64.ActiveCfg = Release|Any CPU
+ {8865807E-C577-4476-BE33-0EBB4E07CD8B}.Release|x64.Build.0 = Release|Any CPU
+ {8865807E-C577-4476-BE33-0EBB4E07CD8B}.Release|x86.ActiveCfg = Release|Any CPU
+ {8865807E-C577-4476-BE33-0EBB4E07CD8B}.Release|x86.Build.0 = Release|Any CPU
+ {028AF0DA-6D64-435F-B1CA-C14829892C4F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {028AF0DA-6D64-435F-B1CA-C14829892C4F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {028AF0DA-6D64-435F-B1CA-C14829892C4F}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {028AF0DA-6D64-435F-B1CA-C14829892C4F}.Debug|x64.Build.0 = Debug|Any CPU
+ {028AF0DA-6D64-435F-B1CA-C14829892C4F}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {028AF0DA-6D64-435F-B1CA-C14829892C4F}.Debug|x86.Build.0 = Debug|Any CPU
+ {028AF0DA-6D64-435F-B1CA-C14829892C4F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {028AF0DA-6D64-435F-B1CA-C14829892C4F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {028AF0DA-6D64-435F-B1CA-C14829892C4F}.Release|x64.ActiveCfg = Release|Any CPU
+ {028AF0DA-6D64-435F-B1CA-C14829892C4F}.Release|x64.Build.0 = Release|Any CPU
+ {028AF0DA-6D64-435F-B1CA-C14829892C4F}.Release|x86.ActiveCfg = Release|Any CPU
+ {028AF0DA-6D64-435F-B1CA-C14829892C4F}.Release|x86.Build.0 = Release|Any CPU
+ {9DB38617-9738-45C3-A101-8B04869CB07F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {9DB38617-9738-45C3-A101-8B04869CB07F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {9DB38617-9738-45C3-A101-8B04869CB07F}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {9DB38617-9738-45C3-A101-8B04869CB07F}.Debug|x64.Build.0 = Debug|Any CPU
+ {9DB38617-9738-45C3-A101-8B04869CB07F}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {9DB38617-9738-45C3-A101-8B04869CB07F}.Debug|x86.Build.0 = Debug|Any CPU
+ {9DB38617-9738-45C3-A101-8B04869CB07F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {9DB38617-9738-45C3-A101-8B04869CB07F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {9DB38617-9738-45C3-A101-8B04869CB07F}.Release|x64.ActiveCfg = Release|Any CPU
+ {9DB38617-9738-45C3-A101-8B04869CB07F}.Release|x64.Build.0 = Release|Any CPU
+ {9DB38617-9738-45C3-A101-8B04869CB07F}.Release|x86.ActiveCfg = Release|Any CPU
+ {9DB38617-9738-45C3-A101-8B04869CB07F}.Release|x86.Build.0 = Release|Any CPU
+ {ABC9A8FF-0332-4987-A2E8-63108394DA26}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {ABC9A8FF-0332-4987-A2E8-63108394DA26}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {ABC9A8FF-0332-4987-A2E8-63108394DA26}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {ABC9A8FF-0332-4987-A2E8-63108394DA26}.Debug|x64.Build.0 = Debug|Any CPU
+ {ABC9A8FF-0332-4987-A2E8-63108394DA26}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {ABC9A8FF-0332-4987-A2E8-63108394DA26}.Debug|x86.Build.0 = Debug|Any CPU
+ {ABC9A8FF-0332-4987-A2E8-63108394DA26}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {ABC9A8FF-0332-4987-A2E8-63108394DA26}.Release|Any CPU.Build.0 = Release|Any CPU
+ {ABC9A8FF-0332-4987-A2E8-63108394DA26}.Release|x64.ActiveCfg = Release|Any CPU
+ {ABC9A8FF-0332-4987-A2E8-63108394DA26}.Release|x64.Build.0 = Release|Any CPU
+ {ABC9A8FF-0332-4987-A2E8-63108394DA26}.Release|x86.ActiveCfg = Release|Any CPU
+ {ABC9A8FF-0332-4987-A2E8-63108394DA26}.Release|x86.Build.0 = Release|Any CPU
+ {DD4F5E02-4366-4CA3-8CF1-941C21C27B6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {DD4F5E02-4366-4CA3-8CF1-941C21C27B6E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {DD4F5E02-4366-4CA3-8CF1-941C21C27B6E}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {DD4F5E02-4366-4CA3-8CF1-941C21C27B6E}.Debug|x64.Build.0 = Debug|Any CPU
+ {DD4F5E02-4366-4CA3-8CF1-941C21C27B6E}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {DD4F5E02-4366-4CA3-8CF1-941C21C27B6E}.Debug|x86.Build.0 = Debug|Any CPU
+ {DD4F5E02-4366-4CA3-8CF1-941C21C27B6E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {DD4F5E02-4366-4CA3-8CF1-941C21C27B6E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {DD4F5E02-4366-4CA3-8CF1-941C21C27B6E}.Release|x64.ActiveCfg = Release|Any CPU
+ {DD4F5E02-4366-4CA3-8CF1-941C21C27B6E}.Release|x64.Build.0 = Release|Any CPU
+ {DD4F5E02-4366-4CA3-8CF1-941C21C27B6E}.Release|x86.ActiveCfg = Release|Any CPU
+ {DD4F5E02-4366-4CA3-8CF1-941C21C27B6E}.Release|x86.Build.0 = Release|Any CPU
+ {776C6198-A229-4C2C-B19E-3F2744BD5279}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {776C6198-A229-4C2C-B19E-3F2744BD5279}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {776C6198-A229-4C2C-B19E-3F2744BD5279}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {776C6198-A229-4C2C-B19E-3F2744BD5279}.Debug|x64.Build.0 = Debug|Any CPU
+ {776C6198-A229-4C2C-B19E-3F2744BD5279}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {776C6198-A229-4C2C-B19E-3F2744BD5279}.Debug|x86.Build.0 = Debug|Any CPU
+ {776C6198-A229-4C2C-B19E-3F2744BD5279}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {776C6198-A229-4C2C-B19E-3F2744BD5279}.Release|Any CPU.Build.0 = Release|Any CPU
+ {776C6198-A229-4C2C-B19E-3F2744BD5279}.Release|x64.ActiveCfg = Release|Any CPU
+ {776C6198-A229-4C2C-B19E-3F2744BD5279}.Release|x64.Build.0 = Release|Any CPU
+ {776C6198-A229-4C2C-B19E-3F2744BD5279}.Release|x86.ActiveCfg = Release|Any CPU
+ {776C6198-A229-4C2C-B19E-3F2744BD5279}.Release|x86.Build.0 = Release|Any CPU
+ {EA780C77-735F-4FA7-8AE9-78413C928BA6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {EA780C77-735F-4FA7-8AE9-78413C928BA6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {EA780C77-735F-4FA7-8AE9-78413C928BA6}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {EA780C77-735F-4FA7-8AE9-78413C928BA6}.Debug|x64.Build.0 = Debug|Any CPU
+ {EA780C77-735F-4FA7-8AE9-78413C928BA6}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {EA780C77-735F-4FA7-8AE9-78413C928BA6}.Debug|x86.Build.0 = Debug|Any CPU
+ {EA780C77-735F-4FA7-8AE9-78413C928BA6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {EA780C77-735F-4FA7-8AE9-78413C928BA6}.Release|Any CPU.Build.0 = Release|Any CPU
+ {EA780C77-735F-4FA7-8AE9-78413C928BA6}.Release|x64.ActiveCfg = Release|Any CPU
+ {EA780C77-735F-4FA7-8AE9-78413C928BA6}.Release|x64.Build.0 = Release|Any CPU
+ {EA780C77-735F-4FA7-8AE9-78413C928BA6}.Release|x86.ActiveCfg = Release|Any CPU
+ {EA780C77-735F-4FA7-8AE9-78413C928BA6}.Release|x86.Build.0 = Release|Any CPU
+ {60326D0F-D9C7-4B8D-B664-BC8DE239AD31}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {60326D0F-D9C7-4B8D-B664-BC8DE239AD31}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {60326D0F-D9C7-4B8D-B664-BC8DE239AD31}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {60326D0F-D9C7-4B8D-B664-BC8DE239AD31}.Debug|x64.Build.0 = Debug|Any CPU
+ {60326D0F-D9C7-4B8D-B664-BC8DE239AD31}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {60326D0F-D9C7-4B8D-B664-BC8DE239AD31}.Debug|x86.Build.0 = Debug|Any CPU
+ {60326D0F-D9C7-4B8D-B664-BC8DE239AD31}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {60326D0F-D9C7-4B8D-B664-BC8DE239AD31}.Release|Any CPU.Build.0 = Release|Any CPU
+ {60326D0F-D9C7-4B8D-B664-BC8DE239AD31}.Release|x64.ActiveCfg = Release|Any CPU
+ {60326D0F-D9C7-4B8D-B664-BC8DE239AD31}.Release|x64.Build.0 = Release|Any CPU
+ {60326D0F-D9C7-4B8D-B664-BC8DE239AD31}.Release|x86.ActiveCfg = Release|Any CPU
+ {60326D0F-D9C7-4B8D-B664-BC8DE239AD31}.Release|x86.Build.0 = Release|Any CPU
+ {635D6C93-D9E2-46C2-9294-5C0713CBA245}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {635D6C93-D9E2-46C2-9294-5C0713CBA245}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {635D6C93-D9E2-46C2-9294-5C0713CBA245}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {635D6C93-D9E2-46C2-9294-5C0713CBA245}.Debug|x64.Build.0 = Debug|Any CPU
+ {635D6C93-D9E2-46C2-9294-5C0713CBA245}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {635D6C93-D9E2-46C2-9294-5C0713CBA245}.Debug|x86.Build.0 = Debug|Any CPU
+ {635D6C93-D9E2-46C2-9294-5C0713CBA245}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {635D6C93-D9E2-46C2-9294-5C0713CBA245}.Release|Any CPU.Build.0 = Release|Any CPU
+ {635D6C93-D9E2-46C2-9294-5C0713CBA245}.Release|x64.ActiveCfg = Release|Any CPU
+ {635D6C93-D9E2-46C2-9294-5C0713CBA245}.Release|x64.Build.0 = Release|Any CPU
+ {635D6C93-D9E2-46C2-9294-5C0713CBA245}.Release|x86.ActiveCfg = Release|Any CPU
+ {635D6C93-D9E2-46C2-9294-5C0713CBA245}.Release|x86.Build.0 = Release|Any CPU
+ {0C37AA6D-37DB-439B-97D5-9E799DDDC831}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {0C37AA6D-37DB-439B-97D5-9E799DDDC831}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {0C37AA6D-37DB-439B-97D5-9E799DDDC831}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {0C37AA6D-37DB-439B-97D5-9E799DDDC831}.Debug|x64.Build.0 = Debug|Any CPU
+ {0C37AA6D-37DB-439B-97D5-9E799DDDC831}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {0C37AA6D-37DB-439B-97D5-9E799DDDC831}.Debug|x86.Build.0 = Debug|Any CPU
+ {0C37AA6D-37DB-439B-97D5-9E799DDDC831}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {0C37AA6D-37DB-439B-97D5-9E799DDDC831}.Release|Any CPU.Build.0 = Release|Any CPU
+ {0C37AA6D-37DB-439B-97D5-9E799DDDC831}.Release|x64.ActiveCfg = Release|Any CPU
+ {0C37AA6D-37DB-439B-97D5-9E799DDDC831}.Release|x64.Build.0 = Release|Any CPU
+ {0C37AA6D-37DB-439B-97D5-9E799DDDC831}.Release|x86.ActiveCfg = Release|Any CPU
+ {0C37AA6D-37DB-439B-97D5-9E799DDDC831}.Release|x86.Build.0 = Release|Any CPU
+ {89593AC0-B761-400D-8213-F6E728980F59}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {89593AC0-B761-400D-8213-F6E728980F59}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {89593AC0-B761-400D-8213-F6E728980F59}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {89593AC0-B761-400D-8213-F6E728980F59}.Debug|x64.Build.0 = Debug|Any CPU
+ {89593AC0-B761-400D-8213-F6E728980F59}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {89593AC0-B761-400D-8213-F6E728980F59}.Debug|x86.Build.0 = Debug|Any CPU
+ {89593AC0-B761-400D-8213-F6E728980F59}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {89593AC0-B761-400D-8213-F6E728980F59}.Release|Any CPU.Build.0 = Release|Any CPU
+ {89593AC0-B761-400D-8213-F6E728980F59}.Release|x64.ActiveCfg = Release|Any CPU
+ {89593AC0-B761-400D-8213-F6E728980F59}.Release|x64.Build.0 = Release|Any CPU
+ {89593AC0-B761-400D-8213-F6E728980F59}.Release|x86.ActiveCfg = Release|Any CPU
+ {89593AC0-B761-400D-8213-F6E728980F59}.Release|x86.Build.0 = Release|Any CPU
+ {312F4C56-7865-47FD-8170-E6555DBC4884}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {312F4C56-7865-47FD-8170-E6555DBC4884}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {312F4C56-7865-47FD-8170-E6555DBC4884}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {312F4C56-7865-47FD-8170-E6555DBC4884}.Debug|x64.Build.0 = Debug|Any CPU
+ {312F4C56-7865-47FD-8170-E6555DBC4884}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {312F4C56-7865-47FD-8170-E6555DBC4884}.Debug|x86.Build.0 = Debug|Any CPU
+ {312F4C56-7865-47FD-8170-E6555DBC4884}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {312F4C56-7865-47FD-8170-E6555DBC4884}.Release|Any CPU.Build.0 = Release|Any CPU
+ {312F4C56-7865-47FD-8170-E6555DBC4884}.Release|x64.ActiveCfg = Release|Any CPU
+ {312F4C56-7865-47FD-8170-E6555DBC4884}.Release|x64.Build.0 = Release|Any CPU
+ {312F4C56-7865-47FD-8170-E6555DBC4884}.Release|x86.ActiveCfg = Release|Any CPU
+ {312F4C56-7865-47FD-8170-E6555DBC4884}.Release|x86.Build.0 = Release|Any CPU
+ {5B428B36-6857-47BA-8DE1-A8207A2DF1E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {5B428B36-6857-47BA-8DE1-A8207A2DF1E8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {5B428B36-6857-47BA-8DE1-A8207A2DF1E8}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {5B428B36-6857-47BA-8DE1-A8207A2DF1E8}.Debug|x64.Build.0 = Debug|Any CPU
+ {5B428B36-6857-47BA-8DE1-A8207A2DF1E8}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {5B428B36-6857-47BA-8DE1-A8207A2DF1E8}.Debug|x86.Build.0 = Debug|Any CPU
+ {5B428B36-6857-47BA-8DE1-A8207A2DF1E8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {5B428B36-6857-47BA-8DE1-A8207A2DF1E8}.Release|Any CPU.Build.0 = Release|Any CPU
+ {5B428B36-6857-47BA-8DE1-A8207A2DF1E8}.Release|x64.ActiveCfg = Release|Any CPU
+ {5B428B36-6857-47BA-8DE1-A8207A2DF1E8}.Release|x64.Build.0 = Release|Any CPU
+ {5B428B36-6857-47BA-8DE1-A8207A2DF1E8}.Release|x86.ActiveCfg = Release|Any CPU
+ {5B428B36-6857-47BA-8DE1-A8207A2DF1E8}.Release|x86.Build.0 = Release|Any CPU
+ {568561C3-C4D9-4783-8103-B68C94E2F8D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {568561C3-C4D9-4783-8103-B68C94E2F8D1}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {568561C3-C4D9-4783-8103-B68C94E2F8D1}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {568561C3-C4D9-4783-8103-B68C94E2F8D1}.Debug|x64.Build.0 = Debug|Any CPU
+ {568561C3-C4D9-4783-8103-B68C94E2F8D1}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {568561C3-C4D9-4783-8103-B68C94E2F8D1}.Debug|x86.Build.0 = Debug|Any CPU
+ {568561C3-C4D9-4783-8103-B68C94E2F8D1}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {568561C3-C4D9-4783-8103-B68C94E2F8D1}.Release|Any CPU.Build.0 = Release|Any CPU
+ {568561C3-C4D9-4783-8103-B68C94E2F8D1}.Release|x64.ActiveCfg = Release|Any CPU
+ {568561C3-C4D9-4783-8103-B68C94E2F8D1}.Release|x64.Build.0 = Release|Any CPU
+ {568561C3-C4D9-4783-8103-B68C94E2F8D1}.Release|x86.ActiveCfg = Release|Any CPU
+ {568561C3-C4D9-4783-8103-B68C94E2F8D1}.Release|x86.Build.0 = Release|Any CPU
+ {65A65ABB-1F74-471C-84C5-E9CBA6F27B74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {65A65ABB-1F74-471C-84C5-E9CBA6F27B74}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {65A65ABB-1F74-471C-84C5-E9CBA6F27B74}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {65A65ABB-1F74-471C-84C5-E9CBA6F27B74}.Debug|x64.Build.0 = Debug|Any CPU
+ {65A65ABB-1F74-471C-84C5-E9CBA6F27B74}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {65A65ABB-1F74-471C-84C5-E9CBA6F27B74}.Debug|x86.Build.0 = Debug|Any CPU
+ {65A65ABB-1F74-471C-84C5-E9CBA6F27B74}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {65A65ABB-1F74-471C-84C5-E9CBA6F27B74}.Release|Any CPU.Build.0 = Release|Any CPU
+ {65A65ABB-1F74-471C-84C5-E9CBA6F27B74}.Release|x64.ActiveCfg = Release|Any CPU
+ {65A65ABB-1F74-471C-84C5-E9CBA6F27B74}.Release|x64.Build.0 = Release|Any CPU
+ {65A65ABB-1F74-471C-84C5-E9CBA6F27B74}.Release|x86.ActiveCfg = Release|Any CPU
+ {65A65ABB-1F74-471C-84C5-E9CBA6F27B74}.Release|x86.Build.0 = Release|Any CPU
+ {71B7CB00-B0B9-4FEB-9E9F-E8B47FAD5408}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {71B7CB00-B0B9-4FEB-9E9F-E8B47FAD5408}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {71B7CB00-B0B9-4FEB-9E9F-E8B47FAD5408}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {71B7CB00-B0B9-4FEB-9E9F-E8B47FAD5408}.Debug|x64.Build.0 = Debug|Any CPU
+ {71B7CB00-B0B9-4FEB-9E9F-E8B47FAD5408}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {71B7CB00-B0B9-4FEB-9E9F-E8B47FAD5408}.Debug|x86.Build.0 = Debug|Any CPU
+ {71B7CB00-B0B9-4FEB-9E9F-E8B47FAD5408}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {71B7CB00-B0B9-4FEB-9E9F-E8B47FAD5408}.Release|Any CPU.Build.0 = Release|Any CPU
+ {71B7CB00-B0B9-4FEB-9E9F-E8B47FAD5408}.Release|x64.ActiveCfg = Release|Any CPU
+ {71B7CB00-B0B9-4FEB-9E9F-E8B47FAD5408}.Release|x64.Build.0 = Release|Any CPU
+ {71B7CB00-B0B9-4FEB-9E9F-E8B47FAD5408}.Release|x86.ActiveCfg = Release|Any CPU
+ {71B7CB00-B0B9-4FEB-9E9F-E8B47FAD5408}.Release|x86.Build.0 = Release|Any CPU
+ {418BE048-D472-40C0-917F-060A9E781B82}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {418BE048-D472-40C0-917F-060A9E781B82}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {418BE048-D472-40C0-917F-060A9E781B82}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {418BE048-D472-40C0-917F-060A9E781B82}.Debug|x64.Build.0 = Debug|Any CPU
+ {418BE048-D472-40C0-917F-060A9E781B82}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {418BE048-D472-40C0-917F-060A9E781B82}.Debug|x86.Build.0 = Debug|Any CPU
+ {418BE048-D472-40C0-917F-060A9E781B82}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {418BE048-D472-40C0-917F-060A9E781B82}.Release|Any CPU.Build.0 = Release|Any CPU
+ {418BE048-D472-40C0-917F-060A9E781B82}.Release|x64.ActiveCfg = Release|Any CPU
+ {418BE048-D472-40C0-917F-060A9E781B82}.Release|x64.Build.0 = Release|Any CPU
+ {418BE048-D472-40C0-917F-060A9E781B82}.Release|x86.ActiveCfg = Release|Any CPU
+ {418BE048-D472-40C0-917F-060A9E781B82}.Release|x86.Build.0 = Release|Any CPU
+ {6BD8DC02-2270-4276-952E-E34E973A1237}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6BD8DC02-2270-4276-952E-E34E973A1237}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6BD8DC02-2270-4276-952E-E34E973A1237}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {6BD8DC02-2270-4276-952E-E34E973A1237}.Debug|x64.Build.0 = Debug|Any CPU
+ {6BD8DC02-2270-4276-952E-E34E973A1237}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {6BD8DC02-2270-4276-952E-E34E973A1237}.Debug|x86.Build.0 = Debug|Any CPU
+ {6BD8DC02-2270-4276-952E-E34E973A1237}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6BD8DC02-2270-4276-952E-E34E973A1237}.Release|Any CPU.Build.0 = Release|Any CPU
+ {6BD8DC02-2270-4276-952E-E34E973A1237}.Release|x64.ActiveCfg = Release|Any CPU
+ {6BD8DC02-2270-4276-952E-E34E973A1237}.Release|x64.Build.0 = Release|Any CPU
+ {6BD8DC02-2270-4276-952E-E34E973A1237}.Release|x86.ActiveCfg = Release|Any CPU
+ {6BD8DC02-2270-4276-952E-E34E973A1237}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -416,31 +620,73 @@ Global
{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}
+ {D0D6B8B0-AC35-B3FA-0B96-A9B0E4A3253E} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
+ {21C6533F-5B99-F63D-D6A0-0D1E74554637} = {D0D6B8B0-AC35-B3FA-0B96-A9B0E4A3253E}
+ {2BDD861B-44BC-463B-88C4-E8E2B516A7EE} = {21C6533F-5B99-F63D-D6A0-0D1E74554637}
+ {98ABB710-9106-4E5C-AA04-D19D5E92EDC1} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
+ {407BCCB7-2AB0-5429-FB5F-A9376121CF06} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
+ {E7E14295-D412-4B43-44D5-0AAD5D27712E} = {407BCCB7-2AB0-5429-FB5F-A9376121CF06}
+ {8865807E-C577-4476-BE33-0EBB4E07CD8B} = {E7E14295-D412-4B43-44D5-0AAD5D27712E}
+ {8CFDC985-5E1F-E566-CA4D-28D1B5AD1B06} = {407BCCB7-2AB0-5429-FB5F-A9376121CF06}
+ {028AF0DA-6D64-435F-B1CA-C14829892C4F} = {8CFDC985-5E1F-E566-CA4D-28D1B5AD1B06}
+ {EDEF9ADB-D556-6B20-2E4B-8F977F5C545A} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
+ {AA22B6BC-789B-946B-F9BD-06D6D86B3211} = {EDEF9ADB-D556-6B20-2E4B-8F977F5C545A}
+ {9DB38617-9738-45C3-A101-8B04869CB07F} = {AA22B6BC-789B-946B-F9BD-06D6D86B3211}
+ {1A80A494-6197-707A-BB12-E5CAFEB6DBC3} = {EDEF9ADB-D556-6B20-2E4B-8F977F5C545A}
+ {ABC9A8FF-0332-4987-A2E8-63108394DA26} = {1A80A494-6197-707A-BB12-E5CAFEB6DBC3}
+ {EDFE54F1-08A0-5241-003B-94DECB51237C} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
+ {50896B77-11EA-ADED-5FA2-195E6FABE81B} = {EDFE54F1-08A0-5241-003B-94DECB51237C}
+ {DD4F5E02-4366-4CA3-8CF1-941C21C27B6E} = {50896B77-11EA-ADED-5FA2-195E6FABE81B}
+ {0BF9C624-18C3-4CFE-0DEF-3D1D0C280507} = {EDFE54F1-08A0-5241-003B-94DECB51237C}
+ {776C6198-A229-4C2C-B19E-3F2744BD5279} = {0BF9C624-18C3-4CFE-0DEF-3D1D0C280507}
+ {036B8169-510F-3971-AD3F-66559BF086AA} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
+ {92885E6D-C52A-2FB4-A33D-9A8FE23A87BF} = {036B8169-510F-3971-AD3F-66559BF086AA}
+ {EA780C77-735F-4FA7-8AE9-78413C928BA6} = {92885E6D-C52A-2FB4-A33D-9A8FE23A87BF}
+ {3794ADC3-BA35-663C-885F-C144081FBEA1} = {036B8169-510F-3971-AD3F-66559BF086AA}
+ {60326D0F-D9C7-4B8D-B664-BC8DE239AD31} = {3794ADC3-BA35-663C-885F-C144081FBEA1}
+ {3AB41C38-05DB-BFCC-9BA0-9BC97041A508} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
+ {43109C86-4551-F94F-E9C3-994B9C735653} = {3AB41C38-05DB-BFCC-9BA0-9BC97041A508}
+ {635D6C93-D9E2-46C2-9294-5C0713CBA245} = {43109C86-4551-F94F-E9C3-994B9C735653}
+ {FE27F79D-3631-2D96-056A-69F8E94CD8E3} = {3AB41C38-05DB-BFCC-9BA0-9BC97041A508}
+ {0C37AA6D-37DB-439B-97D5-9E799DDDC831} = {FE27F79D-3631-2D96-056A-69F8E94CD8E3}
+ {F29503A5-E7F1-B611-4026-76E83B89B0AB} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
+ {8D009ABD-C820-B7D4-2412-0990C1960013} = {F29503A5-E7F1-B611-4026-76E83B89B0AB}
+ {89593AC0-B761-400D-8213-F6E728980F59} = {8D009ABD-C820-B7D4-2412-0990C1960013}
+ {7C677DC5-D0E9-6A09-3DB0-042075A9E677} = {F29503A5-E7F1-B611-4026-76E83B89B0AB}
+ {312F4C56-7865-47FD-8170-E6555DBC4884} = {7C677DC5-D0E9-6A09-3DB0-042075A9E677}
+ {382D38B3-4D74-95AB-947E-DC7B53CBCF32} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
+ {489DA9C1-7DF6-4B43-A5A6-01AD261B3AC8} = {382D38B3-4D74-95AB-947E-DC7B53CBCF32}
+ {5B428B36-6857-47BA-8DE1-A8207A2DF1E8} = {489DA9C1-7DF6-4B43-A5A6-01AD261B3AC8}
+ {A3D04C92-6F74-537C-D0F4-BD2C8CD7687D} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
+ {2BF544BD-4498-3DD9-D97E-96A22189C7D8} = {A3D04C92-6F74-537C-D0F4-BD2C8CD7687D}
+ {568561C3-C4D9-4783-8103-B68C94E2F8D1} = {2BF544BD-4498-3DD9-D97E-96A22189C7D8}
+ {CCF0649B-70C0-BDF5-F1BD-87A1DBED8C5D} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
+ {4FFF0ACB-E93F-597A-5968-6C89F4BB5AC7} = {CCF0649B-70C0-BDF5-F1BD-87A1DBED8C5D}
+ {65A65ABB-1F74-471C-84C5-E9CBA6F27B74} = {4FFF0ACB-E93F-597A-5968-6C89F4BB5AC7}
+ {46A5ACB5-176C-983D-5434-922CD2C33C4A} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
+ {43930E3C-6ABA-C329-E65B-CFA08664DBAF} = {46A5ACB5-176C-983D-5434-922CD2C33C4A}
+ {71B7CB00-B0B9-4FEB-9E9F-E8B47FAD5408} = {43930E3C-6ABA-C329-E65B-CFA08664DBAF}
+ {B6FD6E61-6054-1C88-3299-57EC8B98D824} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
+ {93CD11E9-EE4D-82DD-DD63-4D36831D0976} = {B6FD6E61-6054-1C88-3299-57EC8B98D824}
+ {418BE048-D472-40C0-917F-060A9E781B82} = {93CD11E9-EE4D-82DD-DD63-4D36831D0976}
+ {553D81D4-666B-BE26-75F9-480CB214ACEE} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
+ {3A48F700-9635-2216-D291-85337CE5D869} = {553D81D4-666B-BE26-75F9-480CB214ACEE}
+ {6BD8DC02-2270-4276-952E-E34E973A1237} = {3A48F700-9635-2216-D291-85337CE5D869}
EndGlobalSection
EndGlobal
diff --git a/backend/Tests/Admin/IntegrationTests/Knot.Modules.Admin.IntegrationTests.csproj b/backend/Tests/Admin/IntegrationTests/Knot.Modules.Admin.IntegrationTests.csproj
new file mode 100644
index 0000000..b28cb9d
--- /dev/null
+++ b/backend/Tests/Admin/IntegrationTests/Knot.Modules.Admin.IntegrationTests.csproj
@@ -0,0 +1,26 @@
+
+
+
+ net10.0
+ enable
+ enable
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/backend/Tests/Knot.Modules.Admin.UnitTests/Knot.Modules.Admin.UnitTests.csproj b/backend/Tests/Admin/UnitTests/Knot.Modules.Admin.UnitTests.csproj
similarity index 69%
rename from backend/Tests/Knot.Modules.Admin.UnitTests/Knot.Modules.Admin.UnitTests.csproj
rename to backend/Tests/Admin/UnitTests/Knot.Modules.Admin.UnitTests.csproj
index d15a597..3d58229 100644
--- a/backend/Tests/Knot.Modules.Admin.UnitTests/Knot.Modules.Admin.UnitTests.csproj
+++ b/backend/Tests/Admin/UnitTests/Knot.Modules.Admin.UnitTests.csproj
@@ -1,4 +1,4 @@
-
+
net10.0
@@ -21,8 +21,10 @@
-
-
+
+
+
+
diff --git a/backend/Tests/Knot.Modules.Settings.UnitTests/Knot.Modules.Settings.UnitTests.csproj b/backend/Tests/Admin/UnitTests/Knot.Modules.Settings.UnitTests.csproj
similarity index 76%
rename from backend/Tests/Knot.Modules.Settings.UnitTests/Knot.Modules.Settings.UnitTests.csproj
rename to backend/Tests/Admin/UnitTests/Knot.Modules.Settings.UnitTests.csproj
index 319b9b1..9b38437 100644
--- a/backend/Tests/Knot.Modules.Settings.UnitTests/Knot.Modules.Settings.UnitTests.csproj
+++ b/backend/Tests/Admin/UnitTests/Knot.Modules.Settings.UnitTests.csproj
@@ -1,4 +1,4 @@
-
+
net10.0
@@ -21,8 +21,8 @@
-
-
+
+
diff --git a/backend/Tests/Knot.Modules.Admin.UnitTests/ResetUserPasswordCommandHandlerTests.cs b/backend/Tests/Admin/UnitTests/ResetUserPasswordCommandHandlerTests.cs
similarity index 97%
rename from backend/Tests/Knot.Modules.Admin.UnitTests/ResetUserPasswordCommandHandlerTests.cs
rename to backend/Tests/Admin/UnitTests/ResetUserPasswordCommandHandlerTests.cs
index c98b46e..3c27630 100644
--- a/backend/Tests/Knot.Modules.Admin.UnitTests/ResetUserPasswordCommandHandlerTests.cs
+++ b/backend/Tests/Admin/UnitTests/ResetUserPasswordCommandHandlerTests.cs
@@ -1,5 +1,5 @@
using FluentAssertions;
-using Host.Application.Admin.Commands;
+using Knot.Modules.Admin.Application.Admin.Commands;
using Knot.Modules.Auth.Application.Abstractions;
using Knot.Modules.Auth.Domain;
using Knot.Shared.Kernel;
diff --git a/backend/Tests/Knot.Modules.Profiles.UnitTests/Knot.Modules.Profiles.UnitTests.csproj b/backend/Tests/Auth/IntegrationTests/Knot.Modules.Auth.IntegrationTests.csproj
similarity index 81%
rename from backend/Tests/Knot.Modules.Profiles.UnitTests/Knot.Modules.Profiles.UnitTests.csproj
rename to backend/Tests/Auth/IntegrationTests/Knot.Modules.Auth.IntegrationTests.csproj
index 6a0be6a..1607c89 100644
--- a/backend/Tests/Knot.Modules.Profiles.UnitTests/Knot.Modules.Profiles.UnitTests.csproj
+++ b/backend/Tests/Auth/IntegrationTests/Knot.Modules.Auth.IntegrationTests.csproj
@@ -1,4 +1,4 @@
-
+
net10.0
@@ -10,13 +10,14 @@
-
+
+
diff --git a/backend/Tests/Knot.Modules.Auth.UnitTests/Knot.Modules.Auth.UnitTests.csproj b/backend/Tests/Auth/UnitTests/Knot.Modules.Auth.UnitTests.csproj
similarity index 85%
rename from backend/Tests/Knot.Modules.Auth.UnitTests/Knot.Modules.Auth.UnitTests.csproj
rename to backend/Tests/Auth/UnitTests/Knot.Modules.Auth.UnitTests.csproj
index aea9f1e..ab17424 100644
--- a/backend/Tests/Knot.Modules.Auth.UnitTests/Knot.Modules.Auth.UnitTests.csproj
+++ b/backend/Tests/Auth/UnitTests/Knot.Modules.Auth.UnitTests.csproj
@@ -23,9 +23,10 @@
-
-
+
+
+
diff --git a/backend/Tests/Knot.Modules.Auth.UnitTests/LoginUserCommandHandlerTests.cs b/backend/Tests/Auth/UnitTests/LoginUserCommandHandlerTests.cs
similarity index 100%
rename from backend/Tests/Knot.Modules.Auth.UnitTests/LoginUserCommandHandlerTests.cs
rename to backend/Tests/Auth/UnitTests/LoginUserCommandHandlerTests.cs
diff --git a/backend/Tests/Knot.Modules.Conversations.UnitTests/Chats/CreateChatCommandHandlerTests.cs b/backend/Tests/Conversations/UnitTests/Chats/CreateChatCommandHandlerTests.cs
similarity index 100%
rename from backend/Tests/Knot.Modules.Conversations.UnitTests/Chats/CreateChatCommandHandlerTests.cs
rename to backend/Tests/Conversations/UnitTests/Chats/CreateChatCommandHandlerTests.cs
diff --git a/backend/Tests/Knot.Modules.Conversations.UnitTests/Chats/GetChatsQueryHandlerTests.cs b/backend/Tests/Conversations/UnitTests/Chats/GetChatsQueryHandlerTests.cs
similarity index 100%
rename from backend/Tests/Knot.Modules.Conversations.UnitTests/Chats/GetChatsQueryHandlerTests.cs
rename to backend/Tests/Conversations/UnitTests/Chats/GetChatsQueryHandlerTests.cs
diff --git a/backend/Tests/Knot.Modules.Conversations.UnitTests/GetOrCreateFavoritesCommandHandlerTests.cs b/backend/Tests/Conversations/UnitTests/GetOrCreateFavoritesCommandHandlerTests.cs
similarity index 100%
rename from backend/Tests/Knot.Modules.Conversations.UnitTests/GetOrCreateFavoritesCommandHandlerTests.cs
rename to backend/Tests/Conversations/UnitTests/GetOrCreateFavoritesCommandHandlerTests.cs
diff --git a/backend/Tests/Knot.Modules.Conversations.UnitTests/Knot.Modules.Conversations.UnitTests.csproj b/backend/Tests/Conversations/UnitTests/Knot.Modules.Conversations.UnitTests.csproj
similarity index 72%
rename from backend/Tests/Knot.Modules.Conversations.UnitTests/Knot.Modules.Conversations.UnitTests.csproj
rename to backend/Tests/Conversations/UnitTests/Knot.Modules.Conversations.UnitTests.csproj
index 4939487..11589b9 100644
--- a/backend/Tests/Knot.Modules.Conversations.UnitTests/Knot.Modules.Conversations.UnitTests.csproj
+++ b/backend/Tests/Conversations/UnitTests/Knot.Modules.Conversations.UnitTests.csproj
@@ -23,9 +23,11 @@
-
-
-
+
+
+
+
+
diff --git a/backend/Tests/Knot.Modules.Conversations.UnitTests/Messages/SendMessageCommandHandlerTests.cs b/backend/Tests/Conversations/UnitTests/Messages/SendMessageCommandHandlerTests.cs
similarity index 87%
rename from backend/Tests/Knot.Modules.Conversations.UnitTests/Messages/SendMessageCommandHandlerTests.cs
rename to backend/Tests/Conversations/UnitTests/Messages/SendMessageCommandHandlerTests.cs
index c40e483..8e5192a 100644
--- a/backend/Tests/Knot.Modules.Conversations.UnitTests/Messages/SendMessageCommandHandlerTests.cs
+++ b/backend/Tests/Conversations/UnitTests/Messages/SendMessageCommandHandlerTests.cs
@@ -12,6 +12,8 @@ using Knot.Modules.Conversations.Domain;
using Knot.Modules.Messaging.Domain;
using Knot.Modules.Conversations.Application.Abstractions;
using Knot.Modules.Conversations.Application.Messages.Send;
+using Knot.Modules.Settings.Application.Settings.Abstractions;
+using Knot.Modules.Settings.Application.Settings.DTOs;
namespace Knot.Modules.Conversations.UnitTests.Messages;
@@ -21,6 +23,7 @@ public class SendMessageCommandHandlerTests
private readonly IMessageRepository _messageRepository;
private readonly IChatsUnitOfWork _unitOfWork;
private readonly IMediator _mediator;
+ private readonly IMessagesSettings _messagesSettings;
private readonly SendMessageCommandHandler _handler;
public SendMessageCommandHandlerTests()
@@ -29,8 +32,12 @@ public class SendMessageCommandHandlerTests
_messageRepository = Substitute.For();
_unitOfWork = Substitute.For();
_mediator = Substitute.For();
+ _messagesSettings = Substitute.For();
+
+ var config = new Knot.Modules.Settings.Application.Settings.DTOs.MessagesConfig();
+ _messagesSettings.Current.Returns(config);
- _handler = new SendMessageCommandHandler(_chatRepository, _messageRepository, _unitOfWork, _mediator);
+ _handler = new SendMessageCommandHandler(_chatRepository, _messageRepository, _unitOfWork, _mediator, _messagesSettings);
}
[Fact]
diff --git a/backend/Tests/Federation/IntegrationTests/Knot.Modules.Federation.IntegrationTests.csproj b/backend/Tests/Federation/IntegrationTests/Knot.Modules.Federation.IntegrationTests.csproj
new file mode 100644
index 0000000..1607c89
--- /dev/null
+++ b/backend/Tests/Federation/IntegrationTests/Knot.Modules.Federation.IntegrationTests.csproj
@@ -0,0 +1,23 @@
+
+
+
+ net10.0
+ enable
+ enable
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/backend/Tests/Knot.Modules.Federation.UnitTests/Knot.Modules.Federation.UnitTests.csproj b/backend/Tests/Federation/UnitTests/Knot.Modules.Federation.UnitTests.csproj
similarity index 69%
rename from backend/Tests/Knot.Modules.Federation.UnitTests/Knot.Modules.Federation.UnitTests.csproj
rename to backend/Tests/Federation/UnitTests/Knot.Modules.Federation.UnitTests.csproj
index 3b7ca74..c5416d2 100644
--- a/backend/Tests/Knot.Modules.Federation.UnitTests/Knot.Modules.Federation.UnitTests.csproj
+++ b/backend/Tests/Federation/UnitTests/Knot.Modules.Federation.UnitTests.csproj
@@ -1,4 +1,4 @@
-
+
net10.0
@@ -21,8 +21,10 @@
-
-
+
+
+
+
diff --git a/backend/Tests/Knot.Modules.Klipy.UnitTests/Knot.Modules.Klipy.UnitTests.csproj b/backend/Tests/Klipy/UnitTests/Knot.Modules.Klipy.UnitTests.csproj
similarity index 69%
rename from backend/Tests/Knot.Modules.Klipy.UnitTests/Knot.Modules.Klipy.UnitTests.csproj
rename to backend/Tests/Klipy/UnitTests/Knot.Modules.Klipy.UnitTests.csproj
index 824f599..21ebf72 100644
--- a/backend/Tests/Knot.Modules.Klipy.UnitTests/Knot.Modules.Klipy.UnitTests.csproj
+++ b/backend/Tests/Klipy/UnitTests/Knot.Modules.Klipy.UnitTests.csproj
@@ -1,4 +1,4 @@
-
+
net10.0
@@ -21,8 +21,10 @@
-
-
+
+
+
+
diff --git a/backend/Tests/Knot.Modules.Conversations.UnitTests/CreatePersonalChatCommandHandlerTests.cs b/backend/Tests/Knot.Modules.Conversations.UnitTests/CreatePersonalChatCommandHandlerTests.cs
deleted file mode 100644
index a54275d..0000000
--- a/backend/Tests/Knot.Modules.Conversations.UnitTests/CreatePersonalChatCommandHandlerTests.cs
+++ /dev/null
@@ -1,42 +0,0 @@
-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/Messaging/IntegrationTests/Knot.Modules.Messaging.IntegrationTests.csproj b/backend/Tests/Messaging/IntegrationTests/Knot.Modules.Messaging.IntegrationTests.csproj
new file mode 100644
index 0000000..1607c89
--- /dev/null
+++ b/backend/Tests/Messaging/IntegrationTests/Knot.Modules.Messaging.IntegrationTests.csproj
@@ -0,0 +1,23 @@
+
+
+
+ net10.0
+ enable
+ enable
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/backend/Tests/Knot.Modules.Messaging.UnitTests/Knot.Modules.Messaging.UnitTests.csproj b/backend/Tests/Messaging/UnitTests/Knot.Modules.Messaging.UnitTests.csproj
similarity index 83%
rename from backend/Tests/Knot.Modules.Messaging.UnitTests/Knot.Modules.Messaging.UnitTests.csproj
rename to backend/Tests/Messaging/UnitTests/Knot.Modules.Messaging.UnitTests.csproj
index 6a0be6a..89029aa 100644
--- a/backend/Tests/Knot.Modules.Messaging.UnitTests/Knot.Modules.Messaging.UnitTests.csproj
+++ b/backend/Tests/Messaging/UnitTests/Knot.Modules.Messaging.UnitTests.csproj
@@ -1,4 +1,4 @@
-
+
net10.0
@@ -17,6 +17,8 @@
+
+
diff --git a/backend/Tests/Relations/IntegrationTests/Knot.Modules.Relations.IntegrationTests.csproj b/backend/Tests/Relations/IntegrationTests/Knot.Modules.Relations.IntegrationTests.csproj
new file mode 100644
index 0000000..1607c89
--- /dev/null
+++ b/backend/Tests/Relations/IntegrationTests/Knot.Modules.Relations.IntegrationTests.csproj
@@ -0,0 +1,23 @@
+
+
+
+ net10.0
+ enable
+ enable
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/backend/Tests/Knot.Modules.Relations.UnitTests/Knot.Modules.Relations.UnitTests.csproj b/backend/Tests/Relations/UnitTests/Knot.Modules.Relations.UnitTests.csproj
similarity index 69%
rename from backend/Tests/Knot.Modules.Relations.UnitTests/Knot.Modules.Relations.UnitTests.csproj
rename to backend/Tests/Relations/UnitTests/Knot.Modules.Relations.UnitTests.csproj
index f9ca8bf..33cc70b 100644
--- a/backend/Tests/Knot.Modules.Relations.UnitTests/Knot.Modules.Relations.UnitTests.csproj
+++ b/backend/Tests/Relations/UnitTests/Knot.Modules.Relations.UnitTests.csproj
@@ -1,4 +1,4 @@
-
+
net10.0
@@ -21,8 +21,10 @@
-
-
+
+
+
+
diff --git a/backend/Tests/Shared/IntegrationTests.Shared/BaseIntegrationTest.cs b/backend/Tests/Shared/IntegrationTests.Shared/BaseIntegrationTest.cs
new file mode 100644
index 0000000..8645865
--- /dev/null
+++ b/backend/Tests/Shared/IntegrationTests.Shared/BaseIntegrationTest.cs
@@ -0,0 +1,88 @@
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Mvc.Testing;
+using Microsoft.AspNetCore.TestHost;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using System.Net.Http;
+using Testcontainers.PostgreSql;
+using Testcontainers.MongoDb;
+using Xunit;
+using System.Threading.Tasks;
+using System.Collections.Generic;
+using Knot.Shared.Kernel;
+using NSubstitute;
+
+namespace Knot.IntegrationTests;
+
+public abstract class BaseIntegrationTest : IAsyncLifetime
+{
+ protected readonly PostgreSqlContainer _postgresContainer = new PostgreSqlBuilder()
+ .WithImage("postgres:15-alpine")
+ .Build();
+
+ protected readonly MongoDbContainer _mongoContainer = new MongoDbBuilder()
+ .WithImage("mongo:6.0")
+ .Build();
+
+ protected HttpClient _client;
+ protected WebApplicationFactory _factory;
+ protected readonly Guid _userId = Guid.NewGuid();
+ protected readonly IUserContext _userContextMock = Substitute.For();
+
+ public virtual async Task InitializeAsync()
+ {
+ await _postgresContainer.StartAsync();
+ await _mongoContainer.StartAsync();
+
+ _userContextMock.UserId.Returns(_userId);
+ _userContextMock.IsAuthenticated.Returns(true);
+
+ _factory = new IntegrationTestWebApplicationFactory(
+ _postgresContainer.GetConnectionString(),
+ _mongoContainer.GetConnectionString(),
+ _userContextMock);
+
+ _client = _factory.CreateClient();
+ }
+
+ public virtual async Task DisposeAsync()
+ {
+ await _postgresContainer.StopAsync();
+ await _mongoContainer.StopAsync();
+ _factory?.Dispose();
+ _client?.Dispose();
+ }
+
+ private class IntegrationTestWebApplicationFactory : WebApplicationFactory
+ {
+ private readonly string _pgConnectionString;
+ private readonly string _mongoConnectionString;
+ private readonly IUserContext _userContext;
+
+ public IntegrationTestWebApplicationFactory(string pg, string mongo, IUserContext userContext)
+ {
+ _pgConnectionString = pg;
+ _mongoConnectionString = mongo;
+ _userContext = userContext;
+ }
+
+ protected override void ConfigureWebHost(IWebHostBuilder builder)
+ {
+ builder.ConfigureAppConfiguration((context, config) =>
+ {
+ config.AddInMemoryCollection(new Dictionary
+ {
+ ["ConnectionStrings:DefaultConnection"] = _pgConnectionString,
+ ["ConnectionStrings:MongoConnection"] = _mongoConnectionString,
+ ["DATABASE_URL"] = _pgConnectionString,
+ ["MONGO_CONNECTION"] = _mongoConnectionString,
+ ["KNOT_MASTER_ENCRYPTION_KEY"] = "TestEncryptionKey_32CharactersLong!"
+ });
+ });
+
+ builder.ConfigureTestServices(services => {
+ services.AddScoped(_ => _userContext);
+ });
+ }
+ }
+}
diff --git a/backend/Tests/Shared/IntegrationTests.Shared/Knot.IntegrationTests.Shared.csproj b/backend/Tests/Shared/IntegrationTests.Shared/Knot.IntegrationTests.Shared.csproj
new file mode 100644
index 0000000..b74a66f
--- /dev/null
+++ b/backend/Tests/Shared/IntegrationTests.Shared/Knot.IntegrationTests.Shared.csproj
@@ -0,0 +1,38 @@
+
+
+
+ net10.0
+ enable
+ enable
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/backend/Tests/Knot.Modules.Storage.UnitTests/Knot.Modules.Storage.UnitTests.csproj b/backend/Tests/Storage/UnitTests/Knot.Modules.Storage.UnitTests.csproj
similarity index 69%
rename from backend/Tests/Knot.Modules.Storage.UnitTests/Knot.Modules.Storage.UnitTests.csproj
rename to backend/Tests/Storage/UnitTests/Knot.Modules.Storage.UnitTests.csproj
index bba2b3d..2c59df3 100644
--- a/backend/Tests/Knot.Modules.Storage.UnitTests/Knot.Modules.Storage.UnitTests.csproj
+++ b/backend/Tests/Storage/UnitTests/Knot.Modules.Storage.UnitTests.csproj
@@ -1,4 +1,4 @@
-
+
net10.0
@@ -21,8 +21,10 @@
-
-
+
+
+
+
diff --git a/backend/Tests/Stories/IntegrationTests/Knot.Modules.Stories.IntegrationTests.csproj b/backend/Tests/Stories/IntegrationTests/Knot.Modules.Stories.IntegrationTests.csproj
new file mode 100644
index 0000000..df3476d
--- /dev/null
+++ b/backend/Tests/Stories/IntegrationTests/Knot.Modules.Stories.IntegrationTests.csproj
@@ -0,0 +1,26 @@
+
+
+
+ net10.0
+ enable
+ enable
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/backend/Tests/Stories/IntegrationTests/StoriesTests.cs b/backend/Tests/Stories/IntegrationTests/StoriesTests.cs
new file mode 100644
index 0000000..3a9d464
--- /dev/null
+++ b/backend/Tests/Stories/IntegrationTests/StoriesTests.cs
@@ -0,0 +1,34 @@
+using Knot.IntegrationTests;
+using FluentAssertions;
+using Knot.Modules.Stories.Application.Stories.Commands.CreateStory;
+using Knot.Shared.Kernel;
+using Microsoft.AspNetCore.TestHost;
+using Microsoft.Extensions.DependencyInjection;
+using NSubstitute;
+using System.Net;
+using System.Net.Http.Json;
+using Xunit;
+using System;
+using System.Threading.Tasks;
+
+namespace Knot.IntegrationTests.Stories;
+
+public class StoriesTests : BaseIntegrationTest
+{
+ [Fact]
+ public async Task CreateStory_ShouldReturnOk_WhenValidRequest()
+ {
+ // Arrange
+ var request = new CreateStoryRequest("Text", null, "Hello Integration Test", null);
+
+ // Act
+ var response = await _client.PostAsJsonAsync("api/stories", request);
+
+ // Assert
+ response.StatusCode.Should().Be(HttpStatusCode.OK);
+
+ var content = await response.Content.ReadFromJsonAsync();
+ }
+}
+
+public record CreateStoryRequest(string Type, string? MediaUrl, string? Content, string? BgColor);
diff --git a/backend/Tests/Stories/UnitTests/CreateStoryCommandHandlerTests.cs b/backend/Tests/Stories/UnitTests/CreateStoryCommandHandlerTests.cs
new file mode 100644
index 0000000..604e657
--- /dev/null
+++ b/backend/Tests/Stories/UnitTests/CreateStoryCommandHandlerTests.cs
@@ -0,0 +1,76 @@
+using FluentAssertions;
+using Knot.Modules.Stories.Application.Stories.Commands.CreateStory;
+using Knot.Modules.Stories.Domain;
+using Knot.Shared.Kernel;
+using Knot.Modules.Settings.Application.Settings.Abstractions;
+using Knot.Modules.Settings.Application.Settings.DTOs;
+using Knot.Modules.Stories.Application.Abstractions;
+using NSubstitute;
+using Xunit;
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Knot.Modules.Stories.UnitTests;
+
+public class CreateStoryCommandHandlerTests
+{
+ private readonly IStoryRepository _storyRepository;
+ private readonly ISettingsService _settingsService;
+ private readonly CreateStoryCommandHandler _handler;
+
+ public CreateStoryCommandHandlerTests()
+ {
+ _storyRepository = Substitute.For();
+ _settingsService = Substitute.For();
+ _handler = new CreateStoryCommandHandler(_storyRepository, _settingsService);
+ }
+
+ [Fact]
+ public async Task Handle_ShouldSucceed_WhenValidRequest()
+ {
+ // Arrange
+ var command = new CreateStoryCommand(Guid.NewGuid(), "Text", null, "Hello Story", null);
+
+ // Act
+ var result = await _handler.Handle(command, CancellationToken.None);
+
+ // Assert
+ result.IsSuccess.Should().BeTrue();
+ await _storyRepository.Received(1).AddAsync(Arg.Any(), Arg.Any());
+ }
+
+ [Fact]
+ public async Task Handle_ShouldFail_WhenKlipyDisabledAndKlipyType()
+ {
+ // Arrange
+ var command = new CreateStoryCommand(Guid.NewGuid(), "Klipy", "http://klipy.com/gif", null, null);
+ var settings = new SystemSettingsDto { Klipy = new KlipyConfig { Enabled = false } };
+ _settingsService.GetSettingsAsync(Arg.Any()).Returns(settings);
+
+ // Act
+ var result = await _handler.Handle(command, CancellationToken.None);
+
+ // Assert
+ result.IsFailure.Should().BeTrue();
+ result.Error.Code.Should().Be("Stories.KlipyDisabled");
+ await _storyRepository.DidNotReceive().AddAsync(Arg.Any(), Arg.Any());
+ }
+
+ [Fact]
+ public async Task Handle_ShouldSucceed_WhenKlipyEnabledAndKlipyType()
+ {
+ // Arrange
+ var command = new CreateStoryCommand(Guid.NewGuid(), "Klipy", "http://klipy.com/gif", null, null);
+ var settings = new SystemSettingsDto { Klipy = new KlipyConfig { Enabled = true } };
+ _settingsService.GetSettingsAsync(Arg.Any()).Returns(settings);
+
+ // Act
+ var result = await _handler.Handle(command, CancellationToken.None);
+
+ // Assert
+ result.IsSuccess.Should().BeTrue();
+ await _storyRepository.Received(1).AddAsync(Arg.Any(), Arg.Any());
+ }
+}
+
diff --git a/backend/Tests/Stories/UnitTests/DeleteStoryCommandHandlerTests.cs b/backend/Tests/Stories/UnitTests/DeleteStoryCommandHandlerTests.cs
new file mode 100644
index 0000000..48795ad
--- /dev/null
+++ b/backend/Tests/Stories/UnitTests/DeleteStoryCommandHandlerTests.cs
@@ -0,0 +1,76 @@
+using FluentAssertions;
+using Knot.Modules.Stories.Application.Stories.Commands.DeleteStory;
+using Knot.Modules.Stories.Domain;
+using Knot.Shared.Kernel;
+using Knot.Modules.Stories.Application.Abstractions;
+using NSubstitute;
+using Xunit;
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Knot.Modules.Stories.UnitTests;
+
+public class DeleteStoryCommandHandlerTests
+{
+ private readonly IStoryRepository _storyRepository;
+ private readonly DeleteStoryCommandHandler _handler;
+
+ public DeleteStoryCommandHandlerTests()
+ {
+ _storyRepository = Substitute.For();
+ _handler = new DeleteStoryCommandHandler(_storyRepository);
+ }
+
+ [Fact]
+ public async Task Handle_ShouldReturnError_WhenStoryNotFound()
+ {
+ // Arrange
+ var command = new DeleteStoryCommand(Guid.NewGuid(), Guid.NewGuid());
+ _storyRepository.GetByIdAsync(command.StoryId, Arg.Any()).Returns((Story?)null);
+
+ // Act
+ var result = await _handler.Handle(command, CancellationToken.None);
+
+ // Assert
+ result.IsFailure.Should().BeTrue();
+ result.Error.Should().Be(StoryErrors.StoryNotFound);
+ }
+
+ [Fact]
+ public async Task Handle_ShouldReturnError_WhenUserIsNotOwner()
+ {
+ // Arrange
+ var ownerId = Guid.NewGuid();
+ var viewerId = Guid.NewGuid();
+ var story = Story.Create(ownerId, StoryType.Text, null, "Content", null);
+ var command = new DeleteStoryCommand(viewerId, story.Id);
+
+ _storyRepository.GetByIdAsync(command.StoryId, Arg.Any()).Returns(story);
+
+ // Act
+ var result = await _handler.Handle(command, CancellationToken.None);
+
+ // Assert
+ result.IsFailure.Should().BeTrue();
+ result.Error.Should().Be(StoryErrors.Unauthorized);
+ }
+
+ [Fact]
+ public async Task Handle_ShouldSucceed_WhenOwnerDeletes()
+ {
+ // Arrange
+ var ownerId = Guid.NewGuid();
+ var story = Story.Create(ownerId, StoryType.Text, null, "Content", null);
+ var command = new DeleteStoryCommand(ownerId, story.Id);
+
+ _storyRepository.GetByIdAsync(command.StoryId, Arg.Any()).Returns(story);
+
+ // Act
+ var result = await _handler.Handle(command, CancellationToken.None);
+
+ // Assert
+ result.IsSuccess.Should().BeTrue();
+ await _storyRepository.Received(1).DeleteAsync(story, Arg.Any());
+ }
+}
diff --git a/backend/Tests/Knot.Modules.Stories.UnitTests/Knot.Modules.Stories.UnitTests.csproj b/backend/Tests/Stories/UnitTests/Knot.Modules.Stories.UnitTests.csproj
similarity index 69%
rename from backend/Tests/Knot.Modules.Stories.UnitTests/Knot.Modules.Stories.UnitTests.csproj
rename to backend/Tests/Stories/UnitTests/Knot.Modules.Stories.UnitTests.csproj
index 45223ef..71d70a1 100644
--- a/backend/Tests/Knot.Modules.Stories.UnitTests/Knot.Modules.Stories.UnitTests.csproj
+++ b/backend/Tests/Stories/UnitTests/Knot.Modules.Stories.UnitTests.csproj
@@ -1,4 +1,4 @@
-
+
net10.0
@@ -21,8 +21,10 @@
-
-
+
+
+
+
diff --git a/backend/Tests/Knot.Modules.TelegramImport.UnitTests/Knot.Modules.TelegramImport.UnitTests.csproj b/backend/Tests/TelegramImport/UnitTests/Knot.Modules.TelegramImport.UnitTests.csproj
similarity index 68%
rename from backend/Tests/Knot.Modules.TelegramImport.UnitTests/Knot.Modules.TelegramImport.UnitTests.csproj
rename to backend/Tests/TelegramImport/UnitTests/Knot.Modules.TelegramImport.UnitTests.csproj
index 7ab8a19..bf92713 100644
--- a/backend/Tests/Knot.Modules.TelegramImport.UnitTests/Knot.Modules.TelegramImport.UnitTests.csproj
+++ b/backend/Tests/TelegramImport/UnitTests/Knot.Modules.TelegramImport.UnitTests.csproj
@@ -1,4 +1,4 @@
-
+
net10.0
@@ -21,8 +21,10 @@
-
-
+
+
+
+
diff --git a/backend/Tests/Users/UnitTests/Knot.Modules.Profiles.UnitTests.csproj b/backend/Tests/Users/UnitTests/Knot.Modules.Profiles.UnitTests.csproj
new file mode 100644
index 0000000..3e3dac3
--- /dev/null
+++ b/backend/Tests/Users/UnitTests/Knot.Modules.Profiles.UnitTests.csproj
@@ -0,0 +1,30 @@
+
+
+
+ net10.0
+ enable
+ enable
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/backend/Tests/Knot.Modules.Profiles.UnitTests/UpdateProfileCommandHandlerTests.cs b/backend/Tests/Users/UnitTests/UpdateProfileCommandHandlerTests.cs
similarity index 74%
rename from backend/Tests/Knot.Modules.Profiles.UnitTests/UpdateProfileCommandHandlerTests.cs
rename to backend/Tests/Users/UnitTests/UpdateProfileCommandHandlerTests.cs
index ef99d04..3739a53 100644
--- a/backend/Tests/Knot.Modules.Profiles.UnitTests/UpdateProfileCommandHandlerTests.cs
+++ b/backend/Tests/Users/UnitTests/UpdateProfileCommandHandlerTests.cs
@@ -1,41 +1,38 @@
using FluentAssertions;
-using Knot.Modules.Profiles.Application.Profiles.Commands.UpdateProfile;
+using Knot.Modules.Profiles.Application.Profiles.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;
+using Knot.Modules.Profiles.Application.Abstractions;
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);
+ _handler = new UpdateProfileCommandHandler(_profileRepository);
}
[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);
+ var command = new UpdateProfileCommand(Guid.NewGuid(), "FirstName", "Bio", null);
+ _profileRepository.GetByIdAsync(command.UserId, Arg.Any()).Returns((ProfileDocument?)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.WebRtc.UnitTests/Knot.Modules.WebRtc.UnitTests.csproj b/backend/Tests/WebRtc/UnitTests/Knot.Modules.WebRtc.UnitTests.csproj
similarity index 69%
rename from backend/Tests/Knot.Modules.WebRtc.UnitTests/Knot.Modules.WebRtc.UnitTests.csproj
rename to backend/Tests/WebRtc/UnitTests/Knot.Modules.WebRtc.UnitTests.csproj
index fdc5794..be78aa8 100644
--- a/backend/Tests/Knot.Modules.WebRtc.UnitTests/Knot.Modules.WebRtc.UnitTests.csproj
+++ b/backend/Tests/WebRtc/UnitTests/Knot.Modules.WebRtc.UnitTests.csproj
@@ -1,4 +1,4 @@
-
+
net10.0
@@ -21,8 +21,10 @@
-
-
+
+
+
+
diff --git a/backend/src/Docs/profiles_module_documentation.md b/backend/src/Docs/profiles_module_documentation.md
new file mode 100644
index 0000000..e495128
--- /dev/null
+++ b/backend/src/Docs/profiles_module_documentation.md
@@ -0,0 +1,105 @@
+# Документация модуля Profiles (Профили пользователей)
+
+## Введение
+
+Модуль `Knot.Modules.Profiles` отвечает за управление публичными данными пользователей, их аватарами и настройками отображения в мессенджере Knot Messager.
+
+### Разделение ответственности (Auth vs Profiles)
+В соответствии с принципами **DDD (Domain-Driven Design)** и **Чистой Архитектуры**, модуль Profiles отделен от модуля Auth:
+* **Auth (Postgres):** Занимается идентификацией (логин, пароль, токены, email).
+* **Profiles (MongoDB + S3):** Занимается представлением пользователя (имя, био, аватар, настройки видимости).
+
+Это разделение позволяет изменять структуру профиля (добавлять новые социальные ссылки, поля "О себе") без выполнения миграций в основной реляционной БД.
+
+---
+
+## Архитектура хранения данных (Гибридный подход)
+
+Модуль использует **гибридную схему хранения**, оптимизированную для производительности и гибкости:
+
+1. **Metadata (MongoDB):** Документ `ProfileDocument` содержит все текстовые данные профиля. Использование NoSQL позволяет легко расширять схему данных.
+2. **Media (S3/MinIO):** Файлы аватаров хранятся во внешнем объектном хранилище. В MongoDB хранится только `AvatarUrl` (ссылка на файл).
+3. **Identity Link (UserId):** Поле `Id` в MongoDB-документе совпадает с `UserId` из PostgreSQL. Это единственный ключ для связи модулей.
+
+---
+
+## Доменная модель
+
+### ProfileDocument (Aggregate Root)
+Основная сущность в модуле. Находится в слое `Domain`.
+* **Username:** Уникальный хендл пользователя.
+* **DisplayName:** Публичное имя (может меняться).
+* **Bio:** Короткая биографическая справка.
+* **AvatarUrl:** Путь к файлу в формате `/api/files/{fileId}`.
+* **HideStoryViews:** Флаг приватности (скрывать просмотр сторис от других).
+
+### Доменные события
+Модуль генерирует и слушает события для обеспечения согласованности:
+* `UserRegisteredDomainEvent`: Слушается модулем Profiles (от Auth). При получении создается начальный документ профиля в MongoDB.
+* `ProfileAvatarChangedDomainEvent`: Генерируется при смене аватара (для потенциальной очистки кэша).
+
+---
+
+## Ключевые возможности
+
+### 1. Управление аватарами
+Модуль предоставляет эндпоинты для загрузки и удаления аватаров:
+* **Автоматическое кадрирование:** При вызове `/avatar/crop` используется библиотека `ImageSharp` для вырезания области изображения и ресайза до `400x400` пикселей в формате JPEG.
+* **Авто-очистка:** При загрузке нового аватара модуль автоматически удаляет старый файл из S3, предотвращая появление "файлов-сирот".
+
+### 2. Поиск пользователей
+Реализован через `ProfileRepository` с использованием регулярных выражений MongoDB (case-insensitive) по полям `Username` и `DisplayName`.
+
+### 3. Настройки приватности
+Пользователь может управлять отображением своей активности (например, `HideStoryViews`), что сохраняется непосредственно в документе профиля.
+
+---
+
+## Структура проекта
+
+```text
+Knot.Modules.Profiles/
+├── Application/
+│ ├── Abstractions/ # Интерфейсы IProfileRepository, IAvatarStorageService
+│ ├── Profiles/ # Case-обработчики (Handlers)
+│ │ ├── Avatar/ # Загрузка, обрезка, удаление аватара
+│ │ ├── GetUser/ # Получение данных профиля
+│ │ ├── Search/ # Поиск по базе профилей
+│ │ └── UpdateProfile/ # Обновление текстовых данных
+│ └── Integration/ # Обработчики событий от других модулей
+├── Domain/
+│ ├── ProfileDocument.cs # Корень агрегата (Mongo Document)
+│ └── Events/ # Доменные события
+├── Infrastructure/
+│ └── Database/ # Реализация репозиториев для MongoDB и S3
+├── Presentation/
+│ └── Endpoints/ # Carter-модули (API эндпоинты)
+└── DependencyInjection.cs # Регистрация сервисов и MongoDB клиента
+```
+
+---
+
+## Технические детали реализации
+
+### Интеграция с S3 (MinIO)
+В модуле реализован адаптер `AvatarStorageService`, который инкапсулирует работу с `IFileStorageService`. Он отвечает за:
+1. Генерацию уникальных ключей файлов.
+2. Шифрование контента (через Shared Infrastructure).
+3. Очистку путей (преобразование URL в ID файла для удаления).
+
+### Настройка MongoDB
+Репозиторий `ProfileRepository` использует типизированную коллекцию `IMongoCollection`. Первичный ключ `Id` мапится как строка (BsonType.String) для совместимости с `Guid` из .NET.
+
+---
+
+## API Эндпоинты (Примеры)
+
+| Метод | Путь | Описание |
+| :--- | :--- | :--- |
+| `GET` | `/api/profiles/{id}` | Получить данные профиля пользователя. |
+| `GET` | `/api/profiles/search?q=text` | Найти пользователей по имени или никнейму. |
+| `PUT` | `/api/profiles/profile` | Обновить DisplayName, Bio, Birthday. |
+| `POST` | `/api/profiles/avatar` | Простая загрузка аватара (FormFile). |
+| `POST` | `/api/profiles/avatar/crop` | Загрузка с указанием координат обрезки (x, y, w, h). |
+| `DELETE` | `/api/profiles/avatar` | Удаление текущего аватара и файла из S3. |
+| `PUT` | `/api/profiles/settings` | Обновить настройки приватности профиля. |
diff --git a/backend/src/Docs/settings_module_documentation.md b/backend/src/Docs/settings_module_documentation.md
new file mode 100644
index 0000000..b07387b
--- /dev/null
+++ b/backend/src/Docs/settings_module_documentation.md
@@ -0,0 +1,129 @@
+# Документация модуля Settings (Настройки)
+
+## Введение
+
+Модуль `Knot.Modules.Settings` является централизованным компонентом для управления глобальными конфигурациями и параметрами системы в архитектуре модульного монолита (Modular Monolith) Knot Messager. Он обеспечивает единый источник истины (Single Source of Truth) для настроек всех других модулей (WebRTC, Federation, Messages, Chats, Admin и др.).
+
+## Основные принципы (DDD и Clean Architecture)
+
+- **Независимость и Инкапсуляция:** Модуль выступает как самостоятельная единица и не зависит от реализаций других модулей. Другие модули ссылаются на `Settings` только через абстракции или контракты.
+- **Единственный Источник Истины:** Любое изменение системной конфигурации (включение/отключение фич, лимиты, домены) происходит через этот модуль.
+- **Событийная Модель (Domain Events):** При изменении настроек инфраструктура Settings генерирует доменное событие `SystemSettingsUpdatedDomainEvent`, позволяя другим модулям (например, `Federation` для отправки новых Capabilities) реагировать на изменения асинхронно, не создавая жестких связей.
+
+## Структура модуля
+
+Модуль спроектирован по принципам Чистой Архитектуры и разделен на слои:
+
+```text
+Knot.Modules.Settings/
+├── Application/ # Слой Приложения (Application Layer)
+│ ├── Settings/
+│ │ ├── Abstractions/ # Определение интерфейсов доступа к каждому разделу настроек
+│ │ │ ├── ISettingsService.cs
+│ │ │ ├── IMessagesSettings.cs
+│ │ │ ├── IWebRtcSettings.cs
+│ │ │ └── ...
+│ │ ├── DTOs/ # Структуры данных конфигураций
+│ │ ├── SystemSettingsDto.cs
+│ │ ├── PublicConfigDto.cs
+│ │ ├── KlipyConfig.cs
+│ │ └── ...
+├── Domain/ # Доменный Слой (Domain Layer)
+│ ├── Events/
+│ │ └── SystemSettingsUpdatedDomainEvent.cs # Событие об изменении настроек
+├── Infrastructure/ # Слой Инфраструктуры (Infrastructure Layer)
+│ ├── Configuration/
+│ │ └── SettingsService.cs # Реализация логики кэширования и обновления
+├── DependencyInjection.cs # Регистрация модуля в DI (AddSettingsModule)
+```
+
+## Как это работает?
+
+### 1. Интерфейсы сегрегации (Interface Segregation Principle - ISP)
+
+Чтобы модули не зависели от огромного объекта настройки всей системы `SystemSettingsDto`, в слое `Application/Settings/Abstractions/ISettingsService.cs` мы разделили конфигурации на интерфейсы по специализациям:
+
+- `ISystemSettings` — Системные и общие параметры.
+- `IWebRtcSettings` — Настройки аудио/видео вызовов (WebRTC, TURN).
+- `IMessagesSettings` — Настройки сообщений (файлы, ограничения).
+- `IFederationSettings` — Настройки ActivityPub/Федерации.
+- `IKlipySettings` — Настройки интеграции внешней библиотеки гифок (Klipy).
+
+### 2. Доступ к настройкам из других модулей
+
+Если, например, модулю бесед (Conversations) требуется проверить, включены ли интеграции Klipy, он инжектит специфичный интерфейс `IKlipySettings`, не получая доступа к настройкам админки или WebRTC:
+
+```csharp
+public class CreateStoryCommandHandler
+{
+ private readonly IKlipySettings _klipySettings;
+
+ public CreateStoryCommandHandler(IKlipySettings klipySettings)
+ {
+ _klipySettings = klipySettings;
+ }
+
+ public async Task Handle(...)
+ {
+ if (!_klipySettings.Current.Enabled) {
+ // Отбросить логику
+ }
+ }
+}
+```
+
+### 3. Хранение и шифрование данных
+
+Реализация `SettingsService` хранит данные централизованно (в таблице `SystemSettings` БД через `SystemDbContext` в `Knot.Shared.Infrastructure`) в формате JSON.
+Для безопасности вся JSON-строка **зашифрована** с использованием `IEncryptionService`.
+
+Особенности хранения:
+- **Кэширование**: `SettingsService` зарегистрирован как `Singleton`. При запуске сервера конфигурация загружается один раз из базы данных (или кеша/файла) и сохраняется в памяти.
+- **Обновление**: Метод `UpdateSettingsAsync` сохраняет новый стейт в БД и обновляет объект в памяти (`_current`), предотвращая лишние запросы к базе данных при обычной работе мессенджера.
+
+## Жизненный цикл обновления настроек (Управление)
+
+Администратор изменяет настройки через UI (или API), после чего происходит следующий процесс:
+
+1. Вызывается эндпоинт в модуле **Admin**: `PUT /api/admin/settings`.
+2. Команда `UpdateSettingsCommand` валидирует входные данные.
+3. Команда обращается к `ISettingsService.UpdateSettingsAsync(newSettings)`.
+4. `SettingsService` шифрует новые настройки, обновляет запись в БД, и заменяет кэш в оперативной памяти.
+5. После успешного сохранения, диспетчер MediatR (в `Admin` модуле) публикует `SystemSettingsUpdatedDomainEvent`.
+6. Событие отлавливается через `INotificationHandler` независимыми слушателями, например:
+ - Модулем `Federation` для рассылки всем "соседним" инстансам новых возможностей `Capabilities`.
+
+## Регистрация модуля
+
+При старте приложения в `Host/Program.cs` модуль инициализируется:
+
+```csharp
+// Регистрация сервисов модуля настроек
+builder.Services.AddSettingsModule(builder.Configuration);
+
+// Дополнительно можно вызывать Initialize(...) чтобы загрузить кэш из БД при старте
+```
+
+Под капотом `DependencyInjection.cs`:
+```csharp
+public static IServiceCollection AddSettingsModule(this IServiceCollection services, IConfiguration configuration)
+{
+ services.AddSingleton();
+
+ // Привязываем конкретные абстракции к одному Singleton инстансу
+ services.AddSingleton(sp => sp.GetRequiredService());
+ services.AddSingleton(sp => sp.GetRequiredService());
+ // ... и остальные
+}
+```
+
+## Публичная конфигурация клиента (PublicConfig)
+
+Для клиентской части (Frontend) предусмотрен специальный DTO объект `PublicConfigDto`.
+Это subset (урезанная часть) от `SystemSettingsDto`, которая не содержит чувствительной информации о сервере и ключах:
+
+- Доступные лимиты медиа файлов.
+- Включены ли аудио/видео звонки.
+- Включена ли федерация для поиска глобальных пользователей.
+
+Таким образом, клиент может запросить `GET /api/app/config`, а бэкенд отдаст безопасный набор доступных разрешений через маппинг `SettingsService.Current`.
diff --git a/backend/src/Host/Host.csproj b/backend/src/Host/Host.csproj
index cf96201..a0ea507 100644
--- a/backend/src/Host/Host.csproj
+++ b/backend/src/Host/Host.csproj
@@ -43,3 +43,5 @@
+
+
diff --git a/backend/src/Host/Program.cs b/backend/src/Host/Program.cs
index 71e70aa..c534e42 100644
--- a/backend/src/Host/Program.cs
+++ b/backend/src/Host/Program.cs
@@ -2,6 +2,8 @@ using Knot.Modules.Messaging;
using Carter;
using Knot.Shared.Infrastructure;
using Knot.Modules.Auth;
+using Knot.Modules.Settings;
+using Knot.Modules.Admin;
using Knot.Modules.Conversations;
using Knot.Modules.Conversations.Infrastructure.SignalR;
using Knot.Modules.Auth.Infrastructure.Persistence;
@@ -50,11 +52,16 @@ builder.Configuration.AddInMemoryCollection(
.ToDictionary(kv => kv.Key, kv => kv.Value));
builder.Services.AddAuthModule(builder.Configuration);
+builder.Services.AddSettingsModule(builder.Configuration);
builder.Services.AddConversationsModule(builder.Configuration);
builder.Services.AddSharedInfrastructure(builder.Configuration);
// CQRS / MediatR для команд в Host (например, AdminController)
builder.Services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(typeof(Program).Assembly));
+builder.Services.AddMediatR(cfg => cfg.RegisterServicesFromAssemblies(
+ typeof(Knot.Modules.Settings.DependencyInjection).Assembly,
+ typeof(Knot.Modules.Admin.DependencyInjection).Assembly
+));
// Carter для вызова Minimal APIs (Endpoints)
builder.Services.AddCarter();
@@ -99,8 +106,6 @@ builder.Services.AddRouting(options =>
builder.Services.AddMemoryCache();
builder.Services.AddHttpClient();
-
-
builder.Services.AddSignalR()
.AddJsonProtocol(options =>
{
@@ -162,8 +167,8 @@ using (var scope = app.Services.CreateScope())
Knot.Modules.Messaging.Infrastructure.Persistence.Mongo.EncryptedStringSerializer.EncryptionService = encryptionService;
// Initialize Global Settings Cache
- var settingsService = scope.ServiceProvider.GetRequiredService();
- if (settingsService is Knot.Shared.Infrastructure.Configuration.SettingsService concreteSettings)
+ var settingsService = scope.ServiceProvider.GetRequiredService();
+ if (settingsService is Knot.Modules.Settings.Infrastructure.Configuration.SettingsService concreteSettings)
{
concreteSettings.Initialize();
}
@@ -203,3 +208,4 @@ public class CustomUserIdProvider : IUserIdProvider
return connection.User?.FindFirstValue("sub") ?? connection.User?.FindFirstValue(ClaimTypes.NameIdentifier);
}
}
+
diff --git a/backend/src/Modules/Admin/Application/Admin/Commands/CleanRun/CleanRunCommand.cs b/backend/src/Modules/Admin/Application/Admin/Commands/CleanRun/CleanRunCommand.cs
index 394cb52..4e055b4 100644
--- a/backend/src/Modules/Admin/Application/Admin/Commands/CleanRun/CleanRunCommand.cs
+++ b/backend/src/Modules/Admin/Application/Admin/Commands/CleanRun/CleanRunCommand.cs
@@ -14,7 +14,7 @@ using Knot.Modules.Conversations.Domain;
using MongoDB.Driver;
using Microsoft.EntityFrameworkCore;
-namespace Host.Application.Admin.Commands;
+namespace Knot.Modules.Admin.Application.Admin.Commands;
public record CleanRunCommand(IFileStorageService FileStorage, AuthDbContext IdentityDb) : ICommand;
@@ -99,4 +99,4 @@ internal sealed class CleanRunCommandHandler : ICommandHandler;
@@ -44,4 +44,4 @@ internal sealed class ResetUserPasswordCommandHandler : ICommandHandler;
@@ -37,3 +38,5 @@ internal sealed class TestKlipyConnectionCommandHandler : ICommandHandler;
@@ -43,4 +44,6 @@ internal sealed class UpdateSettingsCommandHandler : ICommandHandler;
@@ -102,4 +102,4 @@ internal sealed class CleanDryRunQueryHandler : IQueryHandler;
@@ -23,4 +23,4 @@ internal sealed class GetDashboardStatsQueryHandler : IQueryHandler;
@@ -24,4 +25,5 @@ internal sealed class GetSettingsQueryHandler : IQueryHandler;
@@ -74,4 +74,4 @@ internal sealed class GetUserDetailsQueryHandler : IQueryHandler>;
@@ -43,4 +43,4 @@ internal sealed class SearchUsersQueryHandler : IQueryHandler
public sealed record SystemSettingsUpdatedDomainEvent(SystemSettingsDto Settings) : IDomainEvent;
+
diff --git a/backend/src/Modules/Admin/Knot.Modules.Admin.csproj b/backend/src/Modules/Admin/Knot.Modules.Admin.csproj
index d057df2..73227c8 100644
--- a/backend/src/Modules/Admin/Knot.Modules.Admin.csproj
+++ b/backend/src/Modules/Admin/Knot.Modules.Admin.csproj
@@ -18,5 +18,7 @@
<_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
index e7ceac8..61b655b 100644
--- a/backend/src/Modules/Admin/Presentation/Endpoints/AdminEndpoints.cs
+++ b/backend/src/Modules/Admin/Presentation/Endpoints/AdminEndpoints.cs
@@ -1,18 +1,19 @@
using Carter;
using Knot.Modules.Auth.Application.Auth.DTOs;
using Knot.Shared.Kernel;
-using Knot.Shared.Kernel.Configuration;
+using Knot.Modules.Settings.Application.Settings.Abstractions;
+using Knot.Modules.Settings.Application.Settings.DTOs;
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 Knot.Modules.Admin.Application.Admin.Queries;
+using Knot.Modules.Admin.Application.Admin.Commands;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Mvc;
-using Host.Application.Admin.Commands.TestKlipy;
+using Knot.Modules.Admin.Application.Admin.Commands.TestKlipy;
namespace Knot.Host.Presentation.Endpoints;
@@ -90,4 +91,5 @@ public sealed class AdminEndpoints : ICarterModule
return Results.Ok(result.Value);
});
}
-}
\ No newline at end of file
+}
+
diff --git a/backend/src/Modules/Auth/Application/Users/Register/RegisterUserCommandHandler.cs b/backend/src/Modules/Auth/Application/Users/Register/RegisterUserCommandHandler.cs
index 5d2ce71..bc6eaa0 100644
--- a/backend/src/Modules/Auth/Application/Users/Register/RegisterUserCommandHandler.cs
+++ b/backend/src/Modules/Auth/Application/Users/Register/RegisterUserCommandHandler.cs
@@ -2,7 +2,8 @@
using Knot.Modules.Auth.Domain;
using Knot.Modules.Auth.Application.Abstractions;
using Knot.Shared.Kernel;
-using Knot.Shared.Kernel.Configuration;
+using Knot.Modules.Settings.Application.Settings.Abstractions;
+using Knot.Modules.Settings.Application.Settings.DTOs;
using Knot.Modules.Auth.Application.Users.Auth;
using BCrypt.Net;
@@ -87,3 +88,4 @@ public sealed class RegisterUserCommandHandler : ICommandHandler
///
public static User Create(string username, string passwordHash, string displayName, string? email = null, string? bio = null)
{
- return new User(Guid.NewGuid(), username, passwordHash, displayName, email, bio);
+ var user = new User(Guid.NewGuid(), username, passwordHash, displayName, email, bio);
+ user.RaiseDomainEvent(new UserRegisteredDomainEvent(user.Id, user.Username, user.DisplayName, user.Bio));
+ return user;
}
///
diff --git a/backend/src/Modules/Auth/Knot.Modules.Auth.csproj b/backend/src/Modules/Auth/Knot.Modules.Auth.csproj
index 887161b..b078e70 100644
--- a/backend/src/Modules/Auth/Knot.Modules.Auth.csproj
+++ b/backend/src/Modules/Auth/Knot.Modules.Auth.csproj
@@ -21,13 +21,17 @@
-
+
+
+
+
+
diff --git a/backend/src/Modules/Conversations/Application/Folders/Commands/AddToFolder/AddToFolderCommand.cs b/backend/src/Modules/Conversations/Application/Folders/Commands/AddToFolder/AddToFolderCommand.cs
index 4c7ba71..c589dbf 100644
--- a/backend/src/Modules/Conversations/Application/Folders/Commands/AddToFolder/AddToFolderCommand.cs
+++ b/backend/src/Modules/Conversations/Application/Folders/Commands/AddToFolder/AddToFolderCommand.cs
@@ -1,6 +1,7 @@
using Knot.Modules.Conversations.Domain;
using Knot.Modules.Conversations.Application.Abstractions;
-using Knot.Shared.Kernel.Configuration;
+using Knot.Modules.Settings.Application.Settings.Abstractions;
+using Knot.Modules.Settings.Application.Settings.DTOs;
using Knot.Shared.Kernel;
using MediatR;
@@ -48,3 +49,4 @@ internal sealed class AddToFolderCommandHandler : ICommandHandler
-
-
+
+
@@ -25,7 +25,7 @@
-
+
@@ -36,3 +36,5 @@
+
+
diff --git a/backend/src/Modules/Federation/Application/Federation/Commands/HandshakeFederationCommand.cs b/backend/src/Modules/Federation/Application/Federation/Commands/HandshakeFederationCommand.cs
index 2b8a4dd..fdb6661 100644
--- a/backend/src/Modules/Federation/Application/Federation/Commands/HandshakeFederationCommand.cs
+++ b/backend/src/Modules/Federation/Application/Federation/Commands/HandshakeFederationCommand.cs
@@ -2,7 +2,8 @@ using Knot.Shared.Kernel.Constants;
using System.Security.Cryptography;
using MediatR;
using Knot.Shared.Kernel;
-using Knot.Shared.Kernel.Configuration;
+using Knot.Modules.Settings.Application.Settings.Abstractions;
+using Knot.Modules.Settings.Application.Settings.DTOs;
namespace Host.Application.Federation.Commands;
@@ -63,4 +64,4 @@ internal sealed class HandshakeFederationCommandHandler : ICommandHandler
+
-
\ 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
index 181a3de..a753458 100644
--- a/backend/src/Modules/Federation/Presentation/Endpoints/FederationEndpoints.cs
+++ b/backend/src/Modules/Federation/Presentation/Endpoints/FederationEndpoints.cs
@@ -9,7 +9,8 @@ using System;
using Host.Application.Federation.Commands;
using Knot.Modules.Federation.Application.Abstractions;
using Knot.Shared.Kernel.Storage;
-using Knot.Shared.Kernel.Configuration;
+using Knot.Modules.Settings.Application.Settings.Abstractions;
+using Knot.Modules.Settings.Application.Settings.DTOs;
namespace Host.Endpoints;
@@ -63,4 +64,4 @@ public sealed class FederationEndpoints : ICarterModule
}
});
}
-}
\ No newline at end of file
+}
diff --git a/backend/src/Modules/Klipy/Application/Klipy/Queries/GetTrendingGifsQuery.cs b/backend/src/Modules/Klipy/Application/Klipy/Queries/GetTrendingGifsQuery.cs
index d8af201..737f764 100644
--- a/backend/src/Modules/Klipy/Application/Klipy/Queries/GetTrendingGifsQuery.cs
+++ b/backend/src/Modules/Klipy/Application/Klipy/Queries/GetTrendingGifsQuery.cs
@@ -1,5 +1,6 @@
using Knot.Shared.Kernel;
-using Knot.Shared.Kernel.Configuration;
+using Knot.Modules.Settings.Application.Settings.Abstractions;
+using Knot.Modules.Settings.Application.Settings.DTOs;
using Knot.Shared.Kernel.Constants;
using Microsoft.Extensions.Caching.Memory;
using System;
@@ -61,3 +62,4 @@ internal sealed class GetTrendingGifsQueryHandler : IQueryHandler(result);
}
}
+
diff --git a/backend/src/Modules/Klipy/Application/Klipy/Queries/SearchGifsQuery.cs b/backend/src/Modules/Klipy/Application/Klipy/Queries/SearchGifsQuery.cs
index 007bc59..485a046 100644
--- a/backend/src/Modules/Klipy/Application/Klipy/Queries/SearchGifsQuery.cs
+++ b/backend/src/Modules/Klipy/Application/Klipy/Queries/SearchGifsQuery.cs
@@ -1,5 +1,6 @@
using Knot.Shared.Kernel;
-using Knot.Shared.Kernel.Configuration;
+using Knot.Modules.Settings.Application.Settings.Abstractions;
+using Knot.Modules.Settings.Application.Settings.DTOs;
using Knot.Shared.Kernel.Constants;
using Microsoft.Extensions.Caching.Memory;
using System;
@@ -65,3 +66,4 @@ internal sealed class SearchGifsQueryHandler : IQueryHandler(result);
}
}
+
diff --git a/backend/src/Modules/Klipy/Knot.Modules.Klipy.csproj b/backend/src/Modules/Klipy/Knot.Modules.Klipy.csproj
index aedece0..9afda16 100644
--- a/backend/src/Modules/Klipy/Knot.Modules.Klipy.csproj
+++ b/backend/src/Modules/Klipy/Knot.Modules.Klipy.csproj
@@ -13,5 +13,7 @@
+
-
\ No newline at end of file
+
+
diff --git a/backend/src/Modules/Messaging/Knot.Modules.Messaging.csproj b/backend/src/Modules/Messaging/Knot.Modules.Messaging.csproj
index 0fec715..f7bcc0b 100644
--- a/backend/src/Modules/Messaging/Knot.Modules.Messaging.csproj
+++ b/backend/src/Modules/Messaging/Knot.Modules.Messaging.csproj
@@ -1,4 +1,4 @@
-
+
@@ -7,7 +7,8 @@
-
+
+
@@ -17,3 +18,5 @@
+
+
diff --git a/backend/src/Modules/Profiles/Application/Abstractions/IAvatarStorageService.cs b/backend/src/Modules/Profiles/Application/Abstractions/IAvatarStorageService.cs
new file mode 100644
index 0000000..45c880c
--- /dev/null
+++ b/backend/src/Modules/Profiles/Application/Abstractions/IAvatarStorageService.cs
@@ -0,0 +1,22 @@
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Knot.Modules.Profiles.Application.Abstractions;
+
+///
+/// Интерфейс для хранения аватаров в S3 (MinIO).
+/// Отдельный от IFileStorageService модуля Storage,
+/// чтобы не создавать прямой зависимости на Storage модуль.
+///
+public interface IAvatarStorageService
+{
+ ///
+ /// Загружает файл в S3-бакет аватаров и возвращает fileId (ключ объекта).
+ ///
+ Task UploadAsync(System.IO.Stream stream, string fileName, string contentType, CancellationToken ct = default);
+
+ ///
+ /// Удаляет файл аватара из S3 по fileId.
+ ///
+ Task DeleteAsync(string fileId, CancellationToken ct = default);
+}
diff --git a/backend/src/Modules/Profiles/Application/Abstractions/IProfileRepository.cs b/backend/src/Modules/Profiles/Application/Abstractions/IProfileRepository.cs
index 8440cdd..d412e41 100644
--- a/backend/src/Modules/Profiles/Application/Abstractions/IProfileRepository.cs
+++ b/backend/src/Modules/Profiles/Application/Abstractions/IProfileRepository.cs
@@ -1,15 +1,15 @@
using System;
+using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Knot.Modules.Profiles.Domain;
-using Knot.Modules.Profiles.Application.Abstractions;
namespace Knot.Modules.Profiles.Application.Abstractions;
public interface IProfileRepository
{
- Task GetByIdAsync(Guid id, CancellationToken cancellationToken = default);
- Task AddAsync(Profile profile, CancellationToken cancellationToken = default);
- void Update(Profile profile);
- Task> SearchProfilesAsync(string query, System.Threading.CancellationToken ct = default);
+ Task GetByIdAsync(Guid userId, CancellationToken ct = default);
+ Task AddAsync(ProfileDocument profile, CancellationToken ct = default);
+ Task UpdateAsync(ProfileDocument profile, CancellationToken ct = default);
+ Task> SearchAsync(string query, CancellationToken ct = default);
}
diff --git a/backend/src/Modules/Profiles/Application/Abstractions/IProfilesUnitOfWork.cs b/backend/src/Modules/Profiles/Application/Abstractions/IProfilesUnitOfWork.cs
index 1c00a7c..eba54c7 100644
--- a/backend/src/Modules/Profiles/Application/Abstractions/IProfilesUnitOfWork.cs
+++ b/backend/src/Modules/Profiles/Application/Abstractions/IProfilesUnitOfWork.cs
@@ -1,11 +1,9 @@
using System.Threading;
using System.Threading.Tasks;
-using Knot.Modules.Profiles.Application.Abstractions;
-using Knot.Modules.Profiles.Domain;
namespace Knot.Modules.Profiles.Application.Abstractions;
public interface IProfilesUnitOfWork
{
- Task SaveChangesAsync(CancellationToken cancellationToken = default);
+ Task SaveChangesAsync(CancellationToken ct = default);
}
diff --git a/backend/src/Modules/Profiles/Application/Profiles/Avatar/CropAvatar.cs b/backend/src/Modules/Profiles/Application/Profiles/Avatar/CropAvatar.cs
index 9732231..fac3893 100644
--- a/backend/src/Modules/Profiles/Application/Profiles/Avatar/CropAvatar.cs
+++ b/backend/src/Modules/Profiles/Application/Profiles/Avatar/CropAvatar.cs
@@ -1,69 +1,71 @@
using Knot.Shared.Kernel;
-using Knot.Modules.Profiles.Domain;
-
-using Knot.Shared.Kernel.Storage;
+using Knot.Modules.Profiles.Application.Abstractions;
+using Knot.Modules.Profiles.Application.Profiles.DTOs;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Processing;
+using System;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
-using Knot.Modules.Profiles.Application.Abstractions;
namespace Knot.Modules.Profiles.Application.Profiles.Avatar;
-public sealed record CropAvatarCommand(Guid ProfileId, Stream FileStream, string FileName, string ContentType, int X, int Y, int Width, int Height) : ICommand;
+public sealed record CropAvatarCommand(
+ Guid UserId,
+ Stream FileStream,
+ string FileName,
+ string ContentType,
+ int X, int Y, int Width, int Height) : ICommand;
-internal sealed class CropAvatarCommandHandler : ICommandHandler
+internal sealed class CropAvatarCommandHandler : ICommandHandler
{
- private readonly IProfileRepository _profileRepository;
- private readonly IProfilesUnitOfWork _unitOfWork;
- private readonly IFileStorageService _fileStorage;
+ private readonly IProfileRepository _repository;
+ private readonly IAvatarStorageService _avatarStorage;
- public CropAvatarCommandHandler(IProfileRepository profileRepository, IProfilesUnitOfWork unitOfWork, IFileStorageService fileStorage)
+ public CropAvatarCommandHandler(IProfileRepository repository, IAvatarStorageService avatarStorage)
{
- _profileRepository = profileRepository;
- _unitOfWork = unitOfWork;
- _fileStorage = fileStorage;
+ _repository = repository;
+ _avatarStorage = avatarStorage;
}
- public async Task> Handle(CropAvatarCommand request, CancellationToken cancellationToken)
+ public async Task> Handle(CropAvatarCommand request, CancellationToken cancellationToken)
{
- var profile = await _profileRepository.GetByIdAsync(request.ProfileId, cancellationToken);
- if (profile == null)
- {
- return Result.Failure(ProfilesErrors.ProfileNotFound);
- }
+ var profile = await _repository.GetByIdAsync(request.UserId, cancellationToken);
+ if (profile is null)
+ return Result.Failure(ProfilesErrors.ProfileNotFound);
- string avatarUrl;
+ // Обрезать и ресайзнуть изображение до 400×400
+ using var ms = await CropAndResizeAsync(request, cancellationToken);
- using (var image = await SixLabors.ImageSharp.Image.LoadAsync(request.FileStream, cancellationToken))
- {
- int startX = Math.Max(0, Math.Min(request.X, image.Width - 1));
- int startY = Math.Max(0, Math.Min(request.Y, image.Height - 1));
- int rectWidth = Math.Max(1, Math.Min(request.Width, image.Width - startX));
- int rectHeight = Math.Max(1, Math.Min(request.Height, image.Height - startY));
+ // Удалить старый аватар из S3
+ if (!string.IsNullOrEmpty(profile.AvatarUrl))
+ await _avatarStorage.DeleteAsync(profile.AvatarUrl, cancellationToken);
- image.Mutate(ctx => ctx.Crop(new SixLabors.ImageSharp.Rectangle(startX, startY, rectWidth, rectHeight)));
- image.Mutate(ctx => ctx.Resize(400, 400));
-
- using var outStream = new MemoryStream();
- await image.SaveAsJpegAsync(outStream, cancellationToken);
- outStream.Position = 0;
-
- var id = await _fileStorage.UploadFileAsync(outStream, request.FileName, "image/jpeg");
- avatarUrl = $"/api/files/{id}";
- }
+ var fileId = await _avatarStorage.UploadAsync(ms, "avatar.jpg", "image/jpeg", cancellationToken);
+ var avatarUrl = $"/api/files/{fileId}";
profile.UpdateAvatar(avatarUrl);
- await _unitOfWork.SaveChangesAsync(cancellationToken);
+ await _repository.UpdateAsync(profile, cancellationToken);
- var dto = new ProfileProfileDto(
- profile.Id,
- profile.Profilename,
- profile.DisplayName,
- profile.Avatar,
- profile.Bio,
- profile.Birthday,
- profile.CreatedAt
- );
+ return Result.Success(UserProfileDto.FromDocument(profile));
+ }
- return Result.Success(dto);
+ private static async Task CropAndResizeAsync(CropAvatarCommand request, CancellationToken ct)
+ {
+ using var image = await Image.LoadAsync(request.FileStream, ct);
+
+ var startX = Math.Clamp(request.X, 0, image.Width - 1);
+ var startY = Math.Clamp(request.Y, 0, image.Height - 1);
+ var width = Math.Clamp(request.Width, 1, image.Width - startX);
+ var height = Math.Clamp(request.Height, 1, image.Height - startY);
+
+ image.Mutate(ctx => ctx
+ .Crop(new Rectangle(startX, startY, width, height))
+ .Resize(400, 400));
+
+ var output = new MemoryStream();
+ await image.SaveAsJpegAsync(output, ct);
+ output.Position = 0;
+ return output;
}
}
diff --git a/backend/src/Modules/Profiles/Application/Profiles/Avatar/DeleteAvatar.cs b/backend/src/Modules/Profiles/Application/Profiles/Avatar/DeleteAvatar.cs
deleted file mode 100644
index cc22a5c..0000000
--- a/backend/src/Modules/Profiles/Application/Profiles/Avatar/DeleteAvatar.cs
+++ /dev/null
@@ -1,44 +0,0 @@
-using Knot.Shared.Kernel;
-using Knot.Modules.Profiles.Domain;
-
-
-using Knot.Modules.Profiles.Application.Abstractions;
-namespace Knot.Modules.Profiles.Application.Profiles.Avatar;
-
-public sealed record DeleteAvatarCommand(Guid ProfileId) : ICommand;
-
-internal sealed class DeleteAvatarCommandHandler : ICommandHandler
-{
- private readonly IProfileRepository _profileRepository;
- private readonly IProfilesUnitOfWork _unitOfWork;
-
- public DeleteAvatarCommandHandler(IProfileRepository profileRepository, IProfilesUnitOfWork unitOfWork)
- {
- _profileRepository = profileRepository;
- _unitOfWork = unitOfWork;
- }
-
- public async Task> Handle(DeleteAvatarCommand request, CancellationToken cancellationToken)
- {
- var profile = await _profileRepository.GetByIdAsync(request.ProfileId, cancellationToken);
- if (profile == null)
- {
- return Result.Failure(ProfilesErrors.ProfileNotFound);
- }
-
- profile.UpdateAvatar(null);
- await _unitOfWork.SaveChangesAsync(cancellationToken);
-
- var dto = new ProfileProfileDto(
- profile.Id,
- profile.Profilename,
- profile.DisplayName,
- profile.Avatar,
- profile.Bio,
- profile.Birthday,
- profile.CreatedAt
- );
-
- return Result.Success(dto);
- }
-}
diff --git a/backend/src/Modules/Profiles/Application/Profiles/Avatar/UploadAvatar.cs b/backend/src/Modules/Profiles/Application/Profiles/Avatar/UploadAvatar.cs
index 17a8401..6f60e8c 100644
--- a/backend/src/Modules/Profiles/Application/Profiles/Avatar/UploadAvatar.cs
+++ b/backend/src/Modules/Profiles/Application/Profiles/Avatar/UploadAvatar.cs
@@ -1,50 +1,79 @@
using Knot.Shared.Kernel;
-using Knot.Modules.Profiles.Domain;
-
-using Knot.Shared.Kernel.Storage;
-
using Knot.Modules.Profiles.Application.Abstractions;
+using Knot.Modules.Profiles.Application.Profiles.DTOs;
+using System;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+
namespace Knot.Modules.Profiles.Application.Profiles.Avatar;
-public sealed record UploadAvatarCommand(Guid ProfileId, Stream FileStream, string FileName, string ContentType) : ICommand;
+// ─── Upload ────────────────────────────────────────────────────────────────
-internal sealed class UploadAvatarCommandHandler : ICommandHandler
+public sealed record UploadAvatarCommand(
+ Guid UserId,
+ Stream FileStream,
+ string FileName,
+ string ContentType) : ICommand;
+
+internal sealed class UploadAvatarCommandHandler : ICommandHandler
{
- private readonly IProfileRepository _profileRepository;
- private readonly IProfilesUnitOfWork _unitOfWork;
- private readonly IFileStorageService _fileStorage;
+ private readonly IProfileRepository _repository;
+ private readonly IAvatarStorageService _avatarStorage;
- public UploadAvatarCommandHandler(IProfileRepository profileRepository, IProfilesUnitOfWork unitOfWork, IFileStorageService fileStorage)
+ public UploadAvatarCommandHandler(IProfileRepository repository, IAvatarStorageService avatarStorage)
{
- _profileRepository = profileRepository;
- _unitOfWork = unitOfWork;
- _fileStorage = fileStorage;
+ _repository = repository;
+ _avatarStorage = avatarStorage;
}
- public async Task> Handle(UploadAvatarCommand request, CancellationToken cancellationToken)
+ public async Task> Handle(UploadAvatarCommand request, CancellationToken cancellationToken)
{
- var profile = await _profileRepository.GetByIdAsync(request.ProfileId, cancellationToken);
- if (profile == null)
- {
- return Result.Failure(ProfilesErrors.ProfileNotFound);
- }
+ var profile = await _repository.GetByIdAsync(request.UserId, cancellationToken);
+ if (profile is null)
+ return Result.Failure(ProfilesErrors.ProfileNotFound);
- var fileId = await _fileStorage.UploadFileAsync(request.FileStream, request.FileName, request.ContentType);
+ // Удалить старый аватар из S3, если был
+ if (!string.IsNullOrEmpty(profile.AvatarUrl))
+ await _avatarStorage.DeleteAsync(profile.AvatarUrl, cancellationToken);
+
+ var fileId = await _avatarStorage.UploadAsync(request.FileStream, request.FileName, request.ContentType, cancellationToken);
var avatarUrl = $"/api/files/{fileId}";
profile.UpdateAvatar(avatarUrl);
- await _unitOfWork.SaveChangesAsync(cancellationToken);
+ await _repository.UpdateAsync(profile, cancellationToken);
- var dto = new ProfileProfileDto(
- profile.Id,
- profile.Profilename,
- profile.DisplayName,
- profile.Avatar,
- profile.Bio,
- profile.Birthday,
- profile.CreatedAt
- );
-
- return Result.Success(dto);
+ return Result.Success(UserProfileDto.FromDocument(profile));
+ }
+}
+
+// ─── Delete ────────────────────────────────────────────────────────────────
+
+public sealed record DeleteAvatarCommand(Guid UserId) : ICommand;
+
+internal sealed class DeleteAvatarCommandHandler : ICommandHandler
+{
+ private readonly IProfileRepository _repository;
+ private readonly IAvatarStorageService _avatarStorage;
+
+ public DeleteAvatarCommandHandler(IProfileRepository repository, IAvatarStorageService avatarStorage)
+ {
+ _repository = repository;
+ _avatarStorage = avatarStorage;
+ }
+
+ public async Task> Handle(DeleteAvatarCommand request, CancellationToken cancellationToken)
+ {
+ var profile = await _repository.GetByIdAsync(request.UserId, cancellationToken);
+ if (profile is null)
+ return Result.Failure(ProfilesErrors.ProfileNotFound);
+
+ if (!string.IsNullOrEmpty(profile.AvatarUrl))
+ await _avatarStorage.DeleteAsync(profile.AvatarUrl, cancellationToken);
+
+ profile.RemoveAvatar();
+ await _repository.UpdateAsync(profile, cancellationToken);
+
+ return Result.Success(UserProfileDto.FromDocument(profile));
}
}
diff --git a/backend/src/Modules/Profiles/Application/Profiles/DTOs/ProfilesRequests.cs b/backend/src/Modules/Profiles/Application/Profiles/DTOs/ProfilesRequests.cs
new file mode 100644
index 0000000..ebf67a7
--- /dev/null
+++ b/backend/src/Modules/Profiles/Application/Profiles/DTOs/ProfilesRequests.cs
@@ -0,0 +1,6 @@
+using System;
+
+namespace Knot.Modules.Profiles.Application.Profiles.DTOs;
+
+public record UpdateProfileRequest(string? DisplayName, string? Bio, DateTime? Birthday);
+public record UpdateSettingsRequest(bool? HideStoryViews);
diff --git a/backend/src/Modules/Profiles/Application/Profiles/DTOs/UpdateProfileRequest.cs b/backend/src/Modules/Profiles/Application/Profiles/DTOs/UpdateProfileRequest.cs
deleted file mode 100644
index f46f152..0000000
--- a/backend/src/Modules/Profiles/Application/Profiles/DTOs/UpdateProfileRequest.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-using System;
-
-using Knot.Modules.Profiles.Application.Abstractions;
-using Knot.Modules.Profiles.Domain;
-namespace Knot.Modules.Profiles.Application.Profiles.DTOs;
-
-public sealed record UpdateProfileRequest(string? DisplayName, string? Bio, DateTime? Birthday);
diff --git a/backend/src/Modules/Profiles/Application/Profiles/DTOs/UpdateSettingsRequest.cs b/backend/src/Modules/Profiles/Application/Profiles/DTOs/UpdateSettingsRequest.cs
deleted file mode 100644
index 7fe83f2..0000000
--- a/backend/src/Modules/Profiles/Application/Profiles/DTOs/UpdateSettingsRequest.cs
+++ /dev/null
@@ -1,5 +0,0 @@
-using Knot.Modules.Profiles.Application.Abstractions;
-using Knot.Modules.Profiles.Domain;
-namespace Knot.Modules.Profiles.Application.Profiles.DTOs;
-
-public sealed record UpdateSettingsRequest(bool? HideStoryViews);
diff --git a/backend/src/Modules/Profiles/Application/Profiles/DTOs/UserDto.cs b/backend/src/Modules/Profiles/Application/Profiles/DTOs/UserDto.cs
deleted file mode 100644
index e897d11..0000000
--- a/backend/src/Modules/Profiles/Application/Profiles/DTOs/UserDto.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using System;
-
-using Knot.Modules.Profiles.Application.Abstractions;
-using Knot.Modules.Profiles.Domain;
-namespace Knot.Modules.Profiles.Application.Profiles.DTOs;
-
-public record ProfileDto(
- Guid Id,
- string Profilename,
- string DisplayName,
- string? Avatar,
- bool IsOnline,
- DateTime LastSeen
-);
diff --git a/backend/src/Modules/Profiles/Application/Profiles/DTOs/UserProfileDto.cs b/backend/src/Modules/Profiles/Application/Profiles/DTOs/UserProfileDto.cs
index d8ef1da..2bdfe81 100644
--- a/backend/src/Modules/Profiles/Application/Profiles/DTOs/UserProfileDto.cs
+++ b/backend/src/Modules/Profiles/Application/Profiles/DTOs/UserProfileDto.cs
@@ -1,18 +1,29 @@
using System;
-
-using Knot.Modules.Profiles.Application.Abstractions;
using Knot.Modules.Profiles.Domain;
+
namespace Knot.Modules.Profiles.Application.Profiles.DTOs;
-public record ProfileProfileDto(
+public record UserProfileDto(
Guid Id,
- string Profilename,
+ string UserName,
string DisplayName,
- string? Avatar,
+ string? AvatarUrl,
string? Bio,
DateTime? Birthday,
DateTime CreatedAt,
- bool? HideStoryViews = null,
+ bool HideStoryViews = false,
bool IsOnline = false,
- DateTime? LastSeen = null
-);
+ DateTime? LastSeen = null)
+{
+ public static UserProfileDto FromDocument(ProfileDocument doc) =>
+ new(
+ doc.Id,
+ doc.Username,
+ doc.DisplayName,
+ doc.AvatarUrl,
+ doc.Bio,
+ doc.Birthday,
+ doc.CreatedAt,
+ doc.HideStoryViews
+ );
+}
diff --git a/backend/src/Modules/Profiles/Application/Profiles/GetUser/GetUser.cs b/backend/src/Modules/Profiles/Application/Profiles/GetUser/GetUser.cs
index 334e7e9..e39eadf 100644
--- a/backend/src/Modules/Profiles/Application/Profiles/GetUser/GetUser.cs
+++ b/backend/src/Modules/Profiles/Application/Profiles/GetUser/GetUser.cs
@@ -1,36 +1,29 @@
using Knot.Shared.Kernel;
-using Knot.Modules.Profiles.Domain;
-
-
using Knot.Modules.Profiles.Application.Abstractions;
+using Knot.Modules.Profiles.Application.Profiles.DTOs;
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
namespace Knot.Modules.Profiles.Application.Profiles.GetProfile;
-public sealed record GetProfileQuery(Guid Id) : IQuery;
+public sealed record GetProfileQuery(Guid UserId) : IQuery;
-internal sealed class GetProfileQueryHandler : IQueryHandler
+internal sealed class GetProfileQueryHandler : IQueryHandler
{
- private readonly IProfileRepository _profileRepository;
+ private readonly IProfileRepository _repository;
- public GetProfileQueryHandler(IProfileRepository profileRepository)
+ public GetProfileQueryHandler(IProfileRepository repository)
{
- _profileRepository = profileRepository;
+ _repository = repository;
}
- public async Task> Handle(GetProfileQuery request, CancellationToken cancellationToken)
+ public async Task> Handle(GetProfileQuery request, CancellationToken cancellationToken)
{
- var profile = await _profileRepository.GetByIdAsync(request.Id, cancellationToken);
- if (profile == null)
- {
- return Result.Failure(ProfilesErrors.ProfileNotFound);
- }
+ var profile = await _repository.GetByIdAsync(request.UserId, cancellationToken);
+ if (profile is null)
+ return Result.Failure(ProfilesErrors.ProfileNotFound);
- var dto = new ProfileProfileDto(
- profile.Id,
- profile.Profilename,
- profile.Name,
- profile.AvatarUrl
- );
-
- return Result.Success(dto);
+ return Result.Success(UserProfileDto.FromDocument(profile));
}
}
diff --git a/backend/src/Modules/Profiles/Application/Profiles/Integration/UserRegisteredDomainEventHandler.cs b/backend/src/Modules/Profiles/Application/Profiles/Integration/UserRegisteredDomainEventHandler.cs
new file mode 100644
index 0000000..ec6ac20
--- /dev/null
+++ b/backend/src/Modules/Profiles/Application/Profiles/Integration/UserRegisteredDomainEventHandler.cs
@@ -0,0 +1,38 @@
+using Knot.Modules.Profiles.Application.Abstractions;
+using Knot.Modules.Profiles.Domain;
+using Knot.Shared.Kernel.Events;
+using MediatR;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Knot.Modules.Profiles.Application.Profiles.Integration;
+
+///
+/// Реакция модуля Profiles на создание пользователя в модуле Auth.
+/// Создает соответствующий документ в MongoDB.
+///
+internal sealed class UserRegisteredDomainEventHandler : INotificationHandler
+{
+ private readonly IProfileRepository _repository;
+
+ public UserRegisteredDomainEventHandler(IProfileRepository repository)
+ {
+ _repository = repository;
+ }
+
+ public async Task Handle(UserRegisteredDomainEvent notification, CancellationToken cancellationToken)
+ {
+ // Проверяем, существует ли уже профиль (защита от дублей)
+ var existing = await _repository.GetByIdAsync(notification.UserId, cancellationToken);
+ if (existing is not null) return;
+
+ var profile = ProfileDocument.Create(
+ notification.UserId,
+ notification.Username,
+ notification.DisplayName,
+ notification.Bio
+ );
+
+ await _repository.AddAsync(profile, cancellationToken);
+ }
+}
diff --git a/backend/src/Modules/Profiles/Application/Profiles/Search/SearchUsers.cs b/backend/src/Modules/Profiles/Application/Profiles/Search/SearchUsers.cs
index b9adadc..162b063 100644
--- a/backend/src/Modules/Profiles/Application/Profiles/Search/SearchUsers.cs
+++ b/backend/src/Modules/Profiles/Application/Profiles/Search/SearchUsers.cs
@@ -1,34 +1,28 @@
using Knot.Shared.Kernel;
-using Knot.Modules.Profiles.Domain;
-
-
using Knot.Modules.Profiles.Application.Abstractions;
+using Knot.Modules.Profiles.Application.Profiles.DTOs;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+
namespace Knot.Modules.Profiles.Application.Profiles.Search;
-public sealed record SearchProfilesQuery(string Query) : IQuery>;
+public sealed record SearchProfilesQuery(string Query) : IQuery>;
-internal sealed class SearchProfilesQueryHandler : IQueryHandler>
+internal sealed class SearchProfilesQueryHandler : IQueryHandler>
{
- private readonly IProfileRepository _profileRepository;
+ private readonly IProfileRepository _repository;
- public SearchProfilesQueryHandler(IProfileRepository profileRepository)
+ public SearchProfilesQueryHandler(IProfileRepository repository)
{
- _profileRepository = profileRepository;
+ _repository = repository;
}
- public async Task>> Handle(SearchProfilesQuery request, CancellationToken cancellationToken)
+ public async Task>> Handle(SearchProfilesQuery request, CancellationToken cancellationToken)
{
- var profiles = await _profileRepository.SearchProfilesAsync(request.Query, cancellationToken);
-
- var result = profiles.Select(profile => new ProfileDto(
- profile.Id,
- profile.Profilename,
- profile.DisplayName,
- profile.Avatar,
- false,
- DateTime.UtcNow
- )).ToList();
-
- return Result.Success(result);
+ var profiles = await _repository.SearchAsync(request.Query, cancellationToken);
+ var dtos = profiles.Select(UserProfileDto.FromDocument).ToList();
+ return Result.Success(dtos);
}
}
diff --git a/backend/src/Modules/Profiles/Application/Profiles/UpdateProfile/UpdateProfile.cs b/backend/src/Modules/Profiles/Application/Profiles/UpdateProfile/UpdateProfile.cs
index 27d24e2..df2d880 100644
--- a/backend/src/Modules/Profiles/Application/Profiles/UpdateProfile/UpdateProfile.cs
+++ b/backend/src/Modules/Profiles/Application/Profiles/UpdateProfile/UpdateProfile.cs
@@ -1,43 +1,40 @@
using Knot.Shared.Kernel;
-
-
+using Knot.Modules.Profiles.Application.Abstractions;
+using Knot.Modules.Profiles.Application.Profiles.DTOs;
+using System;
+using System.Threading;
+using System.Threading.Tasks;
namespace Knot.Modules.Profiles.Application.Profiles.UpdateProfile;
-public sealed record UpdateProfileCommand(Guid UserId, string? DisplayName, string? Bio, DateTime? Birthday) : ICommand;
+public sealed record UpdateProfileCommand(
+ Guid UserId,
+ string? DisplayName,
+ string? Bio,
+ DateTime? Birthday) : ICommand;
internal sealed class UpdateProfileCommandHandler : ICommandHandler
{
- private readonly IProfileRepository _userRepository;
- private readonly IProfilesUnitOfWork _unitOfWork;
+ private readonly IProfileRepository _repository;
- public UpdateProfileCommandHandler(IProfileRepository userRepository, IProfilesUnitOfWork unitOfWork)
+ public UpdateProfileCommandHandler(IProfileRepository repository)
{
- _userRepository = userRepository;
- _unitOfWork = unitOfWork;
+ _repository = repository;
}
public async Task> Handle(UpdateProfileCommand request, CancellationToken cancellationToken)
{
- var user = await _userRepository.GetByIdAsync(request.UserId, cancellationToken);
- if (user == null)
- {
- return Result.Failure(IdentityErrors.UserNotFound);
- }
+ var profile = await _repository.GetByIdAsync(request.UserId, cancellationToken);
+ if (profile is null)
+ return Result.Failure(ProfilesErrors.ProfileNotFound);
- user.UpdateProfile(request.DisplayName ?? user.DisplayName, request.Bio, request.Birthday);
- await _unitOfWork.SaveChangesAsync(cancellationToken);
+ profile.UpdateProfile(
+ request.DisplayName ?? profile.DisplayName,
+ request.Bio,
+ request.Birthday);
- var dto = new UserProfileDto(
- user.Id,
- user.Username,
- user.DisplayName,
- user.Avatar,
- user.Bio,
- user.Birthday,
- user.CreatedAt
- );
+ await _repository.UpdateAsync(profile, cancellationToken);
- return Result.Success(dto);
+ return Result.Success(UserProfileDto.FromDocument(profile));
}
}
diff --git a/backend/src/Modules/Profiles/Application/Profiles/UpdateSettings/UpdateSettings.cs b/backend/src/Modules/Profiles/Application/Profiles/UpdateSettings/UpdateSettings.cs
index e47f941..7b2a702 100644
--- a/backend/src/Modules/Profiles/Application/Profiles/UpdateSettings/UpdateSettings.cs
+++ b/backend/src/Modules/Profiles/Application/Profiles/UpdateSettings/UpdateSettings.cs
@@ -1,41 +1,32 @@
using Knot.Shared.Kernel;
-using Knot.Modules.Profiles.Domain;
-
-
using Knot.Modules.Profiles.Application.Abstractions;
+using Knot.Modules.Profiles.Application.Profiles.DTOs;
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
namespace Knot.Modules.Profiles.Application.Profiles.UpdateSettings;
-public sealed record UpdateSettingsCommand(Guid ProfileId, bool? HideStoryViews) : ICommand;
+public sealed record UpdateSettingsCommand(Guid UserId, bool? HideStoryViews) : ICommand;
-internal sealed class UpdateSettingsCommandHandler : ICommandHandler
+internal sealed class UpdateSettingsCommandHandler : ICommandHandler
{
- private readonly IProfileRepository _profileRepository;
- private readonly IProfilesUnitOfWork _unitOfWork;
+ private readonly IProfileRepository _repository;
- public UpdateSettingsCommandHandler(IProfileRepository profileRepository, IProfilesUnitOfWork unitOfWork)
+ public UpdateSettingsCommandHandler(IProfileRepository repository)
{
- _profileRepository = profileRepository;
- _unitOfWork = unitOfWork;
+ _repository = repository;
}
- public async Task> Handle(UpdateSettingsCommand request, CancellationToken cancellationToken)
+ public async Task> Handle(UpdateSettingsCommand request, CancellationToken cancellationToken)
{
- var profile = await _profileRepository.GetByIdAsync(request.ProfileId, cancellationToken);
- if (profile == null)
- {
- return Result.Failure(ProfilesErrors.ProfileNotFound);
- }
+ var profile = await _repository.GetByIdAsync(request.UserId, cancellationToken);
+ if (profile is null)
+ return Result.Failure(ProfilesErrors.ProfileNotFound);
profile.UpdateSettings(request.HideStoryViews ?? profile.HideStoryViews);
- await _unitOfWork.SaveChangesAsync(cancellationToken);
+ await _repository.UpdateAsync(profile, cancellationToken);
- var dto = new ProfileProfileDto(
- profile.Id,
- profile.Profilename,
- profile.Name,
- profile.AvatarUrl
- );
-
- return Result.Success(dto);
+ return Result.Success(UserProfileDto.FromDocument(profile));
}
}
diff --git a/backend/src/Modules/Profiles/Application/Profiles/UserDto.cs b/backend/src/Modules/Profiles/Application/Profiles/UserDto.cs
deleted file mode 100644
index d1510cc..0000000
--- a/backend/src/Modules/Profiles/Application/Profiles/UserDto.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using System;
-
-using Knot.Modules.Profiles.Application.Abstractions;
-using Knot.Modules.Profiles.Domain;
-namespace Knot.Modules.Profiles.Application.Profiles;
-
-public record ProfileDto(
- Guid Id,
- string Profilename,
- string DisplayName,
- string? Avatar,
- bool IsOnline,
- DateTime LastSeen
-);
diff --git a/backend/src/Modules/Profiles/Application/Profiles/UserProfileDto.cs b/backend/src/Modules/Profiles/Application/Profiles/UserProfileDto.cs
deleted file mode 100644
index 3e4c805..0000000
--- a/backend/src/Modules/Profiles/Application/Profiles/UserProfileDto.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-namespace Knot.Modules.Profiles.Application.Profiles;
-using System;
-public record UserProfileDto(Guid Id, string UserName, string Name, string AvatarUrl)
-{
- public UserProfileDto(Guid a, string b, string c, string d, string e, bool f, int g, int h, int i, string j = "") : this(a,b,c,e) {}
- public UserProfileDto(Guid a, string b, string c, string d, string e, DateTime? f, DateTime g, string h="", string i="", string j="") : this(a,b,c,e) {}
-}
-public record ProfileProfileDto(Guid Id, string UserName, string Name, string AvatarUrl)
-{
- public ProfileProfileDto(Guid a, string b, string c, string d, string e, bool f, int g, int h, int i, string j = "") : this(a,b,c,e) {}
- public ProfileProfileDto(Guid a, string b, string c, string d, string e, DateTime? f, DateTime g, string h="", string i="", string j="") : this(a,b,c,e) {}
-}
diff --git a/backend/src/Modules/Profiles/DependencyInjection.cs b/backend/src/Modules/Profiles/DependencyInjection.cs
new file mode 100644
index 0000000..c9b56bd
--- /dev/null
+++ b/backend/src/Modules/Profiles/DependencyInjection.cs
@@ -0,0 +1,31 @@
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Configuration;
+using Knot.Modules.Profiles.Application.Abstractions;
+using Knot.Modules.Profiles.Infrastructure.Database;
+using MongoDB.Driver;
+
+namespace Knot.Modules.Profiles;
+
+public static class DependencyInjection
+{
+ public static IServiceCollection AddProfilesModule(this IServiceCollection services, IConfiguration configuration)
+ {
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+
+ // MongoDB Registration
+ var mongoConnection = configuration.GetConnectionString("MongoConnection")
+ ?? configuration["MONGO_URL"]
+ ?? "mongodb://localhost:27017";
+
+ var mongoClient = new MongoClient(mongoConnection);
+ var database = mongoClient.GetDatabase("knot_messager");
+ services.AddSingleton(database);
+
+ services.AddMediatR(config =>
+ config.RegisterServicesFromAssembly(typeof(DependencyInjection).Assembly));
+
+ return services;
+ }
+}
diff --git a/backend/src/Modules/Profiles/Domain/Events/ProfileDomainEvents.cs b/backend/src/Modules/Profiles/Domain/Events/ProfileDomainEvents.cs
new file mode 100644
index 0000000..191e9d5
--- /dev/null
+++ b/backend/src/Modules/Profiles/Domain/Events/ProfileDomainEvents.cs
@@ -0,0 +1,14 @@
+using Knot.Shared.Kernel;
+using System;
+
+namespace Knot.Modules.Profiles.Domain.Events;
+
+///
+/// Доменное событие: профиль пользователя создан при регистрации.
+///
+public sealed record ProfileCreatedDomainEvent(Guid UserId, string Username) : IDomainEvent;
+
+///
+/// Доменное событие: аватар профиля был изменён (для возможной инвалидации CDN-кэша).
+///
+public sealed record ProfileAvatarChangedDomainEvent(Guid UserId, string? OldAvatarFileId, string? NewAvatarFileId) : IDomainEvent;
diff --git a/backend/src/Modules/Profiles/Domain/ProfileDocument.cs b/backend/src/Modules/Profiles/Domain/ProfileDocument.cs
new file mode 100644
index 0000000..352d5f7
--- /dev/null
+++ b/backend/src/Modules/Profiles/Domain/ProfileDocument.cs
@@ -0,0 +1,69 @@
+using MongoDB.Bson;
+using MongoDB.Bson.Serialization.Attributes;
+using System;
+
+namespace Knot.Modules.Profiles.Domain;
+
+///
+/// MongoDB-документ профиля пользователя.
+/// Id совпадает с UserId из модуля Auth (Postgres).
+/// Аватар хранится в S3 — здесь лежит только ссылка.
+///
+public sealed class ProfileDocument
+{
+ [BsonId]
+ [BsonRepresentation(BsonType.String)]
+ public Guid Id { get; private set; }
+
+ public string Username { get; private set; }
+
+ public string DisplayName { get; private set; }
+
+ public string? Bio { get; private set; }
+
+ ///
+ /// URL или ключ объекта в S3-хранилище (MinIO).
+ /// Пример: "/api/files/{fileId}"
+ ///
+ public string? AvatarUrl { get; private set; }
+
+ public DateTime? Birthday { get; private set; }
+
+ ///
+ /// Скрывать ли просмотры сторис от других пользователей.
+ ///
+ public bool HideStoryViews { get; private set; }
+
+ public DateTime CreatedAt { get; private set; }
+
+ // Для MongoDB — protected-конструктор через BSON-десериализацию
+ protected ProfileDocument() { }
+
+ private ProfileDocument(Guid id, string username, string displayName, string? bio)
+ {
+ Id = id;
+ Username = username;
+ DisplayName = displayName;
+ Bio = bio;
+ CreatedAt = DateTime.UtcNow;
+ }
+
+ public static ProfileDocument Create(Guid userId, string username, string displayName, string? bio = null)
+ => new(userId, username, displayName, bio);
+
+ public void UpdateProfile(string displayName, string? bio, DateTime? birthday)
+ {
+ DisplayName = displayName;
+ Bio = bio;
+ Birthday = birthday;
+ }
+
+ public void UpdateAvatar(string? avatarUrl)
+ => AvatarUrl = avatarUrl;
+
+ public void RemoveAvatar()
+ => AvatarUrl = null;
+
+ public void UpdateSettings(bool hideStoryViews)
+ => HideStoryViews = hideStoryViews;
+}
diff --git a/backend/src/Modules/Profiles/Infrastructure/Database/AvatarStorageService.cs b/backend/src/Modules/Profiles/Infrastructure/Database/AvatarStorageService.cs
new file mode 100644
index 0000000..41df150
--- /dev/null
+++ b/backend/src/Modules/Profiles/Infrastructure/Database/AvatarStorageService.cs
@@ -0,0 +1,33 @@
+using Knot.Modules.Profiles.Application.Abstractions;
+using Knot.Shared.Kernel.Storage;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Knot.Modules.Profiles.Infrastructure.Database;
+
+///
+/// Адаптер к S3-хранилищу для аватаров.
+/// Инжектирует общий IFileStorageService и передает ему управление.
+///
+internal sealed class AvatarStorageService : IAvatarStorageService
+{
+ private readonly IFileStorageService _storage;
+
+ public AvatarStorageService(IFileStorageService storage)
+ {
+ _storage = storage;
+ }
+
+ public async Task UploadAsync(Stream stream, string fileName, string contentType, CancellationToken ct = default)
+ {
+ return await _storage.UploadFileAsync(stream, fileName, contentType);
+ }
+
+ public async Task DeleteAsync(string fileId, CancellationToken ct = default)
+ {
+ // Если fileId содержит "/api/files/", обрезаем его до чистого ID
+ var cleanId = fileId.Replace("/api/files/", "");
+ await _storage.DeleteFileAsync(cleanId);
+ }
+}
diff --git a/backend/src/Modules/Profiles/Infrastructure/Database/ProfileRepository.cs b/backend/src/Modules/Profiles/Infrastructure/Database/ProfileRepository.cs
new file mode 100644
index 0000000..96f70b8
--- /dev/null
+++ b/backend/src/Modules/Profiles/Infrastructure/Database/ProfileRepository.cs
@@ -0,0 +1,48 @@
+using Knot.Modules.Profiles.Application.Abstractions;
+using Knot.Modules.Profiles.Domain;
+using MongoDB.Driver;
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Knot.Modules.Profiles.Infrastructure.Database;
+
+internal sealed class ProfileRepository : IProfileRepository
+{
+ private readonly IMongoCollection _profiles;
+
+ public ProfileRepository(IMongoDatabase database)
+ {
+ _profiles = database.GetCollection("profiles");
+ }
+
+ public async Task GetByIdAsync(Guid userId, CancellationToken ct = default)
+ {
+ return await _profiles.Find(p => p.Id == userId).FirstOrDefaultAsync(ct);
+ }
+
+ public async Task AddAsync(ProfileDocument profile, CancellationToken ct = default)
+ {
+ await _profiles.InsertOneAsync(profile, null, ct);
+ }
+
+ public async Task UpdateAsync(ProfileDocument profile, CancellationToken ct = default)
+ {
+ await _profiles.ReplaceOneAsync(p => p.Id == profile.Id, profile, new ReplaceOptions { IsUpsert = false }, ct);
+ }
+
+ public async Task> SearchAsync(string query, CancellationToken ct = default)
+ {
+ if (string.IsNullOrWhiteSpace(query))
+ return new List();
+
+ // Простой регистронезависимый поиск по Regex (в реальной системе лучше использовать Text Index)
+ var filter = Builders.Filter.Or(
+ Builders.Filter.Regex(p => p.Username, new MongoDB.Bson.BsonRegularExpression(query, "i")),
+ Builders.Filter.Regex(p => p.DisplayName, new MongoDB.Bson.BsonRegularExpression(query, "i"))
+ );
+
+ return await _profiles.Find(filter).Limit(20).ToListAsync(ct);
+ }
+}
diff --git a/backend/src/Modules/Profiles/Infrastructure/Database/ProfilesUnitOfWork.cs b/backend/src/Modules/Profiles/Infrastructure/Database/ProfilesUnitOfWork.cs
new file mode 100644
index 0000000..230459e
--- /dev/null
+++ b/backend/src/Modules/Profiles/Infrastructure/Database/ProfilesUnitOfWork.cs
@@ -0,0 +1,12 @@
+using Knot.Modules.Profiles.Application.Abstractions;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Knot.Modules.Profiles.Infrastructure.Database;
+
+internal sealed class ProfilesUnitOfWork : IProfilesUnitOfWork
+{
+ // MongoDB updates are atomic per document by default in the driver,
+ // so for simple ProfileDocument updates, we don't need distributed transactions.
+ public Task SaveChangesAsync(CancellationToken ct = default) => Task.CompletedTask;
+}
diff --git a/backend/src/Modules/Profiles/Knot.Modules.Profiles.csproj b/backend/src/Modules/Profiles/Knot.Modules.Profiles.csproj
index 6dbbdda..3a70efd 100644
--- a/backend/src/Modules/Profiles/Knot.Modules.Profiles.csproj
+++ b/backend/src/Modules/Profiles/Knot.Modules.Profiles.csproj
@@ -15,12 +15,22 @@
-
+
-
+
+
+
+
+
+
+
+
+
+
+
diff --git a/backend/src/Modules/Relations/Application/Abstractions/IRelationsDbContext.cs b/backend/src/Modules/Relations/Application/Abstractions/IRelationsDbContext.cs
index 9ea1bba..5584f3e 100644
--- a/backend/src/Modules/Relations/Application/Abstractions/IRelationsDbContext.cs
+++ b/backend/src/Modules/Relations/Application/Abstractions/IRelationsDbContext.cs
@@ -3,7 +3,6 @@ using Microsoft.EntityFrameworkCore;
using System.Threading;
using System.Threading.Tasks;
-using Knot.Modules.Relations.Application.Abstractions;
namespace Knot.Modules.Relations.Application.Abstractions;
public interface IContactsDbContext
diff --git a/backend/src/Modules/Relations/Application/Contacts/ContactDto.cs b/backend/src/Modules/Relations/Application/Contacts/ContactDto.cs
new file mode 100644
index 0000000..98b0fba
--- /dev/null
+++ b/backend/src/Modules/Relations/Application/Contacts/ContactDto.cs
@@ -0,0 +1,41 @@
+using System;
+
+namespace Knot.Modules.Relations.Application.Contacts;
+
+public class ContactDto
+{
+ public Guid Id { get; init; }
+ public string Username { get; init; } = string.Empty;
+ public string DisplayName { get; init; } = string.Empty;
+ public string Avatar { get; init; } = string.Empty;
+ public bool IsOnline { get; init; }
+ public DateTime? LastSeen { get; init; }
+ public Guid RelationId { get; init; }
+ public bool IsBlocked { get; init; }
+ public bool IsExternal { get; init; }
+ public string? Domain { get; init; }
+
+ public ContactDto(
+ Guid id,
+ string username,
+ string displayName,
+ string avatar,
+ bool isOnline,
+ DateTime? lastSeen,
+ Guid relationId,
+ bool isBlocked = false,
+ bool isExternal = false,
+ string? domain = null)
+ {
+ Id = id;
+ Username = username;
+ DisplayName = displayName;
+ Avatar = avatar;
+ IsOnline = isOnline;
+ LastSeen = lastSeen;
+ RelationId = relationId;
+ IsBlocked = isBlocked;
+ IsExternal = isExternal;
+ Domain = domain;
+ }
+}
diff --git a/backend/src/Modules/Relations/Application/Contacts/GetContacts.cs b/backend/src/Modules/Relations/Application/Contacts/GetContacts.cs
index 0ca03ac..43c627a 100644
--- a/backend/src/Modules/Relations/Application/Contacts/GetContacts.cs
+++ b/backend/src/Modules/Relations/Application/Contacts/GetContacts.cs
@@ -10,18 +10,6 @@ using Knot.Modules.Relations.Application.Abstractions;
namespace Knot.Modules.Relations.Application.Contacts;
-public record ContactDto(
- Guid Id,
- string Username,
- string DisplayName,
- string Avatar,
- bool IsOnline,
- DateTime? LastSeen,
- Guid RelationId,
- bool IsBlocked = false,
- bool IsExternal = false,
- string? Domain = null);
-
public record GetContactsQuery(Guid UserId) : IQuery>;
internal sealed class GetContactsQueryHandler : IQueryHandler>
@@ -56,7 +44,7 @@ internal sealed class GetContactsQueryHandler : IQueryHandler(options =>
+ options.UseNpgsql(connectionString));
+
+ services.AddMediatR(config =>
+ config.RegisterServicesFromAssembly(typeof(DependencyInjection).Assembly));
+
+ return services;
+ }
+}
diff --git a/backend/src/Modules/Relations/Knot.Modules.Relations.csproj b/backend/src/Modules/Relations/Knot.Modules.Relations.csproj
index c293968..67e2845 100644
--- a/backend/src/Modules/Relations/Knot.Modules.Relations.csproj
+++ b/backend/src/Modules/Relations/Knot.Modules.Relations.csproj
@@ -8,6 +8,7 @@
+
@@ -15,11 +16,15 @@
-
+
+
+
+
+
diff --git a/backend/src/Modules/Relations/Presentation/Endpoints/FriendsEndpoints.cs b/backend/src/Modules/Relations/Presentation/Endpoints/ContactsEndpoints.cs
similarity index 95%
rename from backend/src/Modules/Relations/Presentation/Endpoints/FriendsEndpoints.cs
rename to backend/src/Modules/Relations/Presentation/Endpoints/ContactsEndpoints.cs
index 7d51408..104e6e2 100644
--- a/backend/src/Modules/Relations/Presentation/Endpoints/FriendsEndpoints.cs
+++ b/backend/src/Modules/Relations/Presentation/Endpoints/ContactsEndpoints.cs
@@ -1,13 +1,12 @@
using Carter;
using Knot.Shared.Kernel;
-using Knot.Modules.Relations.Application.Friends;
-using Knot.Modules.Relations.Application.Friends.DTOs;
+using Knot.Shared.Infrastructure;
+using Knot.Modules.Relations.Application.Contacts;
using MediatR;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Mvc;
-using Knot.Modules.Relations.Application.Contacts;
namespace Knot.Modules.Relations.Presentation.Endpoints;
diff --git a/backend/src/Modules/Relations/rel_err.json b/backend/src/Modules/Relations/rel_err.json
new file mode 100644
index 0000000..91a6b96
--- /dev/null
+++ b/backend/src/Modules/Relations/rel_err.json
@@ -0,0 +1,69 @@
+{
+ "$schema": "http://json.schemastore.org/sarif-1.0.0",
+ "version": "1.0.0",
+ "runs": [
+ {
+ "tool": {
+ "name": "Компилятор Microsoft (R) Visual C#",
+ "version": "5.0.0.0",
+ "fileVersion": "5.0.0-1.25358.103 (75972a5b)",
+ "semanticVersion": "5.0.0",
+ "language": "ru-RU"
+ },
+ "results": [
+ {
+ "ruleId": "CS1520",
+ "level": "error",
+ "message": "Метод должен иметь тип возвращаемого значения",
+ "locations": [
+ {
+ "resultFile": {
+ "uri": "file:///E:/GIT/forkmessager/backend/src/Modules/Relations/Application/Contacts/RemoveContact.cs",
+ "region": {
+ "startLine": 17,
+ "startColumn": 12,
+ "endLine": 17,
+ "endColumn": 35
+ }
+ }
+ }
+ ]
+ },
+ {
+ "ruleId": "CS1520",
+ "level": "error",
+ "message": "Метод должен иметь тип возвращаемого значения",
+ "locations": [
+ {
+ "resultFile": {
+ "uri": "file:///E:/GIT/forkmessager/backend/src/Modules/Relations/Application/Contacts/RemoveContact.cs",
+ "region": {
+ "startLine": 18,
+ "startColumn": 12,
+ "endLine": 18,
+ "endColumn": 39
+ }
+ }
+ }
+ ]
+ }
+ ],
+ "rules": {
+ "CS1520": {
+ "id": "CS1520",
+ "defaultLevel": "error",
+ "helpUri": "https://msdn.microsoft.com/query/roslyn.query?appId=roslyn&k=k(CS1520)",
+ "properties": {
+ "category": "Compiler",
+ "isEnabledByDefault": true,
+ "tags": [
+ "Compiler",
+ "Telemetry",
+ "NotConfigurable"
+ ]
+ }
+ }
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/backend/src/Shared/Knot.Shared.Kernel/Configuration/ISettingsService.cs b/backend/src/Modules/Settings/Application/Settings/Abstractions/ISettingsService.cs
similarity index 88%
rename from backend/src/Shared/Knot.Shared.Kernel/Configuration/ISettingsService.cs
rename to backend/src/Modules/Settings/Application/Settings/Abstractions/ISettingsService.cs
index c053d7a..46cef1d 100644
--- a/backend/src/Shared/Knot.Shared.Kernel/Configuration/ISettingsService.cs
+++ b/backend/src/Modules/Settings/Application/Settings/Abstractions/ISettingsService.cs
@@ -1,7 +1,8 @@
using System.Threading;
using System.Threading.Tasks;
+using Knot.Modules.Settings.Application.Settings.DTOs;
-namespace Knot.Shared.Kernel.Configuration;
+namespace Knot.Modules.Settings.Application.Settings.Abstractions;
public interface ISettingsService
{
diff --git a/backend/src/Modules/Settings/Application/Settings/Commands/UpdateSettingsCommand.cs b/backend/src/Modules/Settings/Application/Settings/Commands/UpdateSettingsCommand.cs
new file mode 100644
index 0000000..5c7b1d9
--- /dev/null
+++ b/backend/src/Modules/Settings/Application/Settings/Commands/UpdateSettingsCommand.cs
@@ -0,0 +1,44 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using MediatR;
+using Knot.Shared.Kernel;
+using System.Security.Cryptography;
+using Knot.Modules.Settings.Application.Settings.DTOs;
+using Knot.Modules.Settings.Application.Settings.Abstractions;
+
+namespace Knot.Modules.Settings.Application.Settings.Commands;
+
+public record UpdateSettingsCommand(SystemSettingsDto Settings) : ICommand;
+
+internal sealed class UpdateSettingsCommandHandler : ICommandHandler
+{
+ private readonly ISettingsService _settingsService;
+ private readonly IMediator _mediator;
+
+ public UpdateSettingsCommandHandler(ISettingsService settingsService, IMediator mediator)
+ {
+ _settingsService = settingsService;
+ _mediator = mediator;
+ }
+
+ public async Task> Handle(UpdateSettingsCommand request, CancellationToken cancellationToken)
+ {
+ if (request.Settings.Federation.Enabled &&
+ (string.IsNullOrEmpty(request.Settings.Federation.PrivateKey) ||
+ string.IsNullOrEmpty(request.Settings.Federation.PublicKey)))
+ {
+ using var rsa = RSA.Create(2048);
+ request.Settings.Federation.PrivateKey = Convert.ToBase64String(rsa.ExportPkcs8PrivateKey());
+ request.Settings.Federation.PublicKey = Convert.ToBase64String(rsa.ExportRSAPublicKey());
+ }
+
+ await _settingsService.UpdateSettingsAsync(request.Settings, cancellationToken);
+
+ // Notify other modules via Domain Event if needed, but here we can just publish
+ await _mediator.Publish(new Knot.Modules.Settings.Domain.Events.SystemSettingsUpdatedDomainEvent(request.Settings), cancellationToken);
+
+ return Result.Success(request.Settings);
+ }
+}
+
diff --git a/backend/src/Modules/Settings/Application/Config/DTOs/PublicConfigDto.cs b/backend/src/Modules/Settings/Application/Settings/DTOs/PublicConfigDto.cs
similarity index 96%
rename from backend/src/Modules/Settings/Application/Config/DTOs/PublicConfigDto.cs
rename to backend/src/Modules/Settings/Application/Settings/DTOs/PublicConfigDto.cs
index 71bd0f2..7fc200a 100644
--- a/backend/src/Modules/Settings/Application/Config/DTOs/PublicConfigDto.cs
+++ b/backend/src/Modules/Settings/Application/Settings/DTOs/PublicConfigDto.cs
@@ -1,7 +1,7 @@
-using Knot.Shared.Kernel.Configuration;
using System.Collections.Generic;
+using Knot.Modules.Settings.Application.Settings.DTOs;
-namespace Knot.Modules.Settings.Application.Config.DTOs;
+namespace Knot.Modules.Settings.Application.Settings.DTOs;
public record PublicConfigDto
{
@@ -74,7 +74,7 @@ public record PublicConfigDto
},
Import = new ImportConfigDto
{
- EnableTelegramImport = settings.Import.EnableTelegramImport
+ Enabled = settings.Import.EnableTelegramImport
},
Federation = new FederationConfigDto
{
@@ -148,7 +148,7 @@ public record KlipyConfigDto
public record ImportConfigDto
{
- public bool EnableTelegramImport { get; init; }
+ public bool Enabled { get; init; }
}
public record FederationConfigDto
@@ -156,4 +156,4 @@ public record FederationConfigDto
public bool Enabled { get; init; }
public string ServerDescription { get; init; } = string.Empty;
public List AllowedDomains { get; init; } = new();
-}
\ No newline at end of file
+}
diff --git a/backend/src/Shared/Knot.Shared.Kernel/Configuration/SystemSettingsDto.cs b/backend/src/Modules/Settings/Application/Settings/DTOs/SystemSettingsDto.cs
similarity index 82%
rename from backend/src/Shared/Knot.Shared.Kernel/Configuration/SystemSettingsDto.cs
rename to backend/src/Modules/Settings/Application/Settings/DTOs/SystemSettingsDto.cs
index 7ed1776..d34cfaf 100644
--- a/backend/src/Shared/Knot.Shared.Kernel/Configuration/SystemSettingsDto.cs
+++ b/backend/src/Modules/Settings/Application/Settings/DTOs/SystemSettingsDto.cs
@@ -1,6 +1,6 @@
using System.Collections.Generic;
-namespace Knot.Shared.Kernel.Configuration;
+namespace Knot.Modules.Settings.Application.Settings.DTOs;
public class SystemConfig
{
@@ -13,22 +13,18 @@ public class SystemConfig
public class StoriesConfig
{
public bool Enabled { get; set; } = true;
- public int MaxStoriesPerPeriod { get; set; } = 5; // 1 to 10
- public int StoryLifetimeHours { get; set; } = 24; // 4 to 48
-
- // Text Stories
+ public int MaxStoriesPerPeriod { get; set; } = 5;
+ public int StoryLifetimeHours { get; set; } = 24;
public bool TextStoriesEnabled { get; set; } = true;
- public int TextStoryDurationSeconds { get; set; } = 15; // 5 to 30
-
- // Media Stories
- public int MediaStoryMaxDurationSeconds { get; set; } = 30; // 5 to 60
- public int MaxMediaSizeBytes { get; set; } = 15 * 1024 * 1024; // 15MB
+ public int TextStoryDurationSeconds { get; set; } = 15;
+ public int MediaStoryMaxDurationSeconds { get; set; } = 30;
+ public int MaxMediaSizeBytes { get; set; } = 15 * 1024 * 1024;
}
public class ChatsConfig
{
public bool SupportGroups { get; set; } = true;
- public int MaxGroupParticipants { get; set; } = 200000; // 5 to 1,000,000
+ public int MaxGroupParticipants { get; set; } = 200000;
public bool AutoCleanChats { get; set; } = false;
public bool AllowChatToGroupConversion { get; set; } = true;
public bool EnableFolders { get; set; } = true;
@@ -36,13 +32,11 @@ public class ChatsConfig
public class MessagesConfig
{
- public int DailyMessageLimitPerUser { get; set; } = 0; // 0 = unlimited
- public int ChatMessageLimit { get; set; } = 0; // 0 = unlimited
-
+ public int DailyMessageLimitPerUser { get; set; } = 0;
+ public int ChatMessageLimit { get; set; } = 0;
public bool AllowMedia { get; set; } = true;
- public int MaxMediaSizeBytes { get; set; } = 50 * 1024 * 1024; // 50MB
+ public int MaxMediaSizeBytes { get; set; } = 50 * 1024 * 1024;
public List AllowedMediaTypes { get; set; } = new() { "image/jpeg", "image/png", "video/mp4", "image/gif" };
-
public bool AllowVoiceMessages { get; set; } = true;
public bool AllowForwarding { get; set; } = true;
public bool AllowReactions { get; set; } = true;
@@ -99,9 +93,9 @@ public class RemoteCapabilities
public class FederationConfig
{
public bool Enabled { get; set; } = false;
- public string ServerDescription { get; set; } = string.Empty; // 20 to 120 chars
- public string? PrivateKey { get; set; } // RSA PEM
- public string? PublicKey { get; set; } // RSA PEM
+ public string ServerDescription { get; set; } = string.Empty;
+ public string? PrivateKey { get; set; }
+ public string? PublicKey { get; set; }
public List AllowedDomains { get; set; } = new();
}
diff --git a/backend/src/Modules/Settings/Application/Config/Queries/GetPublicConfigQuery.cs b/backend/src/Modules/Settings/Application/Settings/Queries/GetPublicConfigQuery.cs
similarity index 66%
rename from backend/src/Modules/Settings/Application/Config/Queries/GetPublicConfigQuery.cs
rename to backend/src/Modules/Settings/Application/Settings/Queries/GetPublicConfigQuery.cs
index 214811d..2e394df 100644
--- a/backend/src/Modules/Settings/Application/Config/Queries/GetPublicConfigQuery.cs
+++ b/backend/src/Modules/Settings/Application/Settings/Queries/GetPublicConfigQuery.cs
@@ -1,16 +1,13 @@
-using Knot.Modules.Settings.Application.Config.DTOs;
-using Knot.Shared.Kernel;
-using Knot.Shared.Kernel.Configuration;
-using MediatR;
-using Knot.Modules.Auth.Application.Auth.DTOs;
-
-using Knot.Modules.Auth.Application.Users;
using System.Threading;
using System.Threading.Tasks;
+using MediatR;
+using Knot.Shared.Kernel;
+using Knot.Modules.Settings.Application.Settings.Abstractions;
+using Knot.Modules.Settings.Application.Settings.DTOs;
-namespace Host.Application.Config.Queries;
+namespace Knot.Modules.Settings.Application.Settings.Queries;
-public record GetPublicConfigQuery : IQuery;
+public record GetPublicConfigQuery() : IQuery;
internal sealed class GetPublicConfigQueryHandler : IQueryHandler
{
@@ -25,4 +22,4 @@ internal sealed class GetPublicConfigQueryHandler : IQueryHandler;
+
+internal sealed class GetSettingsQueryHandler : IQueryHandler
+{
+ private readonly ISettingsService _settingsService;
+
+ public GetSettingsQueryHandler(ISettingsService settingsService)
+ {
+ _settingsService = settingsService;
+ }
+
+ public async Task> Handle(GetSettingsQuery request, CancellationToken cancellationToken)
+ {
+ var settings = await _settingsService.GetSettingsAsync(cancellationToken);
+ return Result.Success(settings);
+ }
+}
diff --git a/backend/src/Modules/Settings/DependencyInjection.cs b/backend/src/Modules/Settings/DependencyInjection.cs
index 4adfa41..c5a9a78 100644
--- a/backend/src/Modules/Settings/DependencyInjection.cs
+++ b/backend/src/Modules/Settings/DependencyInjection.cs
@@ -1,7 +1,26 @@
-namespace Knot.Modules.Settings;
using Microsoft.Extensions.DependencyInjection;
-public static class DependencyInjection {
- public static IServiceCollection AddSettingsModule(this IServiceCollection services) {
+using Microsoft.Extensions.Configuration;
+using Knot.Modules.Settings.Application.Settings.Abstractions;
+using Knot.Modules.Settings.Infrastructure.Configuration;
+
+namespace Knot.Modules.Settings;
+
+public static class DependencyInjection
+{
+ public static IServiceCollection AddSettingsModule(this IServiceCollection services, IConfiguration configuration)
+ {
+ services.AddSingleton();
+
+ services.AddSingleton(sp => sp.GetRequiredService());
+ services.AddSingleton(sp => sp.GetRequiredService());
+ services.AddSingleton(sp => sp.GetRequiredService());
+ services.AddSingleton(sp => sp.GetRequiredService());
+ services.AddSingleton(sp => sp.GetRequiredService());
+ services.AddSingleton(sp => sp.GetRequiredService());
+ services.AddSingleton(sp => sp.GetRequiredService());
+ services.AddSingleton(sp => sp.GetRequiredService());
+ services.AddSingleton(sp => sp.GetRequiredService());
+
return services;
}
}
\ No newline at end of file
diff --git a/backend/src/Modules/Settings/Domain/Events/SystemSettingsUpdatedDomainEvent.cs b/backend/src/Modules/Settings/Domain/Events/SystemSettingsUpdatedDomainEvent.cs
new file mode 100644
index 0000000..68c3f1c
--- /dev/null
+++ b/backend/src/Modules/Settings/Domain/Events/SystemSettingsUpdatedDomainEvent.cs
@@ -0,0 +1,11 @@
+using Knot.Modules.Settings.Application.Settings.DTOs;
+using Knot.Shared.Kernel;
+
+namespace Knot.Modules.Settings.Domain.Events;
+
+///
+/// Domain Event: System settings updated.
+/// Owned by Settings module.
+///
+public sealed record SystemSettingsUpdatedDomainEvent(SystemSettingsDto Settings) : IDomainEvent;
+
diff --git a/backend/src/Shared/Knot.Shared.Infrastructure/Configuration/SettingsService.cs b/backend/src/Modules/Settings/Infrastructure/Configuration/SettingsService.cs
similarity index 88%
rename from backend/src/Shared/Knot.Shared.Infrastructure/Configuration/SettingsService.cs
rename to backend/src/Modules/Settings/Infrastructure/Configuration/SettingsService.cs
index 3a2c431..a3fa9ad 100644
--- a/backend/src/Shared/Knot.Shared.Infrastructure/Configuration/SettingsService.cs
+++ b/backend/src/Modules/Settings/Infrastructure/Configuration/SettingsService.cs
@@ -1,12 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
using System.Text.Json;
-using Knot.Shared.Infrastructure.Persistence;
-using Knot.Shared.Infrastructure.Persistence.Entities;
-using Knot.Shared.Kernel.Configuration;
-using Knot.Shared.Kernel.Security;
+using System.Threading;
+using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
+using Knot.Shared.Infrastructure.Persistence;
+using Knot.Shared.Infrastructure.Persistence.Entities;
+using Knot.Shared.Kernel.Security;
+using Knot.Modules.Settings.Application.Settings.Abstractions;
+using Knot.Modules.Settings.Application.Settings.DTOs;
-namespace Knot.Shared.Infrastructure.Configuration;
+namespace Knot.Modules.Settings.Infrastructure.Configuration;
public class SettingsService : ISettingsService,
ISystemSettings,
@@ -26,7 +32,7 @@ public class SettingsService : ISettingsService,
{
_serviceProvider = serviceProvider;
_encryptionService = encryptionService;
- _current = new SystemSettingsDto(); // Default
+ _current = new SystemSettingsDto();
}
public SystemSettingsDto Current => _current;
@@ -63,7 +69,7 @@ public class SettingsService : ISettingsService,
}
catch
{
- // If decryption fails or JSON is invalid, return default
+ // Fail safe
}
return new SystemSettingsDto();
@@ -88,7 +94,7 @@ public class SettingsService : ISettingsService,
}
await db.SaveChangesAsync(cancellationToken);
- _current = settings; // Update in-memory reference
+ _current = settings;
}
public void Initialize()
diff --git a/backend/src/Modules/Settings/Knot.Modules.Settings.csproj b/backend/src/Modules/Settings/Knot.Modules.Settings.csproj
index aedece0..c78e61a 100644
--- a/backend/src/Modules/Settings/Knot.Modules.Settings.csproj
+++ b/backend/src/Modules/Settings/Knot.Modules.Settings.csproj
@@ -7,11 +7,12 @@
-
-
-
+
-
\ No newline at end of file
+
+
+
+
diff --git a/backend/src/Modules/Settings/Presentation/Endpoints/SettingsEndpoints.cs b/backend/src/Modules/Settings/Presentation/Endpoints/SettingsEndpoints.cs
deleted file mode 100644
index ed43cef..0000000
--- a/backend/src/Modules/Settings/Presentation/Endpoints/SettingsEndpoints.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 SettingsEndpoints : 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);
- });
- }
-}
\ No newline at end of file
diff --git a/backend/src/Modules/Storage/Knot.Modules.Storage.csproj b/backend/src/Modules/Storage/Knot.Modules.Storage.csproj
index 7afe63f..a00fe29 100644
--- a/backend/src/Modules/Storage/Knot.Modules.Storage.csproj
+++ b/backend/src/Modules/Storage/Knot.Modules.Storage.csproj
@@ -17,6 +17,9 @@
+
+
+
diff --git a/backend/src/Modules/Storage/Presentation/Endpoints/FilesEndpoints.cs b/backend/src/Modules/Storage/Presentation/Endpoints/FilesEndpoints.cs
index 8e106d6..5ab0469 100644
--- a/backend/src/Modules/Storage/Presentation/Endpoints/FilesEndpoints.cs
+++ b/backend/src/Modules/Storage/Presentation/Endpoints/FilesEndpoints.cs
@@ -6,7 +6,8 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Mvc;
-using Knot.Shared.Kernel.Configuration;
+using Knot.Modules.Settings.Application.Settings.Abstractions;
+using Knot.Modules.Settings.Application.Settings.DTOs;
using System.Text;
using System.Security.Cryptography;
@@ -85,3 +86,4 @@ public sealed class FilesEndpoints : ICarterModule
});
}
}
+
diff --git a/backend/src/Modules/Stories/Application/Stories/Commands/CreateStory/CreateStoryCommand.cs b/backend/src/Modules/Stories/Application/Stories/Commands/CreateStory/CreateStoryCommand.cs
index d163a6b..0981b7b 100644
--- a/backend/src/Modules/Stories/Application/Stories/Commands/CreateStory/CreateStoryCommand.cs
+++ b/backend/src/Modules/Stories/Application/Stories/Commands/CreateStory/CreateStoryCommand.cs
@@ -3,11 +3,12 @@ using System.Threading;
using System.Threading.Tasks;
using MediatR;
using Knot.Shared.Kernel;
-using Knot.Shared.Kernel.Configuration;
+using Knot.Modules.Settings.Application.Settings.Abstractions;
+using Knot.Modules.Settings.Application.Settings.DTOs;
using Knot.Modules.Stories.Domain;
-namespace Host.Application.Stories.Commands.CreateStory;
+namespace Knot.Modules.Stories.Application.Stories.Commands.CreateStory;
public record CreateStoryCommand(Guid UserId, string Type, string? MediaUrl, string? Content, string? BgColor) : ICommand;
@@ -51,4 +52,3 @@ internal sealed class CreateStoryCommandHandler : ICommandHandler;
internal sealed class DeleteStoryCommandHandler : ICommandHandler
{
- private readonly IStoriesDbContext _context;
+ private readonly Knot.Modules.Stories.Application.Abstractions.IStoryRepository _repository;
- public DeleteStoryCommandHandler(IStoriesDbContext context)
+ public DeleteStoryCommandHandler(Knot.Modules.Stories.Application.Abstractions.IStoryRepository repository)
{
- _context = context;
+ _repository = repository;
}
public async Task> Handle(DeleteStoryCommand request, CancellationToken cancellationToken)
{
- var story = await _context.Stories.FindAsync(new object[] { request.StoryId }, cancellationToken);
+ var story = await _repository.GetByIdAsync(request.StoryId, cancellationToken);
if (story == null)
{
return Result.Failure(StoryErrors.StoryNotFound);
@@ -39,11 +39,12 @@ internal sealed class DeleteStoryCommandHandler : ICommandHandler(StoryErrors.Unauthorized);
}
- _context.Stories.Remove(story);
- await _context.SaveChangesAsync(cancellationToken);
+ await _repository.DeleteAsync(story, cancellationToken);
return Result.Success(new MessageResponse("Story deleted"));
}
}
+
+
diff --git a/backend/src/Modules/Stories/Application/Stories/Commands/ViewStory/ViewStoryCommand.cs b/backend/src/Modules/Stories/Application/Stories/Commands/ViewStory/ViewStoryCommand.cs
index d0c7170..347737e 100644
--- a/backend/src/Modules/Stories/Application/Stories/Commands/ViewStory/ViewStoryCommand.cs
+++ b/backend/src/Modules/Stories/Application/Stories/Commands/ViewStory/ViewStoryCommand.cs
@@ -1,4 +1,4 @@
-using Host.Application.Stories;
+using Knot.Modules.Stories.Application.Stories;
using System;
using System.Linq;
using System.Threading;
@@ -17,7 +17,7 @@ using Knot.Modules.Stories.Infrastructure.Database;
-namespace Host.Application.Stories.Commands.ViewStory;
+namespace Knot.Modules.Stories.Application.Stories.Commands.ViewStory;
public record ViewStoryCommand(Guid UserId, Guid StoryId) : ICommand;
@@ -87,3 +87,5 @@ internal sealed class ViewStoryCommandHandler : ICommandHandler>;
@@ -100,3 +100,4 @@ internal sealed class GetStoriesQueryHandler : IQueryHandler>;
@@ -75,3 +75,5 @@ internal sealed class GetStoryViewersQueryHandler : IQueryHandler;
@@ -77,3 +77,4 @@ internal sealed class GetUserStoriesQueryHandler : IQueryHandler
-
-
+
+
+
+
+
+
diff --git a/backend/src/Modules/Stories/Presentation/Endpoints/StoriesEndpoints.cs b/backend/src/Modules/Stories/Presentation/Endpoints/StoriesEndpoints.cs
index 502caf0..ab1de90 100644
--- a/backend/src/Modules/Stories/Presentation/Endpoints/StoriesEndpoints.cs
+++ b/backend/src/Modules/Stories/Presentation/Endpoints/StoriesEndpoints.cs
@@ -6,14 +6,14 @@ using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Mvc;
-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.DeleteStory;
+using Knot.Modules.Stories.Application.Stories.Queries.GetStories;
+using Knot.Modules.Stories.Application.Stories.Commands.CreateStory;
+using Knot.Modules.Stories.Application.Stories.Queries.GetUserStories;
+using Knot.Modules.Stories.Application.Stories.Commands.ViewStory;
+using Knot.Modules.Stories.Application.Stories.Queries.GetStoryViewers;
+using Knot.Modules.Stories.Application.Stories.Commands.DeleteStory;
-namespace Knot.Host.Presentation.Endpoints;
+namespace Knot.Modules.Stories.Presentation.Endpoints;
public sealed class StoriesEndpoints : ICarterModule
{
@@ -60,3 +60,5 @@ public sealed class StoriesEndpoints : ICarterModule
});
}
}
+
+
diff --git a/backend/src/Modules/TelegramImport/Application/TelegramImport/AnalyzeImportCommand.cs b/backend/src/Modules/TelegramImport/Application/TelegramImport/AnalyzeImportCommand.cs
index e61d838..d4ee4d2 100644
--- a/backend/src/Modules/TelegramImport/Application/TelegramImport/AnalyzeImportCommand.cs
+++ b/backend/src/Modules/TelegramImport/Application/TelegramImport/AnalyzeImportCommand.cs
@@ -11,7 +11,8 @@ using System.Threading.Tasks;
using AngleSharp.Html.Parser;
using AngleSharp.Dom;
using Knot.Shared.Kernel;
-using Knot.Shared.Kernel.Configuration;
+using Knot.Modules.Settings.Application.Settings.Abstractions;
+using Knot.Modules.Settings.Application.Settings.DTOs;
using MediatR;
namespace Knot.Modules.TelegramImport.Application.TelegramImport;
@@ -120,3 +121,4 @@ internal sealed class AnalyzeImportCommandHandler : ICommandHandler
+
+
@@ -13,3 +14,4 @@
+
diff --git a/backend/src/Modules/WebRtc/Application/WebRtc/Queries/GetIceServersQuery.cs b/backend/src/Modules/WebRtc/Application/WebRtc/Queries/GetIceServersQuery.cs
index 3ee4275..aa07b33 100644
--- a/backend/src/Modules/WebRtc/Application/WebRtc/Queries/GetIceServersQuery.cs
+++ b/backend/src/Modules/WebRtc/Application/WebRtc/Queries/GetIceServersQuery.cs
@@ -1,5 +1,6 @@
using Knot.Shared.Kernel;
-using Knot.Shared.Kernel.Configuration;
+using Knot.Modules.Settings.Application.Settings.Abstractions;
+using Knot.Modules.Settings.Application.Settings.DTOs;
using MediatR;
using System.Collections.Generic;
using System.Threading;
@@ -62,4 +63,4 @@ internal sealed class GetIceServersQueryHandler : IQueryHandler
+
-
\ No newline at end of file
+
+
diff --git a/backend/src/Shared/Knot.Shared.Infrastructure/DependencyInjection.cs b/backend/src/Shared/Knot.Shared.Infrastructure/DependencyInjection.cs
index bb518f5..d92ca89 100644
--- a/backend/src/Shared/Knot.Shared.Infrastructure/DependencyInjection.cs
+++ b/backend/src/Shared/Knot.Shared.Infrastructure/DependencyInjection.cs
@@ -4,9 +4,7 @@ using Knot.Shared.Kernel;
using Knot.Shared.Kernel.Security;
using Knot.Shared.Kernel.Storage;
using Knot.Shared.Infrastructure.Persistence;
-using Knot.Shared.Infrastructure.Configuration;
using Knot.Shared.Infrastructure.Statistics;
-using Knot.Shared.Kernel.Configuration;
using Microsoft.EntityFrameworkCore;
using Minio;
using System;
@@ -38,17 +36,6 @@ public static class DependencyInjection
services.AddMemoryCache();
- services.AddSingleton();
- services.AddSingleton(sp => sp.GetRequiredService());
- services.AddSingleton(sp => sp.GetRequiredService());
- services.AddSingleton(sp => sp.GetRequiredService());
- services.AddSingleton(sp => sp.GetRequiredService());
- services.AddSingleton(sp => sp.GetRequiredService());
- services.AddSingleton(sp => sp.GetRequiredService());
- services.AddSingleton(sp => sp.GetRequiredService());
- services.AddSingleton(sp => sp.GetRequiredService());
- services.AddSingleton(sp => sp.GetRequiredService());
-
services.AddScoped();
services.AddHostedService();
diff --git a/backend/src/Shared/Knot.Shared.Infrastructure/Knot.Shared.Infrastructure.csproj b/backend/src/Shared/Knot.Shared.Infrastructure/Knot.Shared.Infrastructure.csproj
index 2b8a639..512c35b 100644
--- a/backend/src/Shared/Knot.Shared.Infrastructure/Knot.Shared.Infrastructure.csproj
+++ b/backend/src/Shared/Knot.Shared.Infrastructure/Knot.Shared.Infrastructure.csproj
@@ -28,3 +28,5 @@
+
+
diff --git a/backend/src/Shared/Knot.Shared.Kernel/Events/IdentityEvents.cs b/backend/src/Shared/Knot.Shared.Kernel/Events/IdentityEvents.cs
new file mode 100644
index 0000000..5f0b6d6
--- /dev/null
+++ b/backend/src/Shared/Knot.Shared.Kernel/Events/IdentityEvents.cs
@@ -0,0 +1,15 @@
+using System;
+using Knot.Shared.Kernel;
+using MediatR;
+
+namespace Knot.Shared.Kernel.Events;
+
+///
+/// Domain event published when a new user is registered in the Identity system.
+/// Handled by other modules to initialize their own data (e.g. Profiles, Notifications).
+///
+public sealed record UserRegisteredDomainEvent(
+ Guid UserId,
+ string Username,
+ string DisplayName,
+ string? Bio) : IDomainEvent;
diff --git a/backend/src/Shared/Knot.Shared.Kernel/Knot.Shared.Kernel.csproj b/backend/src/Shared/Knot.Shared.Kernel/Knot.Shared.Kernel.csproj
index f32bac9..6557bcc 100644
--- a/backend/src/Shared/Knot.Shared.Kernel/Knot.Shared.Kernel.csproj
+++ b/backend/src/Shared/Knot.Shared.Kernel/Knot.Shared.Kernel.csproj
@@ -11,3 +11,4 @@
+