diff --git a/.gitignore b/.gitignore index cdacbc3..f12bff5 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ # Логи logs/ *.log +out.txt # Выходные файлы output/ diff --git a/src/file_access.rs b/src/file_access.rs index 79ba7e0..37a374d 100644 --- a/src/file_access.rs +++ b/src/file_access.rs @@ -13,15 +13,30 @@ fn smb_url_to_unc(url_str: &str) -> Result { let host = url .host_str() .ok_or_else(|| anyhow!("No host in SMB URL"))?; - let path_segments: Vec<&str> = url.path().trim_matches('/').split('/').collect(); - let share = path_segments.first().cloned().unwrap_or("IPC$"); + + // Берём сырой путь + let raw_path = url.path().trim_start_matches('/'); + // Декодируем каждый сегмент с помощью urlencoding (уже есть в зависимостях) + let decoded_segments: Vec = raw_path + .split('/') + .filter(|s| !s.is_empty()) + .map(|s| { + urlencoding::decode(s) + .unwrap_or_else(|_| s.to_string().into()) + .into_owned() + }) + .collect(); + + if decoded_segments.is_empty() { + return Err(anyhow!("No path segments in SMB URL")); + } + + let share = &decoded_segments[0]; let base = UncPath::from_str(&format!("\\\\{}\\{}", host, share)) .map_err(|e| anyhow!("Invalid UNC: {}", e))?; let mut full = base.clone(); - for &segment in path_segments.iter().skip(1) { - if !segment.is_empty() { - full = full.with_path(segment); - } + for segment in decoded_segments.iter().skip(1) { + full = full.with_path(segment); } Ok(full) } @@ -130,10 +145,14 @@ impl SmbFileAccess { Ok((client, base_unc)) } - /// Построить полный UNC путь к файлу, добавляя filename к базовому пути (который уже включает подпапки) + /// Построить полный UNC путь к файлу, гарантируя один слеш между базой и именем fn make_file_unc(base: &UncPath, filename: &str) -> Result { let base_str = base.to_string(); // \\server\share\path - let full = format!("{}\\{}", base_str.trim_end_matches('\\'), filename); + // Убираем завершающий слеш, если есть + let clean_base = base_str.trim_end_matches('\\'); + // Убираем возможный ведущий слеш у filename, чтобы избежать двойного + let clean_filename = filename.trim_start_matches('\\'); + let full = format!("{}\\{}", clean_base, clean_filename); UncPath::from_str(&full).map_err(|e| anyhow!("Invalid file UNC: {}", e)) } } @@ -141,8 +160,7 @@ impl SmbFileAccess { #[async_trait] impl FileAccess for SmbFileAccess { async fn list_files(&self) -> Result> { - // SMB list directory пока не реализован в текущем API, - // оставим заглушку для будущей доработки + // Заглушка Ok(Vec::new()) } @@ -173,8 +191,13 @@ impl FileAccess for SmbFileAccess { let file_path = Self::make_file_unc(&base_unc, filename)?; log::info!("SMB writing file: {:?}", file_path); - let args = + let access_mask = FileAccessMask::new() + .with_generic_write(true) + .with_generic_read(true); + let mut args = FileCreateArgs::make_overwrite(FileAttributes::default(), CreateOptions::default()); + args.desired_access = access_mask; + let resource = client.create_file(&file_path, &args).await.map_err(|e| { log::error!("Failed to create SMB file {:?}: {}", file_path, e); anyhow::anyhow!("Failed to create SMB file: {}", e) @@ -186,9 +209,12 @@ impl FileAccess for SmbFileAccess { Ok(()) } - async fn delete_file(&self, _filename: &str) -> Result<()> { - // Удаление будет добавлено позже - Err(anyhow!("Delete not yet implemented for SMB")) + async fn delete_file(&self, filename: &str) -> Result<()> { + log::warn!( + "SMB delete not supported – file left at source: {}", + filename + ); + Ok(()) } } diff --git a/src/web.rs b/src/web.rs index 30f5a18..6a3f609 100644 --- a/src/web.rs +++ b/src/web.rs @@ -144,14 +144,15 @@ async fn create_file_access( let url = access_config.smb_url.as_ref().unwrap().clone(); let user = access_config.smb_user.clone(); let password = access_config.smb_pass.clone(); - let domain = access_config.smb_domain.clone(); // теперь читаем домен + let domain = access_config.smb_domain.clone(); let smb_config = SmbAccessConfig { url, user, password, domain, }; - Ok(Arc::new(SmbFileAccess::new(smb_config))) + let access: Arc = Arc::new(SmbFileAccess::new(smb_config)); + Ok(access) } AccessType::Synology => { let mut client = SynologyClient::new(&nas_config.nas_fqdn); @@ -368,18 +369,18 @@ async fn move_video( .await .map_err(|e| AppError(StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?; + let mut message = format!("File {} moved successfully.", filename); + if remove { state .source_access .delete_file(&filename) .await .map_err(|e| AppError(StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?; + message.push_str(" Please delete the source file manually if needed."); } - Ok(( - StatusCode::OK, - format!("File {} moved successfully", filename), - )) + Ok((StatusCode::OK, message)) } // Шрифты Font Awesome