SMB чтение/запись работают
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -5,6 +5,7 @@
|
||||
# Логи
|
||||
logs/
|
||||
*.log
|
||||
out.txt
|
||||
|
||||
# Выходные файлы
|
||||
output/
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
13
src/web.rs
13
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<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
|
||||
|
||||
Reference in New Issue
Block a user