orchai/src-tauri/src/commands/project.rs
thibaud-leclere 81d3b72e34 fix: address code review issues (mutex safety, schema, CSP, error handling)
- Replace .unwrap() on Mutex lock with proper error propagation
- Add worktree_path and branch_name columns to processed_tickets
- Add style-src to CSP for Tailwind compatibility
- Implement std::error::Error for AppError
- Check affected row count in update/delete operations
- Handle non-UTF-8 paths in git clone

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 10:16:44 +02:00

83 lines
2.9 KiB
Rust

use crate::error::AppError;
use crate::models::project::Project;
use crate::AppState;
use std::process::Command;
use tauri::State;
#[tauri::command]
pub fn create_project(
state: State<'_, AppState>,
name: String,
path_or_url: String,
base_branch: String,
) -> Result<Project, AppError> {
let is_url = path_or_url.starts_with("http://")
|| path_or_url.starts_with("https://")
|| path_or_url.starts_with("git@");
let (local_path, cloned_from) = if is_url {
let home = dirs::home_dir().ok_or_else(|| AppError::from("Cannot determine home directory".to_string()))?;
let clone_dir = home.join("orchai-repos").join(&name);
std::fs::create_dir_all(&clone_dir)?;
let clone_dir_str = clone_dir.to_str()
.ok_or_else(|| AppError::from("Clone path contains invalid characters".to_string()))?;
let output = Command::new("git")
.args(["clone", &path_or_url, clone_dir_str])
.output()?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
return Err(AppError::from(format!("git clone failed: {}", stderr)));
}
(clone_dir.to_string_lossy().to_string(), Some(path_or_url))
} else {
let path = std::path::Path::new(&path_or_url);
if !path.exists() {
return Err(AppError::from(format!("Path does not exist: {}", path_or_url)));
}
if !path.join(".git").exists() {
return Err(AppError::from(format!("Not a git repository: {}", path_or_url)));
}
(path_or_url, None)
};
let db = state.db.lock().map_err(|e| AppError::from(format!("Database lock failed: {}", e)))?;
let project = Project::insert(&db, &name, &local_path, cloned_from.as_deref(), &base_branch)?;
Ok(project)
}
#[tauri::command]
pub fn list_projects(state: State<'_, AppState>) -> Result<Vec<Project>, AppError> {
let db = state.db.lock().map_err(|e| AppError::from(format!("Database lock failed: {}", e)))?;
let projects = Project::list(&db)?;
Ok(projects)
}
#[tauri::command]
pub fn get_project(state: State<'_, AppState>, id: String) -> Result<Project, AppError> {
let db = state.db.lock().map_err(|e| AppError::from(format!("Database lock failed: {}", e)))?;
let project = Project::get_by_id(&db, &id)?;
Ok(project)
}
#[tauri::command]
pub fn update_project(
state: State<'_, AppState>,
id: String,
name: String,
base_branch: String,
) -> Result<(), AppError> {
let db = state.db.lock().map_err(|e| AppError::from(format!("Database lock failed: {}", e)))?;
Project::update(&db, &id, &name, &base_branch)?;
Ok(())
}
#[tauri::command]
pub fn delete_project(state: State<'_, AppState>, id: String) -> Result<(), AppError> {
let db = state.db.lock().map_err(|e| AppError::from(format!("Database lock failed: {}", e)))?;
Project::delete(&db, &id)?;
Ok(())
}