This commit is contained in:
2025-08-26 16:21:16 +02:00
parent 39d1d9a2b7
commit 8bddcc1b01
14 changed files with 241 additions and 2296 deletions

8
.idea/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

View File

@@ -1,18 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="dataSourceStorageLocal" created-in="RR-252.23892.452">
<data-source name="db.sqlite" uuid="26059583-0fdb-4f6f-ad11-10388e9658c2">
<database-info product="SQLite" version="3.45.1" jdbc-version="4.2" driver-name="SQLite JDBC" driver-version="3.45.1.0" dbms="SQLITE" exact-version="3.45.1" exact-driver-version="3.45">
<identifier-quote-string>&quot;</identifier-quote-string>
</database-info>
<case-sensitivity plain-identifiers="mixed" quoted-identifiers="mixed" />
<secret-storage>master_key</secret-storage>
<auth-provider>no-auth</auth-provider>
<schema-mapping>
<introspection-scope>
<node kind="schema" qname="@" />
</introspection-scope>
</schema-mapping>
</data-source>
</component>
</project>

12
.idea/dataSources.xml generated
View File

@@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
<data-source source="LOCAL" name="db.sqlite" uuid="26059583-0fdb-4f6f-ad11-10388e9658c2">
<driver-ref>sqlite.xerial</driver-ref>
<synchronize>true</synchronize>
<jdbc-driver>org.sqlite.JDBC</jdbc-driver>
<jdbc-url>jdbc:sqlite:$PROJECT_DIR$/db.sqlite</jdbc-url>
<working-dir>$ProjectFileDir$</working-dir>
</data-source>
</component>
</project>

File diff suppressed because it is too large Load Diff

View File

@@ -1,2 +0,0 @@
#n:main
!<md> [0, 0, null, null, -2147483648, -2147483648]

230
.idea/workspace.xml generated
View File

