diff --git a/MiniSpace.APIGateway/src/MiniSpace.APIGateway/ntrada.yml b/MiniSpace.APIGateway/src/MiniSpace.APIGateway/ntrada.yml
index 46a179f44..dae0f3140 100644
--- a/MiniSpace.APIGateway/src/MiniSpace.APIGateway/ntrada.yml
+++ b/MiniSpace.APIGateway/src/MiniSpace.APIGateway/ntrada.yml
@@ -302,6 +302,88 @@ modules:
notifications-service:
localUrl: localhost:5006
url: notifications-service
+
+
+ communication:
+ path: /communication
+ routes:
+ - upstream: /chats
+ method: POST
+ use: downstream
+ downstream: communication-service/communication/chats
+ auth: true
+
+ - upstream: /chats/{chatId}/messages
+ method: POST
+ use: downstream
+ downstream: communication-service/communication/chats/{chatId}/messages
+ auth: true
+ bind:
+ - chatId:{chatId}
+
+ - upstream: /chats/{chatId}/users
+ method: PUT
+ use: downstream
+ downstream: communication-service/communication/chats/{chatId}/users
+ auth: true
+ bind:
+ - chatId:{chatId}
+
+ - upstream: /chats/{chatId}/messages/{messageId}/status
+ method: PUT
+ use: downstream
+ downstream: communication-service/communication/chats/{chatId}/messages/{messageId}/status
+ auth: true
+ bind:
+ - chatId:{chatId}
+ - messageId:{messageId}
+
+ - upstream: /chats/user/{userId}
+ method: GET
+ use: downstream
+ downstream: communication-service/communication/chats/user/{userId}
+ auth: true
+ bind:
+ - userId:{userId}
+
+ - upstream: /chats/{chatId}
+ method: GET
+ use: downstream
+ downstream: communication-service/communication/chats/{chatId}
+ auth: true
+ bind:
+ - chatId:{chatId}
+
+ - upstream: /chats/{chatId}/messages
+ method: GET
+ use: downstream
+ downstream: communication-service/communication/chats/{chatId}/messages
+ auth: true
+ bind:
+ - chatId:{chatId}
+
+ - upstream: /chats/{chatId}/{userId}
+ method: DELETE
+ use: downstream
+ downstream: communication-service/communication/chats/{chatId}/{userId}
+ auth: true
+ bind:
+ - chatId:{chatId}
+ - userId:{userId}
+
+ - upstream: /chats/{chatId}/messages/{messageId}
+ method: DELETE
+ use: downstream
+ downstream: communication-service/communication/chats/{chatId}/messages/{messageId}
+ auth: true
+ bind:
+ - chatId:{chatId}
+ - messageId:{messageId}
+
+ services:
+ communication-service:
+ localUrl: localhost:5016
+ url: communication-service
students:
@@ -349,6 +431,30 @@ modules:
downstream: students-service/students/{studentId}/notifications
auth: true
+ - upstream: /profiles/users/{userId}/views/paginated
+ method: GET
+ use: downstream
+ downstream: students-service/students/profiles/users/{userId}/views/paginated
+ auth: true
+ bind:
+ - userId:{userId}
+
+ - upstream: /profiles/users/{userId}/views/viewed
+ method: GET
+ use: downstream
+ downstream: students-service/students/profiles/users/{userId}/views/viewed
+ auth: true
+ bind:
+ - userId:{userId}
+
+ - upstream: /{blockerId}/blocked-users
+ method: GET
+ use: downstream
+ downstream: students-service/students/{blockerId}/blocked-users
+ auth: true
+ bind:
+ - blockerId:{blockerId}
+
- upstream: /{studentId}
method: PUT
use: downstream
@@ -392,6 +498,32 @@ modules:
downstream: students-service/students/{studentId}/notifications
auth: true
+ - upstream: /profiles/users/{userProfileId}/view
+ method: POST
+ use: downstream
+ downstream: students-service/students/profiles/users/{userProfileId}/view
+ auth: true
+ bind:
+ - userProfileId:{userProfileId}
+
+ - upstream: /{blockerId}/block-user/{blockedUserId}
+ method: POST
+ use: downstream
+ downstream: students-service/students/{blockerId}/block-user/{blockedUserId}
+ auth: true
+ bind:
+ - blockerId:{blockerId}
+ - blockedUserId:{blockedUserId}
+
+ - upstream: /{blockerId}/unblock-user/{blockedUserId}
+ method: POST
+ use: downstream
+ downstream: students-service/students/{blockerId}/unblock-user/{blockedUserId}
+ auth: true
+ bind:
+ - blockerId:{blockerId}
+ - blockedUserId:{blockedUserId}
+
- upstream: /{studentId}/languages-and-interests
method: PUT
use: downstream
@@ -454,6 +586,23 @@ modules:
downstream: events-service/events/student/{studentId}
auth: true
+ - upstream: /users/{userId}/feed
+ method: GET
+ use: downstream
+ downstream: events-service/events/users/{userId}/feed
+ auth: true
+ bind:
+ - userId:{userId}
+
+ - upstream: /users/{userId}/views/paginated
+ method: GET
+ use: downstream
+ downstream: events-service/events/users/{userId}/views/paginated
+ auth: true
+ bind:
+ - userId:{userId}
+
+
- upstream: /{eventId}
method: DELETE
use: downstream
@@ -483,6 +632,14 @@ modules:
downstream: events-service/events/{eventId}/show-interest
auth: true
+ - upstream: /{eventId}/view
+ method: POST
+ use: downstream
+ downstream: events-service/events/{eventId}/view
+ auth: true
+ bind:
+ - eventId:{eventId}
+
- upstream: /{eventId}/show-interest
method: DELETE
use: downstream
@@ -757,6 +914,22 @@ modules:
downstream: friends-service/friends/requests/sent/{userId}
auth: true
+ - upstream: /{userId}/followers
+ method: GET
+ use: downstream
+ downstream: friends-service/friends/{userId}/followers
+ auth: true
+ bind:
+ - userId: {userId}
+
+ - upstream: /{userId}/following
+ method: GET
+ use: downstream
+ downstream: friends-service/friends/{userId}/following
+ auth: true
+ bind:
+ - userId: {userId}
+
- upstream: /requests/{userId}/withdraw
method: PUT
use: downstream
@@ -918,7 +1091,7 @@ modules:
- upstream: /users/{userId}/organizations
method: GET
use: downstream
- downstream: organizations-service/users/{userId}/organizations
+ downstream: organizations-service/organizations/users/{userId}/organizations
auth: true
bind:
- userId: {userId}
@@ -930,6 +1103,16 @@ modules:
auth: true
bind:
- organizationId: {organizationId}
+
+ - upstream: /users/{userId}/organizations/follow
+ method: GET
+ use: downstream
+ downstream: organizations-service/organizations/users/{userId}/organizations/follow
+ auth: true
+ bind:
+ - userId: {userId}
+
+
- upstream: /{organizationId}/details/gallery-users
method: GET
@@ -946,6 +1129,13 @@ modules:
bind:
- organizationId: {organizationId}
+ - upstream: /{organizationId}/requests
+ method: GET
+ use: downstream
+ downstream: organizations-service/organizations/{organizationId}/requests
+ bind:
+ - organizationId: {organizationId}
+
- upstream: /paginated
method: GET
use: downstream
diff --git a/MiniSpace.Services.Communication/.gitignore b/MiniSpace.Services.Communication/.gitignore
new file mode 100644
index 000000000..c619427ad
--- /dev/null
+++ b/MiniSpace.Services.Communication/.gitignore
@@ -0,0 +1,338 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUNIT
+*.VisualState.xml
+TestResult.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+# **/Properties/launchSettings.json
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_i.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# JustCode is a .NET coding add-in
+.JustCode
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+bin/
+obj/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# JetBrains Rider
+.idea/
+*.sln.iml
+
+# CodeRush
+.cr/
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+logs/
+
+
+# Ignore appsettings.json files
+**/appsettings*.json
\ No newline at end of file
diff --git a/MiniSpace.Services.Communication/Dockerfile b/MiniSpace.Services.Communication/Dockerfile
new file mode 100644
index 000000000..88978fc22
--- /dev/null
+++ b/MiniSpace.Services.Communication/Dockerfile
@@ -0,0 +1,17 @@
+FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
+WORKDIR /app
+
+COPY . .
+
+RUN dotnet publish src/MiniSpace.Services.Communication.Api -c Release -o out
+
+FROM mcr.microsoft.com/dotnet/aspnet:8.0
+WORKDIR /app
+
+COPY --from=build /app/out .
+
+ENV ASPNETCORE_URLS=http://*:80
+ENV ASPNETCORE_ENVIRONMENT=docker
+ENV NTRADA_CONFIG=ntrada.docker
+
+ENTRYPOINT ["dotnet", "MiniSpace.Services.Communication.Api.dll"]
\ No newline at end of file
diff --git a/MiniSpace.Services.Communication/LICENSE b/MiniSpace.Services.Communication/LICENSE
new file mode 100644
index 000000000..c3a0bdfb9
--- /dev/null
+++ b/MiniSpace.Services.Communication/LICENSE
@@ -0,0 +1,89 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+## TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+### 1. Definitions.
+
+"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
+
+"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
+
+"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
+
+"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
+
+"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
+
+"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
+
+"Work" shall mean the work of authorship, whether in Source or Object form, that is made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
+
+"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
+
+"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
+
+"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
+
+### 2. Grant of Copyright License.
+
+Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
+
+### 3. Grant of Patent License.
+
+Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
+
+### 4. Redistribution.
+
+You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
+
+(a) You must give any other recipients of the Work or Derivative Works a copy of this License; and
+
+(b) You must cause any modified files to carry prominent notices stating that You changed the files; and
+
+(c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
+
+(d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
+
+You may add Your own copyright notices to Your version of this material, provided that such additional copyright notices are not considered to be modifications of the License.
+
+### 5. Submission of Contributions.
+
+Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
+
+### 6. Trademarks.
+
+This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
+
+### 7. Disclaimer of Warranty.
+
+Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
+
+### 8. Limitation of Liability.
+
+In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
+
+### 9. Accepting Warranty or Additional Liability.
+
+While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, protection, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
+
+## END OF TERMS AND CONDITIONS
+
+### Appendix: How to apply the Apache License to your work.
+
+To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be placed in the LICENSE file in the root directory of your source code (if not already present):
+
+ Copyright 2024 distributed_minispace_team
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/MiniSpace.Services.Communication/MiniSpace.Services.Communication.sln b/MiniSpace.Services.Communication/MiniSpace.Services.Communication.sln
new file mode 100644
index 000000000..ff5faab9b
--- /dev/null
+++ b/MiniSpace.Services.Communication/MiniSpace.Services.Communication.sln
@@ -0,0 +1,48 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.0.31903.59
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{0E976732-0CD9-4D2E-B989-998B124073BA}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MiniSpace.Services.Students.Api", "src\MiniSpace.Services.Students.Api\MiniSpace.Services.Students.Api.csproj", "{D915BFB5-D2D4-44C8-A3A3-379419079F06}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MiniSpace.Services.Students.Application", "src\MiniSpace.Services.Students.Application\MiniSpace.Services.Students.Application.csproj", "{97B39658-9B33-4124-90E5-102FDA7D3733}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MiniSpace.Services.Students.Core", "src\MiniSpace.Services.Students.Core\MiniSpace.Services.Students.Core.csproj", "{C4EFD7FD-9D95-444B-86A4-E4D60E62CD16}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MiniSpace.Services.Students.Infrastructure", "src\MiniSpace.Services.Students.Infrastructure\MiniSpace.Services.Students.Infrastructure.csproj", "{B2997BE8-0CE3-45DD-98E9-80599B070C25}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {D915BFB5-D2D4-44C8-A3A3-379419079F06}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D915BFB5-D2D4-44C8-A3A3-379419079F06}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D915BFB5-D2D4-44C8-A3A3-379419079F06}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D915BFB5-D2D4-44C8-A3A3-379419079F06}.Release|Any CPU.Build.0 = Release|Any CPU
+ {97B39658-9B33-4124-90E5-102FDA7D3733}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {97B39658-9B33-4124-90E5-102FDA7D3733}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {97B39658-9B33-4124-90E5-102FDA7D3733}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {97B39658-9B33-4124-90E5-102FDA7D3733}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C4EFD7FD-9D95-444B-86A4-E4D60E62CD16}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C4EFD7FD-9D95-444B-86A4-E4D60E62CD16}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C4EFD7FD-9D95-444B-86A4-E4D60E62CD16}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C4EFD7FD-9D95-444B-86A4-E4D60E62CD16}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B2997BE8-0CE3-45DD-98E9-80599B070C25}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B2997BE8-0CE3-45DD-98E9-80599B070C25}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B2997BE8-0CE3-45DD-98E9-80599B070C25}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B2997BE8-0CE3-45DD-98E9-80599B070C25}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {D915BFB5-D2D4-44C8-A3A3-379419079F06} = {0E976732-0CD9-4D2E-B989-998B124073BA}
+ {97B39658-9B33-4124-90E5-102FDA7D3733} = {0E976732-0CD9-4D2E-B989-998B124073BA}
+ {C4EFD7FD-9D95-444B-86A4-E4D60E62CD16} = {0E976732-0CD9-4D2E-B989-998B124073BA}
+ {B2997BE8-0CE3-45DD-98E9-80599B070C25} = {0E976732-0CD9-4D2E-B989-998B124073BA}
+ EndGlobalSection
+EndGlobal
diff --git a/MiniSpace.Services.Communication/scripts/build.sh b/MiniSpace.Services.Communication/scripts/build.sh
new file mode 100644
index 000000000..3affad0eb
--- /dev/null
+++ b/MiniSpace.Services.Communication/scripts/build.sh
@@ -0,0 +1,2 @@
+#!/bin/bash
+dotnet build -c release
\ No newline at end of file
diff --git a/MiniSpace.Services.Communication/scripts/dockerize-tag-push.sh b/MiniSpace.Services.Communication/scripts/dockerize-tag-push.sh
new file mode 100755
index 000000000..018a7d1c1
--- /dev/null
+++ b/MiniSpace.Services.Communication/scripts/dockerize-tag-push.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+export ASPNETCORE_ENVIRONMENT=docker
+
+cd ..
+
+docker build -t minispace.services.communication:latest .
+
+docker tag minispace.services.communication:latest adrianvsaint/minispace.services.communication:latest
+
+docker push adrianvsaint/minispace.services.communication:latest
diff --git a/MiniSpace.Services.Communication/scripts/start.sh b/MiniSpace.Services.Communication/scripts/start.sh
new file mode 100644
index 000000000..824533d3b
--- /dev/null
+++ b/MiniSpace.Services.Communication/scripts/start.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+export ASPNETCORE_ENVIRONMENT=local
+cd ../src/MiniSpace.Services.Communication.Api
+dotnet run
\ No newline at end of file
diff --git a/MiniSpace.Services.Communication/scripts/test.sh b/MiniSpace.Services.Communication/scripts/test.sh
new file mode 100644
index 000000000..6046c35a0
--- /dev/null
+++ b/MiniSpace.Services.Communication/scripts/test.sh
@@ -0,0 +1,2 @@
+#!/bin/bash
+dotnet test
\ No newline at end of file
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Api/.gitignore b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Api/.gitignore
new file mode 100644
index 000000000..94196c0d8
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Api/.gitignore
@@ -0,0 +1,339 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+appsettings.local.json
+appsettings.docker.json
+appsettings.json
+
+
+# User-specific files
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUNIT
+*.VisualState.xml
+TestResult.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+# **/Properties/launchSettings.json
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_i.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# JustCode is a .NET coding add-in
+.JustCode
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+bin/
+obj/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# JetBrains Rider
+.idea/
+*.sln.iml
+
+# CodeRush
+.cr/
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+logs/
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Api/MiniSpace.Services.Communication.Api.csproj b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Api/MiniSpace.Services.Communication.Api.csproj
new file mode 100644
index 000000000..7219ce31d
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Api/MiniSpace.Services.Communication.Api.csproj
@@ -0,0 +1,20 @@
+
+
+
+ net8.0
+ latest
+ MiniSpace.Services.Students.Api
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Api/MiniSpace.Services.Communication.Api.sln b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Api/MiniSpace.Services.Communication.Api.sln
new file mode 100644
index 000000000..7eb3ebc70
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Api/MiniSpace.Services.Communication.Api.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.5.002.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MiniSpace.Services.Communication.Api", "MiniSpace.Services.Communication.Api.csproj", "{5DA6DF02-FFC5-4BAB-B1FC-739A9DA14602}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {5DA6DF02-FFC5-4BAB-B1FC-739A9DA14602}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {5DA6DF02-FFC5-4BAB-B1FC-739A9DA14602}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {5DA6DF02-FFC5-4BAB-B1FC-739A9DA14602}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {5DA6DF02-FFC5-4BAB-B1FC-739A9DA14602}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {284B46A7-2ABB-4E20-8B52-4291CC46AE00}
+ EndGlobalSection
+EndGlobal
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Api/Program.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Api/Program.cs
new file mode 100644
index 000000000..601b5b58d
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Api/Program.cs
@@ -0,0 +1,94 @@
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Convey;
+using Convey.Logging;
+using Convey.Types;
+using Convey.WebApi;
+using Convey.WebApi.CQRS;
+using Microsoft.AspNetCore;
+using Microsoft.AspNetCore.SignalR;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.DependencyInjection;
+using MiniSpace.Services.Communication.Application;
+using MiniSpace.Services.Communication.Application.Services;
+using MiniSpace.Services.Communication.Application.Commands;
+using MiniSpace.Services.Communication.Application.Dto;
+using MiniSpace.Services.Communication.Application.Queries;
+using MiniSpace.Services.Communication.Infrastructure;
+using MiniSpace.Services.Communication.Application.Hubs;
+using MiniSpace.Services.Communication.Core.Wrappers;
+using System;
+
+namespace MiniSpace.Services.Communication.Api
+{
+ public class Program
+ {
+ public static async Task Main(string[] args)
+ => await WebHost.CreateDefaultBuilder(args)
+ .ConfigureServices(services =>
+ {
+ services.AddConvey()
+ .AddWebApi()
+ .AddApplication()
+ .AddInfrastructure();
+ services.AddCors(options =>
+ {
+ options.AddPolicy("CorsPolicy",
+ builder => builder
+ .AllowAnyMethod()
+ .AllowAnyHeader()
+ .AllowCredentials()
+ .SetIsOriginAllowed((host) => true));
+ });
+ services.AddSignalR();
+ services.AddAuthentication();
+ services.AddAuthorization();
+ })
+ .Configure(app => app
+ .UseInfrastructure()
+ .UseRouting()
+ .UseCors("CorsPolicy")
+ .UseAuthentication()
+ .UseAuthorization()
+ .UseEndpoints(endpoints =>
+ {
+ endpoints.MapHub("/chatHub").RequireCors("CorsPolicy");
+ })
+ .UseDispatcherEndpoints(endpoints => endpoints
+
+ // Chat-related endpoints
+ .Get>("communication/chats/user/{userId}")
+ .Get("communication/chats/{chatId}")
+ .Get>("communication/chats/{chatId}/messages")
+ .Post("communication/chats", async (cmd, ctx) =>
+ {
+ try
+ {
+ var chatId = await ctx.RequestServices.GetService()
+ .CreateChatAsync(cmd.ChatId, cmd.ParticipantIds, cmd.ChatName);
+ ctx.Response.StatusCode = StatusCodes.Status200OK;
+ await ctx.Response.WriteJsonAsync(new { ChatId = chatId });
+ }
+ catch (Exception ex)
+ {
+ ctx.Response.StatusCode = StatusCodes.Status500InternalServerError;
+ await ctx.Response.WriteJsonAsync(new { Error = ex.Message });
+ }
+ })
+
+ .Put("communication/chats/{chatId}/users")
+ .Delete("communication/chats/{chatId}/{userId}")
+
+ // Message-related endpoints
+ .Post("communication/chats/{chatId}/messages")
+ .Put("communication/chats/{chatId}/messages/{messageId}/status")
+ .Delete("communication/chats/{chatId}/messages/{messageId}")
+ ))
+ .UseLogging()
+ .UseLogging()
+ .Build()
+ .RunAsync();
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Api/Properties/launchSettings.json b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Api/Properties/launchSettings.json
new file mode 100644
index 000000000..41c0e71a3
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Api/Properties/launchSettings.json
@@ -0,0 +1,26 @@
+{
+ "iisSettings": {
+ "windowsAuthentication": false,
+ "anonymousAuthentication": true,
+ "iisExpress": {
+ "applicationUrl": "http://localhost:5016"
+ }
+ },
+ "profiles": {
+ "IIS Express": {
+ "commandName": "IISExpress",
+ "launchBrowser": false,
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "local"
+ }
+ },
+ "MiniSpace.Services.Communication": {
+ "commandName": "Project",
+ "launchBrowser": false,
+ "applicationUrl": "http://localhost:5016",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "local"
+ }
+ }
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/.gitignore b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/.gitignore
new file mode 100644
index 000000000..64def56a6
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/.gitignore
@@ -0,0 +1,331 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUNIT
+*.VisualState.xml
+TestResult.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+# **/Properties/launchSettings.json
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_i.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# JustCode is a .NET coding add-in
+.JustCode
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# JetBrains Rider
+.idea/
+*.sln.iml
+
+# CodeRush
+.cr/
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+logs/
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/AddUserToChat.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/AddUserToChat.cs
new file mode 100644
index 000000000..b9c8b36bf
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/AddUserToChat.cs
@@ -0,0 +1,17 @@
+using Convey.CQRS.Commands;
+using System;
+
+namespace MiniSpace.Services.Communication.Application.Commands
+{
+ public class AddUserToChat : ICommand
+ {
+ public Guid ChatId { get; }
+ public Guid UserId { get; }
+
+ public AddUserToChat(Guid chatId, Guid userId)
+ {
+ ChatId = chatId;
+ UserId = userId;
+ }
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/CreateChat.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/CreateChat.cs
new file mode 100644
index 000000000..8a81dfd02
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/CreateChat.cs
@@ -0,0 +1,20 @@
+using Convey.CQRS.Commands;
+using System;
+using System.Collections.Generic;
+
+namespace MiniSpace.Services.Communication.Application.Commands
+{
+ public class CreateChat : ICommand
+ {
+ public Guid ChatId { get; }
+ public List ParticipantIds { get; }
+ public string ChatName { get; }
+
+ public CreateChat(Guid chatId, List participantIds, string chatName = null)
+ {
+ ChatId = chatId;
+ ParticipantIds = participantIds ?? new List();
+ ChatName = chatName;
+ }
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/DeleteChat.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/DeleteChat.cs
new file mode 100644
index 000000000..f1cf27b57
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/DeleteChat.cs
@@ -0,0 +1,19 @@
+using Convey.CQRS.Commands;
+using Microsoft.AspNetCore.Mvc;
+using System;
+
+namespace MiniSpace.Services.Communication.Application.Commands
+{
+ public class DeleteChat : ICommand
+ {
+ public Guid ChatId { get; set; }
+
+ public Guid UserId { get; set; }
+
+ public DeleteChat(Guid chatId, Guid userId)
+ {
+ ChatId = chatId;
+ UserId = userId;
+ }
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/DeleteMessage.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/DeleteMessage.cs
new file mode 100644
index 000000000..b6fc99d17
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/DeleteMessage.cs
@@ -0,0 +1,21 @@
+using Convey.CQRS.Commands;
+using Microsoft.AspNetCore.Mvc;
+using System;
+
+namespace MiniSpace.Services.Communication.Application.Commands
+{
+ public class DeleteMessage : ICommand
+ {
+ [FromRoute]
+ public Guid MessageId { get; }
+
+ [FromQuery]
+ public Guid ChatId { get; }
+
+ public DeleteMessage(Guid messageId, Guid chatId)
+ {
+ MessageId = messageId;
+ ChatId = chatId;
+ }
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/Handlers/AddUserToChatHandler.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/Handlers/AddUserToChatHandler.cs
new file mode 100644
index 000000000..9a0bbd1be
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/Handlers/AddUserToChatHandler.cs
@@ -0,0 +1,38 @@
+using Convey.CQRS.Commands;
+using MiniSpace.Services.Communication.Application.Commands;
+using MiniSpace.Services.Communication.Application.Events;
+using MiniSpace.Services.Communication.Application.Services;
+using MiniSpace.Services.Communication.Core.Entities;
+using MiniSpace.Services.Communication.Core.Repositories;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MiniSpace.Services.Communication.Application.Commands.Handlers
+{
+ public class AddUserToChatHandler : ICommandHandler
+ {
+ private readonly IUserChatsRepository _userChatsRepository;
+ private readonly IMessageBroker _messageBroker;
+
+ public AddUserToChatHandler(IUserChatsRepository userChatsRepository, IMessageBroker messageBroker)
+ {
+ _userChatsRepository = userChatsRepository;
+ _messageBroker = messageBroker;
+ }
+
+ public async Task HandleAsync(AddUserToChat command, CancellationToken cancellationToken)
+ {
+ var userChats = await _userChatsRepository.GetByUserIdAsync(command.UserId) ?? new UserChats(command.UserId);
+ var chat = userChats.GetChatById(command.ChatId);
+
+ if (chat == null)
+ {
+ chat = new Chat(new List { command.UserId });
+ userChats.AddChat(chat);
+ await _userChatsRepository.AddOrUpdateAsync(userChats);
+ }
+
+ await _messageBroker.PublishAsync(new UserAddedToChat(command.ChatId, command.UserId));
+ }
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/Handlers/CreateChatHandler.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/Handlers/CreateChatHandler.cs
new file mode 100644
index 000000000..121c5316a
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/Handlers/CreateChatHandler.cs
@@ -0,0 +1,31 @@
+using Convey.CQRS.Commands;
+using Microsoft.Extensions.Logging;
+using MiniSpace.Services.Communication.Application.Commands;
+using MiniSpace.Services.Communication.Application.Services;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MiniSpace.Services.Communication.Application.Commands.Handlers
+{
+ public class CreateChatHandler : ICommandHandler
+ {
+ private readonly ICommunicationService _communicationService;
+ private readonly ILogger _logger;
+
+ public CreateChatHandler(ICommunicationService communicationService, ILogger logger)
+ {
+ _communicationService = communicationService;
+ _logger = logger;
+ }
+
+ public async Task HandleAsync(CreateChat command, CancellationToken cancellationToken)
+ {
+ _logger.LogInformation($"Handling CreateChat command for Chat ID: {command.ChatId}");
+
+ // Call the CommunicationService to create the chat
+ var chatId = await _communicationService.CreateChatAsync(command.ChatId, command.ParticipantIds, command.ChatName);
+
+ _logger.LogInformation($"Chat created with ID: {chatId}");
+ }
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/Handlers/DeleteChatHandler.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/Handlers/DeleteChatHandler.cs
new file mode 100644
index 000000000..3622fc5f5
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/Handlers/DeleteChatHandler.cs
@@ -0,0 +1,31 @@
+using Convey.CQRS.Commands;
+using MiniSpace.Services.Communication.Application.Commands;
+using MiniSpace.Services.Communication.Application.Events;
+using MiniSpace.Services.Communication.Application.Services;
+using MiniSpace.Services.Communication.Core.Repositories;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MiniSpace.Services.Communication.Application.Commands.Handlers
+{
+ public class DeleteChatHandler : ICommandHandler
+ {
+ private readonly IUserChatsRepository _userChatsRepository;
+ private readonly IMessageBroker _messageBroker;
+
+ public DeleteChatHandler(IUserChatsRepository userChatsRepository, IMessageBroker messageBroker)
+ {
+ _userChatsRepository = userChatsRepository;
+ _messageBroker = messageBroker;
+ }
+
+ public async Task HandleAsync(DeleteChat command, CancellationToken cancellationToken)
+ {
+ Console.WriteLine($"Received DeleteChat command - ChatId: {command.ChatId}, UserId: {command.UserId}");
+
+ await _userChatsRepository.DeleteChatAsync(command.UserId, command.ChatId);
+
+ await _messageBroker.PublishAsync(new ChatDeleted(command.ChatId));
+ }
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/Handlers/DeleteMessageHandler.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/Handlers/DeleteMessageHandler.cs
new file mode 100644
index 000000000..daaf6af7b
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/Handlers/DeleteMessageHandler.cs
@@ -0,0 +1,40 @@
+using Convey.CQRS.Commands;
+using MiniSpace.Services.Communication.Application.Commands;
+using MiniSpace.Services.Communication.Application.Services;
+using MiniSpace.Services.Communication.Core.Repositories;
+using MiniSpace.Services.Communication.Application.Events;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MiniSpace.Services.Communication.Application.Commands.Handlers
+{
+ public class DeleteMessageHandler : ICommandHandler
+ {
+ private readonly IUserChatsRepository _userChatsRepository;
+ private readonly IMessageBroker _messageBroker;
+
+ public DeleteMessageHandler(IUserChatsRepository userChatsRepository, IMessageBroker messageBroker)
+ {
+ _userChatsRepository = userChatsRepository;
+ _messageBroker = messageBroker;
+ }
+
+ public async Task HandleAsync(DeleteMessage command, CancellationToken cancellationToken)
+ {
+ var userChats = await _userChatsRepository.GetByUserIdAsync(command.ChatId);
+ var chat = userChats?.GetChatById(command.ChatId);
+
+ if (chat != null)
+ {
+ var message = chat.Messages.Find(m => m.Id == command.MessageId);
+ if (message != null)
+ {
+ chat.Messages.Remove(message);
+ await _userChatsRepository.UpdateAsync(userChats);
+
+ await _messageBroker.PublishAsync(new MessageStatusUpdated(command.ChatId, command.MessageId, "Deleted"));
+ }
+ }
+ }
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/Handlers/RemoveUserFromChatHandler.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/Handlers/RemoveUserFromChatHandler.cs
new file mode 100644
index 000000000..5a7291c36
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/Handlers/RemoveUserFromChatHandler.cs
@@ -0,0 +1,36 @@
+using Convey.CQRS.Commands;
+using MiniSpace.Services.Communication.Application.Commands;
+using MiniSpace.Services.Communication.Application.Events;
+using MiniSpace.Services.Communication.Application.Services;
+using MiniSpace.Services.Communication.Core.Repositories;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MiniSpace.Services.Communication.Application.Commands.Handlers
+{
+ public class RemoveUserFromChatHandler : ICommandHandler
+ {
+ private readonly IUserChatsRepository _userChatsRepository;
+ private readonly IMessageBroker _messageBroker;
+
+ public RemoveUserFromChatHandler(IUserChatsRepository userChatsRepository, IMessageBroker messageBroker)
+ {
+ _userChatsRepository = userChatsRepository;
+ _messageBroker = messageBroker;
+ }
+
+ public async Task HandleAsync(RemoveUserFromChat command, CancellationToken cancellationToken)
+ {
+ var userChats = await _userChatsRepository.GetByUserIdAsync(command.UserId);
+ var chat = userChats?.GetChatById(command.ChatId);
+
+ if (chat != null)
+ {
+ chat.ParticipantIds.Remove(command.UserId);
+ await _userChatsRepository.UpdateAsync(userChats);
+
+ await _messageBroker.PublishAsync(new UserAddedToChat(command.ChatId, command.UserId));
+ }
+ }
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/Handlers/SendMessageHandler.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/Handlers/SendMessageHandler.cs
new file mode 100644
index 000000000..c273375f6
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/Handlers/SendMessageHandler.cs
@@ -0,0 +1,152 @@
+using Convey.CQRS.Commands;
+using Microsoft.Extensions.Logging;
+using Microsoft.AspNetCore.SignalR;
+using MiniSpace.Services.Communication.Application.Commands;
+using MiniSpace.Services.Communication.Application.Events;
+using MiniSpace.Services.Communication.Application.Hubs;
+using MiniSpace.Services.Communication.Application.Services;
+using MiniSpace.Services.Communication.Core.Entities;
+using MiniSpace.Services.Communication.Core.Repositories;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using System;
+using MiniSpace.Services.Communication.Application.Dto;
+using System.Collections.Generic;
+
+namespace MiniSpace.Services.Communication.Application.Commands.Handlers
+{
+ public class SendMessageHandler : ICommandHandler
+ {
+ private readonly IUserChatsRepository _userChatsRepository;
+ private readonly IMessageBroker _messageBroker;
+ private readonly ILogger _logger;
+ private readonly IHubContext _hubContext;
+
+ public SendMessageHandler(
+ IUserChatsRepository userChatsRepository,
+ IMessageBroker messageBroker,
+ ILogger logger,
+ IHubContext hubContext)
+ {
+ _userChatsRepository = userChatsRepository;
+ _messageBroker = messageBroker;
+ _logger = logger;
+ _hubContext = hubContext;
+ }
+
+ public async Task HandleAsync(SendMessage command, CancellationToken cancellationToken)
+ {
+ // Retrieve the chat from the sender's chat list
+ var senderChats = await _userChatsRepository.GetByUserIdAsync(command.SenderId);
+ var chat = senderChats?.GetChatById(command.ChatId);
+
+ if (chat == null)
+ {
+ _logger.LogWarning($"Chat with id {command.ChatId} not found for user with id {command.SenderId}. Checking if it exists for other participants...");
+
+ // If the chat is not found, check if it exists for any other participant
+ chat = await FindChatInAllParticipants(command.SenderId, command.ChatId);
+
+ if (chat == null)
+ {
+ _logger.LogInformation($"No existing chat found. Creating a new chat for the participants.");
+ // If no chat is found at all, create a new one
+ // If creating a new chat
+ chat = new Chat(command.ChatId, new List { command.SenderId }, new List());
+
+ await CreateOrUpdateChatForAllParticipants(chat);
+ }
+ else
+ {
+ // Restore the chat for the sender
+ senderChats ??= new UserChats(command.SenderId);
+ senderChats.AddChat(chat);
+ await _userChatsRepository.AddOrUpdateAsync(senderChats);
+ }
+ }
+
+ // Create the message to be sent
+ var message = new Message(
+ chatId: chat.Id,
+ senderId: command.SenderId,
+ receiverId: Guid.Empty, // Unused in this context
+ content: command.Content,
+ type: Enum.Parse(command.MessageType)
+ );
+
+ _logger.LogInformation($"Sending message with id {message.Id} to chat with id {chat.Id}");
+
+ chat.AddMessage(message);
+
+ // Save the updated chat and message to the database
+ await UpdateChatForAllParticipants(chat);
+
+ // Use the message ID from the database for the MessageDto
+ var messageDto = new MessageDto
+ {
+ Id = message.Id, // Use the ID from the database
+ ChatId = chat.Id,
+ SenderId = command.SenderId,
+ Content = command.Content,
+ Timestamp = message.Timestamp // Assuming Message has a Timestamp property
+ };
+
+
+ // Notify the participant via SignalR
+ await ChatHub.SendMessageToUser(_hubContext, command.SenderId.ToString(), messageDto, _logger);
+
+ // Publish the MessageSent event for further processing
+ await _messageBroker.PublishAsync(new MessageSent(
+ chatId: chat.Id,
+ messageId: message.Id,
+ senderId: command.SenderId,
+ content: command.Content
+ ));
+ }
+
+ private async Task FindChatInAllParticipants(Guid senderId, Guid chatId)
+ {
+ // Go through all participant chats to find if the chat already exists
+ var participantIds = await _userChatsRepository.GetParticipantIdsByChatIdAsync(chatId);
+
+ foreach (var participantId in participantIds)
+ {
+ if (participantId == senderId) continue;
+
+ var participantChats = await _userChatsRepository.GetByUserIdAsync(participantId);
+ var existingChat = participantChats?.GetChatById(chatId);
+
+ if (existingChat != null)
+ {
+ _logger.LogInformation($"Chat found for another participant with id {participantId}. Restoring chat for sender.");
+ return existingChat;
+ }
+ }
+
+ return null;
+ }
+
+ private async Task CreateOrUpdateChatForAllParticipants(Chat chat)
+ {
+ foreach (var participantId in chat.ParticipantIds)
+ {
+ var participantChats = await _userChatsRepository.GetByUserIdAsync(participantId) ?? new UserChats(participantId);
+ participantChats.AddChat(chat);
+ await _userChatsRepository.AddOrUpdateAsync(participantChats);
+ }
+ }
+
+ private async Task UpdateChatForAllParticipants(Chat chat)
+ {
+ foreach (var participantId in chat.ParticipantIds)
+ {
+ var participantChats = await _userChatsRepository.GetByUserIdAsync(participantId) ?? new UserChats(participantId);
+ var participantChat = participantChats.GetChatById(chat.Id) ?? chat;
+ participantChat.AddMessage(chat.Messages.Last());
+ await _userChatsRepository.AddOrUpdateAsync(participantChats);
+ }
+ }
+
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/Handlers/UpdateMessageStatusHandler.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/Handlers/UpdateMessageStatusHandler.cs
new file mode 100644
index 000000000..c18402d17
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/Handlers/UpdateMessageStatusHandler.cs
@@ -0,0 +1,36 @@
+using Convey.CQRS.Commands;
+using Microsoft.Extensions.Logging;
+using Microsoft.AspNetCore.SignalR;
+using MiniSpace.Services.Communication.Application.Commands;
+using MiniSpace.Services.Communication.Application.Events;
+using MiniSpace.Services.Communication.Application.Hubs;
+using MiniSpace.Services.Communication.Application.Services;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MiniSpace.Services.Communication.Application.Commands.Handlers
+{
+ public class UpdateMessageStatusHandler : ICommandHandler
+ {
+ private readonly ICommunicationService _communicationService;
+ private readonly IHubContext _hubContext;
+ private readonly ILogger _logger;
+
+ public UpdateMessageStatusHandler(
+ ICommunicationService communicationService,
+ IHubContext hubContext,
+ ILogger logger)
+ {
+ _communicationService = communicationService;
+ _hubContext = hubContext;
+ _logger = logger;
+ }
+
+ public async Task HandleAsync(UpdateMessageStatus command, CancellationToken cancellationToken)
+ {
+ await _communicationService.UpdateMessageStatusAsync(command.ChatId, command.MessageId, command.Status);
+ _logger.LogInformation($"Message status updated: ChatId={command.ChatId}, MessageId={command.MessageId}, Status={command.Status}");
+ await ChatHub.SendMessageStatusUpdate(_hubContext, command.ChatId.ToString(), command.MessageId, command.Status, _logger);
+ }
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/RemoveUserFromChat.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/RemoveUserFromChat.cs
new file mode 100644
index 000000000..53de89eaa
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/RemoveUserFromChat.cs
@@ -0,0 +1,17 @@
+using Convey.CQRS.Commands;
+using System;
+
+namespace MiniSpace.Services.Communication.Application.Commands
+{
+ public class RemoveUserFromChat : ICommand
+ {
+ public Guid ChatId { get; }
+ public Guid UserId { get; }
+
+ public RemoveUserFromChat(Guid chatId, Guid userId)
+ {
+ ChatId = chatId;
+ UserId = userId;
+ }
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/SendMessage.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/SendMessage.cs
new file mode 100644
index 000000000..a5df08bbb
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/SendMessage.cs
@@ -0,0 +1,21 @@
+using Convey.CQRS.Commands;
+using System;
+
+namespace MiniSpace.Services.Communication.Application.Commands
+{
+ public class SendMessage : ICommand
+ {
+ public Guid ChatId { get; }
+ public Guid SenderId { get; }
+ public string Content { get; }
+ public string MessageType { get; }
+
+ public SendMessage(Guid chatId, Guid senderId, string content, string messageType = "Text")
+ {
+ ChatId = chatId;
+ SenderId = senderId;
+ Content = content;
+ MessageType = messageType;
+ }
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/UpdateMessageStatus.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/UpdateMessageStatus.cs
new file mode 100644
index 000000000..cf809704e
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/UpdateMessageStatus.cs
@@ -0,0 +1,19 @@
+using Convey.CQRS.Commands;
+using System;
+
+namespace MiniSpace.Services.Communication.Application.Commands
+{
+ public class UpdateMessageStatus : ICommand
+ {
+ public Guid ChatId { get; set; }
+ public Guid MessageId { get; set; }
+ public string Status { get; set; }
+
+ public UpdateMessageStatus(Guid chatId, Guid messageId, string status)
+ {
+ ChatId = chatId;
+ MessageId = messageId;
+ Status = status;
+ }
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/ContractAttribute.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/ContractAttribute.cs
new file mode 100644
index 000000000..19f02dfbf
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/ContractAttribute.cs
@@ -0,0 +1,7 @@
+namespace MiniSpace.Services.Communication.Application
+{
+ public class ContractAttribute : Attribute
+ {
+
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Dto/ChatDto.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Dto/ChatDto.cs
new file mode 100644
index 000000000..3b7c3224e
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Dto/ChatDto.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+
+namespace MiniSpace.Services.Communication.Application.Dto
+{
+ public class ChatDto
+ {
+ public Guid Id { get; set; }
+ public string Name { get; set; }
+ public List ParticipantIds { get; set; }
+ public List Messages { get; set; }
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Dto/MessageDto.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Dto/MessageDto.cs
new file mode 100644
index 000000000..d4970dd25
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Dto/MessageDto.cs
@@ -0,0 +1,15 @@
+using System;
+
+namespace MiniSpace.Services.Communication.Application.Dto
+{
+ public class MessageDto
+ {
+ public Guid Id { get; set; }
+ public Guid ChatId { get; set; }
+ public Guid SenderId { get; set; }
+ public string Content { get; set; }
+ public DateTime Timestamp { get; set; }
+ public string MessageType { get; set; }
+ public string Status { get; set; }
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Dto/UserChatDto.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Dto/UserChatDto.cs
new file mode 100644
index 000000000..f2bc7eb2f
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Dto/UserChatDto.cs
@@ -0,0 +1,11 @@
+using System;
+using System.Collections.Generic;
+
+namespace MiniSpace.Services.Communication.Application.Dto
+{
+ public class UserChatDto
+ {
+ public Guid UserId { get; set; }
+ public List Chats { get; set; }
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Dto/UserDto.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Dto/UserDto.cs
new file mode 100644
index 000000000..80587cbcf
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Dto/UserDto.cs
@@ -0,0 +1,37 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+
+
+namespace MiniSpace.Services.Communication.Application.Dto
+{
+ [ExcludeFromCodeCoverage]
+ public class UserDto
+ {
+ public Guid Id { get; set; }
+ public string Email { get; set; }
+ public string FirstName { get; set; }
+ public string LastName { get; set; }
+ public string ProfileImageUrl { get; set; }
+ public string Description { get; set; }
+ public DateTime? DateOfBirth { get; set; }
+ public bool EmailNotifications { get; set; }
+ public bool IsBanned { get; set; }
+ public string State { get; set; }
+ public DateTime CreatedAt { get; set; }
+ public string ContactEmail { get; set; }
+ public string BannerUrl { get; set; }
+ public string PhoneNumber { get; set; }
+ public IEnumerable Languages { get; set; }
+ public IEnumerable Interests { get; set; }
+
+ // public IEnumerable Education { get; set; }
+ // public IEnumerable Work { get; set; }
+ public bool IsTwoFactorEnabled { get; set; }
+ public string TwoFactorSecret { get; set; }
+ public IEnumerable InterestedInEvents { get; set; }
+ public IEnumerable SignedUpEvents { get; set; }
+ public string Country { get; set; }
+ public string City { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/ChatCreated.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/ChatCreated.cs
new file mode 100644
index 000000000..736768c16
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/ChatCreated.cs
@@ -0,0 +1,18 @@
+using Convey.CQRS.Events;
+using System;
+using System.Collections.Generic;
+
+namespace MiniSpace.Services.Communication.Application.Events
+{
+ public class ChatCreated : IEvent
+ {
+ public Guid ChatId { get; set; }
+ public List ParticipantIds { get; set; }
+
+ public ChatCreated(Guid chatId, List participantIds)
+ {
+ ChatId = chatId;
+ ParticipantIds = participantIds;
+ }
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/ChatDeleted.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/ChatDeleted.cs
new file mode 100644
index 000000000..6c06f258a
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/ChatDeleted.cs
@@ -0,0 +1,15 @@
+using Convey.CQRS.Events;
+using System;
+
+namespace MiniSpace.Services.Communication.Application.Events
+{
+ public class ChatDeleted : IEvent
+ {
+ public Guid ChatId { get; }
+
+ public ChatDeleted(Guid chatId)
+ {
+ ChatId = chatId;
+ }
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/MessageSent.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/MessageSent.cs
new file mode 100644
index 000000000..8d0d17727
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/MessageSent.cs
@@ -0,0 +1,21 @@
+using Convey.CQRS.Events;
+using System;
+
+namespace MiniSpace.Services.Communication.Application.Events
+{
+ public class MessageSent : IEvent
+ {
+ public Guid ChatId { get; }
+ public Guid MessageId { get; }
+ public Guid SenderId { get; }
+ public string Content { get; }
+
+ public MessageSent(Guid chatId, Guid messageId, Guid senderId, string content)
+ {
+ ChatId = chatId;
+ MessageId = messageId;
+ SenderId = senderId;
+ Content = content;
+ }
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/MessageStatusUpdated.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/MessageStatusUpdated.cs
new file mode 100644
index 000000000..359ed6415
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/MessageStatusUpdated.cs
@@ -0,0 +1,19 @@
+using Convey.CQRS.Events;
+using System;
+
+namespace MiniSpace.Services.Communication.Application.Events
+{
+ public class MessageStatusUpdated : IEvent
+ {
+ public Guid ChatId { get; set; }
+ public Guid MessageId { get; set; }
+ public string Status { get; set; }
+
+ public MessageStatusUpdated(Guid chatId, Guid messageId, string status)
+ {
+ ChatId = chatId;
+ MessageId = messageId;
+ Status = status;
+ }
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/Rejected/ChatCreationRejected.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/Rejected/ChatCreationRejected.cs
new file mode 100644
index 000000000..063316929
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/Rejected/ChatCreationRejected.cs
@@ -0,0 +1,19 @@
+using Convey.CQRS.Events;
+using System;
+
+namespace MiniSpace.Services.Communication.Application.Events.Rejected
+{
+ public class ChatCreationRejected : IRejectedEvent
+ {
+ public Guid ChatId { get; }
+ public string Reason { get; }
+ public string Code { get; }
+
+ public ChatCreationRejected(Guid chatId, string reason, string code)
+ {
+ ChatId = chatId;
+ Reason = reason;
+ Code = code;
+ }
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/Rejected/ChatDeletionRejected.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/Rejected/ChatDeletionRejected.cs
new file mode 100644
index 000000000..7ea9a517a
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/Rejected/ChatDeletionRejected.cs
@@ -0,0 +1,19 @@
+using Convey.CQRS.Events;
+using System;
+
+namespace MiniSpace.Services.Communication.Application.Events.Rejected
+{
+ public class ChatDeletionRejected : IRejectedEvent
+ {
+ public Guid ChatId { get; }
+ public string Reason { get; }
+ public string Code { get; }
+
+ public ChatDeletionRejected(Guid chatId, string reason, string code)
+ {
+ ChatId = chatId;
+ Reason = reason;
+ Code = code;
+ }
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/Rejected/ChatProcessRejected.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/Rejected/ChatProcessRejected.cs
new file mode 100644
index 000000000..51363312c
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/Rejected/ChatProcessRejected.cs
@@ -0,0 +1,19 @@
+using Convey.CQRS.Events;
+using System;
+
+namespace MiniSpace.Services.Communication.Application.Events.Rejected
+{
+ public class ChatProcessRejected : IRejectedEvent
+ {
+ public Guid ChatId { get; }
+ public string Reason { get; }
+ public string Code { get; }
+
+ public ChatProcessRejected(Guid chatId, string reason, string code)
+ {
+ ChatId = chatId;
+ Reason = reason;
+ Code = code;
+ }
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/Rejected/MessageProcessRejected.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/Rejected/MessageProcessRejected.cs
new file mode 100644
index 000000000..334a1f1f5
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/Rejected/MessageProcessRejected.cs
@@ -0,0 +1,19 @@
+using Convey.CQRS.Events;
+using System;
+
+namespace MiniSpace.Services.Communication.Application.Events.Rejected
+{
+ public class MessageProcessRejected : IRejectedEvent
+ {
+ public Guid MessageId { get; }
+ public string Reason { get; }
+ public string Code { get; }
+
+ public MessageProcessRejected(Guid messageId, string reason, string code)
+ {
+ MessageId = messageId;
+ Reason = reason;
+ Code = code;
+ }
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/Rejected/MessageSendRejected.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/Rejected/MessageSendRejected.cs
new file mode 100644
index 000000000..61629d6a5
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/Rejected/MessageSendRejected.cs
@@ -0,0 +1,22 @@
+using Convey.CQRS.Events;
+using System;
+
+namespace MiniSpace.Services.Communication.Application.Events.Rejected
+{
+ public class MessageSendRejected : IRejectedEvent
+ {
+ public Guid ChatId { get; }
+ public Guid MessageId { get; }
+ public string Reason { get; }
+ public string Code { get; }
+
+ public MessageSendRejected(Guid chatId, Guid messageId, string reason, string code)
+ {
+ ChatId = chatId;
+ MessageId = messageId;
+ Reason = reason;
+ Code = code;
+ }
+ }
+
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/Rejected/UserAdditionToChatRejected.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/Rejected/UserAdditionToChatRejected.cs
new file mode 100644
index 000000000..5589a4c38
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/Rejected/UserAdditionToChatRejected.cs
@@ -0,0 +1,21 @@
+using Convey.CQRS.Events;
+using System;
+
+namespace MiniSpace.Services.Communication.Application.Events.Rejected
+{
+ public class UserAdditionToChatRejected : IRejectedEvent
+ {
+ public Guid ChatId { get; }
+ public Guid UserId { get; }
+ public string Reason { get; }
+ public string Code { get; }
+
+ public UserAdditionToChatRejected(Guid chatId, Guid userId, string reason, string code)
+ {
+ ChatId = chatId;
+ UserId = userId;
+ Reason = reason;
+ Code = code;
+ }
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/UserAddedToChat.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/UserAddedToChat.cs
new file mode 100644
index 000000000..84c8c1de1
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/UserAddedToChat.cs
@@ -0,0 +1,17 @@
+using Convey.CQRS.Events;
+using System;
+
+namespace MiniSpace.Services.Communication.Application.Events
+{
+ public class UserAddedToChat : IEvent
+ {
+ public Guid ChatId { get; }
+ public Guid UserId { get; }
+
+ public UserAddedToChat(Guid chatId, Guid userId)
+ {
+ ChatId = chatId;
+ UserId = userId;
+ }
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Exceptions/AppException.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Exceptions/AppException.cs
new file mode 100644
index 000000000..78c42cdd9
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Exceptions/AppException.cs
@@ -0,0 +1,11 @@
+namespace MiniSpace.Services.Communication.Application.Exceptions
+{
+ public class AppException : Exception
+ {
+ public virtual string Code { get; }
+
+ protected AppException(string message) : base(message)
+ {
+ }
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Exceptions/ChatNotFoundException.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Exceptions/ChatNotFoundException.cs
new file mode 100644
index 000000000..af906f1f9
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Exceptions/ChatNotFoundException.cs
@@ -0,0 +1,17 @@
+using System;
+
+namespace MiniSpace.Services.Communication.Application.Exceptions
+{
+ public class ChatNotFoundException : AppException
+ {
+ public override string Code { get; } = "chat_not_found";
+
+ public Guid ChatId { get; }
+
+ public ChatNotFoundException(Guid chatId)
+ : base($"Chat with ID '{chatId}' was not found.")
+ {
+ ChatId = chatId;
+ }
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Exceptions/InvalidChatOperationException.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Exceptions/InvalidChatOperationException.cs
new file mode 100644
index 000000000..ff06611ad
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Exceptions/InvalidChatOperationException.cs
@@ -0,0 +1,14 @@
+using System;
+
+namespace MiniSpace.Services.Communication.Application.Exceptions
+{
+ public class InvalidChatOperationException : AppException
+ {
+ public override string Code { get; } = "invalid_chat_operation";
+
+ public InvalidChatOperationException(string message)
+ : base(message)
+ {
+ }
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Exceptions/MessageNotFoundException.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Exceptions/MessageNotFoundException.cs
new file mode 100644
index 000000000..6b384f680
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Exceptions/MessageNotFoundException.cs
@@ -0,0 +1,17 @@
+using System;
+
+namespace MiniSpace.Services.Communication.Application.Exceptions
+{
+ public class MessageNotFoundException : AppException
+ {
+ public override string Code { get; } = "message_not_found";
+
+ public Guid MessageId { get; }
+
+ public MessageNotFoundException(Guid messageId)
+ : base($"Message with ID '{messageId}' was not found.")
+ {
+ MessageId = messageId;
+ }
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Extensions.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Extensions.cs
new file mode 100644
index 000000000..d03c02a20
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Extensions.cs
@@ -0,0 +1,16 @@
+using Convey;
+using Convey.CQRS.Commands;
+using Convey.CQRS.Events;
+
+namespace MiniSpace.Services.Communication.Application
+{
+ public static class Extensions
+ {
+ public static IConveyBuilder AddApplication(this IConveyBuilder builder)
+ => builder
+ .AddCommandHandlers()
+ .AddEventHandlers()
+ .AddInMemoryCommandDispatcher()
+ .AddInMemoryEventDispatcher();
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Hubs/ChatHub.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Hubs/ChatHub.cs
new file mode 100644
index 000000000..d570aec26
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Hubs/ChatHub.cs
@@ -0,0 +1,108 @@
+using Microsoft.AspNetCore.SignalR;
+using MiniSpace.Services.Communication.Application.Dto;
+using System.Text.Json;
+using System.Threading.Tasks;
+using Microsoft.Extensions.Logging;
+
+namespace MiniSpace.Services.Communication.Application.Hubs
+{
+ public class ChatHub : Hub
+ {
+ private readonly ILogger _logger;
+
+ public ChatHub(ILogger logger)
+ {
+ _logger = logger;
+ }
+
+ public override async Task OnConnectedAsync()
+ {
+ var userId = Context.GetHttpContext().Request.Query["userId"];
+ if (!string.IsNullOrEmpty(userId))
+ {
+ await Groups.AddToGroupAsync(Context.ConnectionId, userId);
+ _logger.LogInformation($"User {userId} connected and added to group with Connection ID: {Context.ConnectionId}");
+ }
+ else
+ {
+ _logger.LogWarning("User ID is missing in the query string.");
+ }
+ await base.OnConnectedAsync();
+ }
+
+ public override async Task OnDisconnectedAsync(Exception exception)
+ {
+ var userId = Context.GetHttpContext().Request.Query["userId"];
+ if (!string.IsNullOrEmpty(userId))
+ {
+ await Groups.RemoveFromGroupAsync(Context.ConnectionId, userId);
+ _logger.LogInformation($"User {userId} disconnected and removed from group with Connection ID: {Context.ConnectionId}");
+ }
+ await base.OnDisconnectedAsync(exception);
+ }
+
+ public async Task SendMessage(string userId, MessageDto message)
+ {
+ var jsonMessage = JsonSerializer.Serialize(message);
+ _logger.LogInformation($"Sending message to user {userId}: {jsonMessage}");
+ await Clients.User(userId).SendAsync("ReceiveMessage", jsonMessage);
+ }
+
+ public async Task SendMessageToGroup(string groupName, MessageDto message)
+ {
+ var jsonMessage = JsonSerializer.Serialize(message);
+ _logger.LogInformation($"Sending message to group {groupName}: {jsonMessage}");
+ await Clients.Group(groupName).SendAsync("ReceiveGroupMessage", jsonMessage);
+ }
+
+ public async Task AddToGroup(string groupName)
+ {
+ _logger.LogInformation($"Adding connection {Context.ConnectionId} to group {groupName}");
+ await Groups.AddToGroupAsync(Context.ConnectionId, groupName);
+ }
+
+ public async Task RemoveFromGroup(string groupName)
+ {
+ _logger.LogInformation($"Removing connection {Context.ConnectionId} from group {groupName}");
+ await Groups.RemoveFromGroupAsync(Context.ConnectionId, groupName);
+ }
+
+ public static async Task SendMessageToUser(IHubContext hubContext, string userId, MessageDto message, ILogger logger)
+ {
+ var jsonMessage = JsonSerializer.Serialize(message);
+ logger.LogInformation($"Sending message to user {userId}: {jsonMessage}");
+ await hubContext.Clients.All.SendAsync("ReceiveMessage", jsonMessage);
+ }
+
+ public static async Task BroadcastMessage(IHubContext hubContext, MessageDto message, ILogger logger)
+ {
+ var jsonMessage = JsonSerializer.Serialize(message);
+ logger.LogInformation($"Broadcasting message to all users: {jsonMessage}");
+ await hubContext.Clients.All.SendAsync("ReceiveMessage", jsonMessage);
+ }
+
+
+ public static async Task SendMessageStatusUpdate(IHubContext hubContext, string chatId, Guid messageId, string status, ILogger logger)
+ {
+ var statusUpdate = new
+ {
+ ChatId = chatId,
+ MessageId = messageId,
+ Status = status
+ };
+
+ logger.LogInformation($"Sending message status update to chat {chatId} for message {messageId} with status {status}");
+
+ var jsonStatusUpdate = JsonSerializer.Serialize(statusUpdate);
+
+ await hubContext.Clients.All.SendAsync("ReceiveMessageStatusUpdate", jsonStatusUpdate);
+ }
+
+ public async Task SendTypingNotification(string chatId, string userId, bool isTyping)
+ {
+ _logger.LogInformation($"User {userId} is typing in chat {chatId}: {isTyping}");
+ await Clients.All.SendAsync("ReceiveTypingNotification", userId, isTyping);
+ }
+
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/IAppContext.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/IAppContext.cs
new file mode 100644
index 000000000..c4b6a6756
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/IAppContext.cs
@@ -0,0 +1,8 @@
+namespace MiniSpace.Services.Communication.Application
+{
+ public interface IAppContext
+ {
+ string RequestId { get; }
+ IIdentityContext Identity { get; }
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/IIdentityContext.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/IIdentityContext.cs
new file mode 100644
index 000000000..50c867b07
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/IIdentityContext.cs
@@ -0,0 +1,14 @@
+namespace MiniSpace.Services.Communication.Application
+{
+ public interface IIdentityContext
+ {
+ Guid Id { get; }
+ string Role { get; }
+ string Name { get; }
+ string Email { get; }
+ bool IsAuthenticated { get; }
+ bool IsAdmin { get; }
+ bool IsBanned { get; }
+ IDictionary Claims { get; }
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/MiniSpace.Services.Communication.Application.csproj b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/MiniSpace.Services.Communication.Application.csproj
new file mode 100644
index 000000000..021af6a32
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/MiniSpace.Services.Communication.Application.csproj
@@ -0,0 +1,21 @@
+
+
+
+ net8.0
+ enable
+ disable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/MiniSpace.Services.Communication.Application.sln b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/MiniSpace.Services.Communication.Application.sln
new file mode 100644
index 000000000..88ccceadd
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/MiniSpace.Services.Communication.Application.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.5.002.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MiniSpace.Services.Communication.Application", "MiniSpace.Services.Communication.Application.csproj", "{85F8B10B-A401-4C70-9202-F2A8C1C7AF61}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {85F8B10B-A401-4C70-9202-F2A8C1C7AF61}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {85F8B10B-A401-4C70-9202-F2A8C1C7AF61}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {85F8B10B-A401-4C70-9202-F2A8C1C7AF61}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {85F8B10B-A401-4C70-9202-F2A8C1C7AF61}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {AE2AFDD2-927B-4122-835F-837E29C3330F}
+ EndGlobalSection
+EndGlobal
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Queries/GetChatById.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Queries/GetChatById.cs
new file mode 100644
index 000000000..db0a551a5
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Queries/GetChatById.cs
@@ -0,0 +1,16 @@
+using Convey.CQRS.Queries;
+using MiniSpace.Services.Communication.Application.Dto;
+using System;
+
+namespace MiniSpace.Services.Communication.Application.Queries
+{
+ public class GetChatById : IQuery
+ {
+ public Guid ChatId { get; }
+
+ public GetChatById(Guid chatId)
+ {
+ ChatId = chatId;
+ }
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Queries/GetMessagesForChat.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Queries/GetMessagesForChat.cs
new file mode 100644
index 000000000..fb7d10508
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Queries/GetMessagesForChat.cs
@@ -0,0 +1,17 @@
+using Convey.CQRS.Queries;
+using MiniSpace.Services.Communication.Application.Dto;
+using System;
+using System.Collections.Generic;
+
+namespace MiniSpace.Services.Communication.Application.Queries
+{
+ public class GetMessagesForChat : IQuery>
+ {
+ public Guid ChatId { get; }
+
+ public GetMessagesForChat(Guid chatId)
+ {
+ ChatId = chatId;
+ }
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Queries/GetUserChats.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Queries/GetUserChats.cs
new file mode 100644
index 000000000..1728621e0
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Queries/GetUserChats.cs
@@ -0,0 +1,21 @@
+using Convey.CQRS.Queries;
+using MiniSpace.Services.Communication.Application.Dto;
+using System;
+using MiniSpace.Services.Communication.Core.Wrappers;
+
+namespace MiniSpace.Services.Communication.Application.Queries
+{
+ public class GetUserChats : IQuery>
+ {
+ public Guid UserId { get; }
+ public int Page { get; }
+ public int PageSize { get; }
+
+ public GetUserChats(Guid userId, int page, int pageSize)
+ {
+ UserId = userId;
+ Page = page > 0 ? page : 1;
+ PageSize = pageSize > 0 ? pageSize : 10;
+ }
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Services/Clients/IStudentsServiceClient.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Services/Clients/IStudentsServiceClient.cs
new file mode 100644
index 000000000..8dd50b92f
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Services/Clients/IStudentsServiceClient.cs
@@ -0,0 +1,11 @@
+using System;
+using System.Threading.Tasks;
+using MiniSpace.Services.Communication.Application.Dto;
+
+namespace MiniSpace.Services.Communication.Application.Services.Clients
+{
+ public interface IStudentsServiceClient
+ {
+ Task GetAsync(Guid id);
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Services/CommunicationService.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Services/CommunicationService.cs
new file mode 100644
index 000000000..96f67ad1d
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Services/CommunicationService.cs
@@ -0,0 +1,124 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Microsoft.Extensions.Logging;
+using MiniSpace.Services.Communication.Application.Commands;
+using MiniSpace.Services.Communication.Application.Events;
+using MiniSpace.Services.Communication.Core.Entities;
+using MiniSpace.Services.Communication.Core.Repositories;
+
+namespace MiniSpace.Services.Communication.Application.Services
+{
+ public class CommunicationService : ICommunicationService
+ {
+ private readonly IUserChatsRepository _userChatsRepository;
+ private readonly IMessageBroker _messageBroker;
+ private readonly ILogger _logger;
+
+ public CommunicationService(IUserChatsRepository userChatsRepository, IMessageBroker messageBroker, ILogger logger)
+ {
+ _userChatsRepository = userChatsRepository;
+ _messageBroker = messageBroker;
+ _logger = logger;
+ }
+
+ public async Task CreateChatAsync(Guid chatId, List participantIds, string chatName = null)
+ {
+ _logger.LogInformation($"Creating chat with ID: {chatId}");
+
+ // Check if the chat already exists
+ Chat existingChat = null;
+ foreach (var participantId in participantIds)
+ {
+ var userChats = await _userChatsRepository.GetByUserIdAsync(participantId);
+ if (userChats != null)
+ {
+ existingChat = userChats.Chats.Find(chat =>
+ chat.ParticipantIds.Count == participantIds.Count &&
+ chat.ParticipantIds.All(pid => participantIds.Contains(pid))
+ );
+
+ if (existingChat != null)
+ {
+ _logger.LogWarning($"Chat with ID: {existingChat.Id} already exists. Returning existing chat ID.");
+ return existingChat.Id;
+ }
+ }
+ }
+
+ // Create a new chat if none exists
+ var newChat = new Chat(chatId, participantIds, new List());
+ foreach (var participantId in participantIds)
+ {
+ var userChats = await _userChatsRepository.GetByUserIdAsync(participantId) ?? new UserChats(participantId);
+ userChats.AddChat(newChat);
+ await _userChatsRepository.AddOrUpdateAsync(userChats);
+ }
+
+ await _messageBroker.PublishAsync(new ChatCreated(newChat.Id, participantIds));
+
+ _logger.LogInformation($"New chat created with ID: {newChat.Id}");
+ return newChat.Id;
+ }
+
+
+ public async Task UpdateMessageStatusAsync(Guid chatId, Guid messageId, string status)
+ {
+ // Retrieve the chat using the GetByChatIdAsync method
+ var chat = await _userChatsRepository.GetByChatIdAsync(chatId);
+ if (chat == null)
+ {
+ _logger.LogWarning($"Chat with ID {chatId} not found.");
+ return;
+ }
+
+ var message = chat.Messages.FirstOrDefault(m => m.Id == messageId);
+ if (message == null)
+ {
+ _logger.LogWarning($"Message with ID {messageId} not found in chat with ID {chatId}.");
+ return;
+ }
+
+ switch (status)
+ {
+ case "Read":
+ message.MarkAsRead();
+ break;
+ case "Unread":
+ message.MarkAsUnread();
+ break;
+ case "Deleted":
+ message.MarkAsDeleted();
+ break;
+ default:
+ _logger.LogWarning($"Unsupported status '{status}' for message ID {messageId} in chat ID {chatId}.");
+ return;
+ }
+
+ foreach (var participantId in chat.ParticipantIds)
+ {
+ var userChats = await _userChatsRepository.GetByUserIdAsync(participantId);
+ if (userChats != null)
+ {
+ var existingChat = userChats.GetChatById(chatId);
+ if (existingChat != null)
+ {
+ var messageToUpdate = existingChat.Messages.FirstOrDefault(m => m.Id == messageId);
+ if (messageToUpdate != null)
+ {
+ _logger.LogInformation($"Updating message status for chat ID {chatId} and message ID {messageId} to {status}");
+ messageToUpdate.MarkAsRead();
+ }
+ _logger.LogInformation($"Updating chat for participant {participantId} with chat ID {chatId}");
+ await _userChatsRepository.UpdateAsync(userChats);
+ }
+ }
+ }
+
+ _logger.LogInformation($"Message status updated: ChatId={chatId}, MessageId={messageId}, Status={status}");
+
+ await _messageBroker.PublishAsync(new MessageStatusUpdated(chatId, messageId, status));
+ }
+
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Services/ICommunicationService.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Services/ICommunicationService.cs
new file mode 100644
index 000000000..da0dd02cd
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Services/ICommunicationService.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using MiniSpace.Services.Communication.Application.Commands;
+using MiniSpace.Services.Communication.Application.Dto;
+
+namespace MiniSpace.Services.Communication.Application.Services
+{
+ public interface ICommunicationService
+ {
+ Task CreateChatAsync(Guid chatId, List participantIds, string chatName = null);
+ Task UpdateMessageStatusAsync(Guid chatId, Guid messageId, string status);
+ // Task> GetUserChatsAsync(Guid userId);
+ // Task GetChatByIdAsync(Guid chatId);
+ // Task SendMessageAsync(SendMessage command);
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Services/IDateTimeProvider.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Services/IDateTimeProvider.cs
new file mode 100644
index 000000000..527fa2c7e
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Services/IDateTimeProvider.cs
@@ -0,0 +1,7 @@
+namespace MiniSpace.Services.Communication.Application.Services
+{
+ public interface IDateTimeProvider
+ {
+ DateTime Now { get; }
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Services/IEventMapper.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Services/IEventMapper.cs
new file mode 100644
index 000000000..0cde987ee
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Services/IEventMapper.cs
@@ -0,0 +1,11 @@
+using Convey.CQRS.Events;
+using MiniSpace.Services.Communication.Core.Events;
+
+namespace MiniSpace.Services.Communication.Application.Services
+{
+ public interface IEventMapper
+ {
+ IEvent Map(IDomainEvent @event);
+ IEnumerable MapAll(IEnumerable events);
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Services/IMessageBroker.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Services/IMessageBroker.cs
new file mode 100644
index 000000000..405bad2e8
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Services/IMessageBroker.cs
@@ -0,0 +1,10 @@
+using Convey.CQRS.Events;
+
+namespace MiniSpace.Services.Communication.Application.Services
+{
+ public interface IMessageBroker
+ {
+ Task PublishAsync(params IEvent[] events);
+ Task PublishAsync(IEnumerable events);
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Services/ISignalRConnectionManager.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Services/ISignalRConnectionManager.cs
new file mode 100644
index 000000000..1df25a459
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Services/ISignalRConnectionManager.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace MiniSpace.Services.Communication.Application.Services
+{
+ public interface ISignalRConnectionManager
+ {
+ Task SendMessageAsync(string user, string message);
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Core/.gitignore b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Core/.gitignore
new file mode 100644
index 000000000..1f7e99963
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Core/.gitignore
@@ -0,0 +1,334 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUNIT
+*.VisualState.xml
+TestResult.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+# **/Properties/launchSettings.json
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_i.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# JustCode is a .NET coding add-in
+.JustCode
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+bin/
+obj/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# JetBrains Rider
+.idea/
+*.sln.iml
+
+# CodeRush
+.cr/
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+logs/
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Core/Entities/AggregateId.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Core/Entities/AggregateId.cs
new file mode 100644
index 000000000..63fe3517a
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Core/Entities/AggregateId.cs
@@ -0,0 +1,50 @@
+using MiniSpace.Services.Communication.Core.Exceptions;
+
+namespace MiniSpace.Services.Communication.Core.Entities
+{
+ public class AggregateId : IEquatable
+ {
+ public Guid Value { get; }
+
+ public AggregateId()
+ {
+ Value = Guid.NewGuid();
+ }
+
+ public AggregateId(Guid value)
+ {
+ if (value == Guid.Empty)
+ {
+ throw new InvalidAggregateIdException();
+ }
+
+ Value = value;
+ }
+
+ public bool Equals(AggregateId other)
+ {
+ if (ReferenceEquals(null, other)) return false;
+ return ReferenceEquals(this, other) || Value.Equals(other.Value);
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (ReferenceEquals(null, obj)) return false;
+ if (ReferenceEquals(this, obj)) return true;
+ return obj.GetType() == GetType() && Equals((AggregateId) obj);
+ }
+
+ public override int GetHashCode()
+ {
+ return Value.GetHashCode();
+ }
+
+ public static implicit operator Guid(AggregateId id)
+ => id.Value;
+
+ public static implicit operator AggregateId(Guid id)
+ => new AggregateId(id);
+
+ public override string ToString() => Value.ToString();
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Core/Entities/AggregateRoot.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Core/Entities/AggregateRoot.cs
new file mode 100644
index 000000000..7d5382101
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Core/Entities/AggregateRoot.cs
@@ -0,0 +1,19 @@
+using MiniSpace.Services.Communication.Core.Events;
+
+namespace MiniSpace.Services.Communication.Core.Entities
+{
+ public abstract class AggregateRoot
+ {
+ private readonly List _events = new List();
+ public IEnumerable Events => _events;
+ public AggregateId Id { get; protected set; }
+ public int Version { get; protected set; }
+
+ protected void AddEvent(IDomainEvent @event)
+ {
+ _events.Add(@event);
+ }
+
+ public void ClearEvents() => _events.Clear();
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Core/Entities/Chat.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Core/Entities/Chat.cs
new file mode 100644
index 000000000..2624138a9
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Core/Entities/Chat.cs
@@ -0,0 +1,31 @@
+namespace MiniSpace.Services.Communication.Core.Entities
+{
+ public class Chat : AggregateRoot
+ {
+ public Guid Id { get; private set; }
+ public List ParticipantIds { get; private set; }
+ public List Messages { get; private set; }
+
+ // Constructor for creating a new chat with a new Id
+ public Chat(List participantIds)
+ {
+ Id = Guid.NewGuid();
+ ParticipantIds = participantIds;
+ Messages = new List();
+ }
+
+ // Constructor for loading an existing chat from the database
+ public Chat(Guid id, List participantIds, List messages)
+ {
+ Id = id;
+ ParticipantIds = participantIds;
+ Messages = messages;
+ }
+
+ public void AddMessage(Message message)
+ {
+ Messages.Add(message);
+ AddEvent(new Events.MessageAddedEvent(Id, message.Id));
+ }
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Core/Entities/Message.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Core/Entities/Message.cs
new file mode 100644
index 000000000..d9023b3b4
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Core/Entities/Message.cs
@@ -0,0 +1,60 @@
+using System;
+
+namespace MiniSpace.Services.Communication.Core.Entities
+{
+ public class Message
+ {
+ public Guid Id { get; private set; }
+ public Guid SenderId { get; private set; }
+ public Guid ReceiverId { get; private set; }
+ public Guid ChatId { get; private set; }
+ public string Content { get; private set; }
+ public DateTime Timestamp { get; private set; }
+ public MessageType Type { get; private set; }
+ public MessageStatus Status { get; private set; }
+
+ // Constructor for creating a new message
+ public Message(Guid chatId, Guid senderId, Guid receiverId, string content, MessageType type)
+ {
+ Id = Guid.NewGuid(); // Generate a new ID for a new message
+ ChatId = chatId;
+ SenderId = senderId;
+ ReceiverId = receiverId;
+ Content = content;
+ Timestamp = DateTime.UtcNow;
+ Type = type;
+ Status = MessageStatus.Sent;
+ }
+
+ // Constructor for loading an existing message from the database
+ public Message(Guid id, Guid chatId, Guid senderId, Guid receiverId, string content, DateTime timestamp, MessageType type, MessageStatus status)
+ {
+ Id = id; // Use the existing ID from the database
+ ChatId = chatId;
+ SenderId = senderId;
+ ReceiverId = receiverId;
+ Content = content;
+ Timestamp = timestamp;
+ Type = type;
+ Status = status;
+ }
+
+ public void MarkAsRead()
+ {
+ Status = MessageStatus.Read;
+ }
+
+ public void MarkAsUnread()
+ {
+ if (Status == MessageStatus.Read)
+ {
+ Status = MessageStatus.Unread;
+ }
+ }
+
+ public void MarkAsDeleted()
+ {
+ Status = MessageStatus.Deleted;
+ }
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Core/Entities/MessageStatus.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Core/Entities/MessageStatus.cs
new file mode 100644
index 000000000..54a0ff61f
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Core/Entities/MessageStatus.cs
@@ -0,0 +1,11 @@
+namespace MiniSpace.Services.Communication.Core.Entities
+{
+ public enum MessageStatus
+ {
+ Sent,
+ Delivered,
+ Unread,
+ Read,
+ Deleted
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Core/Entities/MessageType.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Core/Entities/MessageType.cs
new file mode 100644
index 000000000..67981b777
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Core/Entities/MessageType.cs
@@ -0,0 +1,10 @@
+namespace MiniSpace.Services.Communication.Core.Entities
+{
+ public enum MessageType
+ {
+ Text,
+ Image,
+ Video,
+ File
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Core/Entities/OrganizationChats.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Core/Entities/OrganizationChats.cs
new file mode 100644
index 000000000..b8d75b4f3
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Core/Entities/OrganizationChats.cs
@@ -0,0 +1,55 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace MiniSpace.Services.Communication.Core.Entities
+{
+ public class OrganizationChats
+ {
+ public Guid OrganizationId { get; private set; }
+ public List Chats { get; private set; }
+
+ public OrganizationChats(Guid organizationId)
+ {
+ OrganizationId = organizationId;
+ Chats = new List();
+ }
+
+ public void AddChat(Chat chat)
+ {
+ if (chat == null)
+ throw new ArgumentNullException(nameof(chat));
+
+ Chats.Add(chat);
+ }
+
+ public void RemoveChat(Guid chatId)
+ {
+ var chat = Chats.FirstOrDefault(c => c.Id == chatId);
+ if (chat != null)
+ {
+ Chats.Remove(chat);
+ }
+ }
+
+ public Chat GetChatById(Guid chatId)
+ {
+ return Chats.FirstOrDefault(c => c.Id == chatId);
+ }
+
+ public bool ChatExists(Guid chatId)
+ {
+ return Chats.Any(c => c.Id == chatId);
+ }
+
+ public void UpdateChat(Chat updatedChat)
+ {
+ var existingChat = GetChatById(updatedChat.Id);
+ if (existingChat != null)
+ {
+ Chats.Remove(existingChat);
+ Chats.Add(updatedChat);
+ }
+ }
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Core/Entities/ReactionType.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Core/Entities/ReactionType.cs
new file mode 100644
index 000000000..5625742ab
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Core/Entities/ReactionType.cs
@@ -0,0 +1,11 @@
+namespace MiniSpace.Services.Communication.Core.Entities
+{
+ public enum ReactionType
+ {
+ LoveIt,
+ LikeIt,
+ Wow,
+ ItWasOkay,
+ HateIt
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Core/Entities/UserChats.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Core/Entities/UserChats.cs
new file mode 100644
index 000000000..c77922ee1
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Core/Entities/UserChats.cs
@@ -0,0 +1,24 @@
+namespace MiniSpace.Services.Communication.Core.Entities
+{
+ public class UserChats
+ {
+ public Guid UserId { get; private set; }
+ public List Chats { get; private set; }
+
+ public UserChats(Guid userId)
+ {
+ UserId = userId;
+ Chats = new List();
+ }
+
+ public void AddChat(Chat chat)
+ {
+ Chats.Add(chat);
+ }
+
+ public Chat GetChatById(Guid chatId)
+ {
+ return Chats.FirstOrDefault(chat => chat.Id == chatId);
+ }
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Core/Entities/UserMessages.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Core/Entities/UserMessages.cs
new file mode 100644
index 000000000..06d5b1cc6
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Core/Entities/UserMessages.cs
@@ -0,0 +1,56 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace MiniSpace.Services.Communication.Core.Entities
+{
+ public class UserMessages
+ {
+ public Guid UserId { get; private set; }
+ private List _messages;
+
+ public IEnumerable Messages => _messages.AsReadOnly();
+
+ public UserMessages(Guid userId)
+ {
+ UserId = userId;
+ _messages = new List();
+ }
+
+ public void AddMessage(Message message)
+ {
+ if (message != null)
+ {
+ _messages.Add(message);
+ }
+ }
+
+ public void RemoveMessage(Guid messageId)
+ {
+ _messages.RemoveAll(m => m.Id == messageId);
+ }
+
+ public void MarkMessageAsRead(Guid messageId)
+ {
+ var message = _messages.FirstOrDefault(m => m.Id == messageId);
+ if (message != null)
+ {
+ message.MarkAsRead();
+ }
+ }
+
+ public void MarkMessageAsUnread(Guid messageId)
+ {
+ var message = _messages.FirstOrDefault(m => m.Id == messageId);
+ if (message != null)
+ {
+ message.MarkAsUnread();
+ }
+ }
+
+ public IEnumerable GetMessagesForChat(Guid chatId)
+ {
+ return _messages.Where(m => m.ChatId == chatId);
+ }
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Core/Events/IDomainEvent.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Core/Events/IDomainEvent.cs
new file mode 100644
index 000000000..c9c698407
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Core/Events/IDomainEvent.cs
@@ -0,0 +1,7 @@
+namespace MiniSpace.Services.Communication.Core.Events
+{
+ public interface IDomainEvent
+ {
+
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Core/Events/MessageAddedEvent.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Core/Events/MessageAddedEvent.cs
new file mode 100644
index 000000000..014121ee9
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Core/Events/MessageAddedEvent.cs
@@ -0,0 +1,14 @@
+namespace MiniSpace.Services.Communication.Core.Events
+{
+ public class MessageAddedEvent : IDomainEvent
+ {
+ public Guid ChatId { get; }
+ public Guid MessageId { get; }
+
+ public MessageAddedEvent(Guid chatId, Guid messageId)
+ {
+ ChatId = chatId;
+ MessageId = messageId;
+ }
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Core/Exceptions/DomainException.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Core/Exceptions/DomainException.cs
new file mode 100644
index 000000000..db88f3978
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Core/Exceptions/DomainException.cs
@@ -0,0 +1,11 @@
+namespace MiniSpace.Services.Communication.Core.Exceptions
+{
+ public abstract class DomainException : Exception
+ {
+ public virtual string Code { get; }
+
+ protected DomainException(string message) : base(message)
+ {
+ }
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Core/Exceptions/InvalidAggregateIdException.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Core/Exceptions/InvalidAggregateIdException.cs
new file mode 100644
index 000000000..0c439e479
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Core/Exceptions/InvalidAggregateIdException.cs
@@ -0,0 +1,11 @@
+namespace MiniSpace.Services.Communication.Core.Exceptions
+{
+ public class InvalidAggregateIdException : DomainException
+ {
+ public override string Code { get; } = "invalid_aggregate_id";
+
+ public InvalidAggregateIdException() : base($"Invalid aggregate id.")
+ {
+ }
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Core/MiniSpace.Services.Communication.Core.csproj b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Core/MiniSpace.Services.Communication.Core.csproj
new file mode 100644
index 000000000..cf309aa85
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Core/MiniSpace.Services.Communication.Core.csproj
@@ -0,0 +1,9 @@
+
+
+
+ net8.0
+ enable
+ disable
+
+
+
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Core/MiniSpace.Services.Communication.Core.sln b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Core/MiniSpace.Services.Communication.Core.sln
new file mode 100644
index 000000000..92b21c3c4
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Core/MiniSpace.Services.Communication.Core.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.5.002.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MiniSpace.Services.Communication.Core", "MiniSpace.Services.Communication.Core.csproj", "{42E0A571-69FB-45ED-BACA-E0F72B9C0DE1}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {42E0A571-69FB-45ED-BACA-E0F72B9C0DE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {42E0A571-69FB-45ED-BACA-E0F72B9C0DE1}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {42E0A571-69FB-45ED-BACA-E0F72B9C0DE1}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {42E0A571-69FB-45ED-BACA-E0F72B9C0DE1}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {0A93A2FC-8B65-4C19-A90B-588F58354A02}
+ EndGlobalSection
+EndGlobal
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Core/Repositories/IOrganizationChatsRepository.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Core/Repositories/IOrganizationChatsRepository.cs
new file mode 100644
index 000000000..5fcbf9835
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Core/Repositories/IOrganizationChatsRepository.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using MiniSpace.Services.Communication.Core.Entities;
+
+namespace MiniSpace.Services.Communication.Core.Repositories
+{
+ public interface IOrganizationChatsRepository
+ {
+ Task GetByOrganizationIdAsync(Guid organizationId);
+ Task AddAsync(OrganizationChats organizationChats);
+ Task UpdateAsync(OrganizationChats organizationChats);
+ Task AddOrUpdateAsync(OrganizationChats organizationChats);
+ Task DeleteAsync(Guid organizationId);
+ Task ChatExistsAsync(Guid organizationId, Guid chatId);
+ Task AddChatAsync(Guid organizationId, Chat chat);
+ Task DeleteChatAsync(Guid organizationId, Guid chatId);
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Core/Repositories/IUserChatsRepository.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Core/Repositories/IUserChatsRepository.cs
new file mode 100644
index 000000000..204c8b668
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Core/Repositories/IUserChatsRepository.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using MiniSpace.Services.Communication.Core.Entities;
+
+namespace MiniSpace.Services.Communication.Core.Repositories
+{
+ public interface IUserChatsRepository
+ {
+ Task GetByUserIdAsync(Guid userId);
+ Task AddAsync(UserChats userChats);
+ Task UpdateAsync(UserChats userChats);
+ Task AddOrUpdateAsync(UserChats userChats);
+ Task DeleteAsync(Guid userId);
+ Task ChatExistsAsync(Guid userId, Guid chatId);
+ Task AddChatAsync(Guid userId, Chat chat);
+ Task DeleteChatAsync(Guid userId, Guid chatId);
+ Task GetByChatIdAsync(Guid chatId);
+ Task> GetParticipantIdsByChatIdAsync(Guid chatId);
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Core/Wrappers/PagedResponse.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Core/Wrappers/PagedResponse.cs
new file mode 100644
index 000000000..586f3a6ab
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Core/Wrappers/PagedResponse.cs
@@ -0,0 +1,28 @@
+namespace MiniSpace.Services.Communication.Core.Wrappers
+{
+ public class PagedResponse
+ {
+ public IEnumerable Items { get; }
+ public int TotalPages { get; }
+ public int TotalItems { get; }
+ public int PageSize { get; }
+ public int Page { get; }
+ public bool First { get; }
+ public bool Last { get; }
+ public bool Empty { get; }
+ public int? NextPage => Page < TotalPages ? Page + 1 : (int?)null;
+ public int? PreviousPage => Page > 1 ? Page - 1 : (int?)null;
+
+ public PagedResponse(IEnumerable items, int page, int pageSize, int totalItems)
+ {
+ Items = items;
+ PageSize = pageSize;
+ TotalItems = totalItems;
+ TotalPages = pageSize > 0 ? (int)Math.Ceiling((decimal)totalItems / pageSize) : 0;
+ Page = page;
+ First = page == 1;
+ Last = page == TotalPages;
+ Empty = !items.Any();
+ }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Core/Wrappers/Response.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Core/Wrappers/Response.cs
new file mode 100644
index 000000000..92816d60c
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Core/Wrappers/Response.cs
@@ -0,0 +1,10 @@
+namespace MiniSpace.Services.Communication.Core.Wrappers
+{
+ public class Response
+ {
+ public T Content { get; set; }
+ public bool Succeeded { get; set; }
+ public string[] Errors { get; set; }
+ public string Message { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/.gitignore b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/.gitignore
new file mode 100644
index 000000000..1f7e99963
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/.gitignore
@@ -0,0 +1,334 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUNIT
+*.VisualState.xml
+TestResult.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+# **/Properties/launchSettings.json
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_i.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# JustCode is a .NET coding add-in
+.JustCode
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+bin/
+obj/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# JetBrains Rider
+.idea/
+*.sln.iml
+
+# CodeRush
+.cr/
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+logs/
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Contexts/AppContext.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Contexts/AppContext.cs
new file mode 100644
index 000000000..aa6839fbd
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Contexts/AppContext.cs
@@ -0,0 +1,27 @@
+using MiniSpace.Services.Communication.Application;
+
+namespace MiniSpace.Services.Communication.Infrastructure.Contexts
+{
+ internal class AppContext : IAppContext
+ {
+ public string RequestId { get; }
+ public IIdentityContext Identity { get; }
+
+ internal AppContext() : this(Guid.NewGuid().ToString("N"), IdentityContext.Empty)
+ {
+ }
+
+ internal AppContext(CorrelationContext context) : this(context.CorrelationId,
+ context.User is null ? IdentityContext.Empty : new IdentityContext(context.User))
+ {
+ }
+
+ internal AppContext(string requestId, IIdentityContext identity)
+ {
+ RequestId = requestId;
+ Identity = identity;
+ }
+
+ internal static IAppContext Empty => new AppContext();
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Contexts/AppContextFactory.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Contexts/AppContextFactory.cs
new file mode 100644
index 000000000..8f300c3d8
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Contexts/AppContextFactory.cs
@@ -0,0 +1,36 @@
+using Convey.MessageBrokers;
+using Microsoft.AspNetCore.Http;
+using Newtonsoft.Json;
+using MiniSpace.Services.Communication.Application;
+using MiniSpace.Services.Communication.Infrastructure;
+
+namespace MiniSpace.Services.Communication.Infrastructure.Contexts
+{
+ internal sealed class AppContextFactory : IAppContextFactory
+ {
+ private readonly ICorrelationContextAccessor _contextAccessor;
+ private readonly IHttpContextAccessor _httpContextAccessor;
+
+ public AppContextFactory(ICorrelationContextAccessor contextAccessor, IHttpContextAccessor httpContextAccessor)
+ {
+ _contextAccessor = contextAccessor;
+ _httpContextAccessor = httpContextAccessor;
+ }
+
+ public IAppContext Create()
+ {
+ if (_contextAccessor.CorrelationContext is { })
+ {
+ var payload = JsonConvert.SerializeObject(_contextAccessor.CorrelationContext);
+
+ return string.IsNullOrWhiteSpace(payload)
+ ? AppContext.Empty
+ : new AppContext(JsonConvert.DeserializeObject(payload));
+ }
+
+ var context = _httpContextAccessor.GetCorrelationContext();
+
+ return context is null ? AppContext.Empty : new AppContext(context);
+ }
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Contexts/CorrelationContext.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Contexts/CorrelationContext.cs
new file mode 100644
index 000000000..8c2def9a0
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Contexts/CorrelationContext.cs
@@ -0,0 +1,22 @@
+namespace MiniSpace.Services.Communication.Infrastructure.Contexts
+{
+ internal class CorrelationContext
+ {
+ public string CorrelationId { get; set; }
+ public string SpanContext { get; set; }
+ public UserContext User { get; set; }
+ public string ResourceId { get; set; }
+ public string TraceId { get; set; }
+ public string ConnectionId { get; set; }
+ public string Name { get; set; }
+ public DateTime CreatedAt { get; set; }
+
+ public class UserContext
+ {
+ public string Id { get; set; }
+ public bool IsAuthenticated { get; set; }
+ public string Role { get; set; }
+ public IDictionary Claims { get; set; }
+ }
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Contexts/IdentityContext.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Contexts/IdentityContext.cs
new file mode 100644
index 000000000..1e06fc482
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Contexts/IdentityContext.cs
@@ -0,0 +1,39 @@
+using MiniSpace.Services.Communication.Application;
+
+namespace MiniSpace.Services.Communication.Infrastructure.Contexts
+{
+ internal class IdentityContext : IIdentityContext
+ {
+ public Guid Id { get; }
+ public string Role { get; } = string.Empty;
+ public string Name { get; } = string.Empty;
+ public string Email { get; } = string.Empty;
+ public bool IsAuthenticated { get; }
+ public bool IsAdmin { get; }
+ public bool IsBanned { get; }
+ public IDictionary Claims { get; } = new Dictionary();
+
+ internal IdentityContext()
+ {
+ }
+
+ internal IdentityContext(CorrelationContext.UserContext context)
+ : this(context.Id, context.Role, context.IsAuthenticated, context.Claims)
+ {
+ }
+
+ internal IdentityContext(string id, string role, bool isAuthenticated, IDictionary claims)
+ {
+ Id = Guid.TryParse(id, out var userId) ? userId : Guid.Empty;
+ Role = role ?? string.Empty;
+ IsAuthenticated = isAuthenticated;
+ IsAdmin = Role.Equals("admin", StringComparison.InvariantCultureIgnoreCase);
+ IsBanned = Role.Equals("banned", StringComparison.InvariantCultureIgnoreCase);
+ Claims = claims ?? new Dictionary();
+ Name = Claims.TryGetValue("name", out var name) ? name : string.Empty;
+ Email = Claims.TryGetValue("email", out var email) ? email : string.Empty;
+ }
+
+ internal static IIdentityContext Empty => new IdentityContext();
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs
new file mode 100644
index 000000000..849f12031
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs
@@ -0,0 +1,35 @@
+using Convey.CQRS.Commands;
+using Convey.MessageBrokers;
+using Convey.MessageBrokers.Outbox;
+using Convey.Types;
+
+namespace MiniSpace.Services.Communication.Infrastructure.Decorators
+{
+ [Decorator]
+ internal sealed class OutboxCommandHandlerDecorator : ICommandHandler
+ where TCommand : class, ICommand
+ {
+ private readonly ICommandHandler _handler;
+ private readonly IMessageOutbox _outbox;
+ private readonly string _messageId;
+ private readonly bool _enabled;
+
+ public OutboxCommandHandlerDecorator(ICommandHandler handler, IMessageOutbox outbox,
+ OutboxOptions outboxOptions, IMessagePropertiesAccessor messagePropertiesAccessor)
+ {
+ _handler = handler;
+ _outbox = outbox;
+ _enabled = outboxOptions.Enabled;
+
+ var messageProperties = messagePropertiesAccessor.MessageProperties;
+ _messageId = string.IsNullOrWhiteSpace(messageProperties?.MessageId)
+ ? Guid.NewGuid().ToString("N")
+ : messageProperties.MessageId;
+ }
+
+ public Task HandleAsync(TCommand command, CancellationToken cancellationToken)
+ => _enabled
+ ? _outbox.HandleAsync(_messageId, () => _handler.HandleAsync(command))
+ : _handler.HandleAsync(command);
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs
new file mode 100644
index 000000000..3e1e88706
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs
@@ -0,0 +1,35 @@
+using Convey.CQRS.Events;
+using Convey.MessageBrokers;
+using Convey.MessageBrokers.Outbox;
+using Convey.Types;
+
+namespace MiniSpace.Services.Communication.Infrastructure.Decorators
+{
+ [Decorator]
+ internal sealed class OutboxEventHandlerDecorator : IEventHandler
+ where TEvent : class, IEvent
+ {
+ private readonly IEventHandler _handler;
+ private readonly IMessageOutbox _outbox;
+ private readonly string _messageId;
+ private readonly bool _enabled;
+
+ public OutboxEventHandlerDecorator(IEventHandler handler, IMessageOutbox outbox,
+ OutboxOptions outboxOptions, IMessagePropertiesAccessor messagePropertiesAccessor)
+ {
+ _handler = handler;
+ _outbox = outbox;
+ _enabled = outboxOptions.Enabled;
+
+ var messageProperties = messagePropertiesAccessor.MessageProperties;
+ _messageId = string.IsNullOrWhiteSpace(messageProperties?.MessageId)
+ ? Guid.NewGuid().ToString("N")
+ : messageProperties.MessageId;
+ }
+
+ public Task HandleAsync(TEvent @event, CancellationToken cancellationToken)
+ => _enabled
+ ? _outbox.HandleAsync(_messageId, () => _handler.HandleAsync(@event))
+ : _handler.HandleAsync(@event);
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Exceptions/ExceptionToMessageMapper.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Exceptions/ExceptionToMessageMapper.cs
new file mode 100644
index 000000000..dd910203c
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Exceptions/ExceptionToMessageMapper.cs
@@ -0,0 +1,40 @@
+using Convey.MessageBrokers.RabbitMQ;
+using MiniSpace.Services.Communication.Application.Commands;
+using MiniSpace.Services.Communication.Application.Events.Rejected;
+using MiniSpace.Services.Communication.Application.Exceptions;
+using System;
+
+namespace MiniSpace.Services.Communication.Infrastructure.Exceptions
+{
+ internal sealed class ExceptionToMessageMapper : IExceptionToMessageMapper
+ {
+ public object Map(Exception exception, object message)
+ => exception switch
+ {
+ // ChatNotFoundException ex => message switch
+ // {
+ // DeleteChat command => new ChatDeletionRejected(command.ChatId, "Chat not found", ex.Code),
+ // CreateChat command => new ChatCreationRejected(command.ChatId, "Chat not found", ex.Code),
+ // AddUserToChat command => new UserAdditionToChatRejected(command.ChatId, command.UserId, "Chat not found", ex.Code),
+ // SendMessage command => new MessageSendRejected(command.ChatId, Guid.NewGuid(), "Chat not found", ex.Code),
+ // _ => new ChatProcessRejected(ex.ChatId, ex.Message, ex.Code),
+ // },
+ // MessageNotFoundException ex => message switch
+ // {
+ // DeleteMessage command => new MessageSendRejected(command.ChatId, command.MessageId, "Message not found", ex.Code),
+ // _ => new MessageProcessRejected(ex.MessageId, ex.Message, ex.Code),
+ // },
+ // InvalidChatOperationException ex => message switch
+ // {
+ // AddUserToChat command => new UserAdditionToChatRejected(command.ChatId, command.UserId, ex.Message, ex.Code),
+ // SendMessage command => new MessageSendRejected(command.ChatId, Guid.NewGuid(), ex.Message, ex.Code),
+ // _ => new ChatProcessRejected(Guid.Empty, ex.Message, ex.Code),
+ // },
+ // AppException ex => message switch
+ // {
+ // _ => new ChatProcessRejected(Guid.Empty, ex.Message, ex.Code)
+ // },
+ // _ => null
+ };
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Exceptions/ExceptionToResponseMapper.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Exceptions/ExceptionToResponseMapper.cs
new file mode 100644
index 000000000..6338b7896
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Exceptions/ExceptionToResponseMapper.cs
@@ -0,0 +1,46 @@
+using System.Collections.Concurrent;
+using System.Net;
+using Convey;
+using Convey.WebApi.Exceptions;
+using MiniSpace.Services.Communication.Application.Exceptions;
+using MiniSpace.Services.Communication.Core.Exceptions;
+
+namespace MiniSpace.Services.Communication.Infrastructure.Exceptions
+{
+ internal sealed class ExceptionToResponseMapper : IExceptionToResponseMapper
+ {
+ private static readonly ConcurrentDictionary Codes = new ConcurrentDictionary();
+
+ public ExceptionResponse Map(Exception exception)
+ => exception switch
+ {
+ DomainException ex => new ExceptionResponse(new {code = GetCode(ex), reason = ex.Message},
+ HttpStatusCode.BadRequest),
+ AppException ex => new ExceptionResponse(new {code = GetCode(ex), reason = ex.Message},
+ HttpStatusCode.BadRequest),
+ _ => new ExceptionResponse(new {code = "error", reason = "There was an error."},
+ HttpStatusCode.BadRequest)
+ };
+
+ private static string GetCode(Exception exception)
+ {
+ var type = exception.GetType();
+ if (Codes.TryGetValue(type, out var code))
+ {
+ return code;
+ }
+
+ var exceptionCode = exception switch
+ {
+ DomainException domainException when !string.IsNullOrWhiteSpace(domainException.Code) => domainException
+ .Code,
+ AppException appException when !string.IsNullOrWhiteSpace(appException.Code) => appException.Code,
+ _ => exception.GetType().Name.Underscore().Replace("_exception", string.Empty)
+ };
+
+ Codes.TryAdd(type, exceptionCode);
+
+ return exceptionCode;
+ }
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Extensions.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Extensions.cs
new file mode 100644
index 000000000..a0779ab56
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Extensions.cs
@@ -0,0 +1,165 @@
+using System.Text;
+using Convey;
+using Convey.CQRS.Commands;
+using Convey.CQRS.Events;
+using Convey.CQRS.Queries;
+using Convey.Discovery.Consul;
+using Convey.Docs.Swagger;
+using Convey.HTTP;
+using Convey.LoadBalancing.Fabio;
+using Convey.MessageBrokers;
+using Convey.MessageBrokers.CQRS;
+using Convey.MessageBrokers.Outbox;
+using Convey.MessageBrokers.Outbox.Mongo;
+using Convey.MessageBrokers.RabbitMQ;
+using Convey.Metrics.AppMetrics;
+using Convey.Persistence.MongoDB;
+using Convey.Persistence.Redis;
+using Convey.Security;
+using Convey.Tracing.Jaeger;
+using Convey.Tracing.Jaeger.RabbitMQ;
+using Convey.WebApi;
+using Convey.WebApi.CQRS;
+using Convey.WebApi.Security;
+using Convey.WebApi.Swagger;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.DependencyInjection;
+using Newtonsoft.Json;
+using MiniSpace.Services.Communication.Application;
+using MiniSpace.Services.Communication.Application.Commands;
+using MiniSpace.Services.Communication.Application.Services;
+using MiniSpace.Services.Communication.Core.Repositories;
+using MiniSpace.Services.Communication.Infrastructure.Contexts;
+using MiniSpace.Services.Communication.Infrastructure.Decorators;
+using MiniSpace.Services.Communication.Infrastructure.Exceptions;
+using MiniSpace.Services.Communication.Infrastructure.Logging;
+using MiniSpace.Services.Communication.Infrastructure.Mongo.Documents;
+using MiniSpace.Services.Communication.Infrastructure.Mongo.Repositories;
+using MiniSpace.Services.Communication.Infrastructure.Services;
+using MiniSpace.Services.Communication.Application.Services.Clients;
+using MiniSpace.Services.Communication.Infrastructure.Services.Clients;
+using MiniSpace.Services.Communication.Application.Hubs;
+
+namespace MiniSpace.Services.Communication.Infrastructure
+{
+ public static class Extensions
+ {
+ public static IConveyBuilder AddInfrastructure(this IConveyBuilder builder)
+ {
+ builder.Services.AddSingleton();
+ builder.Services.AddSingleton();
+
+ builder.Services.AddTransient();
+ builder.Services.AddTransient();
+ builder.Services.AddTransient();
+
+ builder.Services.AddSingleton();
+ builder.Services.AddTransient();
+ builder.Services.AddTransient();
+ builder.Services.AddTransient();
+
+ builder.Services.AddTransient(ctx => ctx.GetRequiredService().Create());
+ builder.Services.TryDecorate(typeof(ICommandHandler<>), typeof(OutboxCommandHandlerDecorator<>));
+ builder.Services.TryDecorate(typeof(IEventHandler<>), typeof(OutboxEventHandlerDecorator<>));
+
+
+ return builder
+ .AddErrorHandler()
+ .AddQueryHandlers()
+ .AddInMemoryQueryDispatcher()
+ .AddHttpClient()
+ .AddConsul()
+ .AddFabio()
+ .AddRabbitMq(plugins: p => p.AddJaegerRabbitMqPlugin())
+ .AddMessageOutbox(o => o.AddMongo())
+ .AddExceptionToMessageMapper()
+ .AddMongo()
+ .AddRedis()
+ .AddMetrics()
+ .AddJaeger()
+ .AddHandlersLogging()
+ .AddMongoRepository("organizations_chats")
+ .AddMongoRepository("user_chats")
+ .AddSignalRInfrastructure()
+ .AddWebApiSwaggerDocs()
+ .AddCertificateAuthentication()
+ .AddSecurity();
+ }
+
+ public static IApplicationBuilder UseInfrastructure(this IApplicationBuilder app)
+ {
+ app.UseErrorHandler()
+ .UseSwaggerDocs()
+ .UseJaeger()
+ .UseConvey()
+ .UsePublicContracts()
+ .UseMetrics()
+ .UseCertificateAuthentication()
+ .UseRabbitMq()
+ .SubscribeCommand()
+ .SubscribeCommand()
+ .SubscribeCommand()
+ .SubscribeCommand()
+ .SubscribeCommand()
+ .SubscribeCommand()
+ .SubscribeCommand()
+ ;
+ return app;
+ }
+
+ public static IConveyBuilder AddSignalRInfrastructure(this IConveyBuilder builder)
+ {
+ builder.Services.AddCors(options =>
+ {
+ options.AddPolicy("CorsPolicy",
+ builder => builder
+ .AllowAnyMethod()
+ .AllowAnyHeader()
+ .AllowCredentials()
+ .SetIsOriginAllowed((host) => true));
+ });
+
+ builder.Services.AddSignalR();
+
+ return builder;
+ }
+
+
+ internal static CorrelationContext GetCorrelationContext(this IHttpContextAccessor accessor)
+ => accessor.HttpContext?.Request.Headers.TryGetValue("Correlation-Context", out var json) is true
+ ? JsonConvert.DeserializeObject(json.FirstOrDefault())
+ : null;
+
+ internal static IDictionary GetHeadersToForward(this IMessageProperties messageProperties)
+ {
+ const string sagaHeader = "Saga";
+ if (messageProperties?.Headers is null || !messageProperties.Headers.TryGetValue(sagaHeader, out var saga))
+ {
+ return null;
+ }
+
+ return saga is null
+ ? null
+ : new Dictionary
+ {
+ [sagaHeader] = saga
+ };
+ }
+
+ internal static string GetSpanContext(this IMessageProperties messageProperties, string header)
+ {
+ if (messageProperties is null)
+ {
+ return string.Empty;
+ }
+
+ if (messageProperties.Headers.TryGetValue(header, out var span) && span is byte[] spanBytes)
+ {
+ return Encoding.UTF8.GetString(spanBytes);
+ }
+
+ return string.Empty;
+ }
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/IAppContextFactory.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/IAppContextFactory.cs
new file mode 100644
index 000000000..64a4521dc
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/IAppContextFactory.cs
@@ -0,0 +1,9 @@
+using MiniSpace.Services.Communication.Application;
+
+namespace MiniSpace.Services.Communication.Infrastructure
+{
+ public interface IAppContextFactory
+ {
+ IAppContext Create();
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Logging/Extensions.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Logging/Extensions.cs
new file mode 100644
index 000000000..ef9726c4f
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Logging/Extensions.cs
@@ -0,0 +1,22 @@
+using Convey;
+using Convey.Logging.CQRS;
+using Microsoft.Extensions.DependencyInjection;
+using MiniSpace.Services.Communication.Application.Commands;
+using System.Reflection;
+
+namespace MiniSpace.Services.Communication.Infrastructure.Logging
+{
+ internal static class Extensions
+ {
+ public static IConveyBuilder AddHandlersLogging(this IConveyBuilder builder)
+ {
+ var assembly = typeof(UpdateMessageStatus).Assembly;
+
+ builder.Services.AddSingleton();
+
+ return builder
+ .AddCommandHandlersLogging(assembly)
+ .AddEventHandlersLogging(assembly);
+ }
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Logging/MessageToLogTemplateMapper.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Logging/MessageToLogTemplateMapper.cs
new file mode 100644
index 000000000..bfe40da1b
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Logging/MessageToLogTemplateMapper.cs
@@ -0,0 +1,77 @@
+using Convey.Logging.CQRS;
+using Microsoft.Extensions.Logging;
+using MiniSpace.Services.Communication.Application.Commands;
+using System;
+using System.Collections.Generic;
+
+namespace MiniSpace.Services.Communication.Infrastructure.Logging
+{
+ internal sealed class MessageToLogTemplateMapper : IMessageToLogTemplateMapper
+ {
+ private readonly ILogger _logger;
+
+ private static IReadOnlyDictionary MessageTemplates => new Dictionary
+ {
+ {
+ typeof(AddUserToChat), new HandlerLogTemplate
+ {
+ After = "Added user with id: {UserId} to chat with id: {ChatId}."
+ }
+ },
+ {
+ typeof(CreateChat), new HandlerLogTemplate
+ {
+ After = "Created chat with id: {ChatId}, participants: {ParticipantIds}, and name: '{ChatName}'."
+ }
+ },
+ {
+ typeof(DeleteChat), new HandlerLogTemplate
+ {
+ After = "Deleted chat with id: {ChatId}."
+ }
+ },
+ {
+ typeof(DeleteMessage), new HandlerLogTemplate
+ {
+ After = "Deleted message with id: {MessageId} from chat with id: {ChatId}."
+ }
+ },
+ {
+ typeof(RemoveUserFromChat), new HandlerLogTemplate
+ {
+ After = "Removed user with id: {UserId} from chat with id: {ChatId}."
+ }
+ },
+ {
+ typeof(SendMessage), new HandlerLogTemplate
+ {
+ After = "Sent message in chat with id: {ChatId} by user with id: {SenderId}, content: '{Content}', and type: '{MessageType}'."
+ }
+ },
+ {
+ typeof(UpdateMessageStatus), new HandlerLogTemplate
+ {
+ After = "Updated message status to '{Status}' for message with id: {MessageId} in chat with id: {ChatId}."
+ }
+ }
+ };
+
+ public MessageToLogTemplateMapper(ILogger logger)
+ {
+ _logger = logger;
+ }
+
+ public HandlerLogTemplate Map(TMessage message) where TMessage : class
+ {
+ var messageType = message.GetType();
+ _logger.LogInformation($"Attempting to map message type: {messageType.Name}");
+ if (MessageTemplates.TryGetValue(messageType, out var template))
+ {
+ _logger.LogInformation($"Mapping found. Template: {template.After}");
+ return template;
+ }
+ _logger.LogWarning($"No mapping found for message type: {messageType.Name}");
+ return null;
+ }
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/MiniSpace.Services.Communication.Infrastructure.csproj b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/MiniSpace.Services.Communication.Infrastructure.csproj
new file mode 100644
index 000000000..4992a6cf3
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/MiniSpace.Services.Communication.Infrastructure.csproj
@@ -0,0 +1,40 @@
+
+
+
+ net8.0
+ enable
+ disable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/MiniSpace.Services.Communication.Infrastructure.sln b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/MiniSpace.Services.Communication.Infrastructure.sln
new file mode 100644
index 000000000..4024f4b05
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/MiniSpace.Services.Communication.Infrastructure.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.5.002.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MiniSpace.Services.Communication.Infrastructure", "MiniSpace.Services.Communication.Infrastructure.csproj", "{994B662D-5C21-4077-8970-C61A227B46D4}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {994B662D-5C21-4077-8970-C61A227B46D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {994B662D-5C21-4077-8970-C61A227B46D4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {994B662D-5C21-4077-8970-C61A227B46D4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {994B662D-5C21-4077-8970-C61A227B46D4}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {ADCE09CD-5371-42F5-9AB2-04FCDA89D8EA}
+ EndGlobalSection
+EndGlobal
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Documents/ChatDocument.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Documents/ChatDocument.cs
new file mode 100644
index 000000000..98bf65ee4
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Documents/ChatDocument.cs
@@ -0,0 +1,25 @@
+using Convey.Types;
+using MongoDB.Bson;
+using MongoDB.Bson.Serialization.Attributes;
+using System;
+using System.Collections.Generic;
+
+namespace MiniSpace.Services.Communication.Infrastructure.Mongo.Documents
+{
+ public class ChatDocument : IIdentifiable
+ {
+ [BsonId]
+ [BsonRepresentation(BsonType.String)]
+ public Guid Id { get; set; }
+
+ public List ParticipantIds { get; set; }
+
+ public List Messages { get; set; }
+
+ public ChatDocument()
+ {
+ ParticipantIds = new List();
+ Messages = new List();
+ }
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Documents/Extensions.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Documents/Extensions.cs
new file mode 100644
index 000000000..c82c4f595
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Documents/Extensions.cs
@@ -0,0 +1,84 @@
+using MiniSpace.Services.Communication.Application.Dto;
+using MiniSpace.Services.Communication.Core.Entities;
+using MiniSpace.Services.Communication.Infrastructure.Mongo.Documents;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace MiniSpace.Services.Communication.Infrastructure.Mongo.Documents
+{
+ public static class Extensions
+{
+ // Converts MessageDocument to Message entity
+ public static Message AsEntity(this MessageDocument document)
+ {
+ // Use the existing ID and other properties directly from the document
+ return new Message(
+ document.Id, // Use the existing ID from the document
+ document.ChatId,
+ document.SenderId,
+ document.ReceiverId,
+ document.Content,
+ document.Timestamp,
+ document.Type,
+ document.Status // Ensure the status is loaded correctly
+ );
+ }
+
+ public static MessageDocument AsDocument(this Message entity)
+ {
+ return new MessageDocument
+ {
+ Id = entity.Id,
+ ChatId = entity.ChatId,
+ SenderId = entity.SenderId,
+ ReceiverId = entity.ReceiverId,
+ Content = entity.Content,
+ Timestamp = entity.Timestamp,
+ Type = entity.Type,
+ Status = entity.Status
+ };
+ }
+
+ public static Chat AsEntity(this ChatDocument document)
+ {
+ var messages = document.Messages.Select(m => m.AsEntity()).ToList();
+ return new Chat(document.Id, document.ParticipantIds, messages);
+ }
+
+ public static ChatDocument AsDocument(this Chat entity)
+ {
+ return new ChatDocument
+ {
+ Id = entity.Id,
+ ParticipantIds = entity.ParticipantIds,
+ Messages = entity.Messages.Select(m => m.AsDocument()).ToList()
+ };
+ }
+
+ public static ChatDto AsDto(this Chat entity)
+ {
+ return new ChatDto
+ {
+ Id = entity.Id,
+ ParticipantIds = entity.ParticipantIds,
+ Messages = entity.Messages.Select(m => m.AsDto()).ToList()
+ };
+ }
+
+ public static MessageDto AsDto(this Message entity)
+ {
+ return new MessageDto
+ {
+ Id = entity.Id, // Ensure the ID is correctly mapped
+ ChatId = entity.ChatId,
+ SenderId = entity.SenderId,
+ Content = entity.Content,
+ Timestamp = entity.Timestamp,
+ MessageType = entity.Type.ToString(),
+ Status = entity.Status.ToString()
+ };
+ }
+}
+
+ }
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Documents/MessageDocument.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Documents/MessageDocument.cs
new file mode 100644
index 000000000..24ecd3022
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Documents/MessageDocument.cs
@@ -0,0 +1,22 @@
+using Convey.Types;
+using MongoDB.Bson;
+using MongoDB.Bson.Serialization.Attributes;
+using MiniSpace.Services.Communication.Core.Entities;
+using System;
+
+namespace MiniSpace.Services.Communication.Infrastructure.Mongo.Documents
+{
+ public class MessageDocument : IIdentifiable
+ {
+ [BsonId]
+ [BsonRepresentation(BsonType.String)]
+ public Guid Id { get; set; }
+ public Guid ChatId { get; set; }
+ public Guid SenderId { get; set; }
+ public Guid ReceiverId { get; set; }
+ public string Content { get; set; }
+ public DateTime Timestamp { get; set; }
+ public MessageType Type { get; set; }
+ public MessageStatus Status { get; set; }
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Documents/OrganizationChatsDocument.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Documents/OrganizationChatsDocument.cs
new file mode 100644
index 000000000..f65e70d73
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Documents/OrganizationChatsDocument.cs
@@ -0,0 +1,21 @@
+using Convey.Types;
+using MongoDB.Bson;
+using MongoDB.Bson.Serialization.Attributes;
+using System;
+using System.Collections.Generic;
+
+namespace MiniSpace.Services.Communication.Infrastructure.Mongo.Documents
+{
+ public class OrganizationChatsDocument : IIdentifiable
+ {
+ [BsonId]
+ [BsonRepresentation(BsonType.String)]
+ public Guid Id { get; set; }
+ public Guid OrganizationId { get; set; }
+ public List Chats { get; set; }
+ public OrganizationChatsDocument()
+ {
+ Chats = new List();
+ }
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Documents/OrganizationChatsExtensions.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Documents/OrganizationChatsExtensions.cs
new file mode 100644
index 000000000..7be5c2722
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Documents/OrganizationChatsExtensions.cs
@@ -0,0 +1,32 @@
+using MiniSpace.Services.Communication.Core.Entities;
+using MiniSpace.Services.Communication.Infrastructure.Mongo.Documents;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace MiniSpace.Services.Communication.Infrastructure.Mongo.Documents
+{
+ public static class OrganizationChatsExtensions
+ {
+ public static OrganizationChats AsEntity(this OrganizationChatsDocument document)
+ {
+ var organizationChats = new OrganizationChats(document.OrganizationId);
+ foreach (var chatDocument in document.Chats)
+ {
+ var chat = chatDocument.AsEntity();
+ organizationChats.AddChat(chat);
+ }
+ return organizationChats;
+ }
+
+ public static OrganizationChatsDocument AsDocument(this OrganizationChats entity)
+ {
+ return new OrganizationChatsDocument
+ {
+ Id = Guid.NewGuid(),
+ OrganizationId = entity.OrganizationId,
+ Chats = entity.Chats.Select(c => c.AsDocument()).ToList()
+ };
+ }
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Documents/UserChatsDocument.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Documents/UserChatsDocument.cs
new file mode 100644
index 000000000..8e39b3f25
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Documents/UserChatsDocument.cs
@@ -0,0 +1,21 @@
+using Convey.Types;
+using MongoDB.Bson;
+using MongoDB.Bson.Serialization.Attributes;
+using System;
+using System.Collections.Generic;
+
+namespace MiniSpace.Services.Communication.Infrastructure.Mongo.Documents
+{
+ public class UserChatsDocument : IIdentifiable
+ {
+ [BsonId]
+ [BsonRepresentation(BsonType.String)]
+ public Guid Id { get; set; }
+ public Guid UserId { get; set; }
+ public List Chats { get; set; }
+ public UserChatsDocument()
+ {
+ Chats = new List();
+ }
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Documents/UserChatsExtensions.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Documents/UserChatsExtensions.cs
new file mode 100644
index 000000000..a1a702e2b
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Documents/UserChatsExtensions.cs
@@ -0,0 +1,32 @@
+using MiniSpace.Services.Communication.Core.Entities;
+using MiniSpace.Services.Communication.Infrastructure.Mongo.Documents;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace MiniSpace.Services.Communication.Infrastructure.Mongo.Documents
+{
+ public static class UserChatsExtensions
+ {
+ public static UserChats AsEntity(this UserChatsDocument document)
+ {
+ var userChats = new UserChats(document.UserId);
+ foreach (var chatDocument in document.Chats)
+ {
+ var chat = chatDocument.AsEntity();
+ userChats.AddChat(chat);
+ }
+ return userChats;
+ }
+
+ public static UserChatsDocument AsDocument(this UserChats entity)
+ {
+ return new UserChatsDocument
+ {
+ Id = Guid.NewGuid(),
+ UserId = entity.UserId,
+ Chats = entity.Chats.Select(c => c.AsDocument()).ToList()
+ };
+ }
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Queries/Handlers/GetChatByIdHandler.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Queries/Handlers/GetChatByIdHandler.cs
new file mode 100644
index 000000000..bd7bb4376
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Queries/Handlers/GetChatByIdHandler.cs
@@ -0,0 +1,29 @@
+using System.Threading.Tasks;
+using Convey.CQRS.Queries;
+using MiniSpace.Services.Communication.Application.Dto;
+using MiniSpace.Services.Communication.Application.Queries;
+using MiniSpace.Services.Communication.Core.Repositories;
+using MiniSpace.Services.Communication.Infrastructure.Mongo.Documents;
+
+namespace MiniSpace.Services.Communication.Infrastructure.Mongo.Queries.Handlers
+{
+ public class GetChatByIdHandler : IQueryHandler
+ {
+ private readonly IUserChatsRepository _userChatsRepository;
+ private readonly IOrganizationChatsRepository _organizationChatsRepository;
+
+ public GetChatByIdHandler(IUserChatsRepository userChatsRepository, IOrganizationChatsRepository organizationChatsRepository)
+ {
+ _userChatsRepository = userChatsRepository;
+ _organizationChatsRepository = organizationChatsRepository;
+ }
+
+ public async Task HandleAsync(GetChatById query, CancellationToken cancellationToken)
+ {
+ var userChat = await _userChatsRepository.GetByUserIdAsync(query.ChatId);
+ var chat = userChat?.GetChatById(query.ChatId) ?? (await _organizationChatsRepository.GetByOrganizationIdAsync(query.ChatId))?.GetChatById(query.ChatId);
+
+ return chat?.AsDocument().AsEntity().AsDto();
+ }
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Queries/Handlers/GetMessagesForChatHandler.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Queries/Handlers/GetMessagesForChatHandler.cs
new file mode 100644
index 000000000..84c91211f
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Queries/Handlers/GetMessagesForChatHandler.cs
@@ -0,0 +1,37 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Text.Json;
+using System.Threading;
+using System.Threading.Tasks;
+using Convey.CQRS.Queries;
+using MiniSpace.Services.Communication.Application.Dto;
+using MiniSpace.Services.Communication.Application.Queries;
+using MiniSpace.Services.Communication.Core.Repositories;
+using MiniSpace.Services.Communication.Infrastructure.Mongo.Documents;
+
+
+namespace MiniSpace.Services.Communication.Infrastructure.Mongo.Queries.Handlers
+{
+ public class GetMessagesForChatHandler : IQueryHandler>
+ {
+ private readonly IUserChatsRepository _userChatsRepository;
+
+ public GetMessagesForChatHandler(IUserChatsRepository userChatsRepository)
+ {
+ _userChatsRepository = userChatsRepository;
+ }
+
+ public async Task> HandleAsync(GetMessagesForChat query, CancellationToken cancellationToken)
+ {
+ var chat = await _userChatsRepository.GetByChatIdAsync(query.ChatId);
+
+ if (chat != null)
+ {
+ var messages = chat.Messages.Select(m => m.AsDto()).ToList();
+ return messages;
+ }
+
+ return Enumerable.Empty();
+ }
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Queries/Handlers/GetUserChatsHandler.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Queries/Handlers/GetUserChatsHandler.cs
new file mode 100644
index 000000000..aa0ede7b9
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Queries/Handlers/GetUserChatsHandler.cs
@@ -0,0 +1,45 @@
+using System.Linq;
+using System.Threading.Tasks;
+using Convey.CQRS.Queries;
+using MiniSpace.Services.Communication.Infrastructure.Mongo.Documents;
+using MiniSpace.Services.Communication.Application.Dto;
+using MiniSpace.Services.Communication.Application.Queries;
+using MiniSpace.Services.Communication.Core.Repositories;
+using MiniSpace.Services.Communication.Core.Wrappers;
+
+namespace MiniSpace.Services.Communication.Infrastructure.Mongo.Queries.Handlers
+{
+ public class GetUserChatsHandler : IQueryHandler>
+ {
+ private readonly IUserChatsRepository _userChatsRepository;
+
+ public GetUserChatsHandler(IUserChatsRepository userChatsRepository)
+ {
+ _userChatsRepository = userChatsRepository;
+ }
+
+ public async Task> HandleAsync(GetUserChats query, CancellationToken cancellationToken)
+ {
+ var userChats = await _userChatsRepository.GetByUserIdAsync(query.UserId);
+
+ if (userChats == null || !userChats.Chats.Any())
+ {
+ return new PagedResponse(Enumerable.Empty(), 0, query.PageSize, 0);
+ }
+
+ var paginatedChats = userChats.Chats
+ .Skip((query.Page - 1) * query.PageSize)
+ .Take(query.PageSize)
+ .Select(chat => chat.AsDto())
+ .ToList();
+
+ var userChatDto = new UserChatDto
+ {
+ UserId = query.UserId,
+ Chats = paginatedChats
+ };
+
+ return new PagedResponse(new List { userChatDto }, query.Page, query.PageSize, userChats.Chats.Count);
+ }
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Repositories/OrganizationChatsRepository.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Repositories/OrganizationChatsRepository.cs
new file mode 100644
index 000000000..909cd3a47
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Repositories/OrganizationChatsRepository.cs
@@ -0,0 +1,81 @@
+using MiniSpace.Services.Communication.Core.Entities;
+using MiniSpace.Services.Communication.Core.Repositories;
+using MiniSpace.Services.Communication.Infrastructure.Mongo.Documents;
+using MongoDB.Driver;
+using Convey.Persistence.MongoDB;
+using System;
+using System.Threading.Tasks;
+
+namespace MiniSpace.Services.Communication.Infrastructure.Mongo.Repositories
+{
+ public class OrganizationChatsRepository : IOrganizationChatsRepository
+ {
+ private readonly IMongoRepository _repository;
+
+ public OrganizationChatsRepository(IMongoRepository repository)
+ {
+ _repository = repository;
+ }
+
+ public async Task GetByOrganizationIdAsync(Guid organizationId)
+ {
+ var document = await _repository.GetAsync(x => x.OrganizationId == organizationId);
+ return document?.AsEntity();
+ }
+
+ public async Task AddAsync(OrganizationChats organizationChats)
+ {
+ await _repository.AddAsync(organizationChats.AsDocument());
+ }
+
+ public async Task UpdateAsync(OrganizationChats organizationChats)
+ {
+ await _repository.UpdateAsync(organizationChats.AsDocument());
+ }
+
+ public async Task AddOrUpdateAsync(OrganizationChats organizationChats)
+ {
+ var existingDocument = await _repository.GetAsync(x => x.OrganizationId == organizationChats.OrganizationId);
+ if (existingDocument == null)
+ {
+ await AddAsync(organizationChats);
+ }
+ else
+ {
+ await UpdateAsync(organizationChats);
+ }
+ }
+
+ public async Task DeleteAsync(Guid organizationId)
+ {
+ await _repository.DeleteAsync(x => x.OrganizationId == organizationId);
+ }
+
+ public async Task ChatExistsAsync(Guid organizationId, Guid chatId)
+ {
+ var document = await _repository.GetAsync(x => x.OrganizationId == organizationId && x.Chats.Any(c => c.Id == chatId));
+ return document != null;
+ }
+
+ public async Task AddChatAsync(Guid organizationId, Chat chat)
+ {
+ var organizationChats = await GetByOrganizationIdAsync(organizationId) ?? new OrganizationChats(organizationId);
+ organizationChats.AddChat(chat);
+ await AddOrUpdateAsync(organizationChats);
+ }
+
+ public async Task DeleteChatAsync(Guid organizationId, Guid chatId)
+ {
+ var organizationChats = await GetByOrganizationIdAsync(organizationId);
+ if (organizationChats != null)
+ {
+ var chat = organizationChats.GetChatById(chatId);
+ if (chat != null)
+ {
+ organizationChats.Chats.Remove(chat);
+ await UpdateAsync(organizationChats);
+ }
+ }
+ }
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Repositories/UserChatsRepository.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Repositories/UserChatsRepository.cs
new file mode 100644
index 000000000..1db100707
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Repositories/UserChatsRepository.cs
@@ -0,0 +1,106 @@
+using MiniSpace.Services.Communication.Core.Entities;
+using MiniSpace.Services.Communication.Core.Repositories;
+using MiniSpace.Services.Communication.Infrastructure.Mongo.Documents;
+using MongoDB.Driver;
+using Convey.Persistence.MongoDB;
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace MiniSpace.Services.Communication.Infrastructure.Mongo.Repositories
+{
+ public class UserChatsRepository : IUserChatsRepository
+ {
+ private readonly IMongoRepository _repository;
+
+ public UserChatsRepository(IMongoRepository repository)
+ {
+ _repository = repository;
+ }
+
+ public async Task GetByUserIdAsync(Guid userId)
+ {
+ var document = await _repository.GetAsync(x => x.UserId == userId);
+ return document?.AsEntity();
+ }
+
+ public async Task GetByChatIdAsync(Guid chatId)
+ {
+ var document = await _repository.Collection.Find(x => x.Chats.Any(c => c.Id == chatId)).FirstOrDefaultAsync();
+ var chatDocument = document?.Chats.FirstOrDefault(c => c.Id == chatId);
+ return chatDocument?.AsEntity();
+ }
+
+ public async Task AddAsync(UserChats userChats)
+ {
+ await _repository.AddAsync(userChats.AsDocument());
+ }
+
+ public async Task UpdateAsync(UserChats userChats)
+ {
+ var filter = Builders.Filter.Eq(doc => doc.UserId, userChats.UserId);
+ var update = Builders.Update
+ .Set(doc => doc.Chats, userChats.Chats.Select(chat => chat.AsDocument()).ToList());
+
+ await _repository.Collection.UpdateOneAsync(filter, update);
+ }
+
+ public async Task AddOrUpdateAsync(UserChats userChats)
+ {
+ var existingDocument = await _repository.GetAsync(x => x.UserId == userChats.UserId);
+ if (existingDocument == null)
+ {
+ await AddAsync(userChats);
+ }
+ else
+ {
+ await UpdateAsync(userChats);
+ }
+ }
+
+ public async Task DeleteAsync(Guid userId)
+ {
+ await _repository.DeleteAsync(x => x.UserId == userId);
+ }
+
+ public async Task ChatExistsAsync(Guid userId, Guid chatId)
+ {
+ var document = await _repository.GetAsync(x => x.UserId == userId && x.Chats.Any(c => c.Id == chatId));
+ return document != null;
+ }
+
+ public async Task AddChatAsync(Guid userId, Chat chat)
+ {
+ var userChats = await GetByUserIdAsync(userId) ?? new UserChats(userId);
+ userChats.AddChat(chat);
+ await AddOrUpdateAsync(userChats);
+ }
+
+ public async Task DeleteChatAsync(Guid userId, Guid chatId)
+ {
+ var userChats = await GetByUserIdAsync(userId);
+ if (userChats == null)
+ {
+ return;
+ }
+ var chat = userChats.GetChatById(chatId);
+ if (chat == null)
+ {
+ return;
+ }
+ userChats.Chats.Remove(chat);
+ await UpdateAsync(userChats);
+ }
+
+ public async Task> GetParticipantIdsByChatIdAsync(Guid chatId)
+ {
+ var document = await _repository.Collection
+ .Find(x => x.Chats.Any(c => c.Id == chatId))
+ .FirstOrDefaultAsync();
+
+ var chatDocument = document?.Chats.FirstOrDefault(c => c.Id == chatId);
+ return chatDocument?.ParticipantIds ?? new List();
+ }
+
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Services/Clients/StudentsServiceClient.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Services/Clients/StudentsServiceClient.cs
new file mode 100644
index 000000000..2c20e4f2b
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Services/Clients/StudentsServiceClient.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Text.Json;
+using System.Threading.Tasks;
+using Convey.HTTP;
+using MiniSpace.Services.Communication.Application.Dto;
+using MiniSpace.Services.Communication.Application.Queries;
+using MiniSpace.Services.Communication.Application.Services.Clients;
+
+namespace MiniSpace.Services.Communication.Infrastructure.Services.Clients
+{
+ public class StudentsServiceClient : IStudentsServiceClient
+ {
+ private readonly IHttpClient _httpClient;
+ private readonly string _url;
+
+ public StudentsServiceClient(IHttpClient httpClient, HttpClientOptions options)
+ {
+ _httpClient = httpClient;
+ _url = options.Services["students"];
+ }
+
+ public Task GetAsync(Guid id)
+ => _httpClient.GetAsync($"{_url}/students/{id}");
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Services/DateTimeProvider.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Services/DateTimeProvider.cs
new file mode 100644
index 000000000..692cc86f0
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Services/DateTimeProvider.cs
@@ -0,0 +1,9 @@
+using MiniSpace.Services.Communication.Application.Services;
+
+namespace MiniSpace.Services.Communication.Infrastructure.Services
+{
+ internal sealed class DateTimeProvider : IDateTimeProvider
+ {
+ public DateTime Now => DateTime.UtcNow;
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Services/EventMapper.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Services/EventMapper.cs
new file mode 100644
index 000000000..cfbb8a5b6
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Services/EventMapper.cs
@@ -0,0 +1,32 @@
+using Convey.CQRS.Events;
+using MiniSpace.Services.Communication.Application.Events;
+using MiniSpace.Services.Communication.Application.Services;
+using MiniSpace.Services.Communication.Core.Events;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace MiniSpace.Services.Communication.Infrastructure.Services
+{
+ public class EventMapper : IEventMapper
+ {
+ public IEnumerable MapAll(IEnumerable events)
+ => events.Select(Map).Where(mappedEvent => mappedEvent != null);
+
+ public IEvent Map(IDomainEvent @event)
+ {
+ switch (@event)
+ {
+ case MessageAddedEvent e:
+ return new MessageSent(e.ChatId, e.MessageId, Guid.Empty, string.Empty);
+
+ // Add more cases for other domain events
+ // case SomeOtherDomainEvent e:
+ // return new SomeOtherIntegrationEvent(...);
+
+ default:
+ return null;
+ }
+ }
+ }
+}
diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Services/MessageBroker.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Services/MessageBroker.cs
new file mode 100644
index 000000000..4bb6cbdd4
--- /dev/null
+++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Services/MessageBroker.cs
@@ -0,0 +1,94 @@
+using Convey.CQRS.Events;
+using Convey.MessageBrokers;
+using Convey.MessageBrokers.Outbox;
+using Convey.MessageBrokers.RabbitMQ;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Logging;
+using OpenTracing;
+using MiniSpace.Services.Communication.Application.Services;
+using MiniSpace.Services.Communication.Infrastructure;
+using System.Text.Json;
+
+namespace MiniSpace.Services.Communication.Infrastructure.Services
+{
+ internal sealed class MessageBroker : IMessageBroker
+ {
+ private const string DefaultSpanContextHeader = "span_context";
+ private readonly IBusPublisher _busPublisher;
+ private readonly IMessageOutbox _outbox;
+ private readonly ICorrelationContextAccessor _contextAccessor;
+ private readonly IHttpContextAccessor _httpContextAccessor;
+ private readonly IMessagePropertiesAccessor _messagePropertiesAccessor;
+ private readonly ITracer _tracer;
+ private readonly ILogger _logger;
+ private readonly string _spanContextHeader;
+
+ public MessageBroker(IBusPublisher busPublisher, IMessageOutbox outbox,
+ ICorrelationContextAccessor contextAccessor, IHttpContextAccessor httpContextAccessor,
+ IMessagePropertiesAccessor messagePropertiesAccessor, RabbitMqOptions options, ITracer tracer,
+ ILogger logger)
+ {
+ _busPublisher = busPublisher;
+ _outbox = outbox;
+ _contextAccessor = contextAccessor;
+ _httpContextAccessor = httpContextAccessor;
+ _messagePropertiesAccessor = messagePropertiesAccessor;
+ _tracer = tracer;
+ _logger = logger;
+ _spanContextHeader = string.IsNullOrWhiteSpace(options.SpanContextHeader)
+ ? DefaultSpanContextHeader
+ : options.SpanContextHeader;
+ }
+
+ public Task PublishAsync(params IEvent[] events) => PublishAsync(events?.AsEnumerable());
+
+ public async Task PublishAsync(IEnumerable events)
+ {
+ if (events is null)
+ {
+ return;
+ }
+
+ var messageProperties = _messagePropertiesAccessor.MessageProperties;
+ var originatedMessageId = messageProperties?.MessageId;
+ var correlationId = messageProperties?.CorrelationId;
+ var spanContext = messageProperties?.GetSpanContext(_spanContextHeader);
+ if (string.IsNullOrWhiteSpace(spanContext))
+ {
+ spanContext = _tracer.ActiveSpan is null ? string.Empty : _tracer.ActiveSpan.Context.ToString();
+ }
+
+ var headers = messageProperties.GetHeadersToForward();
+ var correlationContext = _contextAccessor.CorrelationContext ??
+ _httpContextAccessor.GetCorrelationContext();
+
+ foreach (var @event in events)
+ {
+ if (@event is null)
+ {
+ continue;
+ }
+
+ var messageId = Guid.NewGuid().ToString("N");
+ _logger.LogTrace($"Publishing integration event: {@event.GetType().Name} [id: '{messageId}'].");
+ var serializedEvent = JsonSerializer.Serialize(@event, new JsonSerializerOptions
+ {
+ WriteIndented = true, // To make it easier to read in logs
+ PropertyNamingPolicy = JsonNamingPolicy.CamelCase
+ });
+
+ _logger.LogTrace($"Publishing integration event: {@event.GetType().Name} [id: '{messageId}'], Content: {serializedEvent}");
+ // Console.WriteLine($"Publishing Event: {@event.GetType().Name} with ID: {messageId}, Content: {serializedEvent}");
+ if (_outbox.Enabled)
+ {
+ await _outbox.SendAsync(@event, originatedMessageId, messageId, correlationId, spanContext,
+ correlationContext, headers);
+ continue;
+ }
+
+ await _busPublisher.PublishAsync(@event, messageId, correlationId, spanContext, correlationContext,
+ headers);
+ }
+ }
+ }
+}
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Api/Program.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Api/Program.cs
index 802da81d1..42c9125f5 100644
--- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Api/Program.cs
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Api/Program.cs
@@ -17,6 +17,7 @@
using MiniSpace.Services.Events.Application.Services;
using MiniSpace.Services.Events.Application.Wrappers;
using MiniSpace.Services.Events.Infrastructure;
+using MiniSpace.Services.Events.Core.Wrappers;
using System;
using System.IO;
using Microsoft.AspNetCore.Builder;
@@ -36,16 +37,9 @@ public static async Task Main(string[] args)
.AddInfrastructure()
.Build())
.Configure(app => app
- // .UseMiddleware()
- // .UseMiddleware()
.UseInfrastructure()
.UseEndpoints(endpoints => endpoints
- .Get("", ctx => ctx.Response.WriteAsync(ctx.RequestServices.GetService().Name))
- // .Post("events/search", async (cmd, ctx) =>
- // {
- // var pagedResult = await ctx.RequestServices.GetService().BrowseEventsAsync(cmd);
- // await ctx.Response.WriteJsonAsync(pagedResult);
- // })
+ .Get("", ctx => ctx.Response.WriteAsync(ctx.RequestServices.GetService().Name))
.Post("events/search/organizer", async (cmd, ctx) =>
{
var pagedResult = await ctx.RequestServices.GetService().BrowseOrganizerEventsAsync(cmd);
@@ -53,13 +47,14 @@ public static async Task Main(string[] args)
}))
.UseDispatcherEndpoints(endpoints => endpoints
.Get("events/{eventId}")
- .Get>("events/users/{userId}")
+ .Get>("events/users/{userId}")
.Get("events/{eventId}/participants")
.Get("events/{eventId}/rating")
- .Get>("events/paginated")
- .Get>("events/organizer/{organizerId}/paginated")
- .Get>("events/search")
-
+ .Get>("events/paginated")
+ .Get>("events/organizer/{organizerId}/paginated")
+ .Get>("events/search")
+ .Get>("events/users/{userId}/feed")
+ .Get>("events/users/{userId}/views/paginated")
.Put("events/{eventId}")
.Post("events",
afterDispatch: (cmd, ctx) => ctx.Response.Created($"events/{cmd.EventId}"))
@@ -71,6 +66,7 @@ public static async Task Main(string[] args)
.Post("events/{eventId}/rate")
.Delete("events/{eventId}/rate")
.Post("events/{eventId}/participants")
+ .Post("events/{eventId}/view")
.Delete("events/{eventId}/participants")
)
)
@@ -78,75 +74,4 @@ public static async Task Main(string[] args)
.Build()
.RunAsync();
}
-
- public class RequestLoggingMiddleware
- {
- private readonly RequestDelegate _next;
-
- public RequestLoggingMiddleware(RequestDelegate next)
- {
- _next = next;
- }
-
- public async Task InvokeAsync(HttpContext context)
- {
- // Enable buffering so the stream can be read multiple times
- context.Request.EnableBuffering();
-
- // Read the stream as text
- var bodyAsText = await new StreamReader(context.Request.Body).ReadToEndAsync();
-
- // Log the request body
- Console.WriteLine("Received JSON:");
- Console.WriteLine(bodyAsText);
-
- // Reset the stream position to allow the next middleware to read it
- context.Request.Body.Position = 0;
-
- // Call the next middleware in the pipeline
- await _next(context);
- }
- }
-
- public class ExceptionHandlingMiddleware
-{
- private readonly RequestDelegate _next;
- private readonly ILogger _logger;
-
- public ExceptionHandlingMiddleware(RequestDelegate next, ILogger logger)
- {
- _next = next;
- _logger = logger;
- }
-
- public async Task InvokeAsync(HttpContext context)
- {
- try
- {
- await _next(context);
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "An unhandled exception occurred.");
- await HandleExceptionAsync(context, ex);
- }
- }
-
- private static Task HandleExceptionAsync(HttpContext context, Exception exception)
- {
- var statusCode = StatusCodes.Status500InternalServerError;
- var result = JsonSerializer.Serialize(new { error = exception.Message });
-
- if (exception is ArgumentException || exception is InvalidOperationException)
- {
- statusCode = StatusCodes.Status400BadRequest;
- }
-
- context.Response.ContentType = "application/json";
- context.Response.StatusCode = statusCode;
- return context.Response.WriteAsync(result);
- }
-}
-
-
}
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/ViewEventHandler.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/ViewEventHandler.cs
new file mode 100644
index 000000000..a99f9cff2
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/ViewEventHandler.cs
@@ -0,0 +1,63 @@
+using System;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Convey.CQRS.Commands;
+using MiniSpace.Services.Events.Core.Entities;
+using MiniSpace.Services.Events.Core.Repositories;
+using Microsoft.Extensions.Logging;
+
+namespace MiniSpace.Services.Events.Application.Commands.Handlers
+{
+ public class ViewEventHandler : ICommandHandler
+ {
+ private readonly IEventsUserViewsRepository _eventsUserViewsRepository;
+ private readonly IEventRepository _eventRepository;
+ private readonly ILogger _logger;
+
+ public ViewEventHandler(
+ IEventsUserViewsRepository eventsUserViewsRepository,
+ IEventRepository eventRepository,
+ ILogger logger)
+ {
+ _eventsUserViewsRepository = eventsUserViewsRepository;
+ _eventRepository = eventRepository;
+ _logger = logger;
+ }
+
+ public async Task HandleAsync(ViewEvent command, CancellationToken cancellationToken)
+ {
+ // Ensure the event exists
+ var eventExists = await _eventRepository.ExistsAsync(command.EventId);
+ if (!eventExists)
+ {
+ _logger.LogWarning($"Event with ID {command.EventId} not found.");
+ return;
+ }
+
+ // Fetch the user's event views
+ var userViews = await _eventsUserViewsRepository.GetAsync(command.UserId);
+ if (userViews == null)
+ {
+ // If no views exist, create a new EventsViews object for the user
+ userViews = new EventsViews(command.UserId, Enumerable.Empty());
+ }
+
+ // Check if the event has already been viewed
+ var existingView = userViews.Views.FirstOrDefault(v => v.EventId == command.EventId);
+ if (existingView != null)
+ {
+ // Remove the existing view (to update the date)
+ userViews.RemoveView(command.EventId);
+ }
+
+ // Add the new view with the current date
+ userViews.AddView(command.EventId, DateTime.UtcNow);
+
+ // Save the updated views to the repository
+ await _eventsUserViewsRepository.UpdateAsync(userViews);
+
+ _logger.LogInformation($"User {command.UserId} viewed event {command.EventId}.");
+ }
+ }
+}
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/ViewEvent.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/ViewEvent.cs
new file mode 100644
index 000000000..d1a668dea
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/ViewEvent.cs
@@ -0,0 +1,17 @@
+using System;
+using Convey.CQRS.Commands;
+
+namespace MiniSpace.Services.Events.Application.Commands
+{
+ public class ViewEvent : ICommand
+ {
+ public Guid UserId { get; }
+ public Guid EventId { get; }
+
+ public ViewEvent(Guid userId, Guid eventId)
+ {
+ UserId = userId;
+ EventId = eventId;
+ }
+ }
+}
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/EducationDto.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/EducationDto.cs
new file mode 100644
index 000000000..26ce34bd1
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/EducationDto.cs
@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+
+namespace MiniSpace.Services.Events.Application.DTO
+{
+ [ExcludeFromCodeCoverage]
+ public class EducationDto
+ {
+ public string InstitutionName { get; set; }
+ public string Degree { get; set; }
+ public DateTime StartDate { get; set; }
+ public DateTime EndDate { get; set; }
+ public string Description { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/FriendDto.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/FriendDto.cs
index 23aeb3968..4daa0d38e 100644
--- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/FriendDto.cs
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/FriendDto.cs
@@ -7,9 +7,9 @@ namespace MiniSpace.Services.Events.Application.DTO
public class FriendDto
{
public Guid Id { get; set; }
- public Guid StudentId { get; set; }
+ public Guid UserId { get; set; }
public Guid FriendId { get; set; }
public DateTime CreatedAt { get; set; }
- public string FriendState { get; set; }
+ public string State { get; set; }
}
}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/PagedResult.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/PagedResult.cs
deleted file mode 100644
index cad6a334d..000000000
--- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/PagedResult.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-namespace MiniSpace.Services.Events.Application.DTO
-{
- public class PagedResult
- {
- public IEnumerable Items { get; }
- public int Page { get; }
- public int PageSize { get; }
- public int TotalItems { get; }
- public int TotalPages => PageSize > 0 ? (int)Math.Ceiling((decimal)TotalItems / PageSize) : 0;
-
- public int? NextPage => Page < TotalPages ? Page + 1 : (int?)null;
- public int? PreviousPage => Page > 1 ? Page - 1 : (int?)null;
-
- public PagedResult(IEnumerable items, int page, int pageSize, int totalItems)
- {
- Items = items;
- Page = page;
- PageSize = pageSize;
- TotalItems = totalItems;
- }
- }
-}
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/StudentDto.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/StudentDto.cs
index ca61ea03c..2e8f31f6f 100644
--- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/StudentDto.cs
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/StudentDto.cs
@@ -7,6 +7,5 @@ namespace MiniSpace.Services.Events.Application.DTO
public class StudentDto
{
public Guid Id { get; set; }
- public string Name { get; set; }
}
}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/UserEventsViewsDto.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/UserEventsViewsDto.cs
new file mode 100644
index 000000000..47eb5d429
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/UserEventsViewsDto.cs
@@ -0,0 +1,11 @@
+using System;
+using System.Collections.Generic;
+
+namespace MiniSpace.Services.Events.Application.DTO
+{
+ public class UserEventsViewsDto
+ {
+ public Guid UserId { get; set; }
+ public IEnumerable Views { get; set; }
+ }
+}
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/StudentFriendsDto.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/UserFriendsDto.cs
similarity index 77%
rename from MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/StudentFriendsDto.cs
rename to MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/UserFriendsDto.cs
index f22288d5b..90765c36d 100644
--- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/StudentFriendsDto.cs
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/UserFriendsDto.cs
@@ -5,9 +5,9 @@
namespace MiniSpace.Services.Events.Application.DTO
{
[ExcludeFromCodeCoverage]
- public class StudentFriendsDto
+ public class UserFriendsDto
{
- public Guid StudentId { get; set; }
+ public Guid UserId { get; set; }
public List Friends { get; set; } = new List();
}
}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/UserFromServiceDto.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/UserFromServiceDto.cs
new file mode 100644
index 000000000..55706a5ae
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/UserFromServiceDto.cs
@@ -0,0 +1,36 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+
+
+namespace MiniSpace.Services.Events.Application.DTO
+{
+ [ExcludeFromCodeCoverage]
+ public class UserFromServiceDto
+ {
+ public Guid Id { get; set; }
+ public string Email { get; set; }
+ public string FirstName { get; set; }
+ public string LastName { get; set; }
+ public string ProfileImageUrl { get; set; }
+ public string Description { get; set; }
+ public DateTime? DateOfBirth { get; set; }
+ public bool EmailNotifications { get; set; }
+ public bool IsBanned { get; set; }
+ public string State { get; set; }
+ public DateTime CreatedAt { get; set; }
+ public string ContactEmail { get; set; }
+ public string BannerUrl { get; set; }
+ public string PhoneNumber { get; set; }
+ public IEnumerable Languages { get; set; }
+ public IEnumerable Interests { get; set; }
+ public IEnumerable Education { get; set; }
+ public IEnumerable Work { get; set; }
+ public bool IsTwoFactorEnabled { get; set; }
+ public string TwoFactorSecret { get; set; }
+ public IEnumerable InterestedInEvents { get; set; }
+ public IEnumerable SignedUpEvents { get; set; }
+ public string Country { get; set; }
+ public string City { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/ViewDto.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/ViewDto.cs
new file mode 100644
index 000000000..618f4a79c
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/ViewDto.cs
@@ -0,0 +1,11 @@
+using System;
+using System.Collections.Generic;
+
+namespace MiniSpace.Services.Events.Application.DTO
+{
+ public class ViewDto
+ {
+ public Guid EventId { get; set; }
+ public DateTime Date { get; set; }
+ }
+}
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/WorkDto.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/WorkDto.cs
new file mode 100644
index 000000000..77b848b25
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/WorkDto.cs
@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+
+namespace MiniSpace.Services.Events.Application.DTO
+{
+ [ExcludeFromCodeCoverage]
+ public class WorkDto
+ {
+ public string Company { get; set; }
+ public string Position { get; set; }
+ public DateTime StartDate { get; set; }
+ public DateTime EndDate { get; set; }
+ public string Description { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/External/CommentCreated.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/External/CommentCreated.cs
new file mode 100644
index 000000000..521748421
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/External/CommentCreated.cs
@@ -0,0 +1,37 @@
+using Convey.CQRS.Events;
+using Convey.MessageBrokers;
+using System;
+
+namespace MiniSpace.Services.Events.Application.Events.External
+{
+ [Message("comments")]
+ public class CommentCreated : IEvent
+ {
+ public Guid CommentId { get; }
+ public Guid ContextId { get; }
+ public string CommentContext { get; }
+ public Guid UserId { get; }
+ public Guid ParentId { get; }
+ public string TextContent { get; }
+ public DateTime CreatedAt { get; }
+ public DateTime LastUpdatedAt { get; }
+ public int RepliesCount { get; }
+ public bool IsDeleted { get; }
+
+ public CommentCreated(Guid commentId, Guid contextId, string commentContext, Guid userId,
+ Guid parentId, string textContent, DateTime createdAt,
+ DateTime lastUpdatedAt, int repliesCount, bool isDeleted)
+ {
+ CommentId = commentId;
+ ContextId = contextId;
+ CommentContext = commentContext;
+ UserId = userId;
+ ParentId = parentId;
+ TextContent = textContent;
+ CreatedAt = createdAt;
+ LastUpdatedAt = lastUpdatedAt;
+ RepliesCount = repliesCount;
+ IsDeleted = isDeleted;
+ }
+ }
+}
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/External/Handlers/CommentCreatedHandler.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/External/Handlers/CommentCreatedHandler.cs
new file mode 100644
index 000000000..326facb80
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/External/Handlers/CommentCreatedHandler.cs
@@ -0,0 +1,47 @@
+using System;
+using System.Text.Json;
+using System.Threading;
+using System.Threading.Tasks;
+using Convey.CQRS.Events;
+using MiniSpace.Services.Events.Application.Events.External;
+using MiniSpace.Services.Events.Core.Entities;
+using MiniSpace.Services.Events.Core.Repositories;
+
+namespace MiniSpace.Services.Events.Application.Events.External.Handlers
+{
+ public class CommentCreatedHandler : IEventHandler
+ {
+ private readonly IUserCommentsHistoryRepository _userCommentsHistoryRepository;
+
+ public CommentCreatedHandler(IUserCommentsHistoryRepository userCommentsHistoryRepository)
+ {
+ _userCommentsHistoryRepository = userCommentsHistoryRepository;
+ }
+
+ public async Task HandleAsync(CommentCreated @event, CancellationToken cancellationToken = default)
+ {
+
+ var eventJson = JsonSerializer.Serialize(@event, new JsonSerializerOptions
+ {
+ WriteIndented = true // Optional: For pretty-printing the JSON
+ });
+ Console.WriteLine("Received CommentCreated event:");
+ Console.WriteLine(eventJson);
+
+ var comment = new Comment(
+ @event.CommentId,
+ @event.ContextId,
+ @event.CommentContext,
+ @event.UserId,
+ @event.ParentId,
+ @event.TextContent,
+ @event.CreatedAt,
+ @event.LastUpdatedAt,
+ @event.RepliesCount,
+ @event.IsDeleted
+ );
+
+ await _userCommentsHistoryRepository.SaveCommentAsync(@event.UserId, comment);
+ }
+ }
+}
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/External/Handlers/ReactionCreatedHandler.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/External/Handlers/ReactionCreatedHandler.cs
new file mode 100644
index 000000000..e4967cbf3
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/External/Handlers/ReactionCreatedHandler.cs
@@ -0,0 +1,42 @@
+using System;
+using System.Text.Json;
+using System.Threading;
+using System.Threading.Tasks;
+using Convey.CQRS.Events;
+using MiniSpace.Services.Events.Application.Events.External;
+using MiniSpace.Services.Events.Core.Entities;
+using MiniSpace.Services.Events.Core.Repositories;
+
+namespace MiniSpace.Services.Events.Application.Events.External.Handlers
+{
+ public class ReactionCreatedHandler : IEventHandler
+ {
+ private readonly IUserReactionsHistoryRepository _userReactionsHistoryRepository;
+
+ public ReactionCreatedHandler(IUserReactionsHistoryRepository userReactionsHistoryRepository)
+ {
+ _userReactionsHistoryRepository = userReactionsHistoryRepository;
+ }
+
+ public async Task HandleAsync(ReactionCreated @event, CancellationToken cancellationToken = default)
+ {
+ var eventJson = JsonSerializer.Serialize(@event, new JsonSerializerOptions
+ {
+ WriteIndented = true
+ });
+ Console.WriteLine("Received ReactionCreated event:");
+ Console.WriteLine(eventJson);
+
+ var reaction = Reaction.Create(
+ @event.ReactionId,
+ @event.UserId,
+ @event.ReactionType,
+ @event.ContentId,
+ @event.ContentType,
+ @event.TargetType
+ );
+
+ await _userReactionsHistoryRepository.SaveReactionAsync(@event.UserId, reaction);
+ }
+ }
+}
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/External/ReactionCreated.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/External/ReactionCreated.cs
new file mode 100644
index 000000000..0b4b0dfa1
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/External/ReactionCreated.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Convey.CQRS.Events;
+using Convey.MessageBrokers;
+
+namespace MiniSpace.Services.Events.Application.Events.External
+{
+ [Message("reactions")]
+ public class ReactionCreated : IEvent
+ {
+ public Guid ReactionId { get; }
+ public Guid UserId { get; }
+ public Guid ContentId { get; }
+ public string ContentType { get; }
+ public string ReactionType { get; }
+ public string TargetType { get; }
+
+ public ReactionCreated(Guid reactionId, Guid userId, Guid contentId, string contentType, string reactionType, string targetType)
+ {
+ ReactionId = reactionId;
+ UserId = userId;
+ ContentId = contentId;
+ ContentType = contentType;
+ ReactionType = reactionType;
+ TargetType = targetType;
+ }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Queries/GetPaginatedEvents.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Queries/GetPaginatedEvents.cs
index 84b4ae86c..03114a2cf 100644
--- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Queries/GetPaginatedEvents.cs
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Queries/GetPaginatedEvents.cs
@@ -1,10 +1,11 @@
using Convey.CQRS.Queries;
using MiniSpace.Services.Events.Application.DTO;
+using MiniSpace.Services.Events.Core.Wrappers;
using System.Collections.Generic;
namespace MiniSpace.Services.Events.Application.Queries
{
- public class GetPaginatedEvents : IQuery>
+ public class GetPaginatedEvents : IQuery>
{
public int Page { get; set; } = 1;
public int PageSize { get; set; } = 10;
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Queries/GetPaginatedOrganizerEvents.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Queries/GetPaginatedOrganizerEvents.cs
index 6cf1e1bcf..c87907342 100644
--- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Queries/GetPaginatedOrganizerEvents.cs
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Queries/GetPaginatedOrganizerEvents.cs
@@ -1,10 +1,11 @@
using System;
using Convey.CQRS.Queries;
using MiniSpace.Services.Events.Application.DTO;
+using MiniSpace.Services.Events.Core.Wrappers;
namespace MiniSpace.Services.Events.Application.Queries
{
- public class GetPaginatedOrganizerEvents : IQuery>
+ public class GetPaginatedOrganizerEvents : IQuery>
{
public Guid OrganizerId { get; set; }
public int Page { get; set; } = 1;
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Queries/GetPaginatedUserViews.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Queries/GetPaginatedUserViews.cs
new file mode 100644
index 000000000..4acc765f2
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Queries/GetPaginatedUserViews.cs
@@ -0,0 +1,14 @@
+using System;
+using Convey.CQRS.Queries;
+using MiniSpace.Services.Events.Application.DTO;
+using MiniSpace.Services.Events.Core.Wrappers;
+
+namespace MiniSpace.Services.Events.Application.Queries
+{
+ public class GetPaginatedUserViews : IQuery>
+ {
+ public Guid UserId { get; set; }
+ public int Page { get; set; } = 1;
+ public int PageSize { get; set; } = 10;
+ }
+}
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Queries/GetSearchEvents.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Queries/GetSearchEvents.cs
index 64d2e61a6..50cbf6577 100644
--- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Queries/GetSearchEvents.cs
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Queries/GetSearchEvents.cs
@@ -2,10 +2,11 @@
using System.Collections.Generic;
using Convey.CQRS.Queries;
using MiniSpace.Services.Events.Application.DTO;
+using MiniSpace.Services.Events.Core.Wrappers;
namespace MiniSpace.Services.Events.Application.Queries
{
- public class GetSearchEvents : IQuery>
+ public class GetSearchEvents : IQuery>
{
public string Name { get; set; }
public string Organizer { get; set; }
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Queries/GetUserEvents.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Queries/GetUserEvents.cs
index 9d9dfe8cd..a24bdf88a 100644
--- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Queries/GetUserEvents.cs
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Queries/GetUserEvents.cs
@@ -3,12 +3,11 @@
using System.Diagnostics.CodeAnalysis;
using Convey.CQRS.Queries;
using MiniSpace.Services.Events.Application.DTO;
-using MiniSpace.Services.Events.Application.Wrappers;
-
+using MiniSpace.Services.Events.Core.Wrappers;
namespace MiniSpace.Services.Events.Application.Queries
{
[ExcludeFromCodeCoverage]
- public class GetUserEvents : IQuery>
+ public class GetUserEvents : IQuery>
{
public Guid UserId { get; set; }
public string EngagementType { get; set; }
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Queries/GetUserEventsFeed.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Queries/GetUserEventsFeed.cs
new file mode 100644
index 000000000..16b8bd6da
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Queries/GetUserEventsFeed.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
+using Convey.CQRS.Queries;
+using MiniSpace.Services.Events.Application.DTO;
+using MiniSpace.Services.Events.Core.Wrappers;
+
+namespace MiniSpace.Services.Events.Application.Queries
+{
+ [ExcludeFromCodeCoverage]
+ public class GetUserEventsFeed : IQuery>
+ {
+ public Guid UserId { get; set; }
+ public int PageNumber { get; set; } = 1;
+ public int PageSize { get; set; } = 10;
+ public string SortBy { get; set; } = "PublishDate";
+ public string Direction { get; set; } = "asc";
+
+ public GetUserEventsFeed(Guid userId, int pageNumber = 1, int pageSize = 10,
+ string sortBy = "PublishDate", string direction = "asc")
+ {
+ UserId = userId;
+ PageNumber = pageNumber;
+ PageSize = pageSize;
+ SortBy = sortBy;
+ Direction = direction;
+ }
+ }
+}
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Services/Clients/IFriendsServiceClient.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Services/Clients/IFriendsServiceClient.cs
index 605483c2d..07357399d 100644
--- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Services/Clients/IFriendsServiceClient.cs
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Services/Clients/IFriendsServiceClient.cs
@@ -7,6 +7,6 @@ namespace MiniSpace.Services.Events.Application.Services.Clients
{
public interface IFriendsServiceClient
{
- Task> GetAsync(Guid studentId);
+ Task> GetAsync(Guid studentId);
}
}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Services/Clients/IStudentsServiceClient.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Services/Clients/IStudentsServiceClient.cs
index 1ed110d82..11885df3b 100644
--- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Services/Clients/IStudentsServiceClient.cs
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Services/Clients/IStudentsServiceClient.cs
@@ -8,5 +8,6 @@ public interface IStudentsServiceClient
{
Task GetAsync(Guid id);
Task StudentExistsAsync(Guid id);
+ Task GetStudentByIdAsync(Guid studentId);
}
}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Services/IEventRecommendationService.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Services/IEventRecommendationService.cs
new file mode 100644
index 000000000..4a4d19586
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Services/IEventRecommendationService.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using MiniSpace.Services.Events.Application.DTO;
+
+namespace MiniSpace.Services.Events.Application.Services
+{
+ public interface IEventRecommendationService
+ {
+ IEnumerable RankEventsByUserInterest(Guid userId, IEnumerable events, IEnumerable userInterests);
+ }
+}
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Services/IEventService.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Services/IEventService.cs
index 7a3a9429c..486773f1e 100644
--- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Services/IEventService.cs
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Services/IEventService.cs
@@ -2,13 +2,13 @@
using System.Threading.Tasks;
using MiniSpace.Services.Events.Application.Commands;
using MiniSpace.Services.Events.Application.DTO;
-using MiniSpace.Services.Events.Application.Wrappers;
+using MiniSpace.Services.Events.Core.Wrappers;
namespace MiniSpace.Services.Events.Application.Services
{
public interface IEventService
{
- Task>> BrowseEventsAsync(SearchEvents command);
- Task