Refactor: Simplify case directory structure
The distinction between `open/` and `done/` directories for cases has been removed. All cases for a user now reside directly under the user's directory (e.g., `<data_path>/<slug>/<case_id>/`). This simplifies path management and eliminates redundant directory traversals. Key changes include: - Removed `open/` and `done/` subdirectories in path resolution. - Introduced a `paths::case_dir` function as a single source of truth for case directory layout. - Updated various modules (`recovery`, `auth`, `routes`, `transcribe`, `tests`) to use the new path structure. - Adjusted templates to reflect the simplified case status representation.
This commit is contained in:
+47
-54
@@ -10,6 +10,7 @@ use tracing::warn;
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::error::AppError;
|
||||
use crate::routes::user_web::any_document_exists;
|
||||
|
||||
pub(crate) struct RecordingView {
|
||||
pub(crate) filename: String,
|
||||
@@ -23,7 +24,7 @@ struct CaseView {
|
||||
user_slug: String,
|
||||
case_id: String,
|
||||
case_id_short: String,
|
||||
status: String,
|
||||
has_document: bool,
|
||||
most_recent: String,
|
||||
oneliner: Option<String>,
|
||||
recordings: Vec<RecordingView>,
|
||||
@@ -55,17 +56,10 @@ pub async fn handle_audio(
|
||||
.map_err(|_| AppError::BadRequest("Invalid case_id (not a UUID)".into()))?;
|
||||
validate_filename(&filename)?;
|
||||
|
||||
let base = &config.data_path;
|
||||
let open_path = base.join(&user).join("open").join(&case_id).join(&filename);
|
||||
let done_path = base.join(&user).join("done").join(&case_id).join(&filename);
|
||||
|
||||
let file_path = if tokio::fs::try_exists(&open_path).await.unwrap_or(false) {
|
||||
open_path
|
||||
} else if tokio::fs::try_exists(&done_path).await.unwrap_or(false) {
|
||||
done_path
|
||||
} else {
|
||||
let file_path = crate::paths::case_dir(&config.data_path, &user, &case_id).join(&filename);
|
||||
if !tokio::fs::try_exists(&file_path).await.unwrap_or(false) {
|
||||
return Err(AppError::NotFound("Audio file not found".into()));
|
||||
};
|
||||
}
|
||||
|
||||
let bytes = tokio::fs::read(&file_path).await?;
|
||||
|
||||
@@ -116,52 +110,51 @@ async fn scan_cases(data_path: &Path) -> Vec<CaseView> {
|
||||
continue;
|
||||
}
|
||||
|
||||
for status in ["open", "done"] {
|
||||
let status_dir = user_entry.path().join(status);
|
||||
let mut case_entries = match tokio::fs::read_dir(&status_dir).await {
|
||||
Ok(r) => r,
|
||||
let mut case_entries = match tokio::fs::read_dir(user_entry.path()).await {
|
||||
Ok(r) => r,
|
||||
Err(_) => continue,
|
||||
};
|
||||
|
||||
while let Ok(Some(case_entry)) = case_entries.next_entry().await {
|
||||
let case_id = match case_entry.file_name().into_string() {
|
||||
Ok(s) => s,
|
||||
Err(_) => continue,
|
||||
};
|
||||
|
||||
while let Ok(Some(case_entry)) = case_entries.next_entry().await {
|
||||
let case_id = match case_entry.file_name().into_string() {
|
||||
Ok(s) => s,
|
||||
Err(_) => continue,
|
||||
};
|
||||
if uuid::Uuid::parse_str(&case_id).is_err() {
|
||||
warn!(case_id = %case_id, "Skipping non-UUID directory");
|
||||
continue;
|
||||
}
|
||||
if !case_entry.path().is_dir() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let recordings = scan_recordings(&case_entry.path()).await;
|
||||
if recordings.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let most_recent = recordings
|
||||
.last()
|
||||
.map(|r| r.filename.clone())
|
||||
.unwrap_or_default();
|
||||
let case_id_short = case_id.chars().take(8).collect();
|
||||
let oneliner = tokio::fs::read_to_string(case_entry.path().join("oneliner.txt"))
|
||||
.await
|
||||
.ok()
|
||||
.map(|s| s.trim().to_string())
|
||||
.filter(|s| !s.is_empty());
|
||||
|
||||
cases.push(CaseView {
|
||||
user_slug: user_slug.clone(),
|
||||
case_id,
|
||||
case_id_short,
|
||||
status: status.to_string(),
|
||||
most_recent,
|
||||
oneliner,
|
||||
recordings,
|
||||
});
|
||||
if uuid::Uuid::parse_str(&case_id).is_err() {
|
||||
warn!(case_id = %case_id, "Skipping non-UUID directory");
|
||||
continue;
|
||||
}
|
||||
if !case_entry.path().is_dir() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let case_path = case_entry.path();
|
||||
let recordings = scan_recordings(&case_path).await;
|
||||
if recordings.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let most_recent = recordings
|
||||
.last()
|
||||
.map(|r| r.filename.clone())
|
||||
.unwrap_or_default();
|
||||
let case_id_short = case_id.chars().take(8).collect();
|
||||
let oneliner = tokio::fs::read_to_string(case_path.join("oneliner.txt"))
|
||||
.await
|
||||
.ok()
|
||||
.map(|s| s.trim().to_string())
|
||||
.filter(|s| !s.is_empty());
|
||||
let has_document = any_document_exists(&case_path).await;
|
||||
|
||||
cases.push(CaseView {
|
||||
user_slug: user_slug.clone(),
|
||||
case_id,
|
||||
case_id_short,
|
||||
has_document,
|
||||
most_recent,
|
||||
oneliner,
|
||||
recordings,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user