diff --git a/crates/dbx-cli/src/commands.rs b/crates/dbx-cli/src/commands.rs index 3946ccb7e..8ecfd6e85 100644 --- a/crates/dbx-cli/src/commands.rs +++ b/crates/dbx-cli/src/commands.rs @@ -269,18 +269,18 @@ async fn state_with_connection( config: dbx_core::models::connection::ConnectionConfig, ) -> Result { let state = open_state().await?; - state.configs.lock().await.insert(config.id.clone(), config.clone()); + state.configs.write().await.insert(config.id.clone(), config.clone()); match config.db_type { dbx_core::models::connection::DatabaseType::Sqlite => { let path = dbx_core::connection::expand_tilde(&config.host); let pool = dbx_core::db::sqlite::connect_path(&path).await?; - state.connections.lock().await.insert(config.id.clone(), dbx_core::connection::PoolKind::Sqlite(pool)); + state.connections.write().await.insert(config.id.clone(), dbx_core::connection::PoolKind::Sqlite(pool)); } dbx_core::models::connection::DatabaseType::DuckDb => { let path = dbx_core::connection::expand_tilde(&config.host); let pool = dbx_core::db::duckdb_driver::connect_path(&path)?; - state.connections.lock().await.insert(config.id.clone(), dbx_core::connection::PoolKind::DuckDb(pool)); + state.connections.write().await.insert(config.id.clone(), dbx_core::connection::PoolKind::DuckDb(pool)); } _ => { state.get_or_create_pool(&config.id, config.database.as_deref()).await?; @@ -587,9 +587,16 @@ mod tests { ssh_key_passphrase: "key-secret".to_string(), ssh_expose_lan: false, ssh_connect_timeout_secs: dbx_core::models::connection::default_ssh_connect_timeout_secs(), + proxy_enabled: false, + proxy_type: dbx_core::models::connection::ProxyType::Socks5, + proxy_host: String::new(), + proxy_port: 1080, + proxy_username: String::new(), + proxy_password: String::new(), ssl: false, sysdba: false, connection_string: Some(format!("mysql://root:{password}@127.0.0.1:3306/app")), + external_config: None, jdbc_driver_class: None, jdbc_driver_paths: Vec::new(), } diff --git a/crates/dbx-core/src/schema_snapshot.rs b/crates/dbx-core/src/schema_snapshot.rs index 1732e65a4..66e233a4c 100644 --- a/crates/dbx-core/src/schema_snapshot.rs +++ b/crates/dbx-core/src/schema_snapshot.rs @@ -41,7 +41,7 @@ pub async fn snapshot( schema_name: Option<&str>, ) -> Result { let config = { - let configs = state.configs.lock().await; + let configs = state.configs.read().await; configs.get(connection_id).cloned().ok_or("Connection config not found")? }; diff --git a/crates/dbx-core/tests/schema_snapshot.rs b/crates/dbx-core/tests/schema_snapshot.rs index 54c7500ab..bed6036bf 100644 --- a/crates/dbx-core/tests/schema_snapshot.rs +++ b/crates/dbx-core/tests/schema_snapshot.rs @@ -29,9 +29,16 @@ fn sqlite_config(path: &std::path::Path) -> ConnectionConfig { ssh_key_passphrase: String::new(), ssh_expose_lan: false, ssh_connect_timeout_secs: default_ssh_connect_timeout_secs(), + proxy_enabled: false, + proxy_type: dbx_core::models::connection::ProxyType::Socks5, + proxy_host: String::new(), + proxy_port: 1080, + proxy_username: String::new(), + proxy_password: String::new(), ssl: false, sysdba: false, connection_string: None, + external_config: None, jdbc_driver_class: None, jdbc_driver_paths: Vec::new(), } @@ -70,8 +77,8 @@ async fn snapshot_standardizes_sqlite_tables_views_and_metadata() { let state = open_state().await; let config = sqlite_config(&data_path); let pool = dbx_core::db::sqlite::connect_path(&data_path.display().to_string()).await.unwrap(); - state.configs.lock().await.insert(config.id.clone(), config.clone()); - state.connections.lock().await.insert(config.id.clone(), PoolKind::Sqlite(pool)); + state.configs.write().await.insert(config.id.clone(), config.clone()); + state.connections.write().await.insert(config.id.clone(), PoolKind::Sqlite(pool)); let snapshot = snapshot(&state, &config.id, None, None).await.unwrap(); @@ -106,7 +113,7 @@ async fn snapshot_propagates_schema_core_errors() { let data_path = std::env::temp_dir().join(format!("dbx-schema-snapshot-missing-{}.db", uuid::Uuid::new_v4())); let state = open_state().await; let config = sqlite_config(&data_path); - state.configs.lock().await.insert(config.id.clone(), config.clone()); + state.configs.write().await.insert(config.id.clone(), config.clone()); let err = snapshot(&state, &config.id, None, None).await.unwrap_err(); @@ -124,7 +131,7 @@ async fn snapshot_requires_database_for_database_scoped_connections_without_defa config.driver_profile = None; config.host = "127.0.0.1".to_string(); config.port = 3306; - state.configs.lock().await.insert(config.id.clone(), config.clone()); + state.configs.write().await.insert(config.id.clone(), config.clone()); let err = snapshot(&state, &config.id, None, None).await.unwrap_err(); diff --git a/src-tauri/src/commands/agent_runtime.rs b/src-tauri/src/commands/agent_runtime.rs index 6ca16883c..f878862dd 100644 --- a/src-tauri/src/commands/agent_runtime.rs +++ b/src-tauri/src/commands/agent_runtime.rs @@ -398,10 +398,7 @@ async fn load_handoff_connection(app_state: Option<&AppState>, connection_id: &s } } -fn matching_snapshot_connection_name<'a>( - item: &HandoffItem, - snapshot: &'a AgentRuntimeSnapshot, -) -> Option<&'a str> { +fn matching_snapshot_connection_name<'a>(item: &HandoffItem, snapshot: &'a AgentRuntimeSnapshot) -> Option<&'a str> { let handoff_connection_id = item.connection_id.trim(); let active_connection_id = snapshot.active_connection_id.as_deref().map(str::trim)?; if handoff_connection_id.is_empty() || handoff_connection_id != active_connection_id { @@ -411,10 +408,8 @@ fn matching_snapshot_connection_name<'a>( } fn conservative_production_risk(sql: &str) -> dbx_core::sql_safety::RiskMetadata { - let mut risk = risk_for( - sql, - RiskContext { connection_name: "unknown", color: None, environment_label: Some("Production") }, - ); + let mut risk = + risk_for(sql, RiskContext { connection_name: "unknown", color: None, environment_label: Some("Production") }); risk.is_production = true; risk.risk_level = match classify_sql(sql) { OperationClass::Ddl => RiskLevel::Critical, @@ -558,11 +553,7 @@ mod tests { AgentRuntimeState { app_state: Some(Arc::new(AppState::new(storage))), ..runtime_state() } } - fn connection_config( - id: &str, - name: &str, - color: Option<&str>, - ) -> dbx_core::models::connection::ConnectionConfig { + fn connection_config(id: &str, name: &str, color: Option<&str>) -> dbx_core::models::connection::ConnectionConfig { dbx_core::models::connection::ConnectionConfig { id: id.to_string(), name: name.to_string(), @@ -585,9 +576,16 @@ mod tests { ssh_key_passphrase: String::new(), ssh_expose_lan: false, ssh_connect_timeout_secs: dbx_core::models::connection::default_ssh_connect_timeout_secs(), + proxy_enabled: false, + proxy_type: dbx_core::models::connection::ProxyType::Socks5, + proxy_host: String::new(), + proxy_port: 1080, + proxy_username: String::new(), + proxy_password: String::new(), ssl: false, sysdba: false, connection_string: None, + external_config: None, jdbc_driver_class: None, jdbc_driver_paths: Vec::new(), } diff --git a/src/components/grid/DataGrid.vue b/src/components/grid/DataGrid.vue index 3b115df73..c674847c0 100644 --- a/src/components/grid/DataGrid.vue +++ b/src/components/grid/DataGrid.vue @@ -63,15 +63,8 @@ import * as api from "@/lib/api"; import { buildTableSelectSql, quoteTableIdentifier } from "@/lib/tableSelectSql"; import { isHiddenGridColumn, usesSyntheticRowIdKey } from "@/lib/tableEditing"; import { displayCellValue, type CellValue } from "@/lib/cellValue"; -import { buildDataGridSaveStatements, formatGridSqlLiteral } from "@/lib/dataGridSql"; -import { formatMarkdownTable } from "@/lib/markdownTable"; -import { buildXlsxWorkbook } from "@/lib/xlsxExport"; -import { - matchesRowStatusFilter, - rowStatusFilterAfterAddingRow, - type RowStatus, - type RowStatusFilter, -} from "@/lib/gridRowStatus"; +import { formatGridSqlLiteral } from "@/lib/dataGridSql"; +import { matchesRowStatusFilter, type RowStatus, type RowStatusFilter } from "@/lib/gridRowStatus"; import { useAgentRuntimeStore } from "@/stores/agentRuntimeStore"; import { useToast } from "@/composables/useToast"; diff --git a/src/stores/agentRuntimeStore.ts b/src/stores/agentRuntimeStore.ts index 8843dfa2d..f6c293ceb 100644 --- a/src/stores/agentRuntimeStore.ts +++ b/src/stores/agentRuntimeStore.ts @@ -41,18 +41,19 @@ export const useAgentRuntimeStore = defineStore("agentRuntime", () => { } async function syncNow() { - const connectionStore = useConnectionStore(); - const queryStore = useQueryStore(); - const snapshot = buildAgentRuntimeSnapshot({ - tabs: queryStore.tabs, - activeTabId: queryStore.activeTabId, - getConnection: (connectionId) => connectionStore.getConfig(connectionId), - selectedSql: selectedSql.value, - selection: selection.value, - resultSampleLimit: DEFAULT_RESULT_SAMPLE_LIMIT, - }); + if (!globalThis.localStorage) return; try { + const connectionStore = useConnectionStore(); + const queryStore = useQueryStore(); + const snapshot = buildAgentRuntimeSnapshot({ + tabs: queryStore.tabs, + activeTabId: queryStore.activeTabId, + getConnection: (connectionId) => connectionStore.getConfig(connectionId), + selectedSql: selectedSql.value, + selection: selection.value, + resultSampleLimit: DEFAULT_RESULT_SAMPLE_LIMIT, + }); await api.agentRuntimeUpdateSnapshot(snapshot); } catch (err) { console.debug("[DBX] Agent runtime snapshot sync skipped:", err); diff --git a/src/stores/queryStore.ts b/src/stores/queryStore.ts index 4532c5b42..696f1dc0e 100644 --- a/src/stores/queryStore.ts +++ b/src/stores/queryStore.ts @@ -17,16 +17,24 @@ import type { SavedSqlFile } from "@/types/database"; const STORAGE_KEY = "dbx-open-tabs"; const ACTIVE_TAB_KEY = "dbx-active-tab"; +function browserStorage(): Storage | undefined { + return globalThis.localStorage; +} + function saveTabs(tabs: QueryTab[], activeTabId: string | null) { + const storage = browserStorage(); + if (!storage) return; try { - localStorage.setItem(STORAGE_KEY, JSON.stringify(serializeOpenTabs(tabs))); - localStorage.setItem(ACTIVE_TAB_KEY, activeTabId || ""); + storage.setItem(STORAGE_KEY, JSON.stringify(serializeOpenTabs(tabs))); + storage.setItem(ACTIVE_TAB_KEY, activeTabId || ""); } catch {} } function loadSavedTabs(): { tabs: QueryTab[]; activeTabId: string | null } { + const storage = browserStorage(); + if (!storage) return { tabs: [], activeTabId: null }; try { - return restoreOpenTabsState(localStorage.getItem(STORAGE_KEY), localStorage.getItem(ACTIVE_TAB_KEY), { + return restoreOpenTabsState(storage.getItem(STORAGE_KEY), storage.getItem(ACTIVE_TAB_KEY), { queryOnly: isTauriRuntime(), }); } catch {