@@ -1,9 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="AnalysisUIOptions">
<option name="ANALYZE_INJECTED_CODE" value="false" />
<option name="SCOPE_TYPE" value="3" />
</component>
<component name="AutoImportSettings"> <component name="AutoImportSettings">
<option name="autoReloadType" value="ALL" /> <option name="autoReloadType" value="ALL" />
</component> </component>
@@ -15,20 +11,56 @@
</cargoProject> </cargoProject>
</component> </component>
<component name="ChangeListManager"> <component name="ChangeListManager">
<list default="true" id="ca698286-778f-4335-97c8-da35a666c986" name="Changes" comment="init"> <list default="true" id="b2b598c9-ef0b-4cbc-8852-cfbc8ce3920e" name="Changes" comment="">
<change afterPath="$PROJECT_DIR$/src/store/session/client.rs" afterDir="false" /> <change beforePath="$PROJECT_DIR$/.idea/dataSources.local.xml" beforeDir="false" />
<change afterPath="$PROJECT_DIR$/src/store/session/mod.rs" afterDir="false" /> <change beforePath="$PROJECT_DIR$/.idea/dataSources.xml" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/dataSources.local.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/dataSources.local.xml" afterDir="false" /> <change beforePath="$PROJECT_DIR$/.idea/dataSources/26059583-0fdb-4f6f-ad11-10388e9658c2.xml" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/dataSources/26059583-0fdb-4f6f-ad11-10388e9658c2/storage_v2/_src_/schema/main.uQUzAA.meta" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/modules.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/modules.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/ox_speak_server.iml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/ox_speak_server.iml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/vcs.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/vcs.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" /> <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Cargo.lock" beforeDir="false" afterPath="$PROJECT_DIR$/Cargo.lock" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Cargo.toml" beforeDir="false" afterPath="$PROJECT_DIR$/Cargo.toml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/README.md" beforeDir="false" afterPath="$PROJECT_DIR$/README.md" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/app/app.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/app/app.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/app/conf.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/app/conf.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/app/mod.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/app/mod.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/domain/client.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/domain/client.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/domain/event.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/domain/event.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/domain/mod.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/domain/mod.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/domain/models.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/domain/models.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/domain/user.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/domain/user.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/lib.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/lib.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/network/http.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/network/http.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/network/http_routes/channel.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/network/http_routes/channel.rs" afterDir="false" /> <change beforePath="$PROJECT_DIR$/src/network/http_routes/channel.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/network/http_routes/channel.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/network/http_routes/master.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/network/http_routes/master.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/network/http_routes/message.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/network/http_routes/message.rs" afterDir="false" /> <change beforePath="$PROJECT_DIR$/src/network/http_routes/message.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/network/http_routes/message.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/network/http_routes/mod.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/network/http_routes/mod.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/network/http_routes/sub_server.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/network/http_routes/sub_server.rs" afterDir="false" /> <change beforePath="$PROJECT_DIR$/src/network/http_routes/sub_server.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/network/http_routes/sub_server.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/network/http_routes/user.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/network/http_routes/user.rs" afterDir="false" /> <change beforePath="$PROJECT_DIR$/src/network/http_routes/user.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/network/http_routes/user.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/network/http_routes/websocket.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/network/http_routes/websocket.rs" afterDir="false" /> <change beforePath="$PROJECT_DIR$/src/network/http_routes/websocket.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/network/http_routes/websocket.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/network/mod.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/network/mod.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/network/protocol.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/network/protocol.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/network/udp.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/network/udp.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/network/udp_back.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/network/udp_back.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/runtime/dispatcher.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/runtime/dispatcher.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/store/migrations/001_init.sqlite.sql" beforeDir="false" afterPath="$PROJECT_DIR$/src/store/migrations/001_init.sqlite.sql" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/store/mod.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/store/mod.rs" afterDir="false" /> <change beforePath="$PROJECT_DIR$/src/store/mod.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/store/mod.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/store/models/channel.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/store/models/channel.rs" afterDir="false" /> <change beforePath="$PROJECT_DIR$/src/store/models/channel.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/store/models/channel.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/store/models/link_sub_server_user.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/store/models/link_sub_server_user.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/store/models/message.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/store/models/message.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/store/models/mod.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/store/models/mod.rs" afterDir="false" /> <change beforePath="$PROJECT_DIR$/src/store/models/mod.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/store/models/mod.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/store/models/sub_server.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/store/models/sub_server.rs" afterDir="false" /> <change beforePath="$PROJECT_DIR$/src/store/models/sub_server.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/store/models/sub_server.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/store/models/user.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/store/models/user.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/store/repositories/channel_repository.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/store/repositories/channel_repository.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/store/repositories/link_sub_server_user_repository.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/store/repositories/link_sub_server_user_repository.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/store/repositories/message_repository.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/store/repositories/message_repository.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/store/repositories/mod.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/store/repositories/mod.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/store/repositories/sub_server_repository.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/store/repositories/sub_server_repository.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/store/repositories/user_repository.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/store/repositories/user_repository.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/store/session/client.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/store/session/client.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/store/store_service.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/store/store_service.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/utils/shared_store.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/utils/shared_store.rs" afterDir="false" /> <change beforePath="$PROJECT_DIR$/src/utils/shared_store.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/utils/shared_store.rs" afterDir="false" />
</list> </list>
<option name="SHOW_DIALOG" value="false" /> <option name="SHOW_DIALOG" value="false" />
@@ -37,43 +69,32 @@
<option name="LAST_RESOLUTION" value="IGNORE" /> <option name="LAST_RESOLUTION" value="IGNORE" />
</component> </component>
<component name="ExecutionTargetManager" SELECTED_TARGET="RsBuildProfile:dev" /> <component name="ExecutionTargetManager" SELECTED_TARGET="RsBuildProfile:dev" />
<component name="FileTemplateManagerImpl">
<option name="RECENT_TEMPLATES">
<list>
<option value="Rust File" />
</list>
</option>
</component>
<component name="Git.Settings"> <component name="Git.Settings">
<option name="RECENT_BRANCH_BY_REPOSITORY">
<map>
<entry key="$PROJECT_DIR$" value="be761a8c4601d565574e6d42ad4711e6c054fc75" />
</map>
</option>
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" /> <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component> </component>
<component name="MacroExpansionManager"> <component name="MacroExpansionManager">
<option name="directoryName" value="oBePpDlK" /> <option name="directoryName" value="j91G0RKo" />
</component> </component>
<component name="ProjectColorInfo">{ <component name="ProjectColorInfo">{
&quot;associatedIndex&quot;: 5 &quot;associatedIndex&quot;: 7
}</component> }</component>
<component name="ProjectId" id="2zaZ93S6zEp6mJe7Xq5WfvIMexv" /> <component name="ProjectId" id="31h5TOFgo6LtoeKpSInG1hXoNI7" />
<component name="ProjectViewState"> <component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" /> <option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" /> <option name="showLibraryContents" value="true" />
</component> </component>
<component name="PropertiesComponent">{ <component name="PropertiesComponent">{
&quot;keyToString&quot;: { &quot;keyToString&quot;: {
&quot;Cargo.Run.executor&quot;: &quot;Run&quot;, &quot;Cargo.Run ox_speak_server.executor&quot;: &quot;Run&quot;,
&quot;ModuleVcsDetector.initialDetectionPerformed&quot;: &quot;true&quot;, &quot;ModuleVcsDetector.initialDetectionPerformed&quot;: &quot;true&quot;,
&quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;, &quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;,
&quot;RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252&quot;: &quot;true&quot;,
&quot;RunOnceActivity.git.unshallow&quot;: &quot;true&quot;, &quot;RunOnceActivity.git.unshallow&quot;: &quot;true&quot;,
&quot;RunOnceActivity.rust.reset.selective.auto.import&quot;: &quot;true&quot;, &quot;RunOnceActivity.rust.reset.selective.auto.import&quot;: &quot;true&quot;,
&quot;git-widget-placeholder&quot;: &quot;master&quot;, &quot;git-widget-placeholder&quot;: &quot;master&quot;,
&quot;ignore.virus.scanning.warn.message&quot;: &quot;true&quot;, &quot;ignore.virus.scanning.warn.message&quot;: &quot;true&quot;,
&quot;junie.onboarding.icon.badge.shown&quot;: &quot;true&quot;, &quot;junie.onboarding.icon.badge.shown&quot;: &quot;true&quot;,
&quot;last_opened_file_path&quot;: &quot;D:/Dev/ox_speak_server/src/network&quot;, &quot;last_opened_file_path&quot;: &quot;//wsl.localhost/Debian/home/Nell/linux_dev/ox_speak_server&quot;,
&quot;node.js.detected.package.eslint&quot;: &quot;true&quot;, &quot;node.js.detected.package.eslint&quot;: &quot;true&quot;,
&quot;node.js.detected.package.tslint&quot;: &quot;true&quot;, &quot;node.js.detected.package.tslint&quot;: &quot;true&quot;,
&quot;node.js.selected.package.eslint&quot;: &quot;(autodetect)&quot;, &quot;node.js.selected.package.eslint&quot;: &quot;(autodetect)&quot;,
@@ -82,28 +103,14 @@
&quot;org.rust.cargo.project.model.PROJECT_DISCOVERY&quot;: &quot;true&quot;, &quot;org.rust.cargo.project.model.PROJECT_DISCOVERY&quot;: &quot;true&quot;,
&quot;org.rust.cargo.project.model.impl.CargoExternalSystemProjectAware.subscribe.first.balloon&quot;: &quot;&quot;, &quot;org.rust.cargo.project.model.impl.CargoExternalSystemProjectAware.subscribe.first.balloon&quot;: &quot;&quot;,
&quot;org.rust.first.attach.projects&quot;: &quot;true&quot;, &quot;org.rust.first.attach.projects&quot;: &quot;true&quot;,
&quot;run.code.analysis.last.selected.profile&quot;: &quot;pProject Default&quot;, &quot;settings.editor.selected.configurable&quot;: &quot;preferences.pluginManager&quot;,
&quot;settings.editor.selected.configurable&quot;: &quot;junie.allowlist&quot;,
&quot;to.speed.mode.migration.done&quot;: &quot;true&quot;, &quot;to.speed.mode.migration.done&quot;: &quot;true&quot;,
&quot;vue.rearranger.settings.migration&quot;: &quot;true&quot; &quot;vue.rearranger.settings.migration&quot;: &quot;true&quot;
},
&quot;keyToStringList&quot;: {
&quot;DatabaseDriversLRU&quot;: [
&quot;sqlite&quot;
]
} }
}</component> }</component>
<component name="RecentsManager"> <component name="RunManager" selected="Cargo.Run ox_speak_server">
<key name="CopyFile.RECENT_KEYS"> <configuration name="Run ox_speak_server" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
<recent name="D:\Dev\ox_speak_server\src\network" /> <option name="buildProfileId" value="dev" />
</key>
<key name="MoveFile.RECENT_KEYS">
<recent name="D:\Dev\ox_speak_server\src\store" />
<recent name="D:\Dev\ox_speak_server\src\store\repositories" />
</key>
</component>
<component name="RunManager">
<configuration name="Run" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
<option name="command" value="run --package ox_speak_server --bin ox_speak_server" /> <option name="command" value="run --package ox_speak_server --bin ox_speak_server" />
<option name="workingDirectory" value="file://$PROJECT_DIR$" /> <option name="workingDirectory" value="file://$PROJECT_DIR$" />
<envs /> <envs />
@@ -120,133 +127,42 @@
<option name="CARGO.BUILD_TASK_PROVIDER" enabled="true" /> <option name="CARGO.BUILD_TASK_PROVIDER" enabled="true" />
</method> </method>
</configuration> </configuration>
<configuration name="Test ox_speak_server" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
<option name="command" value="test --workspace" />
<option name="workingDirectory" value="file://$PROJECT_DIR$" />
<envs />
<option name="emulateTerminal" value="true" />
<option name="channel" value="DEFAULT" />
<option name="requiredFeatures" value="true" />
<option name="allFeatures" value="false" />
<option name="withSudo" value="false" />
<option name="buildTarget" value="REMOTE" />
<option name="backtrace" value="SHORT" />
<option name="isRedirectInput" value="false" />
<option name="redirectInputPath" value="" />
<method v="2">
<option name="CARGO.BUILD_TASK_PROVIDER" enabled="true" />
</method>
</configuration>
</component> </component>
<component name="RustProjectSettings"> <component name="RustProjectSettings">
<option name="toolchainHomeDirectory" value="$USER_HOME$/.cargo/bin" /> <option name="toolchainHomeDirectory" value="$PROJECT_DIR$/../../.cargo/bin" />
</component> </component>
<component name="TaskManager"> <component name="TaskManager">
<task active="true" id="Default" summary="Default task"> <task active="true" id="Default" summary="Default task">
<changelist id="ca698286-778f-4335-97c8-da35a666c986" name="Changes" comment="" /> <changelist id="b2b598c9-ef0b-4cbc-8852-cfbc8ce3920e" name="Changes" comment="" />
<created>1751970990022</created> <created>1755963473950</created>
<option name="number" value="Default" /> <option name="number" value="Default" />
<option name="presentableId" value="Default" /> <option name="presentableId" value="Default" />
<updated>1751970990022</updated> <updated>1755963473950</updated>
<workItem from="1751970991653" duration="7327000" /> <workItem from="1755963475439" duration="2760000" />
<workItem from="1752016714585" duration="1095000" /> <workItem from="1756139974994" duration="110000" />
<workItem from="1752076174877" duration="11015000" />
<workItem from="1752189013977" duration="4750000" />
<workItem from="1752224469499" duration="14591000" />
<workItem from="1752305152715" duration="19879000" />
<workItem from="1752391851097" duration="12061000" />
<workItem from="1752446764168" duration="8166000" />
<workItem from="1752484190578" duration="17709000" />
<workItem from="1752591656862" duration="429000" />
<workItem from="1752593250094" duration="11969000" />
<workItem from="1752694022400" duration="6212000" />
<workItem from="1752741840195" duration="2946000" />
<workItem from="1752833798325" duration="5366000" />
<workItem from="1752917416027" duration="1192000" />
<workItem from="1752931843330" duration="2938000" />
<workItem from="1752997629708" duration="9070000" />
<workItem from="1753107912894" duration="595000" />
<workItem from="1753225761416" duration="1356000" />
<workItem from="1753282037526" duration="5207000" />
<workItem from="1753397680991" duration="1782000" />
<workItem from="1753399490773" duration="3189000" />
<workItem from="1753436756029" duration="16895000" />
<workItem from="1753521176318" duration="17811000" />
<workItem from="1753601912332" duration="5843000" />
<workItem from="1753718175508" duration="8774000" />
<workItem from="1753800817354" duration="3570000" />
<workItem from="1753804571241" duration="59000" />
<workItem from="1753804642657" duration="236000" />
<workItem from="1753804898179" duration="625000" />
<workItem from="1753805533139" duration="2956000" />
<workItem from="1753868593668" duration="550000" />
<workItem from="1753869165302" duration="389000" />
<workItem from="1753869606067" duration="448000" />
<workItem from="1753870068458" duration="3339000" />
<workItem from="1753975758350" duration="2576000" />
<workItem from="1754003476744" duration="2622000" />
<workItem from="1754006134163" duration="1746000" />
<workItem from="1754007911864" duration="414000" />
<workItem from="1754039613630" duration="3435000" />
<workItem from="1754131509073" duration="779000" />
<workItem from="1754132326149" duration="52000" />
<workItem from="1754132390785" duration="5048000" />
<workItem from="1754211612647" duration="13365000" />
<workItem from="1754321796199" duration="4611000" />
<workItem from="1754409493435" duration="2864000" />
<workItem from="1754521183720" duration="4070000" />
<workItem from="1754761637246" duration="1293000" />
<workItem from="1754781483976" duration="3949000" />
<workItem from="1754923017894" duration="1334000" />
</task> </task>
<task id="LOCAL-00001" summary="init">
<option name="closed" value="true" />
<created>1752591904243</created>
<option name="number" value="00001" />
<option name="presentableId" value="LOCAL-00001" />
<option name="project" value="LOCAL" />
<updated>1752591904243</updated>
</task>
<task id="LOCAL-00002" summary="init">
<option name="closed" value="true" />
<created>1752591989113</created>
<option name="number" value="00002" />
<option name="presentableId" value="LOCAL-00002" />
<option name="project" value="LOCAL" />
<updated>1752591989113</updated>
</task>
<task id="LOCAL-00003" summary="init">
<option name="closed" value="true" />
<created>1753813204617</created>
<option name="number" value="00003" />
<option name="presentableId" value="LOCAL-00003" />
<option name="project" value="LOCAL" />
<updated>1753813204617</updated>
</task>
<task id="LOCAL-00004" summary="init">
<option name="closed" value="true" />
<created>1754213783870</created>
<option name="number" value="00004" />
<option name="presentableId" value="LOCAL-00004" />
<option name="project" value="LOCAL" />
<updated>1754213783870</updated>
</task>
<option name="localTasksCounter" value="5" />
<servers /> <servers />
</component> </component>
<component name="TypeScriptGeneratedFilesManager"> <component name="TypeScriptGeneratedFilesManager">
<option name="version" value="3" /> <option name="version" value="3" />
</component> </component>
<component name="Vcs.Log.Tabs.Properties">
<option name="TAB_STATES">
<map>
<entry key="MAIN">
<value>
<State>
<option name="FILTERS">
<map>
<entry key="branch">
<value>
<list>
<option value="master" />
</list>
</value>
</entry>
</map>
</option>
</State>
</value>
</entry>
</map>
</option>
</component>
<component name="VcsManagerConfiguration">
<MESSAGE value="init" />
<option name="LAST_COMMIT_MESSAGE" value="init" />
</component>
<component name="XSLT-Support.FileAssociations.UIState"> <component name="XSLT-Support.FileAssociations.UIState">
<expand /> <expand />
<select /> <select />

