175 lines
5.6 KiB
Rust
175 lines
5.6 KiB
Rust
use rusqlite::{params, Connection};
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
|
pub struct CloudProduct {
|
|
pub id: String,
|
|
pub vendor: String,
|
|
pub product_type: String,
|
|
pub name: String,
|
|
pub region: Option<String>,
|
|
pub instance_id: Option<String>,
|
|
pub endpoint: Option<String>,
|
|
pub expire_at: Option<String>,
|
|
pub status: String,
|
|
pub config_data: String,
|
|
pub notes: Option<String>,
|
|
pub sort_order: i32,
|
|
pub created_at: Option<String>,
|
|
pub updated_at: Option<String>,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
pub struct SaveCloudProductInput {
|
|
pub vendor: String,
|
|
pub product_type: String,
|
|
pub name: String,
|
|
pub region: Option<String>,
|
|
pub instance_id: Option<String>,
|
|
pub endpoint: Option<String>,
|
|
pub expire_at: Option<String>,
|
|
pub status: String,
|
|
pub config_data: Option<String>,
|
|
pub notes: Option<String>,
|
|
pub sort_order: Option<i32>,
|
|
}
|
|
|
|
fn row_to_cloud_product(row: &rusqlite::Row<'_>) -> rusqlite::Result<CloudProduct> {
|
|
Ok(CloudProduct {
|
|
id: row.get(0)?,
|
|
vendor: row.get(1)?,
|
|
product_type: row.get(2)?,
|
|
name: row.get(3)?,
|
|
region: row.get(4)?,
|
|
instance_id: row.get(5)?,
|
|
endpoint: row.get(6)?,
|
|
expire_at: row.get(7)?,
|
|
status: row.get(8)?,
|
|
config_data: row.get(9)?,
|
|
notes: row.get(10)?,
|
|
sort_order: row.get(11)?,
|
|
created_at: row.get(12)?,
|
|
updated_at: row.get(13)?,
|
|
})
|
|
}
|
|
|
|
const SELECT_COLS: &str =
|
|
"id, vendor, product_type, name, region, instance_id, endpoint, expire_at, \
|
|
status, config_data, notes, sort_order, created_at, updated_at";
|
|
|
|
pub fn list_cloud_products(conn: &Connection) -> Vec<CloudProduct> {
|
|
let sql = format!("SELECT {SELECT_COLS} FROM cloud_products ORDER BY vendor, sort_order, name");
|
|
let mut stmt = match conn.prepare(&sql) {
|
|
Ok(s) => s,
|
|
Err(_) => return vec![],
|
|
};
|
|
stmt.query_map([], row_to_cloud_product)
|
|
.map(|iter| iter.filter_map(|r| r.ok()).collect())
|
|
.unwrap_or_default()
|
|
}
|
|
|
|
fn get_cloud_product_by_id(conn: &Connection, id: &str) -> Option<CloudProduct> {
|
|
let sql = format!("SELECT {SELECT_COLS} FROM cloud_products WHERE id = ?1");
|
|
conn.query_row(&sql, params![id], row_to_cloud_product).ok()
|
|
}
|
|
|
|
pub fn create_cloud_product(
|
|
conn: &Connection,
|
|
input: &SaveCloudProductInput,
|
|
) -> Result<CloudProduct, String> {
|
|
let id = format!("cp-{}", uuid::Uuid::new_v4());
|
|
let config_data = input.config_data.as_deref().unwrap_or("{}");
|
|
conn.execute(
|
|
"INSERT INTO cloud_products
|
|
(id, vendor, product_type, name, region, instance_id, endpoint, expire_at,
|
|
status, config_data, notes, sort_order)
|
|
VALUES (?1,?2,?3,?4,?5,?6,?7,?8,?9,?10,?11,?12)",
|
|
params![
|
|
id,
|
|
input.vendor,
|
|
input.product_type,
|
|
input.name,
|
|
input.region,
|
|
input.instance_id,
|
|
input.endpoint,
|
|
input.expire_at,
|
|
input.status,
|
|
config_data,
|
|
input.notes,
|
|
input.sort_order.unwrap_or(0)
|
|
],
|
|
)
|
|
.map_err(|e| e.to_string())?;
|
|
get_cloud_product_by_id(conn, &id).ok_or_else(|| "创建后查询失败".into())
|
|
}
|
|
|
|
pub fn update_cloud_product(
|
|
conn: &Connection,
|
|
id: &str,
|
|
input: &SaveCloudProductInput,
|
|
) -> Result<CloudProduct, String> {
|
|
let config_data = input.config_data.as_deref().unwrap_or("{}");
|
|
let rows = conn
|
|
.execute(
|
|
"UPDATE cloud_products
|
|
SET vendor=?1, product_type=?2, name=?3, region=?4, instance_id=?5,
|
|
endpoint=?6, expire_at=?7, status=?8, config_data=?9, notes=?10,
|
|
sort_order=?11, updated_at=datetime('now')
|
|
WHERE id=?12",
|
|
params![
|
|
input.vendor,
|
|
input.product_type,
|
|
input.name,
|
|
input.region,
|
|
input.instance_id,
|
|
input.endpoint,
|
|
input.expire_at,
|
|
input.status,
|
|
config_data,
|
|
input.notes,
|
|
input.sort_order.unwrap_or(0),
|
|
id
|
|
],
|
|
)
|
|
.map_err(|e| e.to_string())?;
|
|
if rows == 0 {
|
|
return Err(format!("云产品 {} 不存在", id));
|
|
}
|
|
get_cloud_product_by_id(conn, id).ok_or_else(|| "更新后查询失败".into())
|
|
}
|
|
|
|
pub fn delete_cloud_product_fn(conn: &Connection, id: &str) -> Result<(), String> {
|
|
conn.execute("DELETE FROM cloud_products WHERE id=?1", params![id])
|
|
.map_err(|e| e.to_string())?;
|
|
Ok(())
|
|
}
|
|
|
|
// ── Tauri commands ───────────────────────────────────────────────────────────
|
|
|
|
#[tauri::command]
|
|
pub fn get_cloud_products() -> Result<Vec<CloudProduct>, String> {
|
|
let conn = crate::db::pool().get().map_err(|e| e.to_string())?;
|
|
Ok(list_cloud_products(&conn))
|
|
}
|
|
|
|
#[tauri::command]
|
|
pub fn add_cloud_product(input: SaveCloudProductInput) -> Result<CloudProduct, String> {
|
|
let conn = crate::db::pool().get().map_err(|e| e.to_string())?;
|
|
create_cloud_product(&conn, &input)
|
|
}
|
|
|
|
#[tauri::command]
|
|
pub fn edit_cloud_product(
|
|
id: String,
|
|
input: SaveCloudProductInput,
|
|
) -> Result<CloudProduct, String> {
|
|
let conn = crate::db::pool().get().map_err(|e| e.to_string())?;
|
|
update_cloud_product(&conn, &id, &input)
|
|
}
|
|
|
|
#[tauri::command]
|
|
pub fn delete_cloud_product(id: String) -> Result<(), String> {
|
|
let conn = crate::db::pool().get().map_err(|e| e.to_string())?;
|
|
delete_cloud_product_fn(&conn, &id)
|
|
}
|