refactor(server): extract locate_case_or_404 helper
Consolidate 5 instances of "locate_case + warn! + 404-return" boilerplate (case_actions.rs x4, user_web.rs x1) into a single pub(crate) helper in user_web.rs. The helper takes &CaseId (no String allocation at call sites) plus a context tag that becomes a structured-log field instead of being baked into the message. bulk.rs keeps using the underlying locate_case because its miss semantics differ (silent skip, not 404).
This commit is contained in:
@@ -11,7 +11,7 @@ use time::{format_description::well_known::Rfc3339, Date, OffsetDateTime};
|
||||
|
||||
use crate::analyze::{recovery as analyze_recovery, AnalyzeSender, ANALYSIS_INPUT_FILE, DOCUMENT_FILE};
|
||||
use crate::auth::AuthenticatedWebUser;
|
||||
use crate::case_id::CaseIdPath;
|
||||
use crate::case_id::{CaseId, CaseIdPath};
|
||||
use crate::config::Config;
|
||||
use crate::error::AppError;
|
||||
use crate::routes::web::{scan_recordings, RecordingView};
|
||||
@@ -278,14 +278,14 @@ pub async fn handle_case_detail(
|
||||
)
|
||||
.await;
|
||||
|
||||
let case_dir = locate_case_or_404(
|
||||
&user_root,
|
||||
&case_id,
|
||||
&user.slug,
|
||||
"case detail (possible IDOR probe)",
|
||||
)
|
||||
.await?;
|
||||
let case_id_str = case_id.to_string();
|
||||
let case_dir = match locate_case(&user_root, &case_id_str).await {
|
||||
Some(p) => p,
|
||||
None => {
|
||||
warn!(slug = %user.slug, case_id = %case_id, "case detail: not found (possible IDOR probe)");
|
||||
return Err(AppError::NotFound("Case not found".into()));
|
||||
}
|
||||
};
|
||||
info!(slug = %user.slug, case_id = %case_id, "case detail viewed");
|
||||
|
||||
let recordings = scan_recordings(&case_dir).await;
|
||||
@@ -333,6 +333,32 @@ pub(crate) async fn locate_case(user_root: &Path, case_id: &str) -> Option<std::
|
||||
Some(p)
|
||||
}
|
||||
|
||||
/// Resolve a case directory for a handler, mapping any miss to
|
||||
/// `AppError::NotFound("Case not found")` — matches the legacy 404 body
|
||||
/// byte-for-byte. The `context` tag distinguishes log lines from the
|
||||
/// different handlers (analyze, delete, reset, ...). Callers that need
|
||||
/// a different not-found policy (silent skip in bulk ops) should keep
|
||||
/// using `locate_case` directly.
|
||||
pub(crate) async fn locate_case_or_404(
|
||||
user_root: &Path,
|
||||
case_id: &CaseId,
|
||||
user_slug: &str,
|
||||
context: &str,
|
||||
) -> Result<std::path::PathBuf, AppError> {
|
||||
match locate_case(user_root, &case_id.to_string()).await {
|
||||
Some(p) => Ok(p),
|
||||
None => {
|
||||
warn!(
|
||||
slug = %user_slug,
|
||||
case_id = %case_id,
|
||||
context = %context,
|
||||
"case not found",
|
||||
);
|
||||
Err(AppError::NotFound("Case not found".into()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Scan all of the given user's cases. Returned vec is sorted by most
|
||||
/// recent recording filename (lexicographic ≈ chronological since
|
||||
/// timestamps are ISO-8601), newest first. Soft-deleted cases (with
|
||||
|
||||
Reference in New Issue
Block a user