16
Cargo.lock generated
View File

@@ -1432,12 +1432,12 @@ dependencies = [
[[package]] [[package]]
name = "socket2" name = "socket2"
version = "0.5.10" version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807"
dependencies = [ dependencies = [
"libc", "libc",
"windows-sys 0.52.0", "windows-sys 0.59.0",
] ]
[[package]] [[package]]
@@ -1776,9 +1776,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]] [[package]]
name = "tokio" name = "tokio"
version = "1.46.1" version = "1.47.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0cc3a2344dafbe23a245241fe8b09735b521110d30fcefbbd5feb1797ca35d17" checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038"
dependencies = [ dependencies = [
"backtrace", "backtrace",
"bytes", "bytes",
@@ -1791,7 +1791,7 @@ dependencies = [
"slab", "slab",
"socket2", "socket2",
"tokio-macros", "tokio-macros",
"windows-sys 0.52.0", "windows-sys 0.59.0",
] ]
[[package]] [[package]]
@@ -1969,9 +1969,9 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
[[package]] [[package]]
name = "uuid" name = "uuid"
version = "1.17.0" version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d" checksum = "f33196643e165781c20a5ead5582283a7dacbb87855d867fbc2df3f81eddc1be"
dependencies = [ dependencies = [
"getrandom 0.3.3", "getrandom 0.3.3",
"js-sys", "js-sys",

View File

@@ -17,9 +17,9 @@ debug = true
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
serde_json = "1" serde_json = "1"
parking_lot = "0.12" parking_lot = "0.12"
tokio = { version = "1.46", features = ["full"] } tokio = { version = "1.47", features = ["full"] }
strum = {version = "0.27", features = ["derive"] } strum = {version = "0.27", features = ["derive"] }
uuid = {version = "1.17", features = ["v4", "serde"] } uuid = {version = "1.18", features = ["v4", "serde"] }
event-listener = "5.4" event-listener = "5.4"
dashmap = "6.1" dashmap = "6.1"
bytes = "1.10" bytes = "1.10"

View File

@@ -3,3 +3,4 @@ créer un fichier de migration sqlx :
sqlx migrate add --source src/store/migrations migration_name sqlx migrate add --source src/store/migrations migration_name
``` ```
# Example

View File

@@ -1,11 +1,9 @@
use std::net::SocketAddr; use std::net::SocketAddr;
use std::sync::Arc; use std::sync::Arc;
use axum::{ use axum::{extract::{ws::WebSocket, ws::WebSocketUpgrade, State}, response::Response, Json, Router, routing::{get, post}, middleware};
extract::{ws::WebSocket, ws::WebSocketUpgrade, State}, use axum::body::Body;
response::Response, use axum::http::{HeaderValue, Request, StatusCode};
Json, Router, use axum::middleware::Next;
routing::{get, post}
};
use tokio::net::TcpListener; use tokio::net::TcpListener;
use crate::domain::event::EventBus; use crate::domain::event::EventBus;
use crate::network::http_routes::master; use crate::network::http_routes::master;
@@ -46,7 +44,8 @@ impl HttpServer {
fn create_router(&self) -> Router { fn create_router(&self) -> Router {
let api_route = Router::new() let api_route = Router::new()
.nest("/master", master::create_router()); .nest("/master", master::create_router())
.layer(middleware::from_fn(app_only_middleware));
Router::new() Router::new()
.nest("/api", api_route) .nest("/api", api_route)
@@ -68,3 +67,23 @@ impl HttpServer {
}) })
} }
} }
// Middlewares
async fn app_only_middleware(request: Request<Body>, next: Next) -> Result<Response, StatusCode> {
let headers = request.headers();
let expected_client = HeaderValue::from_static("ox_speak");
let expected_version = HeaderValue::from_static("1.0");
let expected_user_agent = HeaderValue::from_static("OxSpeak/1.0");
if headers.get("X-Client-Type") != Some(&expected_client) ||
headers.get("X-Protocol-Version") != Some(&expected_version) ||
headers.get("User-Agent") != Some(&expected_user_agent) {
return Ok(Response::builder()
.status(StatusCode::BAD_REQUEST)
.body("Invalid client".into())
.unwrap());
}
Ok(next.run(request).await)
}

