perf(api,indexer): optimiser pages, thumbnails, watcher et robustesse fd

- Pages: mode Original (zero-transcoding), ETag/304, cache index CBZ,
  préfetch next 2 pages, filtre Triangle par défaut
- Thumbnails: DCT scaling JPEG via jpeg-decoder (decode 7x plus rapide),
  img.thumbnail() pour resize, support format Original, fix JPEG RGBA8
- API fallback thumbnail: OutputFormat::Original + DCT scaling au lieu
  de WebP full-decode, retour (bytes, content_type) dynamique
- Watcher: remplacement notify par poll léger sans inotify/fd,
  skip poll quand job actif, snapshots en mémoire
- Jobs: mutex exclusif corrigé (tous statuts actifs, tous types exclusifs)
- Robustesse: suppression fs::canonicalize (problèmes fd Docker),
  list_folders avec erreurs explicites, has_children default true
- Backoffice: FormRow items-start pour alignement inputs avec helper text,
  labels settings clarifiés

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-14 23:07:42 +01:00
parent fe54f55f47
commit 6947af10fe
15 changed files with 711 additions and 395 deletions

View File

@@ -156,14 +156,19 @@ fn canonicalize_library_root(root_path: &str) -> Result<PathBuf, ApiError> {
return Err(ApiError::bad_request("root_path must be absolute"));
}
let canonical = std::fs::canonicalize(path)
.map_err(|_| ApiError::bad_request("root_path does not exist or is inaccessible"))?;
if !canonical.is_dir() {
// Avoid fs::canonicalize — it opens extra file descriptors to resolve symlinks
// and can fail on Docker volume mounts (ro, cached) when fd limits are low.
if !path.exists() {
return Err(ApiError::bad_request(format!(
"root_path does not exist: {}",
root_path
)));
}
if !path.is_dir() {
return Err(ApiError::bad_request("root_path must point to a directory"));
}
Ok(canonical)
Ok(path.to_path_buf())
}
use crate::index_jobs::{IndexJobResponse, RebuildRequest};