SMB чтение/запись работают

This commit is contained in:
2026-05-07 12:58:09 +03:00
parent 6ec569f6e6
commit a3fc5e2940
3 changed files with 48 additions and 20 deletions

1
.gitignore vendored
View File

@@ -5,6 +5,7 @@
# Логи
logs/
*.log
out.txt
# Выходные файлы
output/

View File

@@ -13,16 +13,31 @@ fn smb_url_to_unc(url_str: &str) -> Result<UncPath> {
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<String> = 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() {
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<UncPath> {
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<Vec<String>> {
// 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(())
}
}

View File

@@ -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<dyn FileAccess> = 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