View File

@@ -10,9 +10,37 @@ use crate::network::http::HttpState;
pub fn create_router() -> Router<HttpState> { pub fn create_router() -> Router<HttpState> {
Router::new() Router::new()
.route("/auth/", post(join_master_server)) .route("/auth/", post(join))
} }
pub async fn join_master_server(State(state): State<HttpState>) -> Json<HashMap<String, String>> { pub async fn challenge(State(state): State<HttpState>) -> Json<HashMap<String, String>> {
todo!("join master server") // permet de sécuriser les échanges entre le client et le serveur pour qu'elle soit propre à l'application
// l'idée est d'éviter que les système comme les navigateurs, curl ... puisse accéder à l'application
todo!("challenge master server")
}
pub async fn join(State(state): State<HttpState>) -> Json<HashMap<String, String>> {
// Cette page est systématiquement appelé à la première connexion du client
// dans le payload de la requête, on aura les info client
// public key
// dans la réponse, on aura le status
// success, error, password_needed
// match determine_server_state().await {
// ServerState::FirstSetup => {
// // Logique de création du super admin
// // Validation des champs requis (password, username)
// }
// ServerState::RequiresPassword => {
// // Logique d'authentification
// // Validation du mot de passe serveur
// }
// ServerState::Ready => {
// // Logique de connexion normale
// // Juste la public key suffit
// }
// }
todo!("join master server")
} }

