Compare commits
95
Commits
89b2eeea6c
...
android_v2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a426b63b7d | ||
|
|
4d4bc8edd1 | ||
|
|
40589dbb75 | ||
|
|
0b013def2e | ||
|
|
ccffc5a53c | ||
|
|
e8161d23d4 | ||
|
|
89e325556c | ||
|
|
df4feeeee5 | ||
|
|
00ed4b5959 | ||
|
|
a0b50b57b0 | ||
|
|
d4c41b333a | ||
|
|
9daa786cfb | ||
|
|
2d2e4b685e | ||
|
|
46c22300e9 | ||
|
|
d9a20e40b0 | ||
|
|
945134f029 | ||
|
|
f6400ce3ac | ||
|
|
d9462069e2 | ||
|
|
9e715fe3ab | ||
|
|
8c8ef55b58 | ||
|
|
70acad56fb | ||
|
|
629fddfca0 | ||
|
|
b68f68a1f2 | ||
|
|
754e9e8ad0 | ||
|
|
ed7521a563 | ||
|
|
bef30c2c86 | ||
|
|
8c00d1376d | ||
|
|
cea3f4d669 | ||
|
|
8409c51842 | ||
|
|
9560a9235f | ||
|
|
f17edfc0da | ||
|
|
c6bebec599 | ||
|
|
5a71e5bbfa | ||
|
|
5b0133d55e | ||
|
|
dcb733ac01 | ||
|
|
487cb1b12b | ||
|
|
58fdf1aca1 | ||
|
|
118f8b8971 | ||
|
|
8165b74e43 | ||
|
|
8ce4bc714f | ||
|
|
2d0bc0d75c | ||
|
|
d7e75797ef | ||
|
|
58bbdae26c | ||
|
|
3310a3c4a4 | ||
|
|
d8b0d86534 | ||
|
|
dc051fa9ae | ||
|
|
1fb1be47dd | ||
|
|
8399d32490 | ||
|
|
3905094ff4 | ||
|
|
9e8625aea1 | ||
|
|
5b905c94da | ||
|
|
0eecc01374 | ||
|
|
c37e1723d4 | ||
|
|
02043d4d97 | ||
|
|
852efa090e | ||
|
|
c45f4db61c | ||
|
|
02a85fc587 | ||
|
|
e09860700c | ||
|
|
32c9bc43cf | ||
|
|
d96e4ec7d4 | ||
|
|
1558b20470 | ||
|
|
fa185afc73 | ||
|
|
90096ce2bc | ||
|
|
65d5f5fee9 | ||
|
|
e11240f78f | ||
|
|
53f193970c | ||
|
|
f41ad0bcf8 | ||
|
|
b49ff47762 | ||
|
|
182bbe2ad8 | ||
|
|
a04f04a448 | ||
|
|
2ab5b295e8 | ||
|
|
1b8abbc995 | ||
|
|
943699139b | ||
|
|
757142eda7 | ||
|
|
a42007df2d | ||
|
|
9df7d7aaf1 | ||
|
|
4ae7dd60ce | ||
|
|
249c344df8 | ||
|
|
2c6d6f831f | ||
|
|
bce8f92101 | ||
|
|
aa69c44a64 | ||
|
|
4faa7561a0 | ||
|
|
8025340e45 | ||
|
|
60d3cf12ae | ||
|
|
b09e197626 | ||
|
|
f1f8f6e012 | ||
|
|
d3f1e3f361 | ||
|
|
ce212c11c1 | ||
|
|
143c0b7dc4 | ||
|
|
cb93ff7240 | ||
|
|
09e5cbaa76 | ||
|
|
465e84e686 | ||
|
|
44d59dab2a | ||
|
|
b719c50413 | ||
|
|
9438cf1b35 |
+10
@@ -87,3 +87,13 @@ postgres_data/
|
||||
tmp/
|
||||
|
||||
*.txt
|
||||
|
||||
# Android / Kotlin Mobile
|
||||
client-mobile/.gradle/
|
||||
client-mobile/.idea/
|
||||
client-mobile/.run/
|
||||
client-mobile/build/
|
||||
client-mobile/.cxx/
|
||||
client-mobile/local.properties
|
||||
client-mobile/*.iml
|
||||
client-mobile/.kotlin/
|
||||
|
||||
@@ -65,112 +65,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WebRtc", "WebRtc", "{DD6FAF
|
||||
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
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Contracts", "Contracts", "{FFBE31B8-50B9-B415-22BC-2F92C97CADA3}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Admin", "Admin", "{1C8A9B45-FCC1-A5B3-3601-CB39D7CED4E3}"
|
||||
@@ -415,18 +311,6 @@ 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
|
||||
@@ -439,222 +323,6 @@ Global
|
||||
{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
|
||||
{D3C7D267-5646-4907-A451-E7CFA853BBF5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D3C7D267-5646-4907-A451-E7CFA853BBF5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D3C7D267-5646-4907-A451-E7CFA853BBF5}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
@@ -846,58 +514,7 @@ Global
|
||||
{AEFD93B4-59B8-4B02-A694-CA15E4EC1C08} = {0587C5BB-3015-204D-19B6-EB01E03E48B6}
|
||||
{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}
|
||||
{FFBE31B8-50B9-B415-22BC-2F92C97CADA3} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
|
||||
{1C8A9B45-FCC1-A5B3-3601-CB39D7CED4E3} = {FFBE31B8-50B9-B415-22BC-2F92C97CADA3}
|
||||
{D3C7D267-5646-4907-A451-E7CFA853BBF5} = {1C8A9B45-FCC1-A5B3-3601-CB39D7CED4E3}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using FluentAssertions;
|
||||
using Knot.Modules.Admin.Application.Admin.Commands;
|
||||
using Knot.Contracts.Auth.Application.Abstractions;
|
||||
using Knot.Contracts.Auth.Domain;
|
||||
using Knot.Modules.Auth.Application.Abstractions;
|
||||
using Knot.Modules.Auth.Domain;
|
||||
using Knot.Shared.Kernel;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using FluentAssertions;
|
||||
using NSubstitute;
|
||||
using Knot.Contracts.Auth.Application.Abstractions;
|
||||
using Knot.Modules.Auth.Application.Abstractions;
|
||||
using Knot.Modules.Auth.Application.Users.Login;
|
||||
using Knot.Contracts.Auth.Domain;
|
||||
using Knot.Modules.Auth.Domain;
|
||||
using Knot.Shared.Kernel;
|
||||
using Knot.Modules.Auth.Application.Users.Auth;
|
||||
using Xunit;
|
||||
|
||||
@@ -7,9 +7,8 @@ using FluentAssertions;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
using Knot.Shared.Kernel;
|
||||
using Knot.Modules.Conversations.Domain;
|
||||
using Knot.Modules.Messaging.Domain;
|
||||
using Knot.Modules.Conversations.Application.Abstractions;
|
||||
using Knot.Contracts.Conversations.Domain;
|
||||
using Knot.Contracts.Conversations.Application.Abstractions;
|
||||
using Knot.Modules.Conversations.Application.Chats.Create;
|
||||
|
||||
namespace Knot.Modules.Conversations.UnitTests.Chats;
|
||||
|
||||
@@ -7,9 +7,10 @@ using FluentAssertions;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
using Knot.Shared.Kernel;
|
||||
using Knot.Modules.Conversations.Domain;
|
||||
using Knot.Modules.Messaging.Domain;
|
||||
using Knot.Modules.Conversations.Application.Abstractions;
|
||||
using Knot.Contracts.Conversations.Domain;
|
||||
using Knot.Contracts.Conversations.Application.Abstractions;
|
||||
using Knot.Contracts.Messaging.Application.Abstractions;
|
||||
using Knot.Contracts.Messaging.Domain;
|
||||
using Knot.Modules.Conversations.Application.Chats.GetChats;
|
||||
using Knot.Modules.Conversations.Application.DTOs;
|
||||
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
using FluentAssertions;
|
||||
using NSubstitute;
|
||||
using Knot.Modules.Conversations.Application.Abstractions;
|
||||
using Knot.Contracts.Conversations.Application.Abstractions;
|
||||
using Knot.Modules.Conversations.Application.Chats.GetOrCreateFavorites;
|
||||
using Knot.Modules.Conversations.Domain;
|
||||
using Knot.Modules.Messaging.Domain;
|
||||
using Knot.Contracts.Conversations.Domain;
|
||||
using Knot.Shared.Kernel;
|
||||
using Xunit;
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\src\Modules\Chats\Knot.Modules.Conversations.csproj" />
|
||||
<ProjectReference Include="..\..\..\src\Contracts\Conversations\Knot.Contracts.Conversations.csproj" />
|
||||
<ProjectReference Include="..\..\..\src\Modules\Messaging\Knot.Modules.Messaging.csproj" />
|
||||
<ProjectReference Include="..\..\..\src\Modules\Conversations\Knot.Modules.Conversations.csproj" />
|
||||
<ProjectReference Include="..\..\..\src\Modules\Settings\Knot.Modules.Settings.csproj" />
|
||||
|
||||
@@ -5,12 +5,14 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using MediatR;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
using Knot.Contracts.Conversations.Application.Abstractions;
|
||||
using Knot.Contracts.Conversations.Domain;
|
||||
using Knot.Contracts.Messaging.Application.Abstractions;
|
||||
using Knot.Contracts.Messaging.Domain;
|
||||
using Knot.Shared.Kernel;
|
||||
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.Contracts.Settings.Application.Abstractions;
|
||||
using Knot.Contracts.Settings.Application.DTOs;
|
||||
@@ -24,6 +26,8 @@ public class SendMessageCommandHandlerTests
|
||||
private readonly IChatsUnitOfWork _unitOfWork;
|
||||
private readonly IMediator _mediator;
|
||||
private readonly IMessagesSettings _messagesSettings;
|
||||
private readonly IIdempotencyStore _idempotencyStore;
|
||||
private readonly ILogger<SendMessageCommandHandler> _logger;
|
||||
private readonly SendMessageCommandHandler _handler;
|
||||
|
||||
public SendMessageCommandHandlerTests()
|
||||
@@ -33,11 +37,13 @@ public class SendMessageCommandHandlerTests
|
||||
_unitOfWork = Substitute.For<IChatsUnitOfWork>();
|
||||
_mediator = Substitute.For<IMediator>();
|
||||
_messagesSettings = Substitute.For<IMessagesSettings>();
|
||||
_idempotencyStore = Substitute.For<IIdempotencyStore>();
|
||||
_logger = Substitute.For<ILogger<SendMessageCommandHandler>>();
|
||||
|
||||
var config = new Knot.Contracts.Settings.Application.DTOs.MessagesConfig();
|
||||
var config = new MessagesConfig();
|
||||
_messagesSettings.Current.Returns(config);
|
||||
|
||||
_handler = new SendMessageCommandHandler(_chatRepository, _messageRepository, _unitOfWork, _mediator, _messagesSettings);
|
||||
_handler = new SendMessageCommandHandler(_chatRepository, _messageRepository, _unitOfWork, _mediator, _messagesSettings, _idempotencyStore, _logger);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -2,8 +2,8 @@ using FluentAssertions;
|
||||
using Knot.Modules.Stories.Application.Stories.Commands.CreateStory;
|
||||
using Knot.Modules.Stories.Domain;
|
||||
using Knot.Shared.Kernel;
|
||||
using Knot.Contracts.Settings.Application.Abstractions;
|
||||
using Knot.Contracts.Settings.Application.DTOs;
|
||||
using Knot.Modules.Settings.Application.Settings.Abstractions;
|
||||
using Knot.Modules.Settings.Application.Settings.DTOs;
|
||||
using Knot.Modules.Stories.Application.Abstractions;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
using FluentAssertions;
|
||||
using Knot.Modules.Profiles.Application.Profiles.UpdateProfile;
|
||||
using Knot.Contracts.Profiles.Domain;
|
||||
using Knot.Contracts.Profiles.Application.DTOs;
|
||||
using Knot.Modules.Profiles.Domain;
|
||||
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;
|
||||
|
||||
@@ -27,7 +27,7 @@ public class UpdateProfileCommandHandlerTests
|
||||
{
|
||||
// Arrange
|
||||
var command = new UpdateProfileCommand(Guid.NewGuid(), "FirstName", "Bio", null);
|
||||
_profileRepository.GetAsync(command.UserId, Arg.Any<CancellationToken>()).Returns((UserProfileDto?)null);
|
||||
_profileRepository.GetByIdAsync(command.UserId, Arg.Any<CancellationToken>()).Returns((ProfileDocument?)null);
|
||||
|
||||
// Act
|
||||
var result = await _handler.Handle(command, CancellationToken.None);
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
namespace Knot.Contracts.Admin.Abstractions;
|
||||
|
||||
/// <summary>
|
||||
/// Базовый класс для агрегатов - сущностей с бизнес-логикой и доменными событиями.
|
||||
/// </summary>
|
||||
public abstract class AggregateRoot<TId> : Entity<TId> where TId : notnull
|
||||
{
|
||||
private readonly List<IDomainEvent> _domainEvents = new();
|
||||
public IReadOnlyList<IDomainEvent> DomainEvents => _domainEvents.AsReadOnly();
|
||||
|
||||
protected AggregateRoot(TId id) : base(id) { }
|
||||
|
||||
protected void RaiseDomainEvent(IDomainEvent domainEvent)
|
||||
{
|
||||
_domainEvents.Add(domainEvent);
|
||||
}
|
||||
|
||||
public void ClearDomainEvents()
|
||||
{
|
||||
_domainEvents.Clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
namespace Knot.Contracts.Admin.Abstractions;
|
||||
|
||||
/// <summary>
|
||||
/// Базовый класс для сущностей с уникальным идентификатором.
|
||||
/// </summary>
|
||||
public abstract class Entity<TId> where TId : notnull
|
||||
{
|
||||
public TId Id { get; protected set; }
|
||||
|
||||
protected Entity(TId id)
|
||||
{
|
||||
Id = id;
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
if (obj is not Entity<TId> other)
|
||||
return false;
|
||||
|
||||
if (ReferenceEquals(this, other))
|
||||
return true;
|
||||
|
||||
if (GetType() != other.GetType())
|
||||
return false;
|
||||
|
||||
return Id.Equals(other.Id);
|
||||
}
|
||||
|
||||
public override int GetHashCode() => Id.GetHashCode();
|
||||
|
||||
public static bool operator ==(Entity<TId>? a, Entity<TId>? b)
|
||||
{
|
||||
if (a is null && b is null)
|
||||
return true;
|
||||
|
||||
if (a is null || b is null)
|
||||
return false;
|
||||
|
||||
return a.Equals(b);
|
||||
}
|
||||
|
||||
public static bool operator !=(Entity<TId>? a, Entity<TId>? b) => !(a == b);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using MediatR;
|
||||
|
||||
namespace Knot.Contracts.Admin.Abstractions;
|
||||
|
||||
public interface ICommand : IRequest<Result> { }
|
||||
|
||||
public interface ICommand<TResponse> : IRequest<Result<TResponse>> { }
|
||||
|
||||
public interface ICommandHandler<in TCommand> : IRequestHandler<TCommand, Result>
|
||||
where TCommand : ICommand
|
||||
{ }
|
||||
|
||||
public interface ICommandHandler<in TCommand, TResponse> : IRequestHandler<TCommand, Result<TResponse>>
|
||||
where TCommand : ICommand<TResponse>
|
||||
{ }
|
||||
@@ -0,0 +1,8 @@
|
||||
using MediatR;
|
||||
|
||||
namespace Knot.Contracts.Admin.Abstractions;
|
||||
|
||||
/// <summary>
|
||||
/// Интерфейс для доменных событий.
|
||||
/// </summary>
|
||||
public interface IDomainEvent : INotification { }
|
||||
@@ -0,0 +1,13 @@
|
||||
using MediatR;
|
||||
|
||||
namespace Knot.Contracts.Admin.Abstractions;
|
||||
|
||||
public interface IQuery<TResponse> : IRequest<Result<TResponse>>
|
||||
{
|
||||
}
|
||||
|
||||
public interface IQueryHandler<TQuery, TResponse>
|
||||
: IRequestHandler<TQuery, Result<TResponse>>
|
||||
where TQuery : IQuery<TResponse>
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
namespace Knot.Contracts.Admin.Abstractions;
|
||||
|
||||
public record MessageResponse(string Message);
|
||||
@@ -0,0 +1,63 @@
|
||||
namespace Knot.Contracts.Admin.Abstractions;
|
||||
|
||||
/// <summary>
|
||||
/// Представляет ошибку в доменной логике.
|
||||
/// </summary>
|
||||
public sealed record Error(string Code, string Description)
|
||||
{
|
||||
public static readonly Error None = new(string.Empty, string.Empty);
|
||||
public static Error NotFound(string code, string description) => new(code, description);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Общая обертка для результата операции. Позволяет избегать использования исключений для управления потоком.
|
||||
/// </summary>
|
||||
public class Result
|
||||
{
|
||||
public bool IsSuccess { get; }
|
||||
public bool IsFailure => !IsSuccess;
|
||||
public Error Error { get; }
|
||||
|
||||
protected Result(bool isSuccess, Error error)
|
||||
{
|
||||
if (isSuccess && error != Error.None)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
if (!isSuccess && error == Error.None)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
IsSuccess = isSuccess;
|
||||
Error = error;
|
||||
}
|
||||
|
||||
public static Result Success() => new(true, Error.None);
|
||||
public static Result Failure(Error error) => new(false, error);
|
||||
|
||||
public static Result<TValue> Success<TValue>(TValue value) => Result<TValue>.Success(value);
|
||||
public static Result<TValue> Failure<TValue>(Error error) => Result<TValue>.Failure(error);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Результ операции, содержащий значение.
|
||||
/// </summary>
|
||||
public class Result<TValue> : Result
|
||||
{
|
||||
private readonly TValue? _value;
|
||||
|
||||
protected internal Result(TValue? value, bool isSuccess, Error error)
|
||||
: base(isSuccess, error)
|
||||
{
|
||||
_value = value;
|
||||
}
|
||||
|
||||
public TValue Value => IsSuccess
|
||||
? _value!
|
||||
: throw new InvalidOperationException("Нельзя получить значение ошибочного результата.");
|
||||
|
||||
public static Result<TValue> Success(TValue value) => new(value, true, Error.None);
|
||||
public new static Result<TValue> Failure(Error error) => new(default, false, error);
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
namespace Knot.Contracts.Admin.Abstractions;
|
||||
|
||||
public record SuccessResponse(bool Success);
|
||||
@@ -6,10 +6,6 @@
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Shared\Knot.Shared.Kernel\Knot.Shared.Kernel.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MediatR" Version="12.0.1" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.4" />
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Knot.Contracts.Auth.Abstractions;
|
||||
|
||||
public interface IUserQueryService
|
||||
{
|
||||
Task<List<UserInfo>> GetAllUsersAsync(CancellationToken cancellationToken);
|
||||
Task<List<UserInfo>> SearchUsersAsync(string query, CancellationToken cancellationToken);
|
||||
}
|
||||
|
||||
public record UserInfo(Guid Id, string? Avatar, string? UserName, string? DisplayName, DateTime CreatedAt, string? PhoneNumber, string? Email);
|
||||
@@ -0,0 +1,7 @@
|
||||
using Knot.Shared.Kernel;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Knot.Contracts.Auth.Application.Abstractions;
|
||||
|
||||
public record GetUsersExistenceQuery(List<Guid> UserIds) : IQuery<List<Guid>>;
|
||||
@@ -1,4 +1,5 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Knot.Contracts.Auth.Domain;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Knot.Contracts.Auth.Application.Abstractions;
|
||||
|
||||
|
||||
@@ -7,5 +7,4 @@ public interface IAuthUnitOfWork : IUnitOfWork
|
||||
{
|
||||
IUserRepository UserRepository { get; }
|
||||
void Add(UserContract user);
|
||||
Task<int> SaveChangesAsync(CancellationToken ct = default);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ public interface IJwtTokenProvider
|
||||
{
|
||||
string GenerateAccessToken(Guid userId, string username);
|
||||
string GenerateRefreshToken();
|
||||
DateTime GetRefreshTokenExpiry();
|
||||
string Generate(Guid userId, string username, string displayName, string? avatar);
|
||||
string Generate(Domain.UserContract user);
|
||||
}
|
||||
|
||||
@@ -7,5 +7,7 @@ public static class AuthErrors
|
||||
public static Error IdentityInvalidCredentials => new("Auth.InvalidCredentials", "Invalid credentials");
|
||||
public static Error IdentityRegistrationDisabled => new("Auth.RegistrationDisabled", "Registration is disabled");
|
||||
public static Error IdentityUsernameNotUnique => new("Auth.UsernameNotUnique", "Username is already taken");
|
||||
public static Error IdentityRegistrationFailed => new("Auth.RegistrationFailed", "Failed to register user");
|
||||
public static Error RefreshTokenExpired => new("Auth.RefreshTokenExpired", "Refresh token has expired. Please login again.");
|
||||
public static Error UserNotFound => new("Auth.UserNotFound", "User not found");
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace Knot.Contracts.Auth.Application.Auth.DTOs;
|
||||
namespace Knot.Contracts.Auth.Application.Auth.DTOs;
|
||||
|
||||
public class AuthResponseDto
|
||||
{
|
||||
@@ -7,6 +7,7 @@ public class AuthResponseDto
|
||||
public Guid UserId { get; set; }
|
||||
public string Username { get; set; } = string.Empty;
|
||||
public string? DisplayName { get; set; }
|
||||
public string? Avatar { get; set; }
|
||||
}
|
||||
|
||||
public class ResetPasswordDto
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
using Knot.Contracts.Auth.Domain;
|
||||
using MediatR;
|
||||
|
||||
namespace Knot.Contracts.Auth.Application.Users.GetMe;
|
||||
|
||||
public record GetMeQuery() : IRequest<UserContract>;
|
||||
@@ -1,10 +0,0 @@
|
||||
using Knot.Contracts.Auth.Application.Auth.DTOs;
|
||||
using Knot.Shared.Kernel;
|
||||
using MediatR;
|
||||
|
||||
namespace Knot.Contracts.Auth.Application.Users.Login;
|
||||
|
||||
public record LoginUserCommand(
|
||||
string Username,
|
||||
string Password
|
||||
) : IRequest<Result<AuthResponseDto>>;
|
||||
@@ -1,14 +0,0 @@
|
||||
using Knot.Contracts.Auth.Application.Auth.DTOs;
|
||||
using Knot.Contracts.Auth.Domain;
|
||||
using Knot.Shared.Kernel;
|
||||
using MediatR;
|
||||
|
||||
namespace Knot.Contracts.Auth.Application.Users.Register;
|
||||
|
||||
public record RegisterUserCommand(
|
||||
string Username,
|
||||
string Password,
|
||||
string DisplayName,
|
||||
string? Email,
|
||||
string? Bio
|
||||
) : IRequest<Result<AuthResponseDto>>;
|
||||
@@ -19,4 +19,12 @@ public class UserContract
|
||||
public bool IsExternal { get; set; }
|
||||
public string? Domain { get; set; }
|
||||
public DateTime? LastSeen { get; set; }
|
||||
public string? RefreshToken { get; set; }
|
||||
public DateTime? RefreshTokenExpiry { get; set; }
|
||||
|
||||
public void SetRefreshToken(string? refreshToken, DateTime? expiry = null)
|
||||
{
|
||||
RefreshToken = refreshToken;
|
||||
RefreshTokenExpiry = expiry;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
using Knot.Shared.Kernel;
|
||||
|
||||
namespace Knot.Contracts.Auth.Domain;
|
||||
|
||||
/// <summary>
|
||||
/// Доменное событие: изменился статус пользователя (онлайн/офлайн).
|
||||
/// </summary>
|
||||
public sealed record UserStatusChangedDomainEvent(Guid UserId, bool IsOnline, DateTime LastSeen) : IDomainEvent;
|
||||
@@ -0,0 +1,13 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Knot.Contracts.Auth.Domain;
|
||||
|
||||
namespace Knot.Contracts.Auth.Infrastructure.Persistence;
|
||||
|
||||
public interface IAuthDbContext
|
||||
{
|
||||
IQueryable<UserContract> Users { get; }
|
||||
Task<int> SaveChangesAsync(CancellationToken cancellationToken = default);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Knot.Contracts.Cleanup.Abstractions;
|
||||
|
||||
public interface ICleanupService
|
||||
{
|
||||
Task<CleanupData> GetCleanupDataAsync(CancellationToken cancellationToken);
|
||||
Task CleanOrphanMessagesAsync(List<Guid> messageIds, CancellationToken cancellationToken);
|
||||
Task CleanOrphanFilesAsync(List<string> fileIds, CancellationToken cancellationToken);
|
||||
}
|
||||
|
||||
public record CleanupData(
|
||||
List<ChatCleanupInfo> Chats,
|
||||
List<MessageCleanupInfo> Messages,
|
||||
List<StoryCleanupInfo> Stories,
|
||||
List<UserCleanupInfo> Users,
|
||||
List<FileCleanupInfo> Files);
|
||||
|
||||
public record ChatCleanupInfo(Guid Id, string? Avatar);
|
||||
|
||||
public record MessageCleanupInfo(Guid Id, Guid ChatId, bool IsDeleted, string? MediaUrl);
|
||||
|
||||
public record StoryCleanupInfo(Guid Id, string? MediaUrl);
|
||||
|
||||
public record UserCleanupInfo(Guid Id, string? Avatar);
|
||||
|
||||
public record FileCleanupInfo(string FileId);
|
||||
@@ -0,0 +1,10 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Shared\Knot.Shared.Kernel\Knot.Shared.Kernel.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Knot.Contracts.Conversations.Abstractions;
|
||||
|
||||
public interface IChatQueryService
|
||||
{
|
||||
Task<List<ChatInfo>> GetAllChatsAsync(CancellationToken cancellationToken);
|
||||
}
|
||||
|
||||
public record ChatInfo(Guid Id, string? Avatar);
|
||||
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Knot.Shared.Kernel;
|
||||
|
||||
namespace Knot.Contracts.Conversations.Abstractions;
|
||||
|
||||
public interface IUserDeleterService
|
||||
{
|
||||
Task<Result> DeleteUserAsync(Guid userId, CancellationToken cancellationToken);
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Knot.Contracts.Conversations.Abstractions;
|
||||
|
||||
public interface IUserStatusService
|
||||
{
|
||||
bool IsUserOnline(string userId);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
using Knot.Shared.Kernel;
|
||||
|
||||
namespace Knot.Contracts.Conversations.Application.Abstractions;
|
||||
|
||||
public interface IChatsUnitOfWork : IUnitOfWork
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace Knot.Contracts.Conversations.Application.Abstractions;
|
||||
|
||||
/// <summary>
|
||||
/// Хранилище для обеспечения идемпотентности операций.
|
||||
/// Если ключ уже существует — возвращает сохранённый результат без повторного выполнения.
|
||||
/// </summary>
|
||||
public interface IIdempotencyStore
|
||||
{
|
||||
/// <summary>
|
||||
/// Возвращает сохранённый результат по ключу или выполняет factory, сохраняет и возвращает результат.
|
||||
/// </summary>
|
||||
Task<T> GetOrCreateAsync<T>(
|
||||
string key,
|
||||
Func<CancellationToken, Task<T>> factory,
|
||||
TimeSpan? expiration = null,
|
||||
CancellationToken cancellationToken = default);
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
|
||||
namespace Knot.Contracts.Conversations.Application.Abstractions;
|
||||
|
||||
public interface IStoryNotificationsClient
|
||||
{
|
||||
Task StoryViewed(Guid storyId, Guid userId);
|
||||
Task StoryReactionAdded(Guid storyId, Guid userId, string reaction);
|
||||
Task StoryReactionRemoved(Guid storyId, Guid userId);
|
||||
}
|
||||
|
||||
public interface IChatHubClient
|
||||
{
|
||||
Task MessageReceived(object message);
|
||||
Task MessageDeleted(Guid messageId);
|
||||
Task MessageEdited(Guid messageId, object newContent);
|
||||
Task ReactionAdded(Guid messageId, string reaction, Guid userId);
|
||||
Task ReactionRemoved(Guid messageId, string reaction, Guid userId);
|
||||
Task UserStatusChanged(Guid userId, bool isOnline);
|
||||
Task ChatUpdated(object chat);
|
||||
Task ChatDeleted(Guid chatId);
|
||||
Task UserJoinedChat(Guid chatId, Guid userId);
|
||||
Task UserLeftChat(Guid chatId, Guid userId);
|
||||
}
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
|
||||
namespace Knot.Modules.Conversations.Application.Abstractions;
|
||||
namespace Knot.Contracts.Conversations.Application.Abstractions;
|
||||
|
||||
public interface IUserStatusService
|
||||
{
|
||||
+13
-15
@@ -1,6 +1,6 @@
|
||||
using Knot.Shared.Kernel;
|
||||
|
||||
namespace Knot.Modules.Conversations.Domain;
|
||||
namespace Knot.Contracts.Conversations.Domain;
|
||||
|
||||
public sealed record ChatCreatedDomainEvent(Chat Chat) : IDomainEvent;
|
||||
public sealed record ChatMemberAddedDomainEvent(Guid ChatId, Guid UserId) : IDomainEvent;
|
||||
@@ -36,22 +36,23 @@ public sealed class Chat : AggregateRoot<Guid>
|
||||
public string? Avatar { get; private set; }
|
||||
public DateTime CreatedAt { get; private set; }
|
||||
public long LastMessageSequenceId { get; private set; }
|
||||
public bool IsImporting { get; private set; }
|
||||
public Guid? ImportJobId { get; private set; }
|
||||
|
||||
private readonly List<ChatMember> _members = new();
|
||||
public IReadOnlyCollection<ChatMember> Members => _members.AsReadOnly();
|
||||
|
||||
private Chat(Guid id, ChatType type, string? name, string? avatar, string? description = null) : base(id)
|
||||
private Chat(Guid id, ChatType type, string? name, string? avatar, string? description = null, bool isImporting = false, Guid? importJobId = null) : base(id)
|
||||
{
|
||||
Type = type;
|
||||
Name = name;
|
||||
Avatar = avatar;
|
||||
Description = description;
|
||||
CreatedAt = DateTime.UtcNow;
|
||||
IsImporting = isImporting;
|
||||
ImportJobId = importJobId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Создает личный чат между двумя пользователями.
|
||||
/// </summary>
|
||||
public static Chat CreatePersonal()
|
||||
{
|
||||
var chat = new Chat(Guid.NewGuid(), ChatType.Personal, null, null);
|
||||
@@ -59,9 +60,6 @@ public sealed class Chat : AggregateRoot<Guid>
|
||||
return chat;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Создает групповой чат.
|
||||
/// </summary>
|
||||
public static Chat CreateGroup(string name, string? avatar = null)
|
||||
{
|
||||
var chat = new Chat(Guid.NewGuid(), ChatType.Group, name, avatar);
|
||||
@@ -69,16 +67,18 @@ public sealed class Chat : AggregateRoot<Guid>
|
||||
return chat;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Фабричный метод для создания чата.
|
||||
/// </summary>
|
||||
public static Chat Create(string? name, ChatType type, string? avatar = null, string? description = null)
|
||||
public static Chat Create(string? name, ChatType type, string? avatar = null, string? description = null, bool isImporting = false, Guid? importJobId = null)
|
||||
{
|
||||
var chat = new Chat(Guid.NewGuid(), type, name, avatar, description);
|
||||
var chat = new Chat(Guid.NewGuid(), type, name, avatar, description, isImporting, importJobId);
|
||||
chat.RaiseDomainEvent(new ChatCreatedDomainEvent(chat));
|
||||
return chat;
|
||||
}
|
||||
|
||||
public void CompleteImport()
|
||||
{
|
||||
IsImporting = false;
|
||||
}
|
||||
|
||||
public void AddMember(Guid userId, string role = "member")
|
||||
{
|
||||
if (_members.Any(m => m.UserId == userId))
|
||||
@@ -127,7 +127,6 @@ public sealed class ChatMember : Entity<Guid>
|
||||
public long LastReadSequenceId { get; private set; }
|
||||
public Guid? LastDeliveredMessageId { get; private set; }
|
||||
|
||||
// For EF Core
|
||||
private ChatMember() : base(Guid.Empty) { Role = "member"; }
|
||||
|
||||
internal ChatMember(Guid chatId, Guid userId, string role) : base(Guid.NewGuid())
|
||||
@@ -154,4 +153,3 @@ public sealed class ChatMember : Entity<Guid>
|
||||
LastDeliveredMessageId = messageId;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Knot.Contracts.Conversations.Domain;
|
||||
|
||||
public static class ChatConstants
|
||||
{
|
||||
public const int DefaultMessageQueryLimit = 50;
|
||||
public const int MaxSharedMediaQueryLimit = 1000;
|
||||
public const int MaxGroupNameLength = 100;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using Knot.Shared.Kernel;
|
||||
|
||||
namespace Knot.Contracts.Conversations.Domain;
|
||||
|
||||
public static class ChatErrors
|
||||
{
|
||||
public static readonly Error ChatNotFound = new Error("Chat.NotFound", "Чат не найден");
|
||||
public static readonly Error OnlyOwnerCanUpdate = new Error("Chat.OnlyOwnerCanUpdate", "Только владелец может редактировать чат");
|
||||
public static readonly Error Unauthorized = new Error("Chat.Unauthorized", "Нет доступа к этому чату");
|
||||
public static readonly Error FoldersDisabled = new Error("Chat.FoldersDisabled", "Папки отключены");
|
||||
public static readonly Error FileEmpty = new Error("Chat.FileEmpty", "Файл пуст");
|
||||
public static Error FileTooLarge(long maxMb) => new Error("Chat.FileTooLarge", $"Файл слишком большой (максимум {maxMb} МБ)");
|
||||
public static readonly Error ChatsNotFound = new Error("Chat.NotFound", "Чат не найден");
|
||||
public static readonly Error ChatsForbidden = new Error("Chat.Forbidden", "Доступ запрещен");
|
||||
public static readonly Error MediaDisabled = new Error("Chat.MediaDisabled", "Медиафайлы отключены");
|
||||
public static readonly Error PollsDisabled = new Error("Chat.PollsDisabled", "Опросы отключены");
|
||||
public static readonly Error NotFound = new Error("Chat.NotFound", "Не найдено");
|
||||
public static readonly Error NotMember = new Error("Chat.NotMember", "Вы не являетесь участником чата");
|
||||
}
|
||||
+7
-14
@@ -2,7 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using Knot.Shared.Kernel;
|
||||
|
||||
namespace Knot.Modules.Conversations.Domain;
|
||||
namespace Knot.Contracts.Conversations.Domain;
|
||||
|
||||
/// <summary>
|
||||
/// Сущность папки для группировки чатов.
|
||||
@@ -10,7 +10,7 @@ namespace Knot.Modules.Conversations.Domain;
|
||||
public sealed class Folder : AggregateRoot<Guid>
|
||||
{
|
||||
public string Name { get; private set; }
|
||||
public string? Icon { get; private set; } // URL из хранилища
|
||||
public string? Icon { get; private set; }
|
||||
public bool IsDefault { get; private set; }
|
||||
public FolderType Type { get; private set; }
|
||||
|
||||
@@ -33,22 +33,20 @@ public sealed class Folder : AggregateRoot<Guid>
|
||||
|
||||
public enum FolderType
|
||||
{
|
||||
All, // Все чаты
|
||||
New, // Новые (с непрочитанными)
|
||||
Muted, // Без звука
|
||||
Custom // Пользовательская
|
||||
All,
|
||||
New,
|
||||
Muted,
|
||||
Custom
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Настройки конкретного чата для конкретного пользователя.
|
||||
/// Хранятся в PostgreSQL (связь User <-> Chat).
|
||||
/// </summary>
|
||||
public sealed class UserChatSettings : Entity<Guid>
|
||||
{
|
||||
public Guid UserId { get; private set; }
|
||||
public Guid ChatId { get; private set; }
|
||||
|
||||
// Список папок, в которые входит чат для этого пользователя
|
||||
private readonly List<Guid> _folderIds = new();
|
||||
public IReadOnlyCollection<Guid> FolderIds => _folderIds.AsReadOnly();
|
||||
|
||||
@@ -78,17 +76,12 @@ public sealed class UserChatSettings : Entity<Guid>
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Глобальные настройки папок пользователя (скрытие дефолтных и т.д.).
|
||||
/// Будет храниться в MongoDB.
|
||||
/// Глобальные настройки папок пользователя.
|
||||
/// </summary>
|
||||
public sealed class UserFolderSettings : AggregateRoot<Guid>
|
||||
{
|
||||
public Guid UserId { get; private set; }
|
||||
|
||||
// Список ID папок, которые пользователь скрыл (только для дефолтных)
|
||||
public List<Guid> HiddenDefaultFolderIds { get; private set; } = new();
|
||||
|
||||
// Список пользовательских папок (Guid созданных Folder)
|
||||
public List<Guid> CustomFolderIds { get; private set; } = new();
|
||||
|
||||
public UserFolderSettings(Guid userId) : base(Guid.NewGuid())
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
using Knot.Modules.Conversations.Domain;
|
||||
using Knot.Contracts.Conversations.Domain;
|
||||
|
||||
namespace Knot.Modules.Conversations.Domain;
|
||||
namespace Knot.Contracts.Conversations.Domain;
|
||||
|
||||
public interface IChatRepository
|
||||
{
|
||||
@@ -0,0 +1,18 @@
|
||||
using System.Linq;
|
||||
using Knot.Contracts.Conversations.Domain;
|
||||
|
||||
namespace Knot.Contracts.Conversations.Infrastructure.Persistence;
|
||||
|
||||
public interface IChatsDbContext
|
||||
{
|
||||
IQueryable<Chat> Chats { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Упрощенная проекция чата для запросов (не доменная сущность).
|
||||
/// </summary>
|
||||
public class Chat
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public string? Avatar { get; set; }
|
||||
}
|
||||
@@ -8,6 +8,11 @@
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Shared\Knot.Shared.Kernel\Knot.Shared.Kernel.csproj" />
|
||||
<ProjectReference Include="..\Messaging\Knot.Contracts.Messaging.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Knot.Contracts.Klipy.Abstractions;
|
||||
|
||||
public interface IKlipyClient
|
||||
{
|
||||
Task<bool> TestConnectionAsync(CancellationToken cancellationToken = default);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Knot.Contracts.Klipy.Application.Abstractions;
|
||||
|
||||
public interface IKlipyClient
|
||||
{
|
||||
Task<bool> TestConnectionAsync(string apiKey, string appName, CancellationToken cancellationToken = default);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Knot.Contracts.Messaging.Abstractions;
|
||||
|
||||
public interface IMessageQueryService
|
||||
{
|
||||
Task<List<MessageInfo>> GetAllMessagesAsync(CancellationToken cancellationToken);
|
||||
Task DeleteMessagesAsync(List<Guid> messageIds, CancellationToken cancellationToken);
|
||||
}
|
||||
|
||||
public record MessageInfo(Guid Id, Guid ChatId, bool IsDeleted, string? MediaUrl);
|
||||
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Knot.Contracts.Messaging.Abstractions;
|
||||
|
||||
public interface IUserStatsService
|
||||
{
|
||||
Task<Dictionary<Guid, UserStats>> GetStatsForUsersAsync(List<Guid> userIds, CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
||||
public record UserStats(int MessageCount, long StorageSize);
|
||||
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Knot.Contracts.Messaging.Application.Abstractions;
|
||||
|
||||
public interface IChatAccessProvider
|
||||
{
|
||||
Task<List<Guid>> GetValidChatIdsForUserAsync(Guid userId, CancellationToken ct);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace Knot.Contracts.Messaging.Application.Abstractions;
|
||||
|
||||
public interface IMessageNotifier
|
||||
{
|
||||
Task NotifyNewMessageAsync(Guid chatId, object messagePayload, CancellationToken cancellationToken);
|
||||
Task NotifyMessageUpdateAsync(Guid chatId, string updateType, object updatePayload, CancellationToken cancellationToken);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Knot.Contracts.Messaging.Application.Abstractions;
|
||||
|
||||
public interface IMessageQueryService
|
||||
{
|
||||
Task<List<MessageInfo>> GetAllMessagesAsync(CancellationToken cancellationToken);
|
||||
Task<List<MessageInfo>> GetOrphanedMessagesAsync(HashSet<Guid> activeChatIds, CancellationToken cancellationToken);
|
||||
Task DeleteMessagesAsync(List<Guid> messageIds, CancellationToken cancellationToken);
|
||||
}
|
||||
|
||||
public record MessageInfo(Guid Id, Guid ChatId, bool IsDeleted, string? MediaUrl, List<MediaInfo>? Media);
|
||||
|
||||
public record MediaInfo(string Url);
|
||||
@@ -0,0 +1,24 @@
|
||||
using Knot.Contracts.Messaging.Domain;
|
||||
|
||||
namespace Knot.Contracts.Messaging.Application.Abstractions;
|
||||
|
||||
public interface IMessageRepository
|
||||
{
|
||||
void Add(Message message);
|
||||
Task<Message?> GetByIdAsync(Guid id, CancellationToken cancellationToken);
|
||||
Task<List<Message>> GetChatMessagesAsync(Guid chatId, int limit, int offset, CancellationToken cancellationToken);
|
||||
Task<Message?> GetLatestChatMessageAsync(Guid chatId, CancellationToken cancellationToken);
|
||||
Task<List<Message>> GetPinnedMessagesAsync(Guid chatId, CancellationToken cancellationToken);
|
||||
Task<List<Message>> SearchMessagesAsync(string query, Guid? chatId, Guid requestingUserId, CancellationToken cancellationToken);
|
||||
|
||||
Task<List<Message>> GetChatMessagesCursorAsync(Guid chatId, DateTime? cursor, long? sequenceId, int limit, CancellationToken cancellationToken);
|
||||
Task<List<Message>> GetChatMessagesAroundAsync(Guid chatId, long sequenceId, int limit, CancellationToken cancellationToken);
|
||||
Task<Message?> GetLastStoryMessageAsync(Guid chatId, Guid storyId, CancellationToken cancellationToken);
|
||||
|
||||
Task UpdateAsync(Message message, CancellationToken cancellationToken);
|
||||
Task<List<Message>> GetAllMessagesAsync(CancellationToken cancellationToken);
|
||||
Task DeleteChatMessagesAsync(Guid chatId, CancellationToken cancellationToken);
|
||||
Task DeleteUserMessagesAsync(Guid userId, CancellationToken cancellationToken);
|
||||
Task RemoveAsync(Guid id, CancellationToken cancellationToken);
|
||||
Task DeleteAsync(Message message, CancellationToken cancellationToken);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Knot.Contracts.Messaging.Application.Abstractions;
|
||||
|
||||
public interface IUserStatsService
|
||||
{
|
||||
Task<Dictionary<Guid, UserStats>> GetStatsForUsersAsync(List<Guid> userIds, CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
||||
public record UserStats(int MessageCount, long StorageSize);
|
||||
@@ -0,0 +1,33 @@
|
||||
using System;
|
||||
|
||||
namespace Knot.Contracts.Messaging.Domain;
|
||||
|
||||
public class CallMessage : Message
|
||||
{
|
||||
public override string Type => "call";
|
||||
public override string? Content { get; protected set; }
|
||||
public string CallType { get; protected set; }
|
||||
public string CallStatus { get; protected set; }
|
||||
public int? Duration { get; protected set; }
|
||||
|
||||
public CallMessage() : base() { }
|
||||
|
||||
public CallMessage(
|
||||
Guid id,
|
||||
Guid chatId,
|
||||
Guid senderId,
|
||||
string callType,
|
||||
string callStatus,
|
||||
int? duration,
|
||||
Guid? replyToId,
|
||||
Guid? forwardedFromId,
|
||||
DateTime createdAt,
|
||||
bool isImported = false)
|
||||
: base(id, chatId, senderId, replyToId, forwardedFromId, createdAt, isImported)
|
||||
{
|
||||
CallType = callType;
|
||||
CallStatus = callStatus;
|
||||
Duration = duration;
|
||||
Content = $"Call {callStatus}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using Knot.Shared.Kernel;
|
||||
|
||||
namespace Knot.Contracts.Messaging.Domain;
|
||||
|
||||
/// <summary>
|
||||
/// Запись о том, что конкретный пользователь удалил у себя сообщение.
|
||||
/// </summary>
|
||||
public sealed class DeletedMessage : Entity<Guid>
|
||||
{
|
||||
public Guid MessageId { get; private set; }
|
||||
public Guid UserId { get; private set; }
|
||||
|
||||
internal DeletedMessage(Guid messageId, Guid userId) : base(Guid.NewGuid())
|
||||
{
|
||||
MessageId = messageId;
|
||||
UserId = userId;
|
||||
}
|
||||
|
||||
private DeletedMessage() : base(Guid.Empty) { }
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Knot.Contracts.Messaging.Domain;
|
||||
|
||||
public interface IMessageReactionRepository
|
||||
{
|
||||
Task AddAsync(MessageReaction reaction, CancellationToken cancellationToken);
|
||||
Task RemoveAsync(Guid messageId, Guid userId, string emoji, CancellationToken cancellationToken);
|
||||
Task<List<MessageReaction>> GetReactionsForMessageAsync(Guid messageId, CancellationToken cancellationToken);
|
||||
Task<List<MessageReaction>> GetReactionsForMessagesAsync(IEnumerable<Guid> messageIds, CancellationToken cancellationToken);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
|
||||
namespace Knot.Contracts.Messaging.Domain;
|
||||
|
||||
public class Media
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public string Type { get; set; } = string.Empty;
|
||||
public string? Url { get; set; }
|
||||
public string? ThumbnailUrl { get; set; }
|
||||
public long? Size { get; set; }
|
||||
public int Width { get; set; }
|
||||
public int Height { get; set; }
|
||||
public string? FileId { get; set; }
|
||||
public string? Filename { get; set; }
|
||||
public string? Duration { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Knot.Contracts.Messaging.Domain;
|
||||
|
||||
public class MediaMessage : Message
|
||||
{
|
||||
public override string Type => MediaType.ToString().ToLower();
|
||||
public override string? Content { get; protected set; }
|
||||
public string? Caption { get => Content; private set => Content = value; }
|
||||
public MediaType MediaType { get; private set; }
|
||||
|
||||
private List<Media> _media = new();
|
||||
public IReadOnlyCollection<Media> Media => _media.AsReadOnly();
|
||||
|
||||
private MediaMessage() : base() { MediaType = MediaType.File; }
|
||||
|
||||
public MediaMessage(Guid id, Guid chatId, Guid senderId, MediaType mediaType, string? caption, Guid? replyToId, Guid? forwardedFromId, DateTime createdAt, bool isImported)
|
||||
: base(id, chatId, senderId, replyToId, forwardedFromId, createdAt, isImported) { MediaType = mediaType; Caption = caption; }
|
||||
|
||||
public MediaMessage(Guid id, Guid chatId, Guid senderId, string mediaType, string? caption, Guid? replyToId, Guid? forwardedFromId, DateTime createdAt, bool isImported)
|
||||
: this(id, chatId, senderId, Enum.TryParse<MediaType>(mediaType, true, out var mt) ? mt : MediaType.File, caption, replyToId, forwardedFromId, createdAt, isImported) { }
|
||||
|
||||
public void AddMedia(string type, string url, string? filename, long? size, string? duration = null) => _media.Add(new Media { Type = type, Url = url, Filename = filename, FileId = filename, Size = size, Duration = duration });
|
||||
|
||||
public override void Edit(string newCaption) => base.Edit(newCaption);
|
||||
|
||||
public override void Delete() { Caption = null; base.Delete(); }
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Knot.Contracts.Messaging.Domain;
|
||||
|
||||
public enum MediaType
|
||||
{
|
||||
Image,
|
||||
Video,
|
||||
Voice,
|
||||
File
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Knot.Shared.Kernel;
|
||||
|
||||
namespace Knot.Contracts.Messaging.Domain;
|
||||
|
||||
public abstract class Message : AggregateRoot<Guid>
|
||||
{
|
||||
public Guid ChatId { get; protected set; }
|
||||
public Guid SenderId { get; protected set; }
|
||||
public DateTime CreatedAt { get; protected set; }
|
||||
public long SequenceId { get; protected set; }
|
||||
public void SetSequenceId(long sequenceId) => SequenceId = sequenceId;
|
||||
public Guid? ReplyToId { get; protected set; }
|
||||
public Guid? ForwardedFromId { get; protected set; }
|
||||
public MessageState State { get; protected set; }
|
||||
public abstract string Type { get; }
|
||||
public abstract string? Content { get; protected set; }
|
||||
|
||||
public bool IsEdited => HasState(MessageState.IsEdited);
|
||||
public bool IsDeleted => HasState(MessageState.IsDeleted);
|
||||
|
||||
protected List<DeletedMessage> _deletedFor = new();
|
||||
public IReadOnlyCollection<DeletedMessage> DeletedFor => _deletedFor.AsReadOnly();
|
||||
|
||||
protected Message() : base(Guid.Empty) { }
|
||||
|
||||
protected Message(Guid id, Guid chatId, Guid senderId, Guid? replyToId, Guid? forwardedFromId, DateTime createdAt, bool isImported)
|
||||
: base(id)
|
||||
{
|
||||
ChatId = chatId;
|
||||
SenderId = senderId;
|
||||
ReplyToId = replyToId;
|
||||
ForwardedFromId = forwardedFromId;
|
||||
CreatedAt = createdAt;
|
||||
if (isImported) AddState(MessageState.IsImported);
|
||||
}
|
||||
|
||||
public void AddState(MessageState state) => State |= state;
|
||||
public void RemoveState(MessageState state) => State &= ~state;
|
||||
public bool HasState(MessageState state) => (State & state) == state;
|
||||
public bool IsDeletedForUser(Guid userId) => _deletedFor.Exists(d => d.UserId == userId);
|
||||
|
||||
public virtual void Delete() => AddState(MessageState.IsDeleted);
|
||||
public virtual void Edit(string newContent)
|
||||
{
|
||||
Content = newContent;
|
||||
AddState(MessageState.IsEdited);
|
||||
}
|
||||
|
||||
public void DeleteForUser(Guid userId)
|
||||
{
|
||||
if (!_deletedFor.Exists(x => x.UserId == userId))
|
||||
_deletedFor.Add(new DeletedMessage(Id, userId));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using Knot.Shared.Kernel;
|
||||
|
||||
namespace Knot.Contracts.Messaging.Domain;
|
||||
|
||||
/// <summary>
|
||||
/// Доменное событие: сообщение отправлено.
|
||||
/// </summary>
|
||||
public sealed record MessageSentDomainEvent(Guid MessageId, Guid ChatId, Guid SenderId, string? Content) : IDomainEvent;
|
||||
|
||||
/// <summary>
|
||||
/// Доменное событие: сообщение удалено.
|
||||
/// </summary>
|
||||
public sealed record MessageDeletedDomainEvent(Guid MessageId, Guid ChatId) : IDomainEvent;
|
||||
|
||||
/// <summary>
|
||||
/// Доменное событие: сообщение отредактировано.
|
||||
/// </summary>
|
||||
public sealed record MessageEditedDomainEvent(Guid MessageId, Guid ChatId, string NewContent) : IDomainEvent;
|
||||
|
||||
/// <summary>
|
||||
/// Доменное событие: реакция добавлена.
|
||||
/// </summary>
|
||||
public sealed record MessageReactionAddedDomainEvent(Guid MessageId, Guid ChatId, Guid UserId, string Emoji) : IDomainEvent;
|
||||
|
||||
/// <summary>
|
||||
/// Доменное событие: реакция удалена.
|
||||
/// </summary>
|
||||
public sealed record MessageReactionRemovedDomainEvent(Guid MessageId, Guid ChatId, Guid UserId, string Emoji) : IDomainEvent;
|
||||
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
using Knot.Shared.Kernel;
|
||||
|
||||
namespace Knot.Contracts.Messaging.Domain;
|
||||
|
||||
public sealed class MessageReaction : AggregateRoot<Guid>
|
||||
{
|
||||
public Guid MessageId { get; private set; }
|
||||
public Guid UserId { get; private set; }
|
||||
public string Emoji { get; private set; }
|
||||
public DateTime CreatedAt { get; private set; }
|
||||
|
||||
private MessageReaction() : base(Guid.Empty)
|
||||
{
|
||||
Emoji = default!;
|
||||
}
|
||||
|
||||
public MessageReaction(Guid messageId, Guid userId, string emoji) : base(Guid.NewGuid())
|
||||
{
|
||||
MessageId = messageId;
|
||||
UserId = userId;
|
||||
Emoji = emoji;
|
||||
CreatedAt = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace Knot.Contracts.Messaging.Domain;
|
||||
|
||||
/// <summary>
|
||||
/// Состояние сообщения
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum MessageState
|
||||
{
|
||||
None = 0,
|
||||
IsEdited = 1,
|
||||
IsDeleted = 2,
|
||||
IsImported = 4,
|
||||
IsPinned = 8
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Knot.Contracts.Messaging.Domain;
|
||||
|
||||
public class PollMessage : Message
|
||||
{
|
||||
public override string Type => "poll";
|
||||
public override string? Content { get; protected set; }
|
||||
public List<PollOption> Options { get; set; } = new();
|
||||
public List<PollVote> Votes { get; set; } = new();
|
||||
public bool IsMultipleChoice { get; set; }
|
||||
public bool IsAnonymous { get; set; }
|
||||
public DateTime? ExpiresAt { get; set; }
|
||||
public bool IsClosed { get; set; }
|
||||
|
||||
public PollMessage() : base() { }
|
||||
|
||||
public PollMessage(Guid id, Guid chatId, Guid senderId, string? question, List<PollOption>? options, bool isAnonymous, bool isMultiple, DateTime? expiresAt, Guid? replyToId, Guid? forwardedFromId, DateTime createdAt, bool isImported)
|
||||
: base(id, chatId, senderId, replyToId, forwardedFromId, createdAt, isImported)
|
||||
{
|
||||
Content = question ?? "Poll";
|
||||
Options = options ?? new List<PollOption>();
|
||||
IsAnonymous = isAnonymous;
|
||||
IsMultipleChoice = isMultiple;
|
||||
ExpiresAt = expiresAt;
|
||||
}
|
||||
|
||||
public static PollMessage Create(Guid id, Guid chatId, Guid senderId, string? question, List<string> options, bool isAnonymous, bool isMultiple, DateTime? expiresAt, Guid? replyToId, Guid? forwardedFromId)
|
||||
{
|
||||
var poll = new PollMessage(id, chatId, senderId, question, null, isAnonymous, isMultiple, expiresAt, replyToId, forwardedFromId, DateTime.UtcNow, false);
|
||||
foreach (var opt in options)
|
||||
{
|
||||
poll.Options.Add(new PollOption { Text = opt });
|
||||
}
|
||||
return poll;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Knot.Contracts.Messaging.Domain;
|
||||
|
||||
public class PollOption
|
||||
{
|
||||
public Guid Id { get; set; } = Guid.NewGuid();
|
||||
public string Text { get; set; } = string.Empty;
|
||||
public int VoteCount { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using System;
|
||||
|
||||
namespace Knot.Contracts.Messaging.Domain;
|
||||
|
||||
public class PollVote
|
||||
{
|
||||
public Guid OptionId { get; set; }
|
||||
public Guid UserId { get; set; }
|
||||
public DateTime VotedAt { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using System;
|
||||
using Knot.Shared.Kernel;
|
||||
|
||||
namespace Knot.Contracts.Messaging.Domain;
|
||||
|
||||
public sealed class Reaction : Entity<Guid>
|
||||
{
|
||||
public Guid MessageId { get; private set; }
|
||||
public Guid UserId { get; private set; }
|
||||
public string Emoji { get; private set; }
|
||||
|
||||
internal Reaction(Guid messageId, Guid userId, string emoji) : base(Guid.NewGuid())
|
||||
{
|
||||
MessageId = messageId;
|
||||
UserId = userId;
|
||||
Emoji = emoji;
|
||||
}
|
||||
|
||||
private Reaction() : base(Guid.Empty)
|
||||
{
|
||||
Emoji = string.Empty;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
|
||||
namespace Knot.Contracts.Messaging.Domain;
|
||||
|
||||
public class StoryMessage : Message
|
||||
{
|
||||
public override string Type => "story";
|
||||
public override string? Content { get; protected set; }
|
||||
public Guid? StoryId { get; private set; }
|
||||
public string? StoryMediaUrl { get; private set; }
|
||||
public string? StoryMediaType { get; private set; }
|
||||
|
||||
public StoryMessage() : base() { }
|
||||
|
||||
public StoryMessage(Guid id, Guid chatId, Guid senderId, Guid storyId, string? storyMediaUrl, string? storyMediaType, DateTime createdAt)
|
||||
: base(id, chatId, senderId, null, null, createdAt, false)
|
||||
{
|
||||
StoryId = storyId;
|
||||
StoryMediaUrl = storyMediaUrl;
|
||||
StoryMediaType = storyMediaType;
|
||||
}
|
||||
|
||||
public StoryMessage(Guid id, Guid chatId, Guid senderId, Guid storyId, string? storyMediaUrl, string? storyMediaType, string? content, Guid? replyToId, Guid? forwardedFromId, DateTime createdAt, bool isImported)
|
||||
: this(id, chatId, senderId, storyId, storyMediaUrl, storyMediaType, createdAt)
|
||||
{
|
||||
Content = content;
|
||||
ReplyToId = replyToId;
|
||||
ForwardedFromId = forwardedFromId;
|
||||
if (isImported) AddState(MessageState.IsImported);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
|
||||
namespace Knot.Contracts.Messaging.Domain;
|
||||
|
||||
public class TextMessage : Message
|
||||
{
|
||||
public override string Type => "text";
|
||||
public override string? Content { get; protected set; }
|
||||
public string? Quote { get; protected set; }
|
||||
|
||||
public TextMessage() : base() { }
|
||||
|
||||
public TextMessage(Guid id, Guid chatId, Guid senderId, string content, Guid? replyToId, Guid? forwardedFromId, DateTime createdAt, bool isImported = false)
|
||||
: base(id, chatId, senderId, replyToId, forwardedFromId, createdAt, isImported) => Content = content;
|
||||
|
||||
public TextMessage(Guid id, Guid chatId, Guid senderId, string content, Guid? replyToId, string? quote, Guid? forwardedFromId, DateTime createdAt, bool isImported = false)
|
||||
: this(id, chatId, senderId, content, replyToId, forwardedFromId, createdAt, isImported) => Quote = quote;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Knot.Contracts.Messaging.Infrastructure.Persistence;
|
||||
|
||||
public interface IMongoMessageCollection
|
||||
{
|
||||
Task<List<Message>> GetAllAsync(CancellationToken cancellationToken);
|
||||
Task<List<Message>> GetOrphanedMessagesAsync(HashSet<Guid> activeChatIds, CancellationToken cancellationToken);
|
||||
Task DeleteManyAsync(List<Guid> messageIds, CancellationToken cancellationToken);
|
||||
}
|
||||
|
||||
public class Message
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public Guid ChatId { get; set; }
|
||||
public bool IsDeleted { get; set; }
|
||||
public string? MediaUrl { get; set; }
|
||||
public List<Media>? Media { get; set; }
|
||||
}
|
||||
|
||||
public class Media
|
||||
{
|
||||
public string Url { get; set; } = string.Empty;
|
||||
}
|
||||
@@ -1,14 +1,16 @@
|
||||
namespace Knot.Contracts.Profiles.Application.DTOs;
|
||||
namespace Knot.Contracts.Profiles.Application.DTOs;
|
||||
|
||||
public class UserProfileDto
|
||||
{
|
||||
public Guid UserId { get; set; }
|
||||
public Guid Id { get => UserId; set => UserId = value; }
|
||||
public string? DisplayName { get; set; }
|
||||
public string? Username { get; set; }
|
||||
public string? About { get; set; }
|
||||
public string? Avatar { get; set; }
|
||||
public bool IsBot { get; set; }
|
||||
public DateTime? LastSeen { get; set; }
|
||||
public DateTime? Birthday { get; set; }
|
||||
public bool IsPremium { get; set; }
|
||||
public DateTime CreatedAt { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Knot.Contracts.Profiles.Application.DTOs;
|
||||
using Knot.Contracts.Profiles.Application.DTOs;
|
||||
using Knot.Shared.Kernel;
|
||||
|
||||
namespace Knot.Contracts.Profiles.Domain;
|
||||
@@ -12,4 +12,5 @@ public interface IProfileRepository
|
||||
Task<Result<UserProfileDto>> CreateAsync(UserProfileDto dto, CancellationToken cancellationToken = default);
|
||||
Task<Result<UserProfileDto>> UpdateAsync(UserProfileDto dto, CancellationToken cancellationToken = default);
|
||||
Task<Result> DeleteAsync(Guid userId, CancellationToken cancellationToken = default);
|
||||
Task<Result> UpdateStatusAsync(Guid userId, bool isBanned, bool isDeleted, CancellationToken ct = default);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Knot.Contracts.Relations.Domain;
|
||||
|
||||
namespace Knot.Contracts.Relations.Application.Abstractions;
|
||||
|
||||
public interface IFriendshipRepository
|
||||
{
|
||||
Task<List<Friendship>> GetAcceptedFriendshipsAsync(Guid userId, CancellationToken ct = default);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
using Knot.Shared.Kernel;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Knot.Contracts.Relations.Application.Contacts;
|
||||
|
||||
public record CheckBlockedStatusQuery(Guid UserId, List<Guid> CandidateIds) : IQuery<List<Guid>>;
|
||||
@@ -0,0 +1,7 @@
|
||||
using Knot.Shared.Kernel;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Knot.Contracts.Relations.Application.Contacts;
|
||||
|
||||
public record GetBlockedUserIdsQuery(Guid UserId) : IQuery<List<Guid>>;
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace Knot.Contracts.Relations.Domain;
|
||||
|
||||
using System;
|
||||
|
||||
public record Friendship(
|
||||
Guid Id,
|
||||
Guid UserId,
|
||||
Guid FriendId,
|
||||
FriendshipStatus Status,
|
||||
DateTime CreatedAt
|
||||
);
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Knot.Contracts.Relations.Domain;
|
||||
|
||||
public enum FriendshipStatus
|
||||
{
|
||||
Pending,
|
||||
Accepted,
|
||||
Declined
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace Knot.Contracts.Relations.Domain;
|
||||
|
||||
using System;
|
||||
|
||||
public record UserReplica(
|
||||
Guid Id,
|
||||
string Username,
|
||||
string DisplayName,
|
||||
string Avatar,
|
||||
bool IsExternal,
|
||||
string? Domain
|
||||
);
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Knot.Contracts.Settings.Application.DTOs;
|
||||
|
||||
@@ -53,6 +53,7 @@ public class MessagesConfig
|
||||
public class WebRtcConfig
|
||||
{
|
||||
public bool Enabled { get; set; } = false;
|
||||
public bool EnableVoiceCalls { get; set; } = true;
|
||||
public bool EnableVideoCalls { get; set; } = true;
|
||||
public bool EnableScreenSharing { get; set; } = true;
|
||||
public string TurnHost { get; set; } = string.Empty;
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
using Knot.Shared.Kernel;
|
||||
|
||||
namespace Knot.Contracts.Settings.Domain;
|
||||
|
||||
/// <summary>
|
||||
/// Доменное событие: системные настройки обновлены.
|
||||
/// </summary>
|
||||
public sealed record SystemSettingsUpdatedDomainEvent(Application.DTOs.SystemSettingsDto Settings) : IDomainEvent;
|
||||
@@ -0,0 +1,25 @@
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Knot.Contracts.Storage.Abstractions;
|
||||
|
||||
public interface IFileStorageService
|
||||
{
|
||||
// Загружает поток файла и возвращает его уникальный идентификатор (SHA256 хеш или GUID).
|
||||
Task<string> UploadFileAsync(Stream stream, string fileName, string contentType);
|
||||
|
||||
// Скачивает файл и возвращает его расшифрованный поток и тип содержимого.
|
||||
Task<(Stream Stream, string ContentType, string FileName)> DownloadFileAsync(string fileId);
|
||||
|
||||
// Удаляет файл из хранилища.
|
||||
Task DeleteFileAsync(string fileId);
|
||||
|
||||
// Получает список всех файлов в хранилище с их размерами.
|
||||
Task<IEnumerable<(string FileId, long Size)>> ListFilesAsync();
|
||||
|
||||
// Получает размер конкретного файла
|
||||
Task<long> GetFileSizeAsync(string fileId, CancellationToken ct = default);
|
||||
|
||||
// Получает размеры списка файлов
|
||||
Task<Dictionary<string, long>> GetFileSizesAsync(IEnumerable<string> fileIds, CancellationToken ct = default);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Knot.Contracts.Stories.Abstractions;
|
||||
|
||||
public interface IStoryQueryService
|
||||
{
|
||||
Task<List<StoryInfo>> GetAllStoriesAsync(CancellationToken cancellationToken);
|
||||
}
|
||||
|
||||
public record StoryInfo(Guid Id, string? MediaUrl);
|
||||
@@ -0,0 +1,20 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Knot.Contracts.Stories.Infrastructure.Persistence;
|
||||
|
||||
public interface IStoryCollection
|
||||
{
|
||||
Task<List<StoryContract>> GetAllAsync(CancellationToken cancellationToken);
|
||||
}
|
||||
|
||||
public class StoryContract
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public Guid UserId { get; set; }
|
||||
public string? MediaUrl { get; set; }
|
||||
public string? MediaType { get; set; }
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public DateTime? ExpiresAt { get; set; }
|
||||
}
|
||||
@@ -13,6 +13,7 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MediatR" Version="12.0.1" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.4" />
|
||||
<PackageReference Include="MongoDB.Driver" Version="3.2.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AngleSharp" Version="1.4.0" />
|
||||
<PackageReference Include="Carter" Version="10.0.0" />
|
||||
<PackageReference Include="FluentValidation.DependencyInjectionExtensions" Version="12.1.1" />
|
||||
<PackageReference Include="HtmlAgilityPack" Version="1.12.4" />
|
||||
<PackageReference Include="Mapster" Version="7.4.0" />
|
||||
|
||||
+67
-27
@@ -1,31 +1,47 @@
|
||||
using Knot.Modules.Messaging;
|
||||
using Carter;
|
||||
using Knot.Shared.Infrastructure;
|
||||
using Knot.Modules.Auth;
|
||||
using Knot.Modules.Profiles;
|
||||
using Knot.Modules.Settings;
|
||||
using Knot.Modules.Admin;
|
||||
using Knot.Modules.Conversations;
|
||||
using Knot.Modules.Conversations.Infrastructure.SignalR;
|
||||
using Knot.Modules.Auth.Infrastructure.Persistence;
|
||||
using Knot.Modules.Conversations.Infrastructure.Persistence;
|
||||
using Knot.Modules.Storage;
|
||||
using Knot.Modules.Stories;
|
||||
using Knot.Modules.Klipy;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using System.Text;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Security.Claims;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
using Knot.Modules.Admin;
|
||||
using Knot.Modules.Admin.Presentation.Endpoints;
|
||||
using Knot.Modules.Auth;
|
||||
using Knot.Modules.Auth.Infrastructure.Persistence;
|
||||
using Knot.Modules.Auth.Presentation.Endpoints;
|
||||
using Knot.Modules.Conversations;
|
||||
using Knot.Modules.Conversations.Infrastructure.Persistence;
|
||||
using Knot.Modules.Conversations.Infrastructure.SignalR;
|
||||
using Knot.Modules.Conversations.Presentation.Endpoints;
|
||||
using Knot.Modules.Federation;
|
||||
using Knot.Modules.Federation.Presentation.Endpoints;
|
||||
using Knot.Modules.Klipy;
|
||||
using Knot.Modules.Klipy.Presentation.Endpoints;
|
||||
using Knot.Modules.Messaging;
|
||||
using Knot.Modules.Profiles;
|
||||
using Knot.Modules.Profiles.Presentation.Endpoints;
|
||||
using Knot.Modules.Relations;
|
||||
using Knot.Modules.Relations.Presentation.Endpoints;
|
||||
using Knot.Modules.Settings;
|
||||
using Knot.Modules.Settings.Presentation.Endpoints;
|
||||
using Knot.Modules.Storage;
|
||||
using Knot.Modules.Storage.Presentation.Endpoints;
|
||||
using Knot.Modules.Stories;
|
||||
using Knot.Modules.Stories.Presentation.Endpoints;
|
||||
using Knot.Modules.TelegramImport;
|
||||
using Knot.Modules.TelegramImport.Presentation.Endpoints;
|
||||
using Knot.Modules.WebRtc;
|
||||
using Knot.Modules.WebRtc.Presentation.Endpoints;
|
||||
using Knot.Shared.Infrastructure;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using System.Security.Claims;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
|
||||
|
||||
|
||||
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
|
||||
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
@@ -33,7 +49,7 @@ var builder = WebApplication.CreateBuilder(args);
|
||||
// Маппинг стандартных переменных окружения в иерархию .NET
|
||||
var envMappings = new Dictionary<string, string?>
|
||||
{
|
||||
["ConnectionStrings:DefaultConnection"] = builder.Configuration["DATABASE_URL"] ?? "Host=localhost;Database=knot;Username=postgres;Password=postgres",
|
||||
["ConnectionStrings:DefaultConnection"] = builder.Configuration["DATABASE_URL"] ?? builder.Configuration.GetConnectionString("DefaultConnection") ?? "Host=localhost;Database=knot;Username=postgres;Password=postgres",
|
||||
["ConnectionStrings:MongoConnection"] = builder.Configuration["MONGO_CONNECTION"],
|
||||
["Jwt:Secret"] = builder.Configuration["JWT_SECRET"],
|
||||
["Jwt:Issuer"] = builder.Configuration["JWT_ISSUER"],
|
||||
@@ -60,10 +76,13 @@ builder.Services.AddSettingsModule(builder.Configuration);
|
||||
builder.Services.AddMessagingModule(builder.Configuration);
|
||||
builder.Services.AddConversationsModule(builder.Configuration);
|
||||
builder.Services.AddProfilesModule(builder.Configuration);
|
||||
builder.Services.AddRelationsModule(builder.Configuration);
|
||||
builder.Services.AddStorageModule(builder.Configuration);
|
||||
builder.Services.AddStoriesModule(builder.Configuration);
|
||||
builder.Services.AddKlipyModule();
|
||||
builder.Services.AddAdminModule();
|
||||
builder.Services.AddWebRtcModule();
|
||||
builder.Services.AddTelegramImportModule();
|
||||
builder.Services.AddSharedInfrastructure(builder.Configuration);
|
||||
|
||||
// CQRS / MediatR для команд в Host (например, AdminController)
|
||||
@@ -74,12 +93,14 @@ builder.Services.AddMediatR(cfg => cfg.RegisterServicesFromAssemblies(
|
||||
typeof(Knot.Modules.Messaging.DependencyInjection).Assembly,
|
||||
typeof(Knot.Modules.Conversations.DependencyInjection).Assembly,
|
||||
typeof(Knot.Modules.Stories.DependencyInjection).Assembly,
|
||||
typeof(Knot.Modules.Klipy.DependencyInjection).Assembly
|
||||
typeof(Knot.Modules.Klipy.DependencyInjection).Assembly,
|
||||
typeof(Knot.Modules.Relations.DependencyInjection).Assembly,
|
||||
typeof(Knot.Modules.WebRtc.DependencyInjection).Assembly,
|
||||
typeof(Knot.Modules.TelegramImport.DependencyInjection).Assembly,
|
||||
typeof(Knot.Modules.Auth.Infrastructure.Persistence.AuthDbContext).Assembly,
|
||||
typeof(Knot.Modules.Profiles.DependencyInjection).Assembly
|
||||
));
|
||||
|
||||
// Carter для вызова Minimal APIs (Endpoints)
|
||||
builder.Services.AddCarter();
|
||||
|
||||
// Настройка CORS
|
||||
builder.Services.AddCors(options =>
|
||||
{
|
||||
@@ -185,6 +206,9 @@ using (var scope = app.Services.CreateScope())
|
||||
var storiesDb = scope.ServiceProvider.GetRequiredService<Knot.Modules.Stories.Infrastructure.Database.StoriesDbContext>();
|
||||
await storiesDb.Database.MigrateAsync();
|
||||
|
||||
var relationsDb = scope.ServiceProvider.GetRequiredService<Knot.Modules.Relations.Infrastructure.Persistence.RelationsDbContext>();
|
||||
await relationsDb.Database.MigrateAsync();
|
||||
|
||||
// Set Encryption Service for MongoDB serializers
|
||||
var encryptionService = scope.ServiceProvider.GetRequiredService<Knot.Shared.Kernel.Security.IEncryptionService>();
|
||||
Knot.Modules.Messaging.Infrastructure.Persistence.Mongo.EncryptedStringSerializer.EncryptionService = encryptionService;
|
||||
@@ -195,6 +219,10 @@ using (var scope = app.Services.CreateScope())
|
||||
{
|
||||
concreteSettings.Initialize();
|
||||
}
|
||||
|
||||
// Sync user replicas for Relations module on startup
|
||||
var mediator = scope.ServiceProvider.GetRequiredService<IMediator>();
|
||||
await mediator.Send(new Knot.Modules.Relations.Application.Contacts.SyncReplicasCommand());
|
||||
}
|
||||
|
||||
// Настройка конвейера запросов
|
||||
@@ -216,8 +244,20 @@ if (app.Environment.IsDevelopment())
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
|
||||
// Добавляем контроллеры и Carter (Minimal APIs)
|
||||
app.MapCarter();
|
||||
// Регистрация эндпоинтов
|
||||
app.MapAuthEndpoints();
|
||||
app.MapStoriesEndpoints();
|
||||
app.MapContactsEndpoints();
|
||||
app.MapSettingsEndpoints();
|
||||
app.MapProfilesEndpoints();
|
||||
app.MapFederationEndpoints();
|
||||
app.MapKlipyEndpoints();
|
||||
app.MapChatsEndpoints();
|
||||
app.MapMessagesEndpoints();
|
||||
app.MapAdminEndpoints();
|
||||
app.MapFilesEndpoints();
|
||||
app.MapWebRtcEndpoints();
|
||||
app.MapTelegramImportEndpoints();
|
||||
|
||||
// Добавляем SignalR хабы
|
||||
app.MapHub<ChatHub>("/hubs/chat");
|
||||
|
||||
@@ -13,7 +13,8 @@
|
||||
"Secret": "knot_super_secret_key_1234567890_knot",
|
||||
"Issuer": "Knot",
|
||||
"Audience": "KnotUsers",
|
||||
"ExpiryInMinutes": 1440
|
||||
"ExpiryInMinutes": 1440,
|
||||
"RefreshExpiryInDays": 30
|
||||
},
|
||||
"KNOT_MASTER_ENCRYPTION_KEY": "knot_super_secret_key_1234567890_knot"
|
||||
}
|
||||
@@ -1,16 +1,16 @@
|
||||
using Knot.Contracts.Auth.Domain;
|
||||
using Knot.Contracts.Auth.Application.Abstractions;
|
||||
using Knot.Shared.Kernel;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Knot.Contracts.Admin.Abstractions;
|
||||
using Knot.Contracts.Auth.Application.Abstractions;
|
||||
using Knot.Contracts.Auth.Domain;
|
||||
using MediatR;
|
||||
|
||||
namespace Knot.Modules.Admin.Application.Admin.Commands;
|
||||
|
||||
public record BanUserCommand(Guid UserId) : ICommand<Result>;
|
||||
public record BanUserCommand(Guid UserId) : ICommand<SuccessResponse>;
|
||||
|
||||
internal sealed class BanUserCommandHandler : ICommandHandler<BanUserCommand, Result>
|
||||
internal sealed class BanUserCommandHandler : ICommandHandler<BanUserCommand, SuccessResponse>
|
||||
{
|
||||
private readonly IUserRepository _userRepository;
|
||||
private readonly IAuthUnitOfWork _unitOfWork;
|
||||
@@ -21,16 +21,16 @@ internal sealed class BanUserCommandHandler : ICommandHandler<BanUserCommand, Re
|
||||
_unitOfWork = unitOfWork;
|
||||
}
|
||||
|
||||
public async Task<Result<Result>> Handle(BanUserCommand request, CancellationToken cancellationToken)
|
||||
public async Task<Result<SuccessResponse>> Handle(BanUserCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var user = await _userRepository.GetByIdAsync(request.UserId, cancellationToken);
|
||||
if (user == null) return Result.Failure<Result>(Error.NotFound("User.NotFound", "User not found"));
|
||||
if (user == null) return Result.Failure<SuccessResponse>(Error.NotFound("User.NotFound", "User not found"));
|
||||
|
||||
user.IsBanned = true;
|
||||
user.BannedUntil = null; // Permanent ban
|
||||
|
||||
await _userRepository.UpdateAsync(user, cancellationToken);
|
||||
await _unitOfWork.SaveChangesAsync(cancellationToken);
|
||||
return Result.Success(Result.Success());
|
||||
return Result.Success(new SuccessResponse(true));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
using Knot.Contracts.Auth.Domain;
|
||||
using Knot.Contracts.Auth.Application.Abstractions;
|
||||
using Knot.Shared.Kernel;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Knot.Contracts.Admin.Abstractions;
|
||||
using Knot.Contracts.Auth.Application.Abstractions;
|
||||
using Knot.Contracts.Auth.Domain;
|
||||
using MediatR;
|
||||
|
||||
namespace Knot.Modules.Admin.Application.Admin.Commands;
|
||||
|
||||
public record UnbanUserCommand(Guid UserId) : ICommand<Result>;
|
||||
public record UnbanUserCommand(Guid UserId) : ICommand<SuccessResponse>;
|
||||
|
||||
internal sealed class UnbanUserCommandHandler : ICommandHandler<UnbanUserCommand, Result>
|
||||
internal sealed class UnbanUserCommandHandler : ICommandHandler<UnbanUserCommand, SuccessResponse>
|
||||
{
|
||||
private readonly IUserRepository _userRepository;
|
||||
private readonly IAuthUnitOfWork _unitOfWork;
|
||||
@@ -21,16 +21,16 @@ internal sealed class UnbanUserCommandHandler : ICommandHandler<UnbanUserCommand
|
||||
_unitOfWork = unitOfWork;
|
||||
}
|
||||
|
||||
public async Task<Result<Result>> Handle(UnbanUserCommand request, CancellationToken cancellationToken)
|
||||
public async Task<Result<SuccessResponse>> Handle(UnbanUserCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var user = await _userRepository.GetByIdAsync(request.UserId, cancellationToken);
|
||||
if (user == null) return Result.Failure<Result>(Error.NotFound("User.NotFound", "User not found"));
|
||||
if (user == null) return Result.Failure<SuccessResponse>(Error.NotFound("User.NotFound", "User not found"));
|
||||
|
||||
user.IsBanned = false;
|
||||
user.BannedUntil = null;
|
||||
|
||||
await _userRepository.UpdateAsync(user, cancellationToken);
|
||||
await _unitOfWork.SaveChangesAsync(cancellationToken);
|
||||
return Result.Success(Result.Success());
|
||||
return Result.Success(new SuccessResponse(true));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
using Knot.Modules.Admin.Application.Admin.DTOs;
|
||||
using Knot.Modules.Messaging.Domain;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediatR;
|
||||
using Knot.Shared.Kernel;
|
||||
using Knot.Contracts.Admin.Abstractions;
|
||||
using Knot.Contracts.Auth.Infrastructure.Persistence;
|
||||
using Knot.Contracts.Conversations.Domain;
|
||||
using Knot.Contracts.Conversations.Infrastructure.Persistence;
|
||||
using Knot.Contracts.Messaging.Application.Abstractions;
|
||||
using Knot.Shared.Kernel.Storage;
|
||||
using MongoDB.Driver;
|
||||
using Knot.Contracts.Stories.Infrastructure.Persistence;
|
||||
using Knot.Modules.Admin.Application.Admin.DTOs;
|
||||
using MediatR;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Knot.Modules.Auth.Infrastructure.Persistence;
|
||||
using Knot.Modules.Conversations.Infrastructure.Persistence;
|
||||
using Knot.Modules.Stories.Domain;
|
||||
|
||||
namespace Knot.Modules.Admin.Application.Admin.Commands;
|
||||
|
||||
@@ -20,17 +20,22 @@ public record CleanRunCommand() : ICommand<MessageResponse>;
|
||||
|
||||
internal sealed class CleanRunCommandHandler : ICommandHandler<CleanRunCommand, MessageResponse>
|
||||
{
|
||||
private readonly ChatsDbContext _chatsDbContext;
|
||||
private readonly IMongoCollection<Message> _messages;
|
||||
private readonly IMongoCollection<Story> _stories;
|
||||
private readonly Knot.Contracts.Conversations.Infrastructure.Persistence.IChatsDbContext _chatsDbContext;
|
||||
private readonly Knot.Contracts.Messaging.Application.Abstractions.IMessageQueryService _messageQueryService;
|
||||
private readonly Knot.Contracts.Stories.Infrastructure.Persistence.IStoryCollection _storyCollection;
|
||||
private readonly IFileStorageService _fileStorage;
|
||||
private readonly AuthDbContext _identityDb;
|
||||
private readonly Knot.Contracts.Auth.Infrastructure.Persistence.IAuthDbContext _identityDb;
|
||||
|
||||
public CleanRunCommandHandler(ChatsDbContext chatsDbContext, IMongoDatabase mongoDb, IFileStorageService fileStorage, AuthDbContext identityDb)
|
||||
public CleanRunCommandHandler(
|
||||
Knot.Contracts.Conversations.Infrastructure.Persistence.IChatsDbContext chatsDbContext,
|
||||
Knot.Contracts.Messaging.Application.Abstractions.IMessageQueryService messageQueryService,
|
||||
Knot.Contracts.Stories.Infrastructure.Persistence.IStoryCollection storyCollection,
|
||||
IFileStorageService fileStorage,
|
||||
Knot.Contracts.Auth.Infrastructure.Persistence.IAuthDbContext identityDb)
|
||||
{
|
||||
_chatsDbContext = chatsDbContext;
|
||||
_messages = mongoDb.GetCollection<Message>("messages");
|
||||
_stories = mongoDb.GetCollection<Story>("stories");
|
||||
_messageQueryService = messageQueryService;
|
||||
_storyCollection = storyCollection;
|
||||
_fileStorage = fileStorage;
|
||||
_identityDb = identityDb;
|
||||
}
|
||||
@@ -39,30 +44,27 @@ internal sealed class CleanRunCommandHandler : ICommandHandler<CleanRunCommand,
|
||||
{
|
||||
try
|
||||
{
|
||||
var allChats = await _chatsDbContext.Chats.AsNoTracking().ToListAsync(cancellationToken);
|
||||
var allChats = await _chatsDbContext.Chats.ToListAsync(cancellationToken);
|
||||
var activeChatIds = allChats.Select(c => c.Id).ToHashSet();
|
||||
|
||||
var allMessages = await _messages.Find(_ => true).ToListAsync(cancellationToken);
|
||||
|
||||
var orphanMessages = allMessages
|
||||
.Where(m => !activeChatIds.Contains(m.ChatId) || m.IsDeleted)
|
||||
.ToList();
|
||||
var allMessages = await _messageQueryService.GetAllMessagesAsync(cancellationToken);
|
||||
var orphanMessages = await _messageQueryService.GetOrphanedMessagesAsync(activeChatIds, cancellationToken);
|
||||
|
||||
var keptMessages = allMessages
|
||||
.Where(m => activeChatIds.Contains(m.ChatId) && !m.IsDeleted)
|
||||
.Where(m => !orphanMessages.Any(om => om.Id == m.Id) && !m.IsDeleted)
|
||||
.ToList();
|
||||
|
||||
var allMinioFiles = (await _fileStorage.ListFilesAsync()).ToList();
|
||||
|
||||
var allUsers = await _identityDb.Users.AsNoTracking().ToListAsync(cancellationToken);
|
||||
var allUsers = await _identityDb.Users.ToListAsync(cancellationToken);
|
||||
|
||||
var allStories = await _stories.Find(_ => true).ToListAsync(cancellationToken);
|
||||
var allStories = await _storyCollection.GetAllAsync(cancellationToken);
|
||||
|
||||
var validUrls = new HashSet<string>();
|
||||
|
||||
var activeMessageUrls = keptMessages.OfType<MediaMessage>()
|
||||
var activeMessageUrls = keptMessages
|
||||
.Where(m => m.Media != null)
|
||||
.SelectMany(m => m.Media)
|
||||
.SelectMany(m => m.Media!)
|
||||
.Select(me => me.Url)
|
||||
.Where(u => !string.IsNullOrEmpty(u));
|
||||
|
||||
@@ -99,8 +101,7 @@ internal sealed class CleanRunCommandHandler : ICommandHandler<CleanRunCommand,
|
||||
if (orphanMessages.Any())
|
||||
{
|
||||
var orphanIds = orphanMessages.Select(m => m.Id).ToList();
|
||||
var filter = Builders<Message>.Filter.In(m => m.Id, orphanIds);
|
||||
await _messages.DeleteManyAsync(filter, cancellationToken);
|
||||
await _messageQueryService.DeleteMessagesAsync(orphanIds, cancellationToken);
|
||||
}
|
||||
|
||||
return Result.Success(new MessageResponse("Cleanup completed successfully"));
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
using BCrypt.Net;
|
||||
using Knot.Contracts.Auth.Application.Abstractions;
|
||||
using Knot.Contracts.Auth.Domain;
|
||||
using Knot.Modules.Admin.Application.Admin.DTOs;
|
||||
using Knot.Shared.Kernel;
|
||||
using MediatR;
|
||||
|
||||
namespace Knot.Modules.Admin.Application.Admin.Commands.CreateUser;
|
||||
|
||||
public record CreateUserCommand(
|
||||
string Username,
|
||||
string Password,
|
||||
string DisplayName,
|
||||
string? Email = null,
|
||||
string? Bio = null
|
||||
) : ICommand<AdminUserDto>;
|
||||
|
||||
internal sealed class CreateUserCommandHandler : ICommandHandler<CreateUserCommand, AdminUserDto>
|
||||
{
|
||||
private readonly IUserRepository _userRepository;
|
||||
private readonly IAuthUnitOfWork _unitOfWork;
|
||||
|
||||
public CreateUserCommandHandler(IUserRepository userRepository, IAuthUnitOfWork unitOfWork)
|
||||
{
|
||||
_userRepository = userRepository;
|
||||
_unitOfWork = unitOfWork;
|
||||
}
|
||||
|
||||
public async Task<Result<AdminUserDto>> Handle(CreateUserCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
// Проверка уникальности username
|
||||
if (!await _userRepository.IsUsernameUniqueAsync(request.Username, cancellationToken))
|
||||
{
|
||||
return Result.Failure<AdminUserDto>(new Error("Admin.CreateUser.UsernameNotUnique", "Username is already taken"));
|
||||
}
|
||||
|
||||
// Хеширование пароля
|
||||
string passwordHash = BCrypt.Net.BCrypt.HashPassword(request.Password);
|
||||
|
||||
// Создание контракта пользователя
|
||||
var userContract = new UserContract
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Username = request.Username,
|
||||
PasswordHash = passwordHash,
|
||||
DisplayName = request.DisplayName,
|
||||
Email = request.Email,
|
||||
Bio = request.Bio,
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
IsBanned = false,
|
||||
IsOnline = false
|
||||
};
|
||||
|
||||
_unitOfWork.Add(userContract);
|
||||
await _unitOfWork.SaveChangesAsync(cancellationToken);
|
||||
|
||||
return Result.Success(new AdminUserDto(
|
||||
userContract.Id,
|
||||
userContract.Username,
|
||||
userContract.DisplayName,
|
||||
userContract.Email,
|
||||
userContract.Avatar,
|
||||
userContract.CreatedAt,
|
||||
userContract.IsOnline,
|
||||
userContract.LastSeen ?? DateTime.UtcNow,
|
||||
userContract.IsBanned));
|
||||
}
|
||||
}
|
||||
+3
-2
@@ -6,7 +6,7 @@ using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediatR;
|
||||
using Knot.Shared.Kernel;
|
||||
using Knot.Contracts.Admin.Abstractions;
|
||||
|
||||
namespace Knot.Modules.Admin.Application.Admin.Commands;
|
||||
|
||||
@@ -28,7 +28,7 @@ internal sealed class ResetUserPasswordCommandHandler : ICommandHandler<ResetUse
|
||||
var user = await _userRepository.GetByIdAsync(request.UserId, cancellationToken);
|
||||
if (user == null)
|
||||
{
|
||||
return Result.Failure<SuccessResponse>(AuthErrors.UserNotFound);
|
||||
return Result.Failure<SuccessResponse>(Error.NotFound("Auth.UserNotFound", "User not found"));
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(request.NewPassword))
|
||||
@@ -42,3 +42,4 @@ internal sealed class ResetUserPasswordCommandHandler : ICommandHandler<ResetUse
|
||||
return Result.Success(new SuccessResponse(true));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+5
-5
@@ -1,10 +1,10 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediatR;
|
||||
using Knot.Shared.Kernel;
|
||||
using Knot.Contracts.Admin.Abstractions;
|
||||
using Knot.Contracts.Klipy.Application.Abstractions;
|
||||
using Knot.Contracts.Settings.Application.Abstractions;
|
||||
using Knot.Contracts.Settings.Application.DTOs;
|
||||
using Knot.Modules.Klipy.Application.Abstractions;
|
||||
using MediatR;
|
||||
|
||||
namespace Knot.Modules.Admin.Application.Admin.Commands.TestKlipy;
|
||||
|
||||
@@ -13,9 +13,9 @@ public record TestKlipyConnectionCommand(string ApiKey, string AppName) : IComma
|
||||
internal sealed class TestKlipyConnectionCommandHandler : ICommandHandler<TestKlipyConnectionCommand, bool>
|
||||
{
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly IKlipyClient _klipyClient;
|
||||
private readonly Knot.Contracts.Klipy.Application.Abstractions.IKlipyClient _klipyClient;
|
||||
|
||||
public TestKlipyConnectionCommandHandler(ISettingsService settingsService, IKlipyClient klipyClient)
|
||||
public TestKlipyConnectionCommandHandler(ISettingsService settingsService, Knot.Contracts.Klipy.Application.Abstractions.IKlipyClient klipyClient)
|
||||
{
|
||||
_settingsService = settingsService;
|
||||
_klipyClient = klipyClient;
|
||||
|
||||
+4
-4
@@ -1,12 +1,12 @@
|
||||
using Knot.Modules.Admin.Application.Admin.DTOs;
|
||||
using System;
|
||||
using System.Security.Cryptography;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediatR;
|
||||
using Knot.Shared.Kernel;
|
||||
using Knot.Contracts.Admin.Abstractions;
|
||||
using Knot.Contracts.Settings.Application.Abstractions;
|
||||
using Knot.Contracts.Settings.Application.DTOs;
|
||||
using System.Security.Cryptography;
|
||||
using Knot.Modules.Admin.Application.Admin.DTOs;
|
||||
using MediatR;
|
||||
|
||||
namespace Knot.Modules.Admin.Application.Admin.Commands;
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user