mirror of
https://github.com/lorsanstand/Aether.git
synced 2026-06-19 12:05:16 +03:00
Merge pull request #23 from lorsanstand/dev
fix: send and recive message
This commit is contained in:
+142
@@ -0,0 +1,142 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="AutoImportSettings">
|
||||
<option name="autoReloadType" value="SELECTIVE" />
|
||||
</component>
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="ca182f62-efdb-4f7c-b4fa-3db0a3244740" name="Changes" comment="" />
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
||||
<option name="LAST_RESOLUTION" value="IGNORE" />
|
||||
</component>
|
||||
<component name="FileTemplateManagerImpl">
|
||||
<option name="RECENT_TEMPLATES">
|
||||
<list>
|
||||
<option value="Dockerfile" />
|
||||
<option value="Python Script" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="Git.Settings">
|
||||
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
||||
</component>
|
||||
<component name="GitHubPullRequestSearchHistory">{
|
||||
"lastFilter": {
|
||||
"state": "OPEN",
|
||||
"assignee": "lorsanstand"
|
||||
}
|
||||
}</component>
|
||||
<component name="GithubPullRequestsUISettings">{
|
||||
"selectedUrlAndAccountId": {
|
||||
"url": "https://github.com/lorsanstand/Aether.git",
|
||||
"accountId": "7d226d82-cbdd-4f01-a2df-d6a90c41dc0d"
|
||||
}
|
||||
}</component>
|
||||
<component name="ProjectColorInfo">{
|
||||
"associatedIndex": 6
|
||||
}</component>
|
||||
<component name="ProjectId" id="377jRIu8LdWxRZUqlIlKecvcH0N" />
|
||||
<component name="ProjectLevelVcsManager">
|
||||
<ConfirmationsSetting value="2" id="Add" />
|
||||
</component>
|
||||
<component name="ProjectViewState">
|
||||
<option name="hideEmptyMiddlePackages" value="true" />
|
||||
<option name="showLibraryContents" value="true" />
|
||||
</component>
|
||||
<component name="PropertiesComponent">{
|
||||
"keyToString": {
|
||||
"ModuleVcsDetector.initialDetectionPerformed": "true",
|
||||
"Python.test.executor": "Run",
|
||||
"RunOnceActivity.ShowReadmeOnStart": "true",
|
||||
"RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252": "true",
|
||||
"RunOnceActivity.git.unshallow": "true",
|
||||
"RunOnceActivity.typescript.service.memoryLimit.init": "true",
|
||||
"ai.playground.ignore.import.keys.banner.in.settings": "true",
|
||||
"git-widget-placeholder": "dev",
|
||||
"last_opened_file_path": "/home/lorsan/Projects/Aether",
|
||||
"nodejs_package_manager_path": "npm",
|
||||
"settings.editor.selected.configurable": "com.jetbrains.python.configuration.PyActiveSdkModuleConfigurable",
|
||||
"vue.rearranger.settings.migration": "true"
|
||||
}
|
||||
}</component>
|
||||
<component name="RecentsManager">
|
||||
<key name="CopyFile.RECENT_KEYS">
|
||||
<recent name="$PROJECT_DIR$" />
|
||||
<recent name="$PROJECT_DIR$/frontend" />
|
||||
<recent name="$PROJECT_DIR$/backend/scripts" />
|
||||
</key>
|
||||
<key name="MoveFile.RECENT_KEYS">
|
||||
<recent name="$PROJECT_DIR$" />
|
||||
<recent name="$PROJECT_DIR$/backend/app" />
|
||||
<recent name="$PROJECT_DIR$/backend" />
|
||||
</key>
|
||||
</component>
|
||||
<component name="SharedIndexes">
|
||||
<attachedChunks>
|
||||
<set>
|
||||
<option value="bundled-js-predefined-d6986cc7102b-9b0f141eb926-JavaScript-PY-253.30387.173" />
|
||||
<option value="bundled-python-sdk-4762d8aabb82-6d6dccd035ac-com.jetbrains.pycharm.pro.sharedIndexes.bundled-PY-253.30387.173" />
|
||||
</set>
|
||||
</attachedChunks>
|
||||
</component>
|
||||
<component name="TaskManager">
|
||||
<task active="true" id="Default" summary="Default task">
|
||||
<changelist id="ca182f62-efdb-4f7c-b4fa-3db0a3244740" name="Changes" comment="" />
|
||||
<created>1766260993924</created>
|
||||
<option name="number" value="Default" />
|
||||
<option name="presentableId" value="Default" />
|
||||
<updated>1766260993924</updated>
|
||||
<workItem from="1766579069494" duration="537000" />
|
||||
<workItem from="1766821371454" duration="6586000" />
|
||||
<workItem from="1767182789092" duration="61000" />
|
||||
<workItem from="1767436737099" duration="457000" />
|
||||
<workItem from="1767469948303" duration="344000" />
|
||||
<workItem from="1767515834391" duration="3673000" />
|
||||
<workItem from="1767632206964" duration="1009000" />
|
||||
<workItem from="1767702075617" duration="5995000" />
|
||||
<workItem from="1767728758580" duration="1701000" />
|
||||
<workItem from="1767777556289" duration="2238000" />
|
||||
<workItem from="1767894161585" duration="3168000" />
|
||||
<workItem from="1767954037387" duration="3206000" />
|
||||
<workItem from="1768041344558" duration="24517000" />
|
||||
<workItem from="1768145545609" duration="2734000" />
|
||||
<workItem from="1768152718327" duration="3439000" />
|
||||
<workItem from="1768206631384" duration="12898000" />
|
||||
<workItem from="1768234411796" duration="7183000" />
|
||||
<workItem from="1768724458835" duration="4856000" />
|
||||
<workItem from="1768823389064" duration="12094000" />
|
||||
<workItem from="1768917847698" duration="5107000" />
|
||||
<workItem from="1769105143116" duration="384000" />
|
||||
<workItem from="1769175659580" duration="701000" />
|
||||
<workItem from="1769249378321" duration="1583000" />
|
||||
<workItem from="1769255439265" duration="6053000" />
|
||||
<workItem from="1769323001360" duration="8679000" />
|
||||
<workItem from="1769413076919" duration="20372000" />
|
||||
<workItem from="1769440193935" duration="43000" />
|
||||
<workItem from="1769440272013" duration="8398000" />
|
||||
<workItem from="1769526024191" duration="778000" />
|
||||
<workItem from="1769612969196" duration="30000" />
|
||||
<workItem from="1769614076335" duration="1957000" />
|
||||
<workItem from="1769794458540" duration="138000" />
|
||||
<workItem from="1769845775151" duration="151000" />
|
||||
<workItem from="1769845934187" duration="9000" />
|
||||
<workItem from="1770183147191" duration="612000" />
|
||||
<workItem from="1770212893901" duration="4778000" />
|
||||
<workItem from="1770364636251" duration="7167000" />
|
||||
<workItem from="1770412285849" duration="16000" />
|
||||
<workItem from="1773344252513" duration="761000" />
|
||||
<workItem from="1773416003664" duration="3382000" />
|
||||
<workItem from="1773515737128" duration="9276000" />
|
||||
<workItem from="1773642657359" duration="2326000" />
|
||||
<workItem from="1773648107686" duration="1727000" />
|
||||
</task>
|
||||
<servers />
|
||||
</component>
|
||||
<component name="TypeScriptGeneratedFilesManager">
|
||||
<option name="version" value="3" />
|
||||
</component>
|
||||
<component name="com.intellij.coverage.CoverageDataManagerImpl">
|
||||
<SUITE FILE_PATH="coverage/Aether$test.coverage" NAME="test Coverage Results" MODIFIED="1767705414318" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/backend/app" />
|
||||
</component>
|
||||
</project>
|
||||
+142
@@ -0,0 +1,142 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="AutoImportSettings">
|
||||
<option name="autoReloadType" value="SELECTIVE" />
|
||||
</component>
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="ca182f62-efdb-4f7c-b4fa-3db0a3244740" name="Changes" comment="" />
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
||||
<option name="LAST_RESOLUTION" value="IGNORE" />
|
||||
</component>
|
||||
<component name="FileTemplateManagerImpl">
|
||||
<option name="RECENT_TEMPLATES">
|
||||
<list>
|
||||
<option value="Dockerfile" />
|
||||
<option value="Python Script" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="Git.Settings">
|
||||
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
||||
</component>
|
||||
<component name="GitHubPullRequestSearchHistory">{
|
||||
"lastFilter": {
|
||||
"state": "OPEN",
|
||||
"assignee": "lorsanstand"
|
||||
}
|
||||
}</component>
|
||||
<component name="GithubPullRequestsUISettings">{
|
||||
"selectedUrlAndAccountId": {
|
||||
"url": "https://github.com/lorsanstand/Aether.git",
|
||||
"accountId": "7d226d82-cbdd-4f01-a2df-d6a90c41dc0d"
|
||||
}
|
||||
}</component>
|
||||
<component name="ProjectColorInfo">{
|
||||
"associatedIndex": 6
|
||||
}</component>
|
||||
<component name="ProjectId" id="377jRIu8LdWxRZUqlIlKecvcH0N" />
|
||||
<component name="ProjectLevelVcsManager">
|
||||
<ConfirmationsSetting value="2" id="Add" />
|
||||
</component>
|
||||
<component name="ProjectViewState">
|
||||
<option name="hideEmptyMiddlePackages" value="true" />
|
||||
<option name="showLibraryContents" value="true" />
|
||||
</component>
|
||||
<component name="PropertiesComponent">{
|
||||
"keyToString": {
|
||||
"ModuleVcsDetector.initialDetectionPerformed": "true",
|
||||
"Python.test.executor": "Run",
|
||||
"RunOnceActivity.ShowReadmeOnStart": "true",
|
||||
"RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252": "true",
|
||||
"RunOnceActivity.git.unshallow": "true",
|
||||
"RunOnceActivity.typescript.service.memoryLimit.init": "true",
|
||||
"ai.playground.ignore.import.keys.banner.in.settings": "true",
|
||||
"git-widget-placeholder": "dev",
|
||||
"last_opened_file_path": "/home/lorsan/Projects/Aether",
|
||||
"nodejs_package_manager_path": "npm",
|
||||
"settings.editor.selected.configurable": "com.jetbrains.python.configuration.PyActiveSdkModuleConfigurable",
|
||||
"vue.rearranger.settings.migration": "true"
|
||||
}
|
||||
}</component>
|
||||
<component name="RecentsManager">
|
||||
<key name="CopyFile.RECENT_KEYS">
|
||||
<recent name="$PROJECT_DIR$" />
|
||||
<recent name="$PROJECT_DIR$/frontend" />
|
||||
<recent name="$PROJECT_DIR$/backend/scripts" />
|
||||
</key>
|
||||
<key name="MoveFile.RECENT_KEYS">
|
||||
<recent name="$PROJECT_DIR$" />
|
||||
<recent name="$PROJECT_DIR$/backend/app" />
|
||||
<recent name="$PROJECT_DIR$/backend" />
|
||||
</key>
|
||||
</component>
|
||||
<component name="SharedIndexes">
|
||||
<attachedChunks>
|
||||
<set>
|
||||
<option value="bundled-js-predefined-d6986cc7102b-9b0f141eb926-JavaScript-PY-253.30387.173" />
|
||||
<option value="bundled-python-sdk-4762d8aabb82-6d6dccd035ac-com.jetbrains.pycharm.pro.sharedIndexes.bundled-PY-253.30387.173" />
|
||||
</set>
|
||||
</attachedChunks>
|
||||
</component>
|
||||
<component name="TaskManager">
|
||||
<task active="true" id="Default" summary="Default task">
|
||||
<changelist id="ca182f62-efdb-4f7c-b4fa-3db0a3244740" name="Changes" comment="" />
|
||||
<created>1766260993924</created>
|
||||
<option name="number" value="Default" />
|
||||
<option name="presentableId" value="Default" />
|
||||
<updated>1766260993924</updated>
|
||||
<workItem from="1766579069494" duration="537000" />
|
||||
<workItem from="1766821371454" duration="6586000" />
|
||||
<workItem from="1767182789092" duration="61000" />
|
||||
<workItem from="1767436737099" duration="457000" />
|
||||
<workItem from="1767469948303" duration="344000" />
|
||||
<workItem from="1767515834391" duration="3673000" />
|
||||
<workItem from="1767632206964" duration="1009000" />
|
||||
<workItem from="1767702075617" duration="5995000" />
|
||||
<workItem from="1767728758580" duration="1701000" />
|
||||
<workItem from="1767777556289" duration="2238000" />
|
||||
<workItem from="1767894161585" duration="3168000" />
|
||||
<workItem from="1767954037387" duration="3206000" />
|
||||
<workItem from="1768041344558" duration="24517000" />
|
||||
<workItem from="1768145545609" duration="2734000" />
|
||||
<workItem from="1768152718327" duration="3439000" />
|
||||
<workItem from="1768206631384" duration="12898000" />
|
||||
<workItem from="1768234411796" duration="7183000" />
|
||||
<workItem from="1768724458835" duration="4856000" />
|
||||
<workItem from="1768823389064" duration="12094000" />
|
||||
<workItem from="1768917847698" duration="5107000" />
|
||||
<workItem from="1769105143116" duration="384000" />
|
||||
<workItem from="1769175659580" duration="701000" />
|
||||
<workItem from="1769249378321" duration="1583000" />
|
||||
<workItem from="1769255439265" duration="6053000" />
|
||||
<workItem from="1769323001360" duration="8679000" />
|
||||
<workItem from="1769413076919" duration="20372000" />
|
||||
<workItem from="1769440193935" duration="43000" />
|
||||
<workItem from="1769440272013" duration="8398000" />
|
||||
<workItem from="1769526024191" duration="778000" />
|
||||
<workItem from="1769612969196" duration="30000" />
|
||||
<workItem from="1769614076335" duration="1957000" />
|
||||
<workItem from="1769794458540" duration="138000" />
|
||||
<workItem from="1769845775151" duration="151000" />
|
||||
<workItem from="1769845934187" duration="9000" />
|
||||
<workItem from="1770183147191" duration="612000" />
|
||||
<workItem from="1770212893901" duration="4778000" />
|
||||
<workItem from="1770364636251" duration="7167000" />
|
||||
<workItem from="1770412285849" duration="16000" />
|
||||
<workItem from="1773344252513" duration="761000" />
|
||||
<workItem from="1773416003664" duration="3382000" />
|
||||
<workItem from="1773515737128" duration="9276000" />
|
||||
<workItem from="1773642657359" duration="2326000" />
|
||||
<workItem from="1773648107686" duration="1727000" />
|
||||
</task>
|
||||
<servers />
|
||||
</component>
|
||||
<component name="TypeScriptGeneratedFilesManager">
|
||||
<option name="version" value="3" />
|
||||
</component>
|
||||
<component name="com.intellij.coverage.CoverageDataManagerImpl">
|
||||
<SUITE FILE_PATH="coverage/Aether$test.coverage" NAME="test Coverage Results" MODIFIED="1767705414318" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/backend/app" />
|
||||
</component>
|
||||
</project>
|
||||
+150
@@ -0,0 +1,150 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="AutoImportSettings">
|
||||
<option name="autoReloadType" value="SELECTIVE" />
|
||||
</component>
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="ca182f62-efdb-4f7c-b4fa-3db0a3244740" name="Changes" comment="">
|
||||
<change beforePath="$PROJECT_DIR$/backend/app/auth/dependencies.py" beforeDir="false" afterPath="$PROJECT_DIR$/backend/app/auth/dependencies.py" afterDir="false" />
|
||||
</list>
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
||||
<option name="LAST_RESOLUTION" value="IGNORE" />
|
||||
</component>
|
||||
<component name="FileTemplateManagerImpl">
|
||||
<option name="RECENT_TEMPLATES">
|
||||
<list>
|
||||
<option value="Dockerfile" />
|
||||
<option value="Python Script" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="Git.Settings">
|
||||
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
||||
</component>
|
||||
<component name="GitHubPullRequestSearchHistory">{
|
||||
"lastFilter": {
|
||||
"state": "OPEN",
|
||||
"assignee": "lorsanstand"
|
||||
}
|
||||
}</component>
|
||||
<component name="GithubPullRequestsUISettings">{
|
||||
"selectedUrlAndAccountId": {
|
||||
"url": "https://github.com/lorsanstand/Aether.git",
|
||||
"accountId": "7d226d82-cbdd-4f01-a2df-d6a90c41dc0d"
|
||||
}
|
||||
}</component>
|
||||
<component name="ProjectColorInfo">{
|
||||
"associatedIndex": 6
|
||||
}</component>
|
||||
<component name="ProjectId" id="377jRIu8LdWxRZUqlIlKecvcH0N" />
|
||||
<component name="ProjectLevelVcsManager">
|
||||
<ConfirmationsSetting value="2" id="Add" />
|
||||
</component>
|
||||
<component name="ProjectViewState">
|
||||
<option name="hideEmptyMiddlePackages" value="true" />
|
||||
<option name="showLibraryContents" value="true" />
|
||||
</component>
|
||||
<component name="PropertiesComponent">{
|
||||
"keyToString": {
|
||||
"ModuleVcsDetector.initialDetectionPerformed": "true",
|
||||
"Python.test.executor": "Run",
|
||||
"RunOnceActivity.ShowReadmeOnStart": "true",
|
||||
"RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252": "true",
|
||||
"RunOnceActivity.git.unshallow": "true",
|
||||
"RunOnceActivity.typescript.service.memoryLimit.init": "true",
|
||||
"ai.playground.ignore.import.keys.banner.in.settings": "true",
|
||||
"git-widget-placeholder": "dev",
|
||||
"last_opened_file_path": "/home/lorsan/Projects/Aether",
|
||||
"nodejs_package_manager_path": "npm",
|
||||
"settings.editor.selected.configurable": "com.jetbrains.python.configuration.PyActiveSdkModuleConfigurable",
|
||||
"vue.rearranger.settings.migration": "true"
|
||||
}
|
||||
}</component>
|
||||
<component name="RecentsManager">
|
||||
<key name="CopyFile.RECENT_KEYS">
|
||||
<recent name="$PROJECT_DIR$" />
|
||||
<recent name="$PROJECT_DIR$/frontend" />
|
||||
<recent name="$PROJECT_DIR$/backend/scripts" />
|
||||
</key>
|
||||
<key name="MoveFile.RECENT_KEYS">
|
||||
<recent name="$PROJECT_DIR$" />
|
||||
<recent name="$PROJECT_DIR$/backend/app" />
|
||||
<recent name="$PROJECT_DIR$/backend" />
|
||||
</key>
|
||||
</component>
|
||||
<component name="SharedIndexes">
|
||||
<attachedChunks>
|
||||
<set>
|
||||
<option value="bundled-js-predefined-d6986cc7102b-9b0f141eb926-JavaScript-PY-253.29346.142" />
|
||||
<option value="bundled-python-sdk-f2b7a9f6281b-6e1f45a539f7-com.jetbrains.pycharm.pro.sharedIndexes.bundled-PY-253.29346.142" />
|
||||
</set>
|
||||
</attachedChunks>
|
||||
</component>
|
||||
<component name="TaskManager">
|
||||
<task active="true" id="Default" summary="Default task">
|
||||
<changelist id="ca182f62-efdb-4f7c-b4fa-3db0a3244740" name="Changes" comment="" />
|
||||
<created>1766260993924</created>
|
||||
<option name="number" value="Default" />
|
||||
<option name="presentableId" value="Default" />
|
||||
<updated>1766260993924</updated>
|
||||
<workItem from="1766579069494" duration="537000" />
|
||||
<workItem from="1766821371454" duration="6586000" />
|
||||
<workItem from="1767182789092" duration="61000" />
|
||||
<workItem from="1767436737099" duration="457000" />
|
||||
<workItem from="1767469948303" duration="344000" />
|
||||
<workItem from="1767515834391" duration="3673000" />
|
||||
<workItem from="1767632206964" duration="1009000" />
|
||||
<workItem from="1767702075617" duration="5995000" />
|
||||
<workItem from="1767728758580" duration="1701000" />
|
||||
<workItem from="1767777556289" duration="2238000" />
|
||||
<workItem from="1767894161585" duration="3168000" />
|
||||
<workItem from="1767954037387" duration="3206000" />
|
||||
<workItem from="1768041344558" duration="24517000" />
|
||||
<workItem from="1768145545609" duration="2734000" />
|
||||
<workItem from="1768152718327" duration="3439000" />
|
||||
<workItem from="1768206631384" duration="12898000" />
|
||||
<workItem from="1768234411796" duration="7183000" />
|
||||
<workItem from="1768724458835" duration="4856000" />
|
||||
<workItem from="1768823389064" duration="12094000" />
|
||||
<workItem from="1768917847698" duration="5107000" />
|
||||
<workItem from="1769105143116" duration="384000" />
|
||||
<workItem from="1769175659580" duration="701000" />
|
||||
<workItem from="1769249378321" duration="1583000" />
|
||||
<workItem from="1769255439265" duration="6053000" />
|
||||
<workItem from="1769323001360" duration="8679000" />
|
||||
<workItem from="1769413076919" duration="20372000" />
|
||||
<workItem from="1769440193935" duration="43000" />
|
||||
<workItem from="1769440272013" duration="8398000" />
|
||||
<workItem from="1769526024191" duration="778000" />
|
||||
<workItem from="1769612969196" duration="30000" />
|
||||
<workItem from="1769614076335" duration="1957000" />
|
||||
<workItem from="1769794458540" duration="138000" />
|
||||
<workItem from="1769845775151" duration="151000" />
|
||||
<workItem from="1769845934187" duration="9000" />
|
||||
<workItem from="1770183147191" duration="612000" />
|
||||
<workItem from="1770212893901" duration="4778000" />
|
||||
<workItem from="1770364636251" duration="7167000" />
|
||||
<workItem from="1770412285849" duration="16000" />
|
||||
<workItem from="1773344252513" duration="761000" />
|
||||
<workItem from="1773416003664" duration="3382000" />
|
||||
<workItem from="1773515737128" duration="14371000" />
|
||||
<workItem from="1773683889945" duration="3252000" />
|
||||
<workItem from="1774037786590" duration="99000" />
|
||||
<workItem from="1774086898413" duration="1732000" />
|
||||
<workItem from="1774170820260" duration="64000" />
|
||||
<workItem from="1774170890907" duration="3675000" />
|
||||
<workItem from="1774246130820" duration="1316000" />
|
||||
<workItem from="1774247842084" duration="3728000" />
|
||||
<workItem from="1774623135340" duration="10000" />
|
||||
</task>
|
||||
<servers />
|
||||
</component>
|
||||
<component name="TypeScriptGeneratedFilesManager">
|
||||
<option name="version" value="3" />
|
||||
</component>
|
||||
<component name="com.intellij.coverage.CoverageDataManagerImpl">
|
||||
<SUITE FILE_PATH="coverage/Aether$test.coverage" NAME="test Coverage Results" MODIFIED="1767705414318" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/backend/app" />
|
||||
</component>
|
||||
</project>
|
||||
@@ -0,0 +1,110 @@
|
||||
# Исправление проблемы с отправкой сообщений
|
||||
|
||||
## Проблема
|
||||
Когда пользователь отправлял сообщение, оно не появлялось в чате ни у отправителя, ни у получателя до перезагрузки страницы.
|
||||
|
||||
## Корневые причины
|
||||
|
||||
### 1. **Backend - Порядок операций**
|
||||
В файле [backend/app/chats/service.py](backend/app/chats/service.py):
|
||||
- Сообщения отправлялись через WebSocket **ДО** коммита в БД
|
||||
- Это могло привести к отправке неполных или незапомненных данных
|
||||
|
||||
**Исправление:** Перемещены вызовы `await session.commit()` **перед** `await cls._send_ws_message()` во всех трех методах:
|
||||
- `send_message()` - отправка нового сообщения
|
||||
- `update_message()` - редактирование сообщения
|
||||
- `delete_message()` - удаление сообщения
|
||||
|
||||
### 2. **Frontend - Отсутствие оптимистичного обновления**
|
||||
В файле [frontend/src/pages/ChatPage.tsx](frontend/src/pages/ChatPage.tsx):
|
||||
- Приложение ждало получения сообщения через WebSocket перед показом в UI
|
||||
- Без WebSocket подписки или задержки сообщение не отображалось
|
||||
|
||||
**Исправление:** Добавлено оптимистичное обновление UI:
|
||||
- Сообщение добавляется в UI **сразу** после отправки с временным ID (`temp-{timestamp}`)
|
||||
- При получении подтверждения через WebSocket, временное сообщение заменяется на реальное
|
||||
- Если отправка завершится с ошибкой, временное сообщение удаляется
|
||||
|
||||
### 3. **Backend - Логирование и обработка ошибок**
|
||||
В файле [backend/app/services/messenger_service.py](backend/app/services/messenger_service.py):
|
||||
- Добавлена обработка исключений при отправке WebSocket сообщений
|
||||
- Улучшено логирование (удален `print("test")`, заменен на `log.info()`)
|
||||
|
||||
## Что изменилось
|
||||
|
||||
### Backend (3 файла)
|
||||
1. **service.py** - 3 исправления:
|
||||
- `send_message()`: commit перед WebSocket отправкой
|
||||
- `update_message()`: commit перед WebSocket отправкой
|
||||
- `delete_message()`: commit перед WebSocket отправкой
|
||||
|
||||
2. **messenger_service.py** - 2 исправления:
|
||||
- Добавлен try-catch в `handle_message()`
|
||||
- Заменено логирование с `print()` на `log.info()`
|
||||
|
||||
### Frontend (1 файл)
|
||||
1. **ChatPage.tsx** - 2 исправления:
|
||||
- `handleSendMessage()`: добавлено оптимистичное обновление UI
|
||||
- `ws.onmessage()`: улучшена логика замены временных сообщений на реальные
|
||||
|
||||
## Поток отправки сообщения (новый)
|
||||
|
||||
```
|
||||
1. Пользователь нажимает "Отправить"
|
||||
↓
|
||||
2. Frontend создает временное сообщение (temp-ID) и добавляет в UI
|
||||
↓
|
||||
3. Frontend отправляет запрос к backend (/chats/message)
|
||||
↓
|
||||
4. Backend сохраняет сообщение в БД
|
||||
↓
|
||||
5. Backend коммитит транзакцию в БД
|
||||
↓
|
||||
6. Backend отправляет сообщение в Redis (pub/sub)
|
||||
↓
|
||||
7. Redis PubSub listener отправляет WebSocket всем участникам чата
|
||||
↓
|
||||
8. Frontend получает сообщение через WebSocket и заменяет temp-сообщение на реальное
|
||||
↓
|
||||
9. UI обновлена и синхронизирована со всеми участниками
|
||||
```
|
||||
|
||||
## Тестирование
|
||||
|
||||
1. **Базовое тестирование:**
|
||||
- Откройте приложение в 2 браузерах для разных пользователей
|
||||
- Отправьте сообщение из первого браузера
|
||||
- Сообщение должно появиться **сразу** в обоих браузерах
|
||||
- НЕ требуется перезагрузка страницы
|
||||
|
||||
2. **Проверка временных сообщений:**
|
||||
- В DevTools откройте Console
|
||||
- Ищите сообщение вида `temp-TIMESTAMP` в начальном списке
|
||||
- После получения WebSocket ответа, ID должно измениться на UUID
|
||||
|
||||
3. **Проверка обновлений:**
|
||||
- Отредактируйте сообщение
|
||||
- Изменение должно появиться сразу у обоих пользователей
|
||||
|
||||
4. **Проверка удаления:**
|
||||
- Удалите сообщение
|
||||
- Оно должно исчезнуть сразу у обоих пользователей
|
||||
|
||||
## Возможные проблемы и решения
|
||||
|
||||
### Проблема: Сообщения все еще не появляются
|
||||
**Решение:** Проверьте:
|
||||
1. Redis работает (`docker ps | grep redis`)
|
||||
2. Backend логирует: "Starting Redis PubSub subscriber"
|
||||
3. WebSocket подключение открыто (`ws.onopen` в Console)
|
||||
4. Сообщение отправляется: смотрите сетевые запросы в DevTools
|
||||
|
||||
### Проблема: Дублирование сообщений
|
||||
**Решение:** Может быть, если:
|
||||
1. Сообщение приходит через WebSocket и добавляется дважды
|
||||
2. Проверьте логику замены temp-сообщений в `onmessage`
|
||||
|
||||
### Проблема: Сообщение показывается с ошибкой
|
||||
**Решение:**
|
||||
1. Проверьте формат данных в backend (логи Redis)
|
||||
2. Убедитесь, что Message тип совпадает со схемой
|
||||
@@ -51,7 +51,7 @@ async def get_current_superuser(current_user: UserModel = Depends(get_current_us
|
||||
|
||||
async def get_current_verified_user(current_user: UserModel = Depends(get_current_user)):
|
||||
if not current_user.is_verified:
|
||||
log.debug("User has not confirmed the email.", extra={"user_id": str(current_user.id)})
|
||||
raise HTTPException(status.HTTP_403_FORBIDDEN, detail="verify email")
|
||||
log.debug("User has not confirmed the email", extra={"user_id": str(current_user.id)})
|
||||
raise HTTPException(status.HTTP_403_FORBIDDEN, detail="Verify email")
|
||||
|
||||
return current_user
|
||||
@@ -92,14 +92,16 @@ class ChatService:
|
||||
)
|
||||
)
|
||||
|
||||
await cls._send_ws_message(members_ids, Message.model_validate(message_db))
|
||||
|
||||
await ChatDAO.update(
|
||||
session,
|
||||
ChatModel.id==target_chat_id,
|
||||
obj_in={"last_message": message.content}
|
||||
)
|
||||
await session.commit()
|
||||
|
||||
# Send WebSocket message AFTER commit to ensure data is persisted
|
||||
await cls._send_ws_message(members_ids, Message.model_validate(message_db))
|
||||
|
||||
log.info("Message sent", extra={"message_id": message_db.id, "sender_id": sender.id, "chat_id": target_chat_id})
|
||||
return message_db
|
||||
|
||||
@@ -202,9 +204,11 @@ class ChatService:
|
||||
|
||||
member_ids = [member.user_id for member in members]
|
||||
|
||||
await session.commit()
|
||||
|
||||
# Send WebSocket message AFTER commit
|
||||
await cls._send_ws_message(member_ids, Message.model_validate(message_update_db))
|
||||
|
||||
await session.commit()
|
||||
log.info("Message update successfully", extra={"user_id": user.id, "message_id": message_update.id})
|
||||
return message_update_db
|
||||
|
||||
@@ -235,7 +239,9 @@ class ChatService:
|
||||
|
||||
await MessageDAO.delete(session, MessageModel.id==message_id)
|
||||
|
||||
await session.commit()
|
||||
|
||||
# Send WebSocket message AFTER commit
|
||||
await cls._delete_ws_message(member_ids, message_id)
|
||||
|
||||
await session.commit()
|
||||
log.info("Message delete successfully", extra={"user_id": user.id, "message_id": message_exist.id})
|
||||
@@ -18,7 +18,7 @@ class PubSubMessenger:
|
||||
|
||||
@classmethod
|
||||
async def subscribe_to_channels(cls):
|
||||
print("test")
|
||||
log.info("Starting Redis PubSub subscriber")
|
||||
redis_client = await get_redis()
|
||||
pubsub = redis_client.pubsub()
|
||||
|
||||
@@ -46,11 +46,14 @@ class PubSubMessenger:
|
||||
@classmethod
|
||||
async def handle_message(cls, payload, ws: WebSocket):
|
||||
log.debug("Message start sending type: %s", payload["type"])
|
||||
if payload["type"] == "send":
|
||||
await ws.send_json(payload["data"])
|
||||
elif payload["type"] == "del":
|
||||
await ws.send_json({"type": "del", "message_id": payload["data"]})
|
||||
log.info(f"Message sent to user via WebSocket")
|
||||
try:
|
||||
if payload["type"] == "send":
|
||||
await ws.send_json(payload["data"])
|
||||
elif payload["type"] == "del":
|
||||
await ws.send_json({"type": "del", "message_id": payload["data"]})
|
||||
log.info(f"Message sent to user via WebSocket")
|
||||
except Exception as e:
|
||||
log.error(f"Error sending WebSocket message: {e}", exc_info=True)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -142,38 +142,43 @@ export default function ChatPage() {
|
||||
|
||||
// Add or update message in current chat if it belongs to it
|
||||
if (selectedChat && message.chat_id === selectedChat.chat_id) {
|
||||
const isNewMessage = !messages.some(m => m.id === message.id);
|
||||
|
||||
setMessages(prev => {
|
||||
// Check if message already exists (update it if edited)
|
||||
// Check if this is replacing a temporary message (sent by current user)
|
||||
const tempMessageIndex = prev.findIndex(m => m.id.startsWith('temp-') && m.sender_id === message.sender_id && m.content === message.content);
|
||||
|
||||
// Check if message already exists
|
||||
const existingIndex = prev.findIndex(m => m.id === message.id);
|
||||
if (existingIndex !== -1) {
|
||||
// Update existing message
|
||||
|
||||
if (tempMessageIndex !== -1) {
|
||||
// Replace temporary message with real one
|
||||
const updated = [...prev];
|
||||
updated[tempMessageIndex] = message;
|
||||
return updated;
|
||||
} else if (existingIndex !== -1) {
|
||||
// Update existing message (edit case)
|
||||
const updated = [...prev];
|
||||
updated[existingIndex] = message;
|
||||
return updated;
|
||||
} else {
|
||||
// Add new message from other user
|
||||
return [...prev, message];
|
||||
}
|
||||
// Add new message
|
||||
return [...prev, message];
|
||||
});
|
||||
|
||||
// Handle scroll and unread counter for new messages
|
||||
if (isNewMessage) {
|
||||
// Check if this is our own message
|
||||
const isOwnMessage = message.sender_id === user?.id;
|
||||
const isOwnMessage = message.sender_id === user?.id;
|
||||
|
||||
// Use ref to check current position synchronously
|
||||
setTimeout(() => {
|
||||
if (isOwnMessage || isAtBottomRef.current) {
|
||||
// Auto-scroll if it's our message or if at bottom
|
||||
scrollToBottom(true);
|
||||
} else {
|
||||
// Increment unread counter if not at bottom and not our message
|
||||
setUnreadCount(prev => prev + 1);
|
||||
setShowScrollButton(true);
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
// Use ref to check current position synchronously
|
||||
setTimeout(() => {
|
||||
if (isOwnMessage || isAtBottomRef.current) {
|
||||
// Auto-scroll if it's our message or if at bottom
|
||||
scrollToBottom(true);
|
||||
} else {
|
||||
// Increment unread counter if not at bottom and not our message
|
||||
setUnreadCount(prev => prev + 1);
|
||||
setShowScrollButton(true);
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
|
||||
// Update chat list with new last message
|
||||
@@ -354,15 +359,32 @@ export default function ChatPage() {
|
||||
try {
|
||||
setSendingMessage(true);
|
||||
|
||||
const messageContent = messageText.trim();
|
||||
|
||||
// Create a temporary message to show immediately (optimistic update)
|
||||
const tempMessage: Message = {
|
||||
id: `temp-${Date.now()}`, // Temporary ID
|
||||
sender_id: user?.id || 0,
|
||||
chat_id: selectedChat?.chat_id || '',
|
||||
content: messageContent,
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString(),
|
||||
};
|
||||
|
||||
// Add message to UI immediately
|
||||
setMessages(prev => [...prev, tempMessage]);
|
||||
|
||||
// Send message with chat_id (если чат существует) или recipient_id (если новый чат)
|
||||
await chatService.sendMessage({
|
||||
content: messageText.trim(),
|
||||
const sentMessage = await chatService.sendMessage({
|
||||
content: messageContent,
|
||||
chat_id: selectedChat?.chat_id,
|
||||
recipient_id: selectedChat?.user_id,
|
||||
});
|
||||
|
||||
// Don't add message locally - it will come via WebSocket
|
||||
// This prevents duplication
|
||||
// Replace temporary message with real one from backend
|
||||
setMessages(prev =>
|
||||
prev.map(m => m.id === tempMessage.id ? sentMessage : m)
|
||||
);
|
||||
|
||||
// Clear input
|
||||
setMessageText('');
|
||||
@@ -370,7 +392,7 @@ export default function ChatPage() {
|
||||
// Update chat list with new last message
|
||||
const updatedChats = chats.map(chat =>
|
||||
chat.chat_id === selectedChat?.chat_id
|
||||
? { ...chat, last_message: messageText.trim() }
|
||||
? { ...chat, last_message: messageContent }
|
||||
: chat
|
||||
);
|
||||
setChats(updatedChats);
|
||||
@@ -378,11 +400,18 @@ export default function ChatPage() {
|
||||
|
||||
// Update cache for specific chat
|
||||
if (selectedChat?.chat_id) {
|
||||
updateChatCache(selectedChat.chat_id, { last_message: messageText.trim() });
|
||||
updateChatCache(selectedChat.chat_id, { last_message: messageContent });
|
||||
}
|
||||
|
||||
// Auto-scroll to bottom
|
||||
setTimeout(() => {
|
||||
scrollToBottom(true);
|
||||
}, 100);
|
||||
} catch (err: any) {
|
||||
console.error('Failed to send message:', err);
|
||||
alert('Не удалось отправить сообщение');
|
||||
// Remove temporary message if sending failed
|
||||
setMessages(prev => prev.filter(m => !m.id.startsWith('temp-')));
|
||||
} finally {
|
||||
setSendingMessage(false);
|
||||
// Keep input focused after everything is done
|
||||
|
||||
Reference in New Issue
Block a user