View File

@@ -3,7 +3,6 @@ pub mod channel;
pub mod user; pub mod user;
pub mod message; pub mod message;
pub mod link_sub_server_user; pub mod link_sub_server_user;
mod models;
pub use sub_server::*; pub use sub_server::*;
pub use channel::*; pub use channel::*;

View File

@@ -103,7 +103,7 @@ pub struct ClientManager {
/// Index des clients par channel_id /// Index des clients par channel_id
voice_channel_clients: SharedArcMap<Channel, SharedArcHashSet<Client>>, // channel_id -> HashSet<session_id> voice_channel_clients: SharedArcMap<Channel, SharedArcHashSet<Client>>, // channel_id -> HashSet<session_id>
/// Index des clients par sub_server_id /// Index des clients par sub_server_id
sub_server_clients: SharedArcMap<Uuid, SharedArcVec<Client>>, // sub_server_id -> Vec<session_id> sub_server_clients: SharedArcMap<SubServer, SharedArcHashSet<Client>>, // sub_server_id -> Vec<session_id>
/// Index des clients par adresse UDP /// Index des clients par adresse UDP
udp_clients: SharedArcMap<SocketAddr, String>, // udp_address -> session_id udp_clients: SharedArcMap<SocketAddr, String>, // udp_address -> session_id
} }
@@ -135,9 +135,18 @@ impl ClientManager {
arc_client arc_client
} }
pub fn join_sub_server(&self, client: &Arc<Client>, sub_server: &SubServer) {
let clients = self.sub_server_clients.get(sub_server).unwrap_or_else(|| {
let new_clients = Arc::new(SharedArcHashSet::new());
self.sub_server_clients.insert_arc(sub_server.clone(), new_clients.clone());
new_clients
});
clients.insert_arc(client.clone());
}
/// Obtient un client par son session_id /// Obtient un client par son session_id
pub fn get_client(&self, session_id: &Uuid) -> Option<Arc<Client>> { pub fn get_client(&self, session_id: &Uuid) -> Option<Arc<Client>> {
// Convertir &str en String pour les clés
self.clients.get(session_id) self.clients.get(session_id)
} }
@@ -159,35 +168,14 @@ impl ClientManager {
/// ///
/// # Returns /// # Returns
/// `true` si le client a été trouvé et modifié, `false` sinon /// `true` si le client a été trouvé et modifié, `false` sinon
pub fn modify_client<F>(&self, session_id: &str, f: F) -> bool pub fn modify_client<F>(&self, client: &Client, f: F) -> bool
where where
F: FnOnce(&mut Client), F: FnOnce(&mut Client),
{ {
// Convertir &str en String pour les clés // Convertir &str en String pour les clés
self.clients.modify(&session_id.to_string(), f)
todo!()
} }
// ===== OPÉRATIONS ATOMIQUES HAUT NIVEAU =====
// Nettoie tous les index d'un client (appelé lors de la suppression)
// pub(crate) fn cleanup_client_indexes(&self, session_id: &str) {
// if let Some(client) = self.get_client(session_id) {
// // Nettoyer l'index des channels
// if let Some(channel_id) = client.current_channel {
// self.remove_from_channel_index(session_id, channel_id);
// }
//
// // Nettoyer l'index des sub-servers
// if let Some(sub_server_id) = client.current_sub_server {
// self.remove_from_sub_server_index(session_id, sub_server_id);
// }
//
// // Nettoyer l'index UDP
// if let Some(udp_address) = client.udp_socket {
// self.udp_clients.remove(&udp_address);
// }
// }
// }
} }
impl ClientManager { impl ClientManager {
@@ -219,16 +207,7 @@ impl ClientManager {
pub fn remove_client_from_sub_servers(&self, client: &Client) { pub fn remove_client_from_sub_servers(&self, client: &Client) {
for (sub_server, clients) in self.sub_server_clients.iter() { for (sub_server, clients) in self.sub_server_clients.iter() {
let mut to_remove = Vec::<usize>::new(); clients.remove(client);
for (index, client) in clients.iter().enumerate() {
if client == client {
to_remove.push(index);
}
}
for idx in to_remove.iter() {
clients.
}
} }
} }
} }

View File

@@ -246,63 +246,52 @@ impl<T> SharedVec<T> {
/// Supprime l'élément à l'index donné et le retourne. /// Supprime l'élément à l'index donné et le retourne.
/// Retourne `None` si l'index est hors limites. /// Retourne `None` si l'index est hors limites.
///
/// Exemple : `let removed = clients.delete(2); // Supprime le 3e client`
pub fn delete(&self, index: usize) -> Option<T> pub fn delete(&self, index: usize) -> Option<T>
where where
T: Clone, T: Clone,
{ {
loop { let current = self.inner.load_full();
let current = self.inner.load();
if index >= current.len() { if index >= current.len() {
return None; return None;
} }
let mut new_vec = current.as_ref().clone(); let mut new_vec = current.as_ref().clone();
let removed_item = new_vec.remove(index); let removed_item = new_vec.remove(index);
self.inner.store(Arc::new(new_vec));
let new_arc = Arc::new(new_vec); Some(removed_item)
if self.inner.compare_and_swap(&current, new_arc).is_ok() {
return Some(removed_item);
}
// Retry si le CAS a échoué
}
} }
/// Supprime et retourne le dernier élément du vecteur. /// Supprime et retourne le dernier élément du vecteur.
/// Retourne `None` si le vecteur est vide. /// Retourne `None` si le vecteur est vide.
///
/// Exemple : `let last_client = clients.pop(); // Supprime le dernier client`
pub fn pop(&self) -> Option<T> pub fn pop(&self) -> Option<T>
where where
T: Clone, T: Clone,
{ {
loop { let current = self.inner.load_full();
let current = self.inner.load();
if current.is_empty() { if current.is_empty() {
return None; return None;
} }
let mut new_vec = current.as_ref().clone(); let mut new_vec = current.as_ref().clone();
let popped_item = new_vec.pop(); let popped_item = new_vec.pop();
self.inner.store(Arc::new(new_vec));
let new_arc = Arc::new(new_vec); popped_item
if self.inner.compare_and_swap(&current, new_arc).is_ok() {
return popped_item;
}
// Retry si le CAS a échoué
}
} }
/// Supprime tous les éléments qui correspondent au prédicat donné. /// Supprime tous les éléments qui correspondent au prédicat donné.
/// Retourne le nombre d'éléments supprimés. /// Retourne le nombre d'éléments supprimés.
///
/// Exemple : `let removed = clients.delete_matching(|c| c.is_disconnected());`
pub fn delete_matching<F>(&self, predicate: F) -> usize pub fn delete_matching<F>(&self, predicate: F) -> usize
where where
T: Clone, T: Clone,
F: Fn(&T) -> bool, F: Fn(&T) -> bool,
{ {
loop { let current = self.inner.load_full();
let current = self.inner.load();
let mut new_vec = Vec::with_capacity(current.len()); let mut new_vec = Vec::with_capacity(current.len());
let mut removed_count = 0; let mut removed_count = 0;
@@ -314,19 +303,11 @@ impl<T> SharedVec<T> {
} }
} }
if removed_count == 0 { if removed_count > 0 {
return 0; self.inner.store(Arc::new(new_vec));
} }
removed_count
let new_arc = Arc::new(new_vec);
if self.inner.compare_and_swap(&current, new_arc).is_ok() {
return removed_count;
} }
// Retry si le CAS a échoué
}
}
/// Clear optimisé /// Clear optimisé
/// ///
@@ -939,45 +920,29 @@ impl<T> SharedArcVec<T> {
/// Supprime l'élément à l'index donné et le retourne. /// Supprime l'élément à l'index donné et le retourne.
/// Retourne `None` si l'index est hors limites. /// Retourne `None` si l'index est hors limites.
pub fn delete(&self, index: usize) -> Option<Arc<T>> { pub fn delete(&self, index: usize) -> Option<Arc<T>> {
loop { let current = self.inner.load_full();
let current = self.inner.load();
if index >= current.len() { if index >= current.len() {
return None; return None;
} }
let mut new_vec = current.as_ref().clone(); let mut new_vec = current.as_ref().clone();
let removed_item = new_vec.remove(index); let removed_item = new_vec.remove(index);
self.inner.store(Arc::new(new_vec));
let new_arc = Arc::new(new_vec); Some(removed_item)
if self.inner.compare_and_swap(&current, new_arc).is_ok() {
return Some(removed_item);
}
// Retry si le CAS a échoué
}
} }
/// Supprime et retourne le dernier élément du vecteur. /// Supprime et retourne le dernier élément du vecteur.
/// Retourne `None` si le vecteur est vide. /// Retourne `None` si le vecteur est vide.
pub fn pop(&self) -> Option<Arc<T>> { pub fn pop(&self) -> Option<Arc<T>> {
loop { let current = self.inner.load_full();
let current = self.inner.load();
if current.is_empty() { if current.is_empty() {
return None; return None;
} }
let mut new_vec = current.as_ref().clone(); let mut new_vec = current.as_ref().clone();
let popped_item = new_vec.pop(); let popped_item = new_vec.pop();
self.inner.store(Arc::new(new_vec));
let new_arc = Arc::new(new_vec); popped_item
if self.inner.compare_and_swap(&current, new_arc).is_ok() {
return popped_item;
}
// Retry si le CAS a échoué
}
} }
/// Supprime tous les éléments qui correspondent au prédicat donné. /// Supprime tous les éléments qui correspondent au prédicat donné.
@@ -986,8 +951,7 @@ impl<T> SharedArcVec<T> {
where where
F: Fn(&T) -> bool, F: Fn(&T) -> bool,
{ {
loop { let current = self.inner.load_full();
let current = self.inner.load();
let mut new_vec = Vec::with_capacity(current.len()); let mut new_vec = Vec::with_capacity(current.len());
let mut removed_count = 0; let mut removed_count = 0;
@@ -999,17 +963,10 @@ impl<T> SharedArcVec<T> {
} }
} }
if removed_count == 0 { if removed_count > 0 {
return 0; self.inner.store(Arc::new(new_vec));
}
let new_arc = Arc::new(new_vec);
if self.inner.compare_and_swap(&current, new_arc).is_ok() {
return removed_count;
}
// Retry si le CAS a échoué
} }
removed_count
} }
/// Supprime le premier élément trouvé qui correspond au prédicat. /// Supprime le premier élément trouvé qui correspond au prédicat.
@@ -1018,8 +975,7 @@ impl<T> SharedArcVec<T> {
where where
F: Fn(&T) -> bool, F: Fn(&T) -> bool,
{ {
loop { let current = self.inner.load_full();
let current = self.inner.load();
let mut found_index = None; let mut found_index = None;
for (index, item) in current.iter().enumerate() { for (index, item) in current.iter().enumerate() {
@@ -1032,16 +988,10 @@ impl<T> SharedArcVec<T> {
if let Some(index) = found_index { if let Some(index) = found_index {
let mut new_vec = current.as_ref().clone(); let mut new_vec = current.as_ref().clone();
let removed_item = new_vec.remove(index); let removed_item = new_vec.remove(index);
self.inner.store(Arc::new(new_vec));
let new_arc = Arc::new(new_vec); Some(removed_item)
if self.inner.compare_and_swap(&current, new_arc).is_ok() {
return Some(removed_item);
}
// Retry si le CAS a échoué
} else { } else {
return None; None
}
} }
} }