migrar Alpha a Tauri app con Turso/libsql como BD local

This commit is contained in:
Ricardo Gonzalez
2026-05-25 21:37:31 -05:00
parent ee5db5b67e
commit c8bc002b7d
14 changed files with 11569 additions and 1 deletions
+201
View File
@@ -0,0 +1,201 @@
use serde::{Deserialize, Serialize};
use std::sync::Mutex;
use tauri::State;
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Project {
pub id: i64,
pub name: String,
pub key: Option<String>,
pub description: Option<String>,
pub status: String,
pub start_date: Option<String>,
pub end_date: Option<String>,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct WorkItem {
pub id: i64,
pub project_id: i64,
pub code: Option<String>,
pub title: String,
pub description: Option<String>,
#[serde(rename = "type")]
pub wi_type: String,
pub status: String,
pub priority: String,
}
async fn get_conn(db_path: &str) -> Result<libsql::Connection, String> {
let db = libsql::Builder::new_local(db_path)
.build()
.await
.map_err(|e| format!("DB open: {e}"))?;
let conn = db.connect().map_err(|e| format!("Connect: {e}"))?;
conn.execute_batch(
"CREATE TABLE IF NOT EXISTS projects (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
key TEXT,
description TEXT,
status TEXT DEFAULT 'active',
start_date TEXT,
end_date TEXT,
created_at TEXT DEFAULT (datetime('now'))
);
CREATE TABLE IF NOT EXISTS work_items (
id INTEGER PRIMARY KEY,
project_id INTEGER NOT NULL,
code TEXT,
title TEXT NOT NULL,
description TEXT,
type TEXT DEFAULT 'task',
status TEXT DEFAULT 'backlog',
priority TEXT DEFAULT 'medium',
created_at TEXT DEFAULT (datetime('now')),
FOREIGN KEY (project_id) REFERENCES projects(id)
);"
)
.await
.map_err(|e| format!("Migration: {e}"))?;
Ok(conn)
}
pub mod commands {
use super::*;
#[tauri::command]
pub async fn get_projects(state: State<'_, Mutex<String>>) -> Result<Vec<Project>, String> {
let db_path = state.lock().map_err(|e| e.to_string())?.clone();
let conn = get_conn(&db_path).await?;
let mut rows = conn
.query("SELECT id, name, key, description, status, start_date, end_date FROM projects ORDER BY name", ())
.await
.map_err(|e| format!("Query: {e}"))?;
let mut projects = Vec::new();
while let Some(row) = rows.next().await.map_err(|e| format!("Row: {e}"))? {
projects.push(Project {
id: row.get::<i64>(0).unwrap_or(0),
name: row.get::<String>(1).unwrap_or_default(),
key: row.get::<String>(2).ok(),
description: row.get::<String>(3).ok(),
status: row.get::<String>(4).unwrap_or_else(|_| "active".into()),
start_date: row.get::<String>(5).ok(),
end_date: row.get::<String>(6).ok(),
});
}
Ok(projects)
}
#[tauri::command]
pub async fn save_project(state: State<'_, Mutex<String>>, project: Project) -> Result<i64, String> {
let db_path = state.lock().map_err(|e| e.to_string())?.clone();
let conn = get_conn(&db_path).await?;
conn.execute(
"INSERT OR REPLACE INTO projects (id, name, key, description, status, start_date, end_date)
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)",
libsql::params![
project.id,
project.name,
project.key,
project.description,
project.status,
project.start_date,
project.end_date,
],
)
.await
.map_err(|e| format!("Insert: {e}"))?;
Ok(conn.last_insert_rowid())
}
#[tauri::command]
pub async fn delete_project(state: State<'_, Mutex<String>>, id: i64) -> Result<(), String> {
let db_path = state.lock().map_err(|e| e.to_string())?.clone();
let conn = get_conn(&db_path).await?;
conn.execute("DELETE FROM work_items WHERE project_id = ?1", libsql::params![id])
.await
.map_err(|e| format!("Delete WIs: {e}"))?;
conn.execute("DELETE FROM projects WHERE id = ?1", libsql::params![id])
.await
.map_err(|e| format!("Delete project: {e}"))?;
Ok(())
}
#[tauri::command]
pub async fn get_work_items(state: State<'_, Mutex<String>>, project_id: i64) -> Result<Vec<WorkItem>, String> {
let db_path = state.lock().map_err(|e| e.to_string())?.clone();
let conn = get_conn(&db_path).await?;
let mut rows = conn
.query(
"SELECT id, project_id, code, title, description, type, status, priority FROM work_items WHERE project_id = ?1 ORDER BY created_at DESC",
libsql::params![project_id],
)
.await
.map_err(|e| format!("Query: {e}"))?;
let mut items = Vec::new();
while let Some(row) = rows.next().await.map_err(|e| format!("Row: {e}"))? {
items.push(WorkItem {
id: row.get::<i64>(0).unwrap_or(0),
project_id: row.get::<i64>(1).unwrap_or(0),
code: row.get::<String>(2).ok(),
title: row.get::<String>(3).unwrap_or_default(),
description: row.get::<String>(4).ok(),
wi_type: row.get::<String>(5).unwrap_or_else(|_| "task".into()),
status: row.get::<String>(6).unwrap_or_else(|_| "backlog".into()),
priority: row.get::<String>(7).unwrap_or_else(|_| "medium".into()),
});
}
Ok(items)
}
#[tauri::command]
pub async fn save_work_item(state: State<'_, Mutex<String>>, item: WorkItem) -> Result<i64, String> {
let db_path = state.lock().map_err(|e| e.to_string())?.clone();
let conn = get_conn(&db_path).await?;
conn.execute(
"INSERT OR REPLACE INTO work_items (id, project_id, code, title, description, type, status, priority)
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)",
libsql::params![
item.id,
item.project_id,
item.code,
item.title,
item.description,
item.wi_type,
item.status,
item.priority,
],
)
.await
.map_err(|e| format!("Insert: {e}"))?;
Ok(conn.last_insert_rowid())
}
#[tauri::command]
pub async fn delete_work_item(state: State<'_, Mutex<String>>, id: i64) -> Result<(), String> {
let db_path = state.lock().map_err(|e| e.to_string())?.clone();
let conn = get_conn(&db_path).await?;
conn.execute("DELETE FROM work_items WHERE id = ?1", libsql::params![id])
.await
.map_err(|e| format!("Delete: {e}"))?;
Ok(())
}
}