Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 18 additions & 6 deletions filepi-server/src/handlers/files.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,12 @@ pub async fn get_files(
let entry_path = entry.path();

// Create FileInfo with absolute path and current directory context
files.push(FileInfo::from_path(&entry_path, &full_path).map_err(|e| {
error!("Error creating FileInfo: {}", e);
AppError::InternalError(format!("Failed to read file info: {}", e))
})?);
files.push(
FileInfo::from_path(&entry_path, &full_path, &config.root_dir).map_err(|e| {
error!("Error creating FileInfo: {}", e);
AppError::InternalError(format!("Failed to read file info: {}", e))
})?,
);
}

result_handler::format_result(&mut files, &params)
Expand Down Expand Up @@ -194,7 +196,12 @@ pub async fn get_videos(
continue;
}

video_files.push(FileInfo::from_path(&file_path, &full_path).unwrap());
video_files.push(
FileInfo::from_path(&file_path, &full_path, &config.root_dir).map_err(|e| {
error!("Error creating FileInfo: {}", e);
AppError::InternalError(format!("Failed to read file info: {}", e))
})?,
);
}

result_handler::format_result(&mut video_files, &params)
Expand Down Expand Up @@ -282,7 +289,12 @@ pub async fn search(
continue;
}

matching_files.push(FileInfo::from_path(&file_path, &path).unwrap());
matching_files.push(
FileInfo::from_path(&file_path, &full_path, &config.root_dir).map_err(|e| {
error!("Error creating FileInfo: {}", e);
AppError::InternalError(format!("Failed to read file info: {}", e))
})?,
);
}

result_handler::format_result(&mut matching_files, &params)
Expand Down
37 changes: 29 additions & 8 deletions filepi-server/src/models/file_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,14 @@ pub struct FileInfo {
}

impl FileInfo {
pub fn from_path<P: AsRef<Path>, T: AsRef<Path>>(
pub fn from_path<P: AsRef<Path>, C: AsRef<Path>, R: AsRef<Path>>(
absolute_path: P,
current_dir: T,
current_dir: C,
root_dir: R,
) -> std::io::Result<Self> {
let path = absolute_path.as_ref();
let current = current_dir.as_ref();
let root = root_dir.as_ref();
let metadata = fs::metadata(path)?;

// Basic info
Expand All @@ -32,7 +35,12 @@ impl FileInfo {
.map(|n| n.to_string_lossy().to_string())
.unwrap_or_default();

let full_name = String::from(path.to_str().unwrap());
// full_name should be relative to current_dir (without leading /)
let full_name = path
.strip_prefix(current)
.ok()
.map(|rel| rel.to_string_lossy().to_string())
.unwrap_or_else(|| String::from(path.to_str().unwrap()));
Comment on lines +38 to +43

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Consider handling non-UTF-8 paths more gracefully.

The fallback uses .unwrap() which will panic if the path contains invalid UTF-8 sequences. While rare, this could occur with certain filesystem edge cases.

Apply this diff for safer error handling:

         let full_name = path
             .strip_prefix(current)
             .ok()
             .map(|rel| rel.to_string_lossy().to_string())
-            .unwrap_or_else(|| String::from(path.to_str().unwrap()));
+            .unwrap_or_else(|| path.to_string_lossy().to_string());
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// full_name should be relative to current_dir (without leading /)
let full_name = path
.strip_prefix(current)
.ok()
.map(|rel| rel.to_string_lossy().to_string())
.unwrap_or_else(|| String::from(path.to_str().unwrap()));
// full_name should be relative to current_dir (without leading /)
let full_name = path
.strip_prefix(current)
.ok()
.map(|rel| rel.to_string_lossy().to_string())
.unwrap_or_else(|| path.to_string_lossy().to_string());
🤖 Prompt for AI Agents
In filepi-server/src/models/file_info.rs around lines 38 to 43, the fallback
branch uses path.to_str().unwrap() which will panic on non-UTF-8 paths; replace
the unwrap with a safe conversion (e.g., use path.to_string_lossy().to_string()
or otherwise handle the non-UTF-8 case) so the code never panics on invalid
UTF-8; optionally log or propagate an error when lossy conversion occurs if you
need to track that condition.


let size = match get_size(path) {
Ok(size) => size,
Expand All @@ -49,7 +57,17 @@ impl FileInfo {
.created()
.ok()
.and_then(|t| t.duration_since(UNIX_EPOCH).ok())
.map(|d| d.as_millis());
.map(|d| d.as_millis())
.or_else(|| {
// Fallback to ctime on Unix systems
#[cfg(unix)]
{
use std::os::unix::fs::MetadataExt;
Some(metadata.ctime() as u128 * 1000)
}
#[cfg(not(unix))]
None
});

let modified_time = metadata
.modified()
Expand All @@ -62,12 +80,15 @@ impl FileInfo {
// Owner info (Unix/Linux only)
let owner = get_file_owner(path);

// Parent directory
let parent_dir = path.parent().map(|p| p.to_string_lossy().to_string());
// Parent directory is current_dir (relative to root_dir without leading /)
let parent_dir = current
.strip_prefix(root)
.ok()
.map(|rel| rel.to_string_lossy().to_string());

// Relative path from current directory
// Relative path from root_dir to file (without leading /)
let rel_path = path
.strip_prefix(&current_dir)
.strip_prefix(root)
.ok()
.map(|rel| rel.to_string_lossy().to_string());

Expand Down