diff --git a/backend/Cargo.lock b/backend/Cargo.lock index 2f009e1d..cebf93bf 100644 --- a/backend/Cargo.lock +++ b/backend/Cargo.lock @@ -51,7 +51,7 @@ dependencies = [ "bytes", "cfg-if", "http", - "indexmap", + "indexmap 2.7.0", "schemars", "serde", "serde_json", @@ -71,7 +71,7 @@ dependencies = [ "aide", "axum", "axum_typed_multipart", - "indexmap", + "indexmap 2.7.0", "schemars", ] @@ -662,6 +662,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" dependencies = [ "powerfmt", + "serde", ] [[package]] @@ -966,6 +967,12 @@ version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + [[package]] name = "hashbrown" version = "0.15.2" @@ -983,7 +990,7 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" dependencies = [ - "hashbrown", + "hashbrown 0.15.2", ] [[package]] @@ -1298,6 +1305,17 @@ dependencies = [ "icu_properties", ] +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + [[package]] name = "indexmap" version = "2.7.0" @@ -1305,7 +1323,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.15.2", "serde", ] @@ -2068,7 +2086,7 @@ dependencies = [ "bytes", "chrono", "dyn-clone", - "indexmap", + "indexmap 2.7.0", "schemars_derive", "serde", "serde_json", @@ -2177,13 +2195,43 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_with" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6b6f7f2fcb69f747921f79f3926bd1e203fce4fef62c268dd3abfb6d86029aa" +dependencies = [ + "base64 0.22.1", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.7.0", + "serde", + "serde_derive", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d00caa5193a3c8362ac2b73be6b9e768aa5a4b2f721d8f4b339600c3cb51f8e" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.90", +] + [[package]] name = "serde_yaml" version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap", + "indexmap 2.7.0", "itoa", "ryu", "serde", @@ -2329,9 +2377,9 @@ dependencies = [ "futures-intrusive", "futures-io", "futures-util", - "hashbrown", + "hashbrown 0.15.2", "hashlink", - "indexmap", + "indexmap 2.7.0", "log", "memchr", "once_cell", @@ -2581,6 +2629,7 @@ dependencies = [ "schemars", "serde", "serde_json", + "serde_with", "serde_yaml", "sqlx", "sync_lib", @@ -2722,6 +2771,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" dependencies = [ "deranged", + "itoa", "num-conv", "powerfmt", "serde", diff --git a/backend/config-e2e.yml b/backend/config-e2e.yml index 17b745ea..5018c716 100644 --- a/backend/config-e2e.yml +++ b/backend/config-e2e.yml @@ -1,29 +1,27 @@ database: databases_directory_path: databases max_connections_per_vault: 12 - + cursor_timeout_seconds: 60 + cursor_broadcast_interval_seconds: 1 server: host: 0.0.0.0 port: 3000 max_body_size_mb: 512 max_clients_per_vault: 256 response_timeout_seconds: 60 - users: user_configs: - - name: admin - token: test-token-change-me - vault_access: - type: allow_access_to_all - - - name: other-admin - token: test-token-change-me2 - vault_access: - type: allow_access_to_all - - - name: test - token: other-test-token - vault_access: - type: allow_list - allowed: - - default + - name: admin + token: test-token-change-me + vault_access: + type: allow_access_to_all + - name: other-admin + token: test-token-change-me2 + vault_access: + type: allow_access_to_all + - name: test + token: other-test-token + vault_access: + type: allow_list + allowed: + - default diff --git a/backend/sync_server/Cargo.toml b/backend/sync_server/Cargo.toml index e593dc3b..4aa1a373 100644 --- a/backend/sync_server/Cargo.toml +++ b/backend/sync_server/Cargo.toml @@ -38,6 +38,7 @@ serde_json = "1.0.140" clap-verbosity-flag = "3.0.3" bimap = "0.6.3" ts-rs = { version = "10.1", features = ["uuid-impl", "chrono-impl"] } +serde_with = "3.12.0" [lints] workspace = true diff --git a/backend/sync_server/src/app_state/cursors.rs b/backend/sync_server/src/app_state/cursors.rs index 851566a7..d5aa01e4 100644 --- a/backend/sync_server/src/app_state/cursors.rs +++ b/backend/sync_server/src/app_state/cursors.rs @@ -1,8 +1,6 @@ use core::time::Duration; use std::{collections::HashMap, sync::Arc}; -use chrono::TimeDelta; -use sqlx::types::chrono::Utc; use tokio::sync::Mutex; use super::{ @@ -10,15 +8,13 @@ use super::{ websocket::{ broadcasts::Broadcasts, models::{ - ClientCursors, CursorPositionFromServer, WebSocketServerMessage, + ClientCursors, CursorPositionFromServer, CursorSpan, WebSocketServerMessage, WebSocketServerMessageWithOrigin, }, }, }; use crate::config::database_config::DatabaseConfig; -const BACKGROUND_TASK_INTERVAL: Duration = Duration::from_secs(1); - #[derive(Clone, Debug)] pub struct Cursors { config: DatabaseConfig, @@ -39,7 +35,7 @@ impl Cursors { &self, vault_id: VaultId, device_id: &DeviceId, - document_to_cursors: HashMap>, + document_to_cursors: HashMap>, ) { let mut vault_to_cursors = self.vault_to_cursors.lock().await; @@ -76,7 +72,7 @@ impl Cursors { loop { self.remove_expired_cursors().await; self.broadcast_cursors().await; - tokio::time::sleep(BACKGROUND_TASK_INTERVAL).await; + tokio::time::sleep(self.config.cursor_broadcast_interval).await; } } @@ -109,16 +105,16 @@ impl Cursors { #[derive(Clone, Debug)] struct ClientCursorsWithTimeToLive { client_cursors: ClientCursors, - last_updated: chrono::DateTime, + last_updated: std::time::Instant, } impl ClientCursorsWithTimeToLive { fn new(client_cursors: ClientCursors) -> Self { Self { client_cursors, - last_updated: Utc::now(), + last_updated: std::time::Instant::now(), } } - pub fn is_expired(&self, ttl: TimeDelta) -> bool { Utc::now() - self.last_updated > ttl } + pub fn is_expired(&self, ttl: Duration) -> bool { self.last_updated.elapsed() > ttl } } diff --git a/backend/sync_server/src/config/database_config.rs b/backend/sync_server/src/config/database_config.rs index 6f91e19c..118d805e 100644 --- a/backend/sync_server/src/config/database_config.rs +++ b/backend/sync_server/src/config/database_config.rs @@ -1,13 +1,15 @@ -use std::path::PathBuf; +use std::{path::PathBuf, time::Duration}; -use chrono::TimeDelta; use log::debug; use serde::{Deserialize, Serialize}; +use serde_with::serde_as; use crate::consts::{ - DEFAULT_CURSOR_TIMEOUT, DEFAULT_DATABASES_DIRECTORY_PATH, DEFAULT_MAX_CONNECTIONS_PER_VAULT, + DEFAULT_CURSOR_BROADCAST_INTERVAL, DEFAULT_CURSOR_TIMEOUT, DEFAULT_DATABASES_DIRECTORY_PATH, + DEFAULT_MAX_CONNECTIONS_PER_VAULT, }; +#[serde_with::serde_as] #[derive(Debug, Deserialize, Serialize, Clone)] pub struct DatabaseConfig { #[serde(default = "default_databases_directory_path")] @@ -16,8 +18,16 @@ pub struct DatabaseConfig { #[serde(default = "default_max_connections_per_vault")] pub max_connections_per_vault: u32, - #[serde(default = "default_cursor_timeout")] - pub cursor_timeout: TimeDelta, + #[serde(default = "default_cursor_timeout", rename = "cursor_timeout_seconds")] + #[serde_as(as = "serde_with::DurationSeconds")] + pub cursor_timeout: Duration, + + #[serde( + default = "default_cursor_broadcast_interval", + rename = "cursor_broadcast_interval_seconds" + )] + #[serde_as(as = "serde_with::DurationSeconds")] + pub cursor_broadcast_interval: Duration, } fn default_databases_directory_path() -> PathBuf { @@ -30,17 +40,23 @@ fn default_max_connections_per_vault() -> u32 { DEFAULT_MAX_CONNECTIONS_PER_VAULT } -fn default_cursor_timeout() -> TimeDelta { - debug!("Using default cursor timeout: {DEFAULT_CURSOR_TIMEOUT}"); +fn default_cursor_timeout() -> Duration { + debug!("Using default cursor timeout: {DEFAULT_CURSOR_TIMEOUT:?}"); DEFAULT_CURSOR_TIMEOUT } +fn default_cursor_broadcast_interval() -> Duration { + debug!("Using default cursor broadcast interval: {DEFAULT_CURSOR_BROADCAST_INTERVAL:?}"); + DEFAULT_CURSOR_BROADCAST_INTERVAL +} + impl Default for DatabaseConfig { fn default() -> Self { Self { databases_directory_path: default_databases_directory_path(), max_connections_per_vault: default_max_connections_per_vault(), cursor_timeout: default_cursor_timeout(), + cursor_broadcast_interval: default_cursor_broadcast_interval(), } } } diff --git a/backend/sync_server/src/consts.rs b/backend/sync_server/src/consts.rs index 03d5f4c2..01927335 100644 --- a/backend/sync_server/src/consts.rs +++ b/backend/sync_server/src/consts.rs @@ -1,10 +1,11 @@ -use chrono::TimeDelta; +use std::time::Duration; pub const DEFAULT_CONFIG_PATH: &str = "config.yml"; pub const DEFAULT_DATABASES_DIRECTORY_PATH: &str = "databases"; pub const DEFAULT_MAX_CONNECTIONS_PER_VAULT: u32 = 12; -pub const DEFAULT_CURSOR_TIMEOUT: TimeDelta = TimeDelta::seconds(60); +pub const DEFAULT_CURSOR_TIMEOUT: Duration = Duration::from_secs(60); +pub const DEFAULT_CURSOR_BROADCAST_INTERVAL: Duration = Duration::from_secs(1); pub const DEFAULT_HOST: &str = "127.0.0.1"; pub const DEFAULT_PORT: u16 = 3000;