use dbx_core::connection::AppState; use std::collections::{HashMap, HashSet}; use std::path::PathBuf; use std::sync::Arc; use tokio::sync::{broadcast, watch, Mutex, RwLock}; use tokio_util::sync::CancellationToken; use crate::sse::TransferProgressChannel; pub struct LoginRateLimit { pub fail_count: u32, pub locked_until: Option, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct WebExportFile { pub file_path: String, pub download_filename: String, pub format: String, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct NacosImportContext { pub owner_session: Option, pub connection_id: String, pub target_namespace: String, pub plan_hash: String, } pub struct WebState { pub app: Arc, pub data_dir: PathBuf, pub public_base_path: String, pub password_disabled: bool, pub password_hash: RwLock>, pub sessions: RwLock>, pub sse_channels: RwLock>>, pub transfer_progress_channels: RwLock>>, pub table_import_channels: RwLock>>, pub sql_file_executions: RwLock>, pub nacos_imports: RwLock>, pub login_rate_limit: Mutex, /// Completed Web export temp files waiting for the browser download. pub export_files: RwLock>, pub ssh_prompts: Arc, } impl WebState { pub async fn remove_sse_channel(&self, id: &str) { self.sse_channels.write().await.remove(id); } /// Test helper: full field set so new WebState fields don't break scattered test fixtures. #[cfg(test)] pub fn for_tests(app: Arc, data_dir: PathBuf) -> Self { Self { app, data_dir, public_base_path: "/".to_string(), password_disabled: false, password_hash: RwLock::new(None), sessions: RwLock::new(HashSet::new()), sse_channels: RwLock::new(HashMap::new()), transfer_progress_channels: RwLock::new(HashMap::new()), table_import_channels: RwLock::new(HashMap::new()), sql_file_executions: RwLock::new(HashMap::new()), nacos_imports: RwLock::new(HashMap::new()), login_rate_limit: Mutex::new(LoginRateLimit { fail_count: 0, locked_until: None }), export_files: RwLock::new(HashMap::new()), ssh_prompts: Arc::new(crate::ssh_prompt::SshPromptHub::new()), } } }