max / goingson
10 files changed,
+96 insertions,
-62 deletions
| @@ -1887,7 +1887,7 @@ dependencies = [ | |||
| 1887 | 1887 | "strum", | |
| 1888 | 1888 | "strum_macros", | |
| 1889 | 1889 | "tagtree", | |
| 1890 | - | "thiserror 1.0.69", | |
| 1890 | + | "thiserror 2.0.18", | |
| 1891 | 1891 | "uuid", | |
| 1892 | 1892 | ] | |
| 1893 | 1893 | ||
| @@ -1916,10 +1916,9 @@ dependencies = [ | |||
| 1916 | 1916 | "chrono", | |
| 1917 | 1917 | "chrono-tz", | |
| 1918 | 1918 | "csv", | |
| 1919 | - | "dirs", | |
| 1920 | 1919 | "docengine", | |
| 1921 | 1920 | "flate2", | |
| 1922 | - | "futures", | |
| 1921 | + | "futures-util", | |
| 1923 | 1922 | "goingson-core", | |
| 1924 | 1923 | "goingson-db-sqlite", | |
| 1925 | 1924 | "goingson-plugin-runtime", | |
| @@ -1933,7 +1932,6 @@ dependencies = [ | |||
| 1933 | 1932 | "notify-debouncer-mini", | |
| 1934 | 1933 | "open", | |
| 1935 | 1934 | "openssl", | |
| 1936 | - | "parking_lot", | |
| 1937 | 1935 | "pter", | |
| 1938 | 1936 | "rand 0.9.2", | |
| 1939 | 1937 | "reqwest 0.12.28", | |
| @@ -1951,7 +1949,7 @@ dependencies = [ | |||
| 1951 | 1949 | "tauri-plugin-window-state", | |
| 1952 | 1950 | "tempfile", | |
| 1953 | 1951 | "theme-common", | |
| 1954 | - | "thiserror 1.0.69", | |
| 1952 | + | "thiserror 2.0.18", | |
| 1955 | 1953 | "tokio", | |
| 1956 | 1954 | "tokio-native-tls", | |
| 1957 | 1955 | "tokio-util", | |
| @@ -1974,7 +1972,7 @@ dependencies = [ | |||
| 1974 | 1972 | "serde", | |
| 1975 | 1973 | "serde_json", | |
| 1976 | 1974 | "tempfile", | |
| 1977 | - | "thiserror 1.0.69", | |
| 1975 | + | "thiserror 2.0.18", | |
| 1978 | 1976 | "tokio", | |
| 1979 | 1977 | "toml 0.8.2", | |
| 1980 | 1978 | "tracing", | |
| @@ -2723,7 +2721,12 @@ version = "3.6.3" | |||
| 2723 | 2721 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 2724 | 2722 | checksum = "eebcc3aff044e5944a8fbaf69eb277d11986064cba30c468730e8b9909fb551c" | |
| 2725 | 2723 | dependencies = [ | |
| 2724 | + | "byteorder", | |
| 2725 | + | "linux-keyutils", | |
| 2726 | 2726 | "log", | |
| 2727 | + | "security-framework 2.11.1", | |
| 2728 | + | "security-framework 3.7.0", | |
| 2729 | + | "windows-sys 0.60.2", | |
| 2727 | 2730 | "zeroize", | |
| 2728 | 2731 | ] | |
| 2729 | 2732 | ||
| @@ -2870,6 +2873,16 @@ dependencies = [ | |||
| 2870 | 2873 | ] | |
| 2871 | 2874 | ||
| 2872 | 2875 | [[package]] | |
| 2876 | + | name = "linux-keyutils" | |
| 2877 | + | version = "0.2.5" | |
| 2878 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 2879 | + | checksum = "83270a18e9f90d0707c41e9f35efada77b64c0e6f3f1810e71c8368a864d5590" | |
| 2880 | + | dependencies = [ | |
| 2881 | + | "bitflags 2.11.0", | |
| 2882 | + | "libc", | |
| 2883 | + | ] | |
| 2884 | + | ||
| 2885 | + | [[package]] | |
| 2873 | 2886 | name = "linux-raw-sys" | |
| 2874 | 2887 | version = "0.12.1" | |
| 2875 | 2888 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| @@ -3134,7 +3147,7 @@ dependencies = [ | |||
| 3134 | 3147 | "openssl-probe", | |
| 3135 | 3148 | "openssl-sys", | |
| 3136 | 3149 | "schannel", | |
| 3137 | - | "security-framework", | |
| 3150 | + | "security-framework 3.7.0", | |
| 3138 | 3151 | "security-framework-sys", | |
| 3139 | 3152 | "tempfile", | |
| 3140 | 3153 | ] | |
| @@ -4636,7 +4649,7 @@ dependencies = [ | |||
| 4636 | 4649 | "openssl-probe", | |
| 4637 | 4650 | "rustls-pki-types", | |
| 4638 | 4651 | "schannel", | |
| 4639 | - | "security-framework", | |
| 4652 | + | "security-framework 3.7.0", | |
| 4640 | 4653 | ] | |
| 4641 | 4654 | ||
| 4642 | 4655 | [[package]] | |
| @@ -4663,7 +4676,7 @@ dependencies = [ | |||
| 4663 | 4676 | "rustls-native-certs", | |
| 4664 | 4677 | "rustls-platform-verifier-android", | |
| 4665 | 4678 | "rustls-webpki", | |
| 4666 | - | "security-framework", | |
| 4679 | + | "security-framework 3.7.0", | |
| 4667 | 4680 | "security-framework-sys", | |
| 4668 | 4681 | "webpki-root-certs", | |
| 4669 | 4682 | "windows-sys 0.61.2", | |
| @@ -4677,9 +4690,9 @@ checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" | |||
| 4677 | 4690 | ||
| 4678 | 4691 | [[package]] | |
| 4679 | 4692 | name = "rustls-webpki" | |
| 4680 | - | version = "0.103.10" | |
| 4693 | + | version = "0.103.13" | |
| 4681 | 4694 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 4682 | - | checksum = "df33b2b81ac578cabaf06b89b0631153a3f416b0a886e8a7a1707fb51abbd1ef" | |
| 4695 | + | checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" | |
| 4683 | 4696 | dependencies = [ | |
| 4684 | 4697 | "ring", | |
| 4685 | 4698 | "rustls-pki-types", | |
| @@ -4790,6 +4803,19 @@ dependencies = [ | |||
| 4790 | 4803 | ||
| 4791 | 4804 | [[package]] | |
| 4792 | 4805 | name = "security-framework" | |
| 4806 | + | version = "2.11.1" | |
| 4807 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 4808 | + | checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" | |
| 4809 | + | dependencies = [ | |
| 4810 | + | "bitflags 2.11.0", | |
| 4811 | + | "core-foundation 0.9.4", | |
| 4812 | + | "core-foundation-sys", | |
| 4813 | + | "libc", | |
| 4814 | + | "security-framework-sys", | |
| 4815 | + | ] | |
| 4816 | + | ||
| 4817 | + | [[package]] | |
| 4818 | + | name = "security-framework" | |
| 4793 | 4819 | version = "3.7.0" | |
| 4794 | 4820 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 4795 | 4821 | checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" |
| @@ -21,7 +21,7 @@ uuid = { version = "1.16", features = ["v4", "v5", "serde"] } | |||
| 21 | 21 | serde = { version = "1.0.228", features = ["derive"] } | |
| 22 | 22 | serde_json = "1.0.149" | |
| 23 | 23 | async-trait = "0.1" | |
| 24 | - | thiserror = "1" | |
| 24 | + | thiserror = "2" | |
| 25 | 25 | ||
| 26 | 26 | # Async runtime | |
| 27 | 27 | tokio = { version = "1.49", features = ["rt-multi-thread", "macros"] } | |
| @@ -36,7 +36,7 @@ argon2 = "0.5" | |||
| 36 | 36 | async-imap = { version = "0.11", default-features = false, features = ["runtime-tokio"] } | |
| 37 | 37 | tokio-native-tls = "0.3" | |
| 38 | 38 | tokio-util = "0.7" | |
| 39 | - | futures = "0.3" | |
| 39 | + | futures-util = { version = "0.3", default-features = false, features = ["std", "async-await"] } | |
| 40 | 40 | mailparse = "0.16" | |
| 41 | 41 | lettre = { version = "0.11", default-features = false, features = ["tokio1", "tokio1-native-tls", "smtp-transport", "builder"] } | |
| 42 | 42 | ||
| @@ -49,10 +49,7 @@ base64 = "0.22" | |||
| 49 | 49 | sha2 = "0.10" | |
| 50 | 50 | ||
| 51 | 51 | # Secure credential storage | |
| 52 | - | keyring = "3" | |
| 53 | - | ||
| 54 | - | # Config | |
| 55 | - | dotenvy = "0.15" | |
| 52 | + | keyring = { version = "3", features = ["apple-native", "linux-native", "windows-native"] } | |
| 56 | 53 | ||
| 57 | 54 | # Tauri | |
| 58 | 55 | tauri = "2.10.2" | |
| @@ -90,9 +87,6 @@ open = "5" | |||
| 90 | 87 | # Markdown rendering + HTML sanitization | |
| 91 | 88 | docengine = { path = "../../MNW/shared/docengine" } | |
| 92 | 89 | ||
| 93 | - | # Filesystem | |
| 94 | - | dirs = "6" | |
| 95 | - | ||
| 96 | 90 | # Logging | |
| 97 | 91 | tracing = "0.1" | |
| 98 | 92 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } |
| @@ -44,7 +44,7 @@ uuid = { workspace = true } | |||
| 44 | 44 | async-imap = { workspace = true } | |
| 45 | 45 | tokio-native-tls = { workspace = true } | |
| 46 | 46 | tokio-util = { workspace = true } | |
| 47 | - | futures = { workspace = true } | |
| 47 | + | futures-util = { workspace = true } | |
| 48 | 48 | mailparse = { workspace = true } | |
| 49 | 49 | lettre = { workspace = true } | |
| 50 | 50 | ||
| @@ -72,8 +72,6 @@ flate2 = { workspace = true } | |||
| 72 | 72 | # Theme loading | |
| 73 | 73 | theme-common = { workspace = true } | |
| 74 | 74 | toml = { workspace = true } | |
| 75 | - | dirs = { workspace = true } | |
| 76 | - | ||
| 77 | 75 | # Browser opening | |
| 78 | 76 | open = { workspace = true } | |
| 79 | 77 | ||
| @@ -85,7 +83,6 @@ keyring = { workspace = true } | |||
| 85 | 83 | ||
| 86 | 84 | # HTML email to readable markdown | |
| 87 | 85 | pter = { path = "../../../pter" } | |
| 88 | - | parking_lot = "0.12.5" | |
| 89 | 86 | ||
| 90 | 87 | # === Desktop-only dependencies (not available on iOS/Android) === | |
| 91 | 88 |
| @@ -46,7 +46,7 @@ pub async fn sync_email_account_inner( | |||
| 46 | 46 | full_sync: Option<bool>, | |
| 47 | 47 | ) -> Result<SyncResponse, ApiError> { | |
| 48 | 48 | // Acquire per-account lock to prevent concurrent syncs on the same account | |
| 49 | - | if !state.email_sync_locks.lock().insert(id) { | |
| 49 | + | if !state.email_sync_locks.lock().expect("email_sync_locks poisoned").insert(id) { | |
| 50 | 50 | return Err(ApiError::bad_request("Sync already in progress for this account")); | |
| 51 | 51 | } | |
| 52 | 52 | ||
| @@ -67,8 +67,12 @@ pub async fn sync_email_account_inner( | |||
| 67 | 67 | Ok(result) | |
| 68 | 68 | }.await; | |
| 69 | 69 | ||
| 70 | - | // Always release the lock (parking_lot::Mutex — safe even if async block panicked) | |
| 71 | - | state.email_sync_locks.lock().remove(&id); | |
| 70 | + | // Always release the lock. The lock is not held across the await, so it cannot be poisoned | |
| 71 | + | // by an async-block panic; unwrap_or_else handles the unreachable poison case defensively. | |
| 72 | + | state.email_sync_locks | |
| 73 | + | .lock() | |
| 74 | + | .unwrap_or_else(|p| p.into_inner()) | |
| 75 | + | .remove(&id); | |
| 72 | 76 | ||
| 73 | 77 | result | |
| 74 | 78 | } |
| @@ -128,7 +128,7 @@ pub async fn start_oauth( | |||
| 128 | 128 | ||
| 129 | 129 | // Store PKCE verifier and flow details server-side (never sent to frontend) | |
| 130 | 130 | { | |
| 131 | - | let mut flows = state.pending_oauth_flows.lock(); | |
| 131 | + | let mut flows = state.pending_oauth_flows.lock().expect("pending_oauth_flows poisoned"); | |
| 132 | 132 | flows.insert(start_result.state.clone(), crate::state::PendingOAuthFlow { | |
| 133 | 133 | code_verifier: start_result.code_verifier, | |
| 134 | 134 | provider_id: provider_id.clone(), | |
| @@ -163,7 +163,7 @@ pub async fn complete_oauth( | |||
| 163 | 163 | ) -> Result<OAuthCompleteResponse, ApiError> { | |
| 164 | 164 | // Look up and consume the pending flow by state token (CSRF validation) | |
| 165 | 165 | let flow = { | |
| 166 | - | let mut flows = state.pending_oauth_flows.lock(); | |
| 166 | + | let mut flows = state.pending_oauth_flows.lock().expect("pending_oauth_flows poisoned"); | |
| 167 | 167 | flows.remove(&input.state) | |
| 168 | 168 | }.ok_or_else(|| ApiError::bad_request("Invalid or expired OAuth state token"))?; | |
| 169 | 169 |
| @@ -72,7 +72,7 @@ pub struct SyncSettingsInput { | |||
| 72 | 72 | ||
| 73 | 73 | /// Extract the sync client from state. Clones the Arc for use across await points. | |
| 74 | 74 | fn get_sync_client(state: &AppState) -> Option<std::sync::Arc<synckit_client::SyncKitClient>> { | |
| 75 | - | state.sync_client.read().clone() | |
| 75 | + | state.sync_client.read().expect("sync_client poisoned").clone() | |
| 76 | 76 | } | |
| 77 | 77 | ||
| 78 | 78 | fn require_sync_client(state: &AppState) -> Result<std::sync::Arc<synckit_client::SyncKitClient>, ApiError> { | |
| @@ -171,7 +171,7 @@ pub async fn sync_start_auth( | |||
| 171 | 171 | ||
| 172 | 172 | // Store PKCE verifier server-side (never sent to frontend) | |
| 173 | 173 | { | |
| 174 | - | let mut flows = state.pending_oauth_flows.lock(); | |
| 174 | + | let mut flows = state.pending_oauth_flows.lock().expect("pending_oauth_flows poisoned"); | |
| 175 | 175 | flows.insert(csrf_state.clone(), crate::state::PendingOAuthFlow { | |
| 176 | 176 | code_verifier, | |
| 177 | 177 | provider_id: "synckit".to_string(), | |
| @@ -197,7 +197,7 @@ pub async fn sync_complete_auth( | |||
| 197 | 197 | ||
| 198 | 198 | // Look up and consume the pending flow by state token (CSRF + PKCE validation) | |
| 199 | 199 | let flow = { | |
| 200 | - | let mut flows = state.pending_oauth_flows.lock(); | |
| 200 | + | let mut flows = state.pending_oauth_flows.lock().expect("pending_oauth_flows poisoned"); | |
| 201 | 201 | flows.remove(&input.state) | |
| 202 | 202 | }.ok_or_else(|| ApiError::bad_request("Invalid or expired OAuth state token"))?; | |
| 203 | 203 | ||
| @@ -381,10 +381,16 @@ pub async fn sync_subscribe( | |||
| 381 | 381 | return Err(ApiError::bad_request("Not authenticated")); | |
| 382 | 382 | } | |
| 383 | 383 | ||
| 384 | - | let response = client | |
| 384 | + | let response = match client | |
| 385 | 385 | .create_subscription_checkout("standard", &interval) | |
| 386 | 386 | .await | |
| 387 | - | .map_api_err("Failed to create checkout", ApiError::external_service)?; | |
| 387 | + | { | |
| 388 | + | Ok(r) => r, | |
| 389 | + | Err(e) => { | |
| 390 | + | tracing::error!(error = %e, debug = ?e, "Subscription checkout failed"); | |
| 391 | + | return Err(ApiError::external_service(format!("Failed to create checkout: {e}"))); | |
| 392 | + | } | |
| 393 | + | }; | |
| 388 | 394 | ||
| 389 | 395 | // Open in default browser | |
| 390 | 396 | if let Err(e) = open::that(&response.checkout_url) { |
| @@ -3,7 +3,7 @@ | |||
| 3 | 3 | use async_imap::Client; | |
| 4 | 4 | use base64::{engine::general_purpose::STANDARD as BASE64, Engine}; | |
| 5 | 5 | use chrono::{DateTime, TimeZone, Utc}; | |
| 6 | - | use futures::StreamExt; | |
| 6 | + | use futures_util::StreamExt; | |
| 7 | 7 | use goingson_core::{EmailAccount, FolderSyncState}; | |
| 8 | 8 | use tokio::net::TcpStream; | |
| 9 | 9 | use tokio_native_tls::native_tls::TlsConnector as NativeTlsConnector; | |
| @@ -651,7 +651,7 @@ impl ImapClient { | |||
| 651 | 651 | .await | |
| 652 | 652 | .map_err(|e| format!("List folders error: {}", e))?; | |
| 653 | 653 | ||
| 654 | - | use futures::StreamExt; | |
| 654 | + | use futures_util::StreamExt; | |
| 655 | 655 | let folders: Vec<String> = folders_stream | |
| 656 | 656 | .filter_map(|result| async { | |
| 657 | 657 | result.ok().map(|name| name.name().to_string()) |
| @@ -17,8 +17,7 @@ use goingson_db_sqlite::{ | |||
| 17 | 17 | }; | |
| 18 | 18 | use sqlx::SqlitePool; | |
| 19 | 19 | use std::path::PathBuf; | |
| 20 | - | use std::sync::Arc; | |
| 21 | - | use parking_lot::RwLock; | |
| 20 | + | use std::sync::{Arc, Mutex, RwLock}; | |
| 22 | 21 | use tokio::sync::Mutex as TokioMutex; | |
| 23 | 22 | use synckit_client::{SyncKitClient, SyncKitConfig}; | |
| 24 | 23 | use tauri::{AppHandle, Manager}; | |
| @@ -53,11 +52,11 @@ pub struct AppState { | |||
| 53 | 52 | pub sync_client: RwLock<Option<Arc<SyncKitClient>>>, | |
| 54 | 53 | pub sync_lock: Arc<TokioMutex<()>>, | |
| 55 | 54 | /// Per-account email sync locks to prevent concurrent syncs on the same account. | |
| 56 | - | pub email_sync_locks: Arc<parking_lot::Mutex<std::collections::HashSet<goingson_core::EmailAccountId>>>, | |
| 55 | + | pub email_sync_locks: Arc<Mutex<std::collections::HashSet<goingson_core::EmailAccountId>>>, | |
| 57 | 56 | /// Per-account token refresh locks to prevent concurrent refreshes. | |
| 58 | - | pub token_refresh_locks: Arc<parking_lot::Mutex<std::collections::HashMap<uuid::Uuid, Arc<TokioMutex<()>>>>>, | |
| 57 | + | pub token_refresh_locks: Arc<Mutex<std::collections::HashMap<uuid::Uuid, Arc<TokioMutex<()>>>>>, | |
| 59 | 58 | /// Pending OAuth flows keyed by state token (CSRF + PKCE verifier stored server-side). | |
| 60 | - | pub pending_oauth_flows: Arc<parking_lot::Mutex<std::collections::HashMap<String, PendingOAuthFlow>>>, | |
| 59 | + | pub pending_oauth_flows: Arc<Mutex<std::collections::HashMap<String, PendingOAuthFlow>>>, | |
| 61 | 60 | pub data_dir: PathBuf, | |
| 62 | 61 | } | |
| 63 | 62 | ||
| @@ -162,16 +161,16 @@ impl AppState { | |||
| 162 | 161 | sync_accounts, | |
| 163 | 162 | sync_client: RwLock::new(sync_client.map(Arc::new)), | |
| 164 | 163 | sync_lock: Arc::new(TokioMutex::new(())), | |
| 165 | - | email_sync_locks: Arc::new(parking_lot::Mutex::new(std::collections::HashSet::new())), | |
| 166 | - | token_refresh_locks: Arc::new(parking_lot::Mutex::new(std::collections::HashMap::new())), | |
| 167 | - | pending_oauth_flows: Arc::new(parking_lot::Mutex::new(std::collections::HashMap::new())), | |
| 164 | + | email_sync_locks: Arc::new(Mutex::new(std::collections::HashSet::new())), | |
| 165 | + | token_refresh_locks: Arc::new(Mutex::new(std::collections::HashMap::new())), | |
| 166 | + | pending_oauth_flows: Arc::new(Mutex::new(std::collections::HashMap::new())), | |
| 168 | 167 | data_dir: app_data_dir, | |
| 169 | 168 | }) | |
| 170 | 169 | } | |
| 171 | 170 | ||
| 172 | 171 | /// Gets or creates a per-account token refresh lock. | |
| 173 | 172 | pub fn token_refresh_lock(&self, account_id: uuid::Uuid) -> Arc<TokioMutex<()>> { | |
| 174 | - | let mut locks = self.token_refresh_locks.lock(); | |
| 173 | + | let mut locks = self.token_refresh_locks.lock().expect("token_refresh_locks poisoned"); | |
| 175 | 174 | locks.entry(account_id) | |
| 176 | 175 | .or_insert_with(|| Arc::new(TokioMutex::new(()))) | |
| 177 | 176 | .clone() | |
| @@ -240,18 +239,25 @@ fn load_sync_client(data_dir: &std::path::Path) -> Option<SyncKitClient> { | |||
| 240 | 239 | let client = SyncKitClient::new(SyncKitConfig { server_url, api_key }); | |
| 241 | 240 | ||
| 242 | 241 | // Try to restore session from keychain | |
| 243 | - | if let Some(creds) = crate::oauth::CredentialStore::get_sync_token() { | |
| 244 | - | client.restore_session(&creds.token, creds.user_id, creds.app_id); | |
| 245 | - | if client.is_token_expired() { | |
| 246 | - | warn!("Stored sync token is expired, clearing session"); | |
| 247 | - | client.clear_session(); | |
| 248 | - | } else { | |
| 249 | - | match client.try_load_key_from_keychain() { | |
| 250 | - | Ok(true) => info!("Sync encryption key loaded from keychain"), | |
| 251 | - | Ok(false) => debug!("No sync encryption key in keychain"), | |
| 252 | - | Err(e) => warn!("Failed to load sync encryption key: {}", e), | |
| 242 | + | match crate::oauth::CredentialStore::get_sync_token() { | |
| 243 | + | Some(creds) => { | |
| 244 | + | info!("Restoring sync session from keychain (user={})", creds.user_id); | |
| 245 | + | client.restore_session(&creds.token, creds.user_id, creds.app_id); | |
| 246 | + | if client.is_token_expired() { | |
| 247 | + | warn!("Stored sync token is expired, clearing session"); | |
| 248 | + | client.clear_session(); | |
| 249 | + | } else { | |
| 250 | + | info!("Sync session restored successfully"); | |
| 251 | + | match client.try_load_key_from_keychain() { | |
| 252 | + | Ok(true) => info!("Sync encryption key loaded from keychain"), | |
| 253 | + | Ok(false) => debug!("No sync encryption key in keychain"), | |
| 254 | + | Err(e) => warn!("Failed to load sync encryption key: {}", e), | |
| 255 | + | } | |
| 253 | 256 | } | |
| 254 | 257 | } | |
| 258 | + | None => { | |
| 259 | + | info!("No sync token found in keychain — user will need to authenticate"); | |
| 260 | + | } | |
| 255 | 261 | } | |
| 256 | 262 | ||
| 257 | 263 | Some(client) |
| @@ -72,7 +72,7 @@ pub async fn start_sync_scheduler(app: tauri::AppHandle, cancel: CancellationTok | |||
| 72 | 72 | } | |
| 73 | 73 | }; | |
| 74 | 74 | ||
| 75 | - | let client: Arc<synckit_client::SyncKitClient> = match state.sync_client.read().clone() { | |
| 75 | + | let client: Arc<synckit_client::SyncKitClient> = match state.sync_client.read().expect("sync_client poisoned").clone() { | |
| 76 | 76 | Some(c) => c, | |
| 77 | 77 | None => continue, | |
| 78 | 78 | }; |
| @@ -8,8 +8,8 @@ use goingson_core::{ProjectId, UserId}; | |||
| 8 | 8 | use goingson_db_sqlite::{ | |
| 9 | 9 | init_pool, run_migrations, | |
| 10 | 10 | SqliteAttachmentRepository, SqliteBackupSettingsRepository, SqliteContactRepository, | |
| 11 | - | SqliteEmailAccountRepository, SqliteEmailRepository, SqliteEventRepository, | |
| 12 | - | SqliteMilestoneRepository, | |
| 11 | + | SqliteDailyNoteRepository, SqliteEmailAccountRepository, SqliteEmailRepository, | |
| 12 | + | SqliteEventRepository, SqliteMilestoneRepository, | |
| 13 | 13 | SqliteProjectRepository, SqliteSavedViewRepository, SqliteMonthlyReviewRepository, | |
| 14 | 14 | SqliteSearchRepository, SqliteStatsRepository, SqliteSyncAccountRepository, | |
| 15 | 15 | SqliteTaskRepository, SqliteWeeklyReviewRepository, | |
| @@ -62,6 +62,7 @@ pub async fn setup_test_state() -> (Arc<AppState>, UserId) { | |||
| 62 | 62 | emails: Arc::new(SqliteEmailRepository::new(pool.clone())), | |
| 63 | 63 | email_accounts: Arc::new(SqliteEmailAccountRepository::new(pool.clone())), | |
| 64 | 64 | contacts: Arc::new(SqliteContactRepository::new(pool.clone())), | |
| 65 | + | daily_notes: Arc::new(SqliteDailyNoteRepository::new(pool.clone())), | |
| 65 | 66 | attachments: Arc::new(SqliteAttachmentRepository::new(pool.clone())), | |
| 66 | 67 | stats: Arc::new(SqliteStatsRepository::new(pool.clone())), | |
| 67 | 68 | search: Arc::new(SqliteSearchRepository::new(pool.clone())), | |
| @@ -71,11 +72,11 @@ pub async fn setup_test_state() -> (Arc<AppState>, UserId) { | |||
| 71 | 72 | monthly_reviews: Arc::new(SqliteMonthlyReviewRepository::new(pool.clone())), | |
| 72 | 73 | backup_settings: Arc::new(SqliteBackupSettingsRepository::new(pool.clone())), | |
| 73 | 74 | sync_accounts: Arc::new(SqliteSyncAccountRepository::new(pool.clone())), | |
| 74 | - | sync_client: parking_lot::RwLock::new(None), | |
| 75 | + | sync_client: std::sync::RwLock::new(None), | |
| 75 | 76 | sync_lock: Arc::new(tokio::sync::Mutex::new(())), | |
| 76 | - | email_sync_locks: Arc::new(parking_lot::Mutex::new(std::collections::HashSet::new())), | |
| 77 | - | token_refresh_locks: Arc::new(parking_lot::Mutex::new(std::collections::HashMap::new())), | |
| 78 | - | pending_oauth_flows: Arc::new(parking_lot::Mutex::new(std::collections::HashMap::new())), | |
| 77 | + | email_sync_locks: Arc::new(std::sync::Mutex::new(std::collections::HashSet::new())), | |
| 78 | + | token_refresh_locks: Arc::new(std::sync::Mutex::new(std::collections::HashMap::new())), | |
| 79 | + | pending_oauth_flows: Arc::new(std::sync::Mutex::new(std::collections::HashMap::new())), | |
| 79 | 80 | data_dir: std::path::PathBuf::from("/tmp/goingson-test"), | |
| 80 | 81 | }; | |
| 81 | 82 |