use rusqlite::{params, Connection}; use serde::{Deserialize, Serialize}; #[derive(Debug, Serialize, Deserialize, Clone)] pub struct DockerApp { pub id: String, pub product_id: String, pub name: String, pub access_url: Option, pub username: Option, pub password: Option, pub notes: Option, pub sort_order: i32, pub created_at: Option, pub updated_at: Option, } #[derive(Debug, Deserialize)] pub struct SaveDockerAppInput { pub product_id: String, pub name: String, pub access_url: Option, pub username: Option, pub password: Option, pub notes: Option, pub sort_order: Option, } const COLS: &str = "id, product_id, name, access_url, username, password, notes, sort_order, created_at, updated_at"; fn row_to_app(row: &rusqlite::Row<'_>) -> rusqlite::Result { Ok(DockerApp { id: row.get(0)?, product_id: row.get(1)?, name: row.get(2)?, access_url: row.get(3)?, username: row.get(4)?, password: row.get(5)?, notes: row.get(6)?, sort_order: row.get(7)?, created_at: row.get(8)?, updated_at: row.get(9)?, }) } fn get_by_id(conn: &Connection, id: &str) -> Option { let sql = format!("SELECT {COLS} FROM docker_apps WHERE id = ?1"); conn.query_row(&sql, params![id], row_to_app).ok() } // ── Tauri commands ─────────────────────────────────────────────────────────── #[tauri::command] pub fn get_docker_apps(product_id: String) -> Result, String> { let conn = crate::db::pool().get().map_err(|e| e.to_string())?; let sql = format!( "SELECT {COLS} FROM docker_apps WHERE product_id = ?1 ORDER BY sort_order, name" ); let mut stmt = conn.prepare(&sql).map_err(|e| e.to_string())?; let rows = stmt .query_map(params![product_id], row_to_app) .map_err(|e| e.to_string())? .filter_map(|r| r.ok()) .collect(); Ok(rows) } #[tauri::command] pub fn add_docker_app(input: SaveDockerAppInput) -> Result { let conn = crate::db::pool().get().map_err(|e| e.to_string())?; let id = format!("dapp-{}", uuid::Uuid::new_v4()); conn.execute( "INSERT INTO docker_apps (id, product_id, name, access_url, username, password, notes, sort_order) VALUES (?1,?2,?3,?4,?5,?6,?7,?8)", params![ id, input.product_id, input.name, input.access_url, input.username, input.password, input.notes, input.sort_order.unwrap_or(0) ], ) .map_err(|e| e.to_string())?; get_by_id(&conn, &id).ok_or_else(|| "创建后查询失败".into()) } #[tauri::command] pub fn edit_docker_app(id: String, input: SaveDockerAppInput) -> Result { let conn = crate::db::pool().get().map_err(|e| e.to_string())?; let rows = conn.execute( "UPDATE docker_apps SET name=?1, access_url=?2, username=?3, password=?4, notes=?5, sort_order=?6, updated_at=datetime('now') WHERE id=?7", params![ input.name, input.access_url, input.username, input.password, input.notes, input.sort_order.unwrap_or(0), id ], ) .map_err(|e| e.to_string())?; if rows == 0 { return Err(format!("应用 {} 不存在", id)); } get_by_id(&conn, &id).ok_or_else(|| "更新后查询失败".into()) } #[tauri::command] pub fn delete_docker_app(id: String) -> Result<(), String> { let conn = crate::db::pool().get().map_err(|e| e.to_string())?; conn.execute("DELETE FROM docker_apps WHERE id=?1", params![id]) .map_err(|e| e.to_string())?; Ok(()) }