feat(hardening): production logging, single-instance, CSP, and scoped asset protocol

Phase 4 of the 0.1.0 release prep:
- tauri-plugin-log: rotating file log (5 MB, Info) in the app log dir plus
  stdout; all 54 backend println!/eprintln! sites migrated to log macros so
  release builds finally produce diagnostics
- tauri-plugin-single-instance (registered first): second launches focus the
  existing window instead of racing the same SQLite DB with a second worker
  fleet; tauri-plugin-window-state persists window geometry
- real CSP replacing null (scripts self-only, media/images via the asset
  protocol, IPC endpoints whitelisted, object-src none) with a devCsp
  variant for Vite HMR
- asset protocol scope narrowed from '**' to thumbnails statically plus
  per-folder runtime grants (setup, add_folder, update_folder_path)

Panic audit (plan item) concluded with no changes: worker loops already
catch and log all Result errors; remaining unwraps are startup fail-fast,
poisoned-lock convention, infallible-by-construction, or test code.
This commit is contained in:
2026-06-12 20:12:03 +01:00
parent 890c23bdce
commit 3fb4a9685f
10 changed files with 454 additions and 61 deletions
+17
View File
@@ -227,6 +227,14 @@ pub async fn add_folder(
return Err("Path is not a valid directory".into());
}
// Let the webview load media from this folder via the asset protocol.
if let Err(error) = app
.asset_protocol_scope()
.allow_directory(&folder_path, true)
{
log::error!("Failed to allow asset scope for {}: {}", path, error);
}
let name = folder_path
.file_name()
.map(|n| n.to_string_lossy().to_string())
@@ -397,6 +405,15 @@ pub async fn update_folder_path(
if !new_path_buf.is_dir() {
return Err(format!("Path is not a valid directory: {}", new_path));
}
// Let the webview load media from the relocated folder via the asset protocol.
if let Err(error) = app
.asset_protocol_scope()
.allow_directory(&new_path_buf, true)
{
log::error!("Failed to allow asset scope for {}: {}", new_path, error);
}
let new_name = new_path_buf
.file_name()
.map(|n| n.to_string_lossy().to_string())