fix(error): migrate session submodules + btmsg/bttask backends to AppError
- session/*.rs (sessions, layout, settings, ssh, agents, metrics, anchors) now return Result<T, AppError> internally, not just at command boundary - btmsg.rs and bttask.rs backends migrated to AppError::Database - 116 cargo tests passing
This commit is contained in:
parent
eb04e7e5b5
commit
f19b69f018
11 changed files with 264 additions and 255 deletions
|
|
@ -3,6 +3,7 @@
|
|||
use rusqlite::params;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use super::SessionDb;
|
||||
use crate::error::AppError;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct AgentMessageRecord {
|
||||
|
|
@ -37,21 +38,21 @@ impl SessionDb {
|
|||
project_id: &str,
|
||||
sdk_session_id: Option<&str>,
|
||||
messages: &[AgentMessageRecord],
|
||||
) -> Result<(), String> {
|
||||
) -> Result<(), AppError> {
|
||||
let conn = self.conn.lock().unwrap();
|
||||
// Wrap DELETE+INSERTs in a transaction to prevent partial writes on crash
|
||||
let tx = conn.unchecked_transaction()
|
||||
.map_err(|e| format!("Begin transaction failed: {e}"))?;
|
||||
.map_err(|e| AppError::database(format!("Begin transaction failed: {e}")))?;
|
||||
|
||||
// Clear previous messages for this session
|
||||
tx.execute(
|
||||
"DELETE FROM agent_messages WHERE session_id = ?1",
|
||||
params![session_id],
|
||||
).map_err(|e| format!("Delete old messages failed: {e}"))?;
|
||||
).map_err(|e| AppError::database(format!("Delete old messages failed: {e}")))?;
|
||||
|
||||
let mut stmt = tx.prepare(
|
||||
"INSERT INTO agent_messages (session_id, project_id, sdk_session_id, message_type, content, parent_id, created_at) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)"
|
||||
).map_err(|e| format!("Prepare insert failed: {e}"))?;
|
||||
).map_err(|e| AppError::database(format!("Prepare insert failed: {e}")))?;
|
||||
|
||||
for msg in messages {
|
||||
stmt.execute(params![
|
||||
|
|
@ -62,21 +63,21 @@ impl SessionDb {
|
|||
msg.content,
|
||||
msg.parent_id,
|
||||
msg.created_at,
|
||||
]).map_err(|e| format!("Insert message failed: {e}"))?;
|
||||
]).map_err(|e| AppError::database(format!("Insert message failed: {e}")))?;
|
||||
}
|
||||
drop(stmt);
|
||||
tx.commit().map_err(|e| format!("Commit failed: {e}"))?;
|
||||
tx.commit().map_err(|e| AppError::database(format!("Commit failed: {e}")))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn load_agent_messages(&self, project_id: &str) -> Result<Vec<AgentMessageRecord>, String> {
|
||||
pub fn load_agent_messages(&self, project_id: &str) -> Result<Vec<AgentMessageRecord>, AppError> {
|
||||
let conn = self.conn.lock().unwrap();
|
||||
let mut stmt = conn.prepare(
|
||||
"SELECT id, session_id, project_id, sdk_session_id, message_type, content, parent_id, created_at
|
||||
FROM agent_messages
|
||||
WHERE project_id = ?1
|
||||
ORDER BY created_at ASC"
|
||||
).map_err(|e| format!("Query prepare failed: {e}"))?;
|
||||
).map_err(|e| AppError::database(format!("Query prepare failed: {e}")))?;
|
||||
|
||||
let messages = stmt.query_map(params![project_id], |row| {
|
||||
Ok(AgentMessageRecord {
|
||||
|
|
@ -89,14 +90,14 @@ impl SessionDb {
|
|||
parent_id: row.get(6)?,
|
||||
created_at: row.get(7)?,
|
||||
})
|
||||
}).map_err(|e| format!("Query failed: {e}"))?
|
||||
}).map_err(|e| AppError::database(format!("Query failed: {e}")))?
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.map_err(|e| format!("Row read failed: {e}"))?;
|
||||
.map_err(|e| AppError::database(format!("Row read failed: {e}")))?;
|
||||
|
||||
Ok(messages)
|
||||
}
|
||||
|
||||
pub fn save_project_agent_state(&self, state: &ProjectAgentState) -> Result<(), String> {
|
||||
pub fn save_project_agent_state(&self, state: &ProjectAgentState) -> Result<(), AppError> {
|
||||
let conn = self.conn.lock().unwrap();
|
||||
conn.execute(
|
||||
"INSERT OR REPLACE INTO project_agent_state (project_id, last_session_id, sdk_session_id, status, cost_usd, input_tokens, output_tokens, last_prompt, updated_at) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9)",
|
||||
|
|
@ -111,15 +112,15 @@ impl SessionDb {
|
|||
state.last_prompt,
|
||||
state.updated_at,
|
||||
],
|
||||
).map_err(|e| format!("Save project agent state failed: {e}"))?;
|
||||
).map_err(|e| AppError::database(format!("Save project agent state failed: {e}")))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn load_project_agent_state(&self, project_id: &str) -> Result<Option<ProjectAgentState>, String> {
|
||||
pub fn load_project_agent_state(&self, project_id: &str) -> Result<Option<ProjectAgentState>, AppError> {
|
||||
let conn = self.conn.lock().unwrap();
|
||||
let mut stmt = conn.prepare(
|
||||
"SELECT project_id, last_session_id, sdk_session_id, status, cost_usd, input_tokens, output_tokens, last_prompt, updated_at FROM project_agent_state WHERE project_id = ?1"
|
||||
).map_err(|e| format!("Query prepare failed: {e}"))?;
|
||||
).map_err(|e| AppError::database(format!("Query prepare failed: {e}")))?;
|
||||
|
||||
let result = stmt.query_row(params![project_id], |row| {
|
||||
Ok(ProjectAgentState {
|
||||
|
|
@ -138,7 +139,7 @@ impl SessionDb {
|
|||
match result {
|
||||
Ok(state) => Ok(Some(state)),
|
||||
Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None),
|
||||
Err(e) => Err(format!("Load project agent state failed: {e}")),
|
||||
Err(e) => Err(AppError::database(format!("Load project agent state failed: {e}"))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
use rusqlite::params;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use super::SessionDb;
|
||||
use crate::error::AppError;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct SessionAnchorRecord {
|
||||
|
|
@ -17,14 +18,14 @@ pub struct SessionAnchorRecord {
|
|||
}
|
||||
|
||||
impl SessionDb {
|
||||
pub fn save_session_anchors(&self, anchors: &[SessionAnchorRecord]) -> Result<(), String> {
|
||||
pub fn save_session_anchors(&self, anchors: &[SessionAnchorRecord]) -> Result<(), AppError> {
|
||||
if anchors.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
let conn = self.conn.lock().unwrap();
|
||||
let mut stmt = conn.prepare(
|
||||
"INSERT OR REPLACE INTO session_anchors (id, project_id, message_id, anchor_type, content, estimated_tokens, turn_index, created_at) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)"
|
||||
).map_err(|e| format!("Prepare anchor insert failed: {e}"))?;
|
||||
).map_err(|e| AppError::database(format!("Prepare anchor insert failed: {e}")))?;
|
||||
|
||||
for anchor in anchors {
|
||||
stmt.execute(params![
|
||||
|
|
@ -36,16 +37,16 @@ impl SessionDb {
|
|||
anchor.estimated_tokens,
|
||||
anchor.turn_index,
|
||||
anchor.created_at,
|
||||
]).map_err(|e| format!("Insert anchor failed: {e}"))?;
|
||||
]).map_err(|e| AppError::database(format!("Insert anchor failed: {e}")))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn load_session_anchors(&self, project_id: &str) -> Result<Vec<SessionAnchorRecord>, String> {
|
||||
pub fn load_session_anchors(&self, project_id: &str) -> Result<Vec<SessionAnchorRecord>, AppError> {
|
||||
let conn = self.conn.lock().unwrap();
|
||||
let mut stmt = conn.prepare(
|
||||
"SELECT id, project_id, message_id, anchor_type, content, estimated_tokens, turn_index, created_at FROM session_anchors WHERE project_id = ?1 ORDER BY turn_index ASC"
|
||||
).map_err(|e| format!("Query anchors failed: {e}"))?;
|
||||
).map_err(|e| AppError::database(format!("Query anchors failed: {e}")))?;
|
||||
|
||||
let anchors = stmt.query_map(params![project_id], |row| {
|
||||
Ok(SessionAnchorRecord {
|
||||
|
|
@ -58,33 +59,33 @@ impl SessionDb {
|
|||
turn_index: row.get(6)?,
|
||||
created_at: row.get(7)?,
|
||||
})
|
||||
}).map_err(|e| format!("Query anchors failed: {e}"))?
|
||||
}).map_err(|e| AppError::database(format!("Query anchors failed: {e}")))?
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.map_err(|e| format!("Read anchor row failed: {e}"))?;
|
||||
.map_err(|e| AppError::database(format!("Read anchor row failed: {e}")))?;
|
||||
|
||||
Ok(anchors)
|
||||
}
|
||||
|
||||
pub fn delete_session_anchor(&self, id: &str) -> Result<(), String> {
|
||||
pub fn delete_session_anchor(&self, id: &str) -> Result<(), AppError> {
|
||||
let conn = self.conn.lock().unwrap();
|
||||
conn.execute("DELETE FROM session_anchors WHERE id = ?1", params![id])
|
||||
.map_err(|e| format!("Delete anchor failed: {e}"))?;
|
||||
.map_err(|e| AppError::database(format!("Delete anchor failed: {e}")))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn delete_project_anchors(&self, project_id: &str) -> Result<(), String> {
|
||||
pub fn delete_project_anchors(&self, project_id: &str) -> Result<(), AppError> {
|
||||
let conn = self.conn.lock().unwrap();
|
||||
conn.execute("DELETE FROM session_anchors WHERE project_id = ?1", params![project_id])
|
||||
.map_err(|e| format!("Delete project anchors failed: {e}"))?;
|
||||
.map_err(|e| AppError::database(format!("Delete project anchors failed: {e}")))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn update_anchor_type(&self, id: &str, anchor_type: &str) -> Result<(), String> {
|
||||
pub fn update_anchor_type(&self, id: &str, anchor_type: &str) -> Result<(), AppError> {
|
||||
let conn = self.conn.lock().unwrap();
|
||||
conn.execute(
|
||||
"UPDATE session_anchors SET anchor_type = ?2 WHERE id = ?1",
|
||||
params![id, anchor_type],
|
||||
).map_err(|e| format!("Update anchor type failed: {e}"))?;
|
||||
).map_err(|e| AppError::database(format!("Update anchor type failed: {e}")))?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
use rusqlite::params;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use super::SessionDb;
|
||||
use crate::error::AppError;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct LayoutState {
|
||||
|
|
@ -11,29 +12,29 @@ pub struct LayoutState {
|
|||
}
|
||||
|
||||
impl SessionDb {
|
||||
pub fn save_layout(&self, layout: &LayoutState) -> Result<(), String> {
|
||||
pub fn save_layout(&self, layout: &LayoutState) -> Result<(), AppError> {
|
||||
let conn = self.conn.lock().unwrap();
|
||||
let pane_ids_json = serde_json::to_string(&layout.pane_ids)
|
||||
.map_err(|e| format!("Serialize pane_ids failed: {e}"))?;
|
||||
.map_err(|e| AppError::validation(format!("Serialize pane_ids failed: {e}")))?;
|
||||
conn.execute(
|
||||
"UPDATE layout_state SET preset = ?1, pane_ids = ?2 WHERE id = 1",
|
||||
params![layout.preset, pane_ids_json],
|
||||
).map_err(|e| format!("Layout save failed: {e}"))?;
|
||||
).map_err(|e| AppError::database(format!("Layout save failed: {e}")))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn load_layout(&self) -> Result<LayoutState, String> {
|
||||
pub fn load_layout(&self) -> Result<LayoutState, AppError> {
|
||||
let conn = self.conn.lock().unwrap();
|
||||
let mut stmt = conn
|
||||
.prepare("SELECT preset, pane_ids FROM layout_state WHERE id = 1")
|
||||
.map_err(|e| format!("Layout query failed: {e}"))?;
|
||||
.map_err(|e| AppError::database(format!("Layout query failed: {e}")))?;
|
||||
|
||||
stmt.query_row([], |row| {
|
||||
let preset: String = row.get(0)?;
|
||||
let pane_ids_json: String = row.get(1)?;
|
||||
let pane_ids: Vec<String> = serde_json::from_str(&pane_ids_json).unwrap_or_default();
|
||||
Ok(LayoutState { preset, pane_ids })
|
||||
}).map_err(|e| format!("Layout read failed: {e}"))
|
||||
}).map_err(|e| AppError::database(format!("Layout read failed: {e}")))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
use rusqlite::params;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use super::SessionDb;
|
||||
use crate::error::AppError;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct SessionMetric {
|
||||
|
|
@ -22,7 +23,7 @@ pub struct SessionMetric {
|
|||
}
|
||||
|
||||
impl SessionDb {
|
||||
pub fn save_session_metric(&self, metric: &SessionMetric) -> Result<(), String> {
|
||||
pub fn save_session_metric(&self, metric: &SessionMetric) -> Result<(), AppError> {
|
||||
let conn = self.conn.lock().unwrap();
|
||||
conn.execute(
|
||||
"INSERT INTO session_metrics (project_id, session_id, start_time, end_time, peak_tokens, turn_count, tool_call_count, cost_usd, model, status, error_message) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11)",
|
||||
|
|
@ -39,22 +40,22 @@ impl SessionDb {
|
|||
metric.status,
|
||||
metric.error_message,
|
||||
],
|
||||
).map_err(|e| format!("Save session metric failed: {e}"))?;
|
||||
).map_err(|e| AppError::database(format!("Save session metric failed: {e}")))?;
|
||||
|
||||
// Enforce retention: keep last 100 per project
|
||||
conn.execute(
|
||||
"DELETE FROM session_metrics WHERE project_id = ?1 AND id NOT IN (SELECT id FROM session_metrics WHERE project_id = ?1 ORDER BY end_time DESC LIMIT 100)",
|
||||
params![metric.project_id],
|
||||
).map_err(|e| format!("Prune session metrics failed: {e}"))?;
|
||||
).map_err(|e| AppError::database(format!("Prune session metrics failed: {e}")))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn load_session_metrics(&self, project_id: &str, limit: i64) -> Result<Vec<SessionMetric>, String> {
|
||||
pub fn load_session_metrics(&self, project_id: &str, limit: i64) -> Result<Vec<SessionMetric>, AppError> {
|
||||
let conn = self.conn.lock().unwrap();
|
||||
let mut stmt = conn.prepare(
|
||||
"SELECT id, project_id, session_id, start_time, end_time, peak_tokens, turn_count, tool_call_count, cost_usd, model, status, error_message FROM session_metrics WHERE project_id = ?1 ORDER BY end_time DESC LIMIT ?2"
|
||||
).map_err(|e| format!("Query prepare failed: {e}"))?;
|
||||
).map_err(|e| AppError::database(format!("Query prepare failed: {e}")))?;
|
||||
|
||||
let metrics = stmt.query_map(params![project_id, limit], |row| {
|
||||
Ok(SessionMetric {
|
||||
|
|
@ -71,9 +72,9 @@ impl SessionDb {
|
|||
status: row.get(10)?,
|
||||
error_message: row.get(11)?,
|
||||
})
|
||||
}).map_err(|e| format!("Query failed: {e}"))?
|
||||
}).map_err(|e| AppError::database(format!("Query failed: {e}")))?
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.map_err(|e| format!("Row read failed: {e}"))?;
|
||||
.map_err(|e| AppError::database(format!("Row read failed: {e}")))?;
|
||||
|
||||
Ok(metrics)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
use rusqlite::params;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use super::SessionDb;
|
||||
use crate::error::AppError;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Session {
|
||||
|
|
@ -20,11 +21,11 @@ pub struct Session {
|
|||
}
|
||||
|
||||
impl SessionDb {
|
||||
pub fn list_sessions(&self) -> Result<Vec<Session>, String> {
|
||||
pub fn list_sessions(&self) -> Result<Vec<Session>, AppError> {
|
||||
let conn = self.conn.lock().unwrap();
|
||||
let mut stmt = conn
|
||||
.prepare("SELECT id, type, title, shell, cwd, args, group_name, created_at, last_used_at FROM sessions ORDER BY last_used_at DESC")
|
||||
.map_err(|e| format!("Query prepare failed: {e}"))?;
|
||||
.map_err(|e| AppError::database(format!("Query prepare failed: {e}")))?;
|
||||
|
||||
let sessions = stmt
|
||||
.query_map([], |row| {
|
||||
|
|
@ -42,14 +43,14 @@ impl SessionDb {
|
|||
last_used_at: row.get(8)?,
|
||||
})
|
||||
})
|
||||
.map_err(|e| format!("Query failed: {e}"))?
|
||||
.map_err(|e| AppError::database(format!("Query failed: {e}")))?
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.map_err(|e| format!("Row read failed: {e}"))?;
|
||||
.map_err(|e| AppError::database(format!("Row read failed: {e}")))?;
|
||||
|
||||
Ok(sessions)
|
||||
}
|
||||
|
||||
pub fn save_session(&self, session: &Session) -> Result<(), String> {
|
||||
pub fn save_session(&self, session: &Session) -> Result<(), AppError> {
|
||||
let conn = self.conn.lock().unwrap();
|
||||
let args_json = session.args.as_ref().map(|a| serde_json::to_string(a).unwrap_or_default());
|
||||
conn.execute(
|
||||
|
|
@ -65,36 +66,36 @@ impl SessionDb {
|
|||
session.created_at,
|
||||
session.last_used_at,
|
||||
],
|
||||
).map_err(|e| format!("Insert failed: {e}"))?;
|
||||
).map_err(|e| AppError::database(format!("Insert failed: {e}")))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn delete_session(&self, id: &str) -> Result<(), String> {
|
||||
pub fn delete_session(&self, id: &str) -> Result<(), AppError> {
|
||||
let conn = self.conn.lock().unwrap();
|
||||
conn.execute("DELETE FROM sessions WHERE id = ?1", params![id])
|
||||
.map_err(|e| format!("Delete failed: {e}"))?;
|
||||
.map_err(|e| AppError::database(format!("Delete failed: {e}")))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn update_title(&self, id: &str, title: &str) -> Result<(), String> {
|
||||
pub fn update_title(&self, id: &str, title: &str) -> Result<(), AppError> {
|
||||
let conn = self.conn.lock().unwrap();
|
||||
conn.execute(
|
||||
"UPDATE sessions SET title = ?1 WHERE id = ?2",
|
||||
params![title, id],
|
||||
).map_err(|e| format!("Update failed: {e}"))?;
|
||||
).map_err(|e| AppError::database(format!("Update failed: {e}")))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn update_group(&self, id: &str, group_name: &str) -> Result<(), String> {
|
||||
pub fn update_group(&self, id: &str, group_name: &str) -> Result<(), AppError> {
|
||||
let conn = self.conn.lock().unwrap();
|
||||
conn.execute(
|
||||
"UPDATE sessions SET group_name = ?1 WHERE id = ?2",
|
||||
params![group_name, id],
|
||||
).map_err(|e| format!("Update group failed: {e}"))?;
|
||||
).map_err(|e| AppError::database(format!("Update group failed: {e}")))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn touch_session(&self, id: &str) -> Result<(), String> {
|
||||
pub fn touch_session(&self, id: &str) -> Result<(), AppError> {
|
||||
let conn = self.conn.lock().unwrap();
|
||||
let now = std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
|
|
@ -103,7 +104,7 @@ impl SessionDb {
|
|||
conn.execute(
|
||||
"UPDATE sessions SET last_used_at = ?1 WHERE id = ?2",
|
||||
params![now, id],
|
||||
).map_err(|e| format!("Touch failed: {e}"))?;
|
||||
).map_err(|e| AppError::database(format!("Touch failed: {e}")))?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,40 +2,41 @@
|
|||
|
||||
use rusqlite::params;
|
||||
use super::SessionDb;
|
||||
use crate::error::AppError;
|
||||
|
||||
impl SessionDb {
|
||||
pub fn get_setting(&self, key: &str) -> Result<Option<String>, String> {
|
||||
pub fn get_setting(&self, key: &str) -> Result<Option<String>, AppError> {
|
||||
let conn = self.conn.lock().unwrap();
|
||||
let mut stmt = conn
|
||||
.prepare("SELECT value FROM settings WHERE key = ?1")
|
||||
.map_err(|e| format!("Settings query failed: {e}"))?;
|
||||
.map_err(|e| AppError::database(format!("Settings query failed: {e}")))?;
|
||||
let result = stmt.query_row(params![key], |row| row.get(0));
|
||||
match result {
|
||||
Ok(val) => Ok(Some(val)),
|
||||
Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None),
|
||||
Err(e) => Err(format!("Settings read failed: {e}")),
|
||||
Err(e) => Err(AppError::database(format!("Settings read failed: {e}"))),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_setting(&self, key: &str, value: &str) -> Result<(), String> {
|
||||
pub fn set_setting(&self, key: &str, value: &str) -> Result<(), AppError> {
|
||||
let conn = self.conn.lock().unwrap();
|
||||
conn.execute(
|
||||
"INSERT OR REPLACE INTO settings (key, value) VALUES (?1, ?2)",
|
||||
params![key, value],
|
||||
).map_err(|e| format!("Settings write failed: {e}"))?;
|
||||
).map_err(|e| AppError::database(format!("Settings write failed: {e}")))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_all_settings(&self) -> Result<Vec<(String, String)>, String> {
|
||||
pub fn get_all_settings(&self) -> Result<Vec<(String, String)>, AppError> {
|
||||
let conn = self.conn.lock().unwrap();
|
||||
let mut stmt = conn
|
||||
.prepare("SELECT key, value FROM settings ORDER BY key")
|
||||
.map_err(|e| format!("Settings query failed: {e}"))?;
|
||||
.map_err(|e| AppError::database(format!("Settings query failed: {e}")))?;
|
||||
let settings = stmt
|
||||
.query_map([], |row| Ok((row.get(0)?, row.get(1)?)))
|
||||
.map_err(|e| format!("Settings query failed: {e}"))?
|
||||
.map_err(|e| AppError::database(format!("Settings query failed: {e}")))?
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.map_err(|e| format!("Settings read failed: {e}"))?;
|
||||
.map_err(|e| AppError::database(format!("Settings read failed: {e}")))?;
|
||||
Ok(settings)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
use rusqlite::params;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use super::SessionDb;
|
||||
use crate::error::AppError;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct SshSession {
|
||||
|
|
@ -19,11 +20,11 @@ pub struct SshSession {
|
|||
}
|
||||
|
||||
impl SessionDb {
|
||||
pub fn list_ssh_sessions(&self) -> Result<Vec<SshSession>, String> {
|
||||
pub fn list_ssh_sessions(&self) -> Result<Vec<SshSession>, AppError> {
|
||||
let conn = self.conn.lock().unwrap();
|
||||
let mut stmt = conn
|
||||
.prepare("SELECT id, name, host, port, username, key_file, folder, color, created_at, last_used_at FROM ssh_sessions ORDER BY last_used_at DESC")
|
||||
.map_err(|e| format!("SSH query prepare failed: {e}"))?;
|
||||
.map_err(|e| AppError::database(format!("SSH query prepare failed: {e}")))?;
|
||||
|
||||
let sessions = stmt
|
||||
.query_map([], |row| {
|
||||
|
|
@ -40,14 +41,14 @@ impl SessionDb {
|
|||
last_used_at: row.get(9)?,
|
||||
})
|
||||
})
|
||||
.map_err(|e| format!("SSH query failed: {e}"))?
|
||||
.map_err(|e| AppError::database(format!("SSH query failed: {e}")))?
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.map_err(|e| format!("SSH row read failed: {e}"))?;
|
||||
.map_err(|e| AppError::database(format!("SSH row read failed: {e}")))?;
|
||||
|
||||
Ok(sessions)
|
||||
}
|
||||
|
||||
pub fn save_ssh_session(&self, session: &SshSession) -> Result<(), String> {
|
||||
pub fn save_ssh_session(&self, session: &SshSession) -> Result<(), AppError> {
|
||||
let conn = self.conn.lock().unwrap();
|
||||
conn.execute(
|
||||
"INSERT OR REPLACE INTO ssh_sessions (id, name, host, port, username, key_file, folder, color, created_at, last_used_at) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10)",
|
||||
|
|
@ -63,14 +64,14 @@ impl SessionDb {
|
|||
session.created_at,
|
||||
session.last_used_at,
|
||||
],
|
||||
).map_err(|e| format!("SSH insert failed: {e}"))?;
|
||||
).map_err(|e| AppError::database(format!("SSH insert failed: {e}")))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn delete_ssh_session(&self, id: &str) -> Result<(), String> {
|
||||
pub fn delete_ssh_session(&self, id: &str) -> Result<(), AppError> {
|
||||
let conn = self.conn.lock().unwrap();
|
||||
conn.execute("DELETE FROM ssh_sessions WHERE id = ?1", params![id])
|
||||
.map_err(|e| format!("SSH delete failed: {e}"))?;
|
||||
.map_err(|e| AppError::database(format!("SSH delete failed: {e}")))?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue