fix: harden Tauri backend — replace unwrap panics, fix watcher lifecycle, strengthen CSP
Replace all .unwrap() calls on repo with repo_ref()/repo_mut() helpers that return error strings instead of panicking. Stop the old file watcher before starting a new one on workspace switch to prevent accumulation. Add object-src and base-uri directives to CSP.
This commit is contained in:
parent
e0c7292a7e
commit
3c11539f02
|
|
@ -88,6 +88,16 @@ fn ensure_repo(state: &mut AppState) -> Result<(), String> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get an immutable reference to the repo, returning an error if not initialized.
|
||||||
|
fn repo_ref(state: &AppState) -> Result<&TaskRepository, String> {
|
||||||
|
state.repo.as_ref().ok_or_else(|| "Repository not initialized".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a mutable reference to the repo, returning an error if not initialized.
|
||||||
|
fn repo_mut(state: &mut AppState) -> Result<&mut TaskRepository, String> {
|
||||||
|
state.repo.as_mut().ok_or_else(|| "Repository not initialized".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
// ── Config commands ──────────────────────────────────────────────────
|
// ── Config commands ──────────────────────────────────────────────────
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
|
|
@ -164,9 +174,7 @@ fn init_workspace(path: String) -> Result<(), String> {
|
||||||
fn get_lists(state: State<'_, Mutex<AppState>>) -> Result<Vec<TaskList>, String> {
|
fn get_lists(state: State<'_, Mutex<AppState>>) -> Result<Vec<TaskList>, String> {
|
||||||
let mut s = lock_state(&state)?;
|
let mut s = lock_state(&state)?;
|
||||||
ensure_repo(&mut s)?;
|
ensure_repo(&mut s)?;
|
||||||
s.repo
|
repo_ref(&s)?
|
||||||
.as_ref()
|
|
||||||
.unwrap()
|
|
||||||
.get_lists()
|
.get_lists()
|
||||||
.map_err(|e| e.to_string())
|
.map_err(|e| e.to_string())
|
||||||
}
|
}
|
||||||
|
|
@ -179,9 +187,7 @@ fn create_list(
|
||||||
let mut s = lock_state(&state)?;
|
let mut s = lock_state(&state)?;
|
||||||
ensure_repo(&mut s)?;
|
ensure_repo(&mut s)?;
|
||||||
mute_watcher(&mut s);
|
mute_watcher(&mut s);
|
||||||
s.repo
|
repo_mut(&mut s)?
|
||||||
.as_mut()
|
|
||||||
.unwrap()
|
|
||||||
.create_list(name)
|
.create_list(name)
|
||||||
.map_err(|e| e.to_string())
|
.map_err(|e| e.to_string())
|
||||||
}
|
}
|
||||||
|
|
@ -195,9 +201,7 @@ fn delete_list(
|
||||||
ensure_repo(&mut s)?;
|
ensure_repo(&mut s)?;
|
||||||
mute_watcher(&mut s);
|
mute_watcher(&mut s);
|
||||||
let id = Uuid::parse_str(&list_id).map_err(|e| e.to_string())?;
|
let id = Uuid::parse_str(&list_id).map_err(|e| e.to_string())?;
|
||||||
s.repo
|
repo_mut(&mut s)?
|
||||||
.as_mut()
|
|
||||||
.unwrap()
|
|
||||||
.delete_list(id)
|
.delete_list(id)
|
||||||
.map_err(|e| e.to_string())
|
.map_err(|e| e.to_string())
|
||||||
}
|
}
|
||||||
|
|
@ -212,9 +216,7 @@ fn list_tasks(
|
||||||
let mut s = lock_state(&state)?;
|
let mut s = lock_state(&state)?;
|
||||||
ensure_repo(&mut s)?;
|
ensure_repo(&mut s)?;
|
||||||
let id = Uuid::parse_str(&list_id).map_err(|e| e.to_string())?;
|
let id = Uuid::parse_str(&list_id).map_err(|e| e.to_string())?;
|
||||||
s.repo
|
repo_ref(&s)?
|
||||||
.as_ref()
|
|
||||||
.unwrap()
|
|
||||||
.list_tasks(id)
|
.list_tasks(id)
|
||||||
.map_err(|e| e.to_string())
|
.map_err(|e| e.to_string())
|
||||||
}
|
}
|
||||||
|
|
@ -239,9 +241,7 @@ fn create_task(
|
||||||
let parent_uuid = Uuid::parse_str(&pid).map_err(|e| e.to_string())?;
|
let parent_uuid = Uuid::parse_str(&pid).map_err(|e| e.to_string())?;
|
||||||
task.parent_id = Some(parent_uuid);
|
task.parent_id = Some(parent_uuid);
|
||||||
}
|
}
|
||||||
s.repo
|
repo_mut(&mut s)?
|
||||||
.as_mut()
|
|
||||||
.unwrap()
|
|
||||||
.create_task(id, task)
|
.create_task(id, task)
|
||||||
.map_err(|e| e.to_string())
|
.map_err(|e| e.to_string())
|
||||||
}
|
}
|
||||||
|
|
@ -256,9 +256,7 @@ fn update_task(
|
||||||
ensure_repo(&mut s)?;
|
ensure_repo(&mut s)?;
|
||||||
mute_watcher(&mut s);
|
mute_watcher(&mut s);
|
||||||
let id = Uuid::parse_str(&list_id).map_err(|e| e.to_string())?;
|
let id = Uuid::parse_str(&list_id).map_err(|e| e.to_string())?;
|
||||||
s.repo
|
repo_mut(&mut s)?
|
||||||
.as_mut()
|
|
||||||
.unwrap()
|
|
||||||
.update_task(id, task)
|
.update_task(id, task)
|
||||||
.map_err(|e| e.to_string())
|
.map_err(|e| e.to_string())
|
||||||
}
|
}
|
||||||
|
|
@ -274,7 +272,7 @@ fn delete_task(
|
||||||
mute_watcher(&mut s);
|
mute_watcher(&mut s);
|
||||||
let lid = Uuid::parse_str(&list_id).map_err(|e| e.to_string())?;
|
let lid = Uuid::parse_str(&list_id).map_err(|e| e.to_string())?;
|
||||||
let tid = Uuid::parse_str(&task_id).map_err(|e| e.to_string())?;
|
let tid = Uuid::parse_str(&task_id).map_err(|e| e.to_string())?;
|
||||||
let repo = s.repo.as_mut().unwrap();
|
let repo = repo_mut(&mut s)?;
|
||||||
// Cascade-delete subtasks first
|
// Cascade-delete subtasks first
|
||||||
let all_tasks = repo.list_tasks(lid).map_err(|e| e.to_string())?;
|
let all_tasks = repo.list_tasks(lid).map_err(|e| e.to_string())?;
|
||||||
let child_ids: Vec<Uuid> = all_tasks
|
let child_ids: Vec<Uuid> = all_tasks
|
||||||
|
|
@ -300,7 +298,7 @@ fn toggle_task(
|
||||||
mute_watcher(&mut s);
|
mute_watcher(&mut s);
|
||||||
let lid = Uuid::parse_str(&list_id).map_err(|e| e.to_string())?;
|
let lid = Uuid::parse_str(&list_id).map_err(|e| e.to_string())?;
|
||||||
let tid = Uuid::parse_str(&task_id).map_err(|e| e.to_string())?;
|
let tid = Uuid::parse_str(&task_id).map_err(|e| e.to_string())?;
|
||||||
let repo = s.repo.as_mut().unwrap();
|
let repo = repo_mut(&mut s)?;
|
||||||
let mut task = repo.get_task(lid, tid).map_err(|e| e.to_string())?;
|
let mut task = repo.get_task(lid, tid).map_err(|e| e.to_string())?;
|
||||||
match task.status {
|
match task.status {
|
||||||
TaskStatus::Backlog => task.complete(),
|
TaskStatus::Backlog => task.complete(),
|
||||||
|
|
@ -334,9 +332,7 @@ fn reorder_task(
|
||||||
mute_watcher(&mut s);
|
mute_watcher(&mut s);
|
||||||
let lid = Uuid::parse_str(&list_id).map_err(|e| e.to_string())?;
|
let lid = Uuid::parse_str(&list_id).map_err(|e| e.to_string())?;
|
||||||
let tid = Uuid::parse_str(&task_id).map_err(|e| e.to_string())?;
|
let tid = Uuid::parse_str(&task_id).map_err(|e| e.to_string())?;
|
||||||
s.repo
|
repo_mut(&mut s)?
|
||||||
.as_mut()
|
|
||||||
.unwrap()
|
|
||||||
.reorder_task(lid, tid, new_position)
|
.reorder_task(lid, tid, new_position)
|
||||||
.map_err(|e| e.to_string())
|
.map_err(|e| e.to_string())
|
||||||
}
|
}
|
||||||
|
|
@ -356,9 +352,7 @@ fn move_task(
|
||||||
let from = Uuid::parse_str(&from_list_id).map_err(|e| e.to_string())?;
|
let from = Uuid::parse_str(&from_list_id).map_err(|e| e.to_string())?;
|
||||||
let to = Uuid::parse_str(&to_list_id).map_err(|e| e.to_string())?;
|
let to = Uuid::parse_str(&to_list_id).map_err(|e| e.to_string())?;
|
||||||
let tid = Uuid::parse_str(&task_id).map_err(|e| e.to_string())?;
|
let tid = Uuid::parse_str(&task_id).map_err(|e| e.to_string())?;
|
||||||
s.repo
|
repo_mut(&mut s)?
|
||||||
.as_mut()
|
|
||||||
.unwrap()
|
|
||||||
.move_task(from, to, tid)
|
.move_task(from, to, tid)
|
||||||
.map_err(|e| e.to_string())
|
.map_err(|e| e.to_string())
|
||||||
}
|
}
|
||||||
|
|
@ -373,9 +367,7 @@ fn rename_list(
|
||||||
ensure_repo(&mut s)?;
|
ensure_repo(&mut s)?;
|
||||||
mute_watcher(&mut s);
|
mute_watcher(&mut s);
|
||||||
let id = Uuid::parse_str(&list_id).map_err(|e| e.to_string())?;
|
let id = Uuid::parse_str(&list_id).map_err(|e| e.to_string())?;
|
||||||
s.repo
|
repo_mut(&mut s)?
|
||||||
.as_mut()
|
|
||||||
.unwrap()
|
|
||||||
.rename_list(id, new_name)
|
.rename_list(id, new_name)
|
||||||
.map_err(|e| e.to_string())
|
.map_err(|e| e.to_string())
|
||||||
}
|
}
|
||||||
|
|
@ -390,9 +382,7 @@ fn set_group_by_due_date(
|
||||||
ensure_repo(&mut s)?;
|
ensure_repo(&mut s)?;
|
||||||
mute_watcher(&mut s);
|
mute_watcher(&mut s);
|
||||||
let id = Uuid::parse_str(&list_id).map_err(|e| e.to_string())?;
|
let id = Uuid::parse_str(&list_id).map_err(|e| e.to_string())?;
|
||||||
s.repo
|
repo_mut(&mut s)?
|
||||||
.as_mut()
|
|
||||||
.unwrap()
|
|
||||||
.set_group_by_due_date(id, enabled)
|
.set_group_by_due_date(id, enabled)
|
||||||
.map_err(|e| e.to_string())
|
.map_err(|e| e.to_string())
|
||||||
}
|
}
|
||||||
|
|
@ -405,9 +395,7 @@ fn get_group_by_due_date(
|
||||||
let mut s = lock_state(&state)?;
|
let mut s = lock_state(&state)?;
|
||||||
ensure_repo(&mut s)?;
|
ensure_repo(&mut s)?;
|
||||||
let id = Uuid::parse_str(&list_id).map_err(|e| e.to_string())?;
|
let id = Uuid::parse_str(&list_id).map_err(|e| e.to_string())?;
|
||||||
s.repo
|
repo_ref(&s)?
|
||||||
.as_ref()
|
|
||||||
.unwrap()
|
|
||||||
.get_group_by_due_date(id)
|
.get_group_by_due_date(id)
|
||||||
.map_err(|e| e.to_string())
|
.map_err(|e| e.to_string())
|
||||||
}
|
}
|
||||||
|
|
@ -498,6 +486,10 @@ async fn sync_workspace(
|
||||||
|
|
||||||
#[cfg(not(target_os = "android"))]
|
#[cfg(not(target_os = "android"))]
|
||||||
fn start_watcher(handle: tauri::AppHandle, path: PathBuf) {
|
fn start_watcher(handle: tauri::AppHandle, path: PathBuf) {
|
||||||
|
// Stop any existing watcher before starting a new one
|
||||||
|
if let Ok(mut w) = WATCHER.lock() {
|
||||||
|
*w = None;
|
||||||
|
}
|
||||||
let handle = handle.clone();
|
let handle = handle.clone();
|
||||||
let debouncer = new_debouncer(
|
let debouncer = new_debouncer(
|
||||||
std::time::Duration::from_millis(500),
|
std::time::Duration::from_millis(500),
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"security": {
|
"security": {
|
||||||
"csp": "default-src 'self'; style-src 'self' 'unsafe-inline'; font-src 'self' https://fonts.gstatic.com; connect-src ipc: http://ipc.localhost"
|
"csp": "default-src 'self'; style-src 'self' 'unsafe-inline'; font-src 'self' https://fonts.gstatic.com; connect-src ipc: http://ipc.localhost; object-src 'none'; base-uri 'self'"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"bundle": {
|
"bundle": {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue