use rusqlite::{params, Connection}; use serde::{Deserialize, Serialize}; #[derive(Debug, Serialize, Deserialize, Clone)] pub struct ApiKey { pub id: String, pub name: String, pub provider: String, pub api_key: String, pub base_url: Option, pub model: Option, pub notes: Option, pub created_at: Option, pub updated_at: Option, } #[derive(Debug, Deserialize)] pub struct SaveApiKeyInput { pub name: String, pub provider: String, pub api_key: String, pub base_url: Option, pub model: Option, pub notes: Option, } fn row_to_api_key(row: &rusqlite::Row<'_>) -> rusqlite::Result { Ok(ApiKey { id: row.get(0)?, name: row.get(1)?, provider: row.get(2)?, api_key: row.get(3)?, base_url: row.get(4)?, model: row.get(5)?, notes: row.get(6)?, created_at: row.get(7)?, updated_at: row.get(8)?, }) } const SELECT_COLS: &str = "id, name, provider, api_key, base_url, model, notes, created_at, updated_at"; pub fn list_api_keys(conn: &Connection) -> Vec { let sql = format!("SELECT {SELECT_COLS} FROM api_keys ORDER BY updated_at DESC"); let mut stmt = match conn.prepare(&sql) { Ok(s) => s, Err(_) => return vec![], }; stmt.query_map([], row_to_api_key) .map(|iter| iter.filter_map(|r| r.ok()).collect()) .unwrap_or_default() } pub fn create_api_key(conn: &Connection, input: &SaveApiKeyInput) -> Result { let id = format!("ak-{}", uuid::Uuid::new_v4()); conn.execute( "INSERT INTO api_keys (id, name, provider, api_key, base_url, model, notes) VALUES (?1,?2,?3,?4,?5,?6,?7)", params![id, input.name, input.provider, input.api_key, input.base_url, input.model, input.notes], ) .map_err(|e| e.to_string())?; get_api_key(conn, &id).ok_or_else(|| "创建后查询失败".into()) } pub fn get_api_key(conn: &Connection, id: &str) -> Option { let sql = format!("SELECT {SELECT_COLS} FROM api_keys WHERE id = ?1"); conn.query_row(&sql, params![id], row_to_api_key).ok() } pub fn update_api_key(conn: &Connection, id: &str, input: &SaveApiKeyInput) -> Result { let rows = conn.execute( "UPDATE api_keys SET name=?1, provider=?2, api_key=?3, base_url=?4, model=?5, notes=?6, updated_at=datetime('now') WHERE id=?7", params![input.name, input.provider, input.api_key, input.base_url, input.model, input.notes, id], ) .map_err(|e| e.to_string())?; if rows == 0 { return Err(format!("API Key {} 不存在", id)); } get_api_key(conn, id).ok_or_else(|| "更新后查询失败".into()) } pub fn delete_api_key_fn(conn: &Connection, id: &str) -> Result<(), String> { conn.execute("DELETE FROM api_keys WHERE id=?1", params![id]).map_err(|e| e.to_string())?; Ok(()) } // ── Tauri commands ─────────────────────────────────────────────────────────── #[tauri::command] pub fn get_api_keys() -> Result, String> { let conn = crate::db::pool().get().map_err(|e| e.to_string())?; Ok(list_api_keys(&conn)) } #[tauri::command] pub fn add_api_key(input: SaveApiKeyInput) -> Result { let conn = crate::db::pool().get().map_err(|e| e.to_string())?; create_api_key(&conn, &input) } #[tauri::command] pub fn edit_api_key(id: String, input: SaveApiKeyInput) -> Result { let conn = crate::db::pool().get().map_err(|e| e.to_string())?; update_api_key(&conn, &id, &input) } #[tauri::command] pub fn delete_api_key(id: String) -> Result<(), String> { let conn = crate::db::pool().get().map_err(|e| e.to_string())?; delete_api_key_fn(&conn, &id) }