Initial project import
This commit is contained in:
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
/target
|
||||||
|
.env
|
||||||
|
.DS_Store
|
||||||
|
*.xlsx
|
||||||
|
*.osheet
|
||||||
|
*.json
|
||||||
1480
Cargo.lock
generated
Normal file
1480
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
17
Cargo.toml
Normal file
17
Cargo.toml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
[package]
|
||||||
|
name = "ae_anons"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
reqwest = { version = "0.11", features = ["json", "multipart"] }
|
||||||
|
serde = { version = "1.0.228", features = ["derive"] }
|
||||||
|
serde_json = "1.0.149"
|
||||||
|
thiserror = "2.0.18"
|
||||||
|
urlencoding = "2.1.3"
|
||||||
|
tokio = { version = "1.51.1", features = ["full"] }
|
||||||
|
csv = "1.4.0"
|
||||||
|
dotenv = "0.15.0"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
tokio = { version = "1.51.1", features = ["full", "rt-multi-thread"] }
|
||||||
1357
src/lib.rs
Normal file
1357
src/lib.rs
Normal file
File diff suppressed because it is too large
Load Diff
133
src/main.rs
Normal file
133
src/main.rs
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
use dotenv::dotenv;
|
||||||
|
use std::env;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::Write;
|
||||||
|
use std::path::Path;
|
||||||
|
use serde_json::Value;
|
||||||
|
|
||||||
|
use ae_anons::{SynologyClient, SynologyError};
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> Result<(), SynologyError> {
|
||||||
|
dotenv().ok();
|
||||||
|
|
||||||
|
let nas_fqdn = env::var("NAS_FQDN").expect("❌ Переменная NAS_FQDN не задана в .env");
|
||||||
|
let nas_user = env::var("NAS_USER").expect("❌ Переменная NAS_USER не задана в .env");
|
||||||
|
let nas_pass = env::var("NAS_PASS").expect("❌ Переменная NAS_PASS не задана в .env");
|
||||||
|
let nas_file = env::var("NAS_FILE").expect("❌ Переменная NAS_FILE не задана в .env");
|
||||||
|
|
||||||
|
let mut client = SynologyClient::new(&nas_fqdn);
|
||||||
|
|
||||||
|
client.login(&nas_user, &nas_pass).await?;
|
||||||
|
println!("✅ Logged in");
|
||||||
|
|
||||||
|
let info = client.get_info().await?;
|
||||||
|
println!("📡 Connected to: {}", info.hostname);
|
||||||
|
|
||||||
|
// Получаем имя файла и ожидаемый путь
|
||||||
|
let file_name_full = Path::new(&nas_file).file_name().unwrap().to_str().unwrap();
|
||||||
|
let search_name = file_name_full.replace(".osheet", "");
|
||||||
|
let expected_path = Path::new(&nas_file).parent().unwrap().to_str().unwrap();
|
||||||
|
|
||||||
|
// Преобразуем путь в формат API (Team Folder -> team-folders, убираем пробелы, приводим к нижнему регистру)
|
||||||
|
let api_format_path = expected_path
|
||||||
|
.replace("Team Folder", "team-folders")
|
||||||
|
.replace(" ", "");
|
||||||
|
|
||||||
|
let full_expected_path_api = format!("{}/{}", api_format_path, file_name_full);
|
||||||
|
|
||||||
|
println!("\n🔍 Looking for exact file: {}", full_expected_path_api);
|
||||||
|
println!(" (converted from: {})", nas_file);
|
||||||
|
|
||||||
|
// Ищем файл по имени
|
||||||
|
let search_result = client.search_file_by_name(&search_name).await?;
|
||||||
|
|
||||||
|
// Извлекаем все найденные файлы
|
||||||
|
let items = search_result["data"]["items"].as_array()
|
||||||
|
.ok_or_else(|| SynologyError::InvalidResponseFormat("No items in search result".to_string()))?;
|
||||||
|
|
||||||
|
if items.is_empty() {
|
||||||
|
return Err(SynologyError::Api {
|
||||||
|
code: 404,
|
||||||
|
message: Some(format!("File '{}' not found", file_name_full)),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ищем файл с точным совпадением пути (в формате API)
|
||||||
|
let exact_match: Vec<&Value> = items.iter()
|
||||||
|
.filter(|item| {
|
||||||
|
let display_path = item.get("display_path")
|
||||||
|
.and_then(|p| p.as_str())
|
||||||
|
.unwrap_or("");
|
||||||
|
display_path == full_expected_path_api
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if exact_match.is_empty() {
|
||||||
|
println!("\n⚠️ File not found at exact path: {}", full_expected_path_api);
|
||||||
|
println!(" Found {} file(s) with similar name:", items.len());
|
||||||
|
|
||||||
|
for (i, item) in items.iter().enumerate() {
|
||||||
|
let name = item.get("name")
|
||||||
|
.and_then(|n| n.as_str())
|
||||||
|
.unwrap_or("unknown");
|
||||||
|
let display_path = item.get("display_path")
|
||||||
|
.and_then(|p| p.as_str())
|
||||||
|
.unwrap_or("unknown");
|
||||||
|
let file_id = item.get("file_id")
|
||||||
|
.and_then(|id| id.as_str())
|
||||||
|
.unwrap_or("unknown");
|
||||||
|
println!(" {}. {} (at: {}, ID: {})", i + 1, name, display_path, file_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Err(SynologyError::Api {
|
||||||
|
code: 400,
|
||||||
|
message: Some(format!("File not found at: {}", full_expected_path_api)),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if exact_match.len() > 1 {
|
||||||
|
println!("\n⚠️ Found {} files at the same location!", exact_match.len());
|
||||||
|
return Err(SynologyError::Api {
|
||||||
|
code: 400,
|
||||||
|
message: Some("Multiple files found at the same location".to_string()),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Берём единственный подходящий файл
|
||||||
|
let file = exact_match[0];
|
||||||
|
let file_id = file["file_id"]
|
||||||
|
.as_str()
|
||||||
|
.ok_or_else(|| SynologyError::InvalidResponseFormat("Missing file_id".to_string()))?;
|
||||||
|
|
||||||
|
let actual_file_name = file["name"]
|
||||||
|
.as_str()
|
||||||
|
.ok_or_else(|| SynologyError::InvalidResponseFormat("Missing name".to_string()))?;
|
||||||
|
|
||||||
|
let actual_path = file["display_path"]
|
||||||
|
.as_str()
|
||||||
|
.unwrap_or("unknown");
|
||||||
|
|
||||||
|
println!(" 📄 Found exact match: {} at {}", actual_file_name, actual_path);
|
||||||
|
println!(" 📄 File ID: {}", file_id);
|
||||||
|
|
||||||
|
// Экспортируем файл
|
||||||
|
println!("\n📊 Exporting file...");
|
||||||
|
|
||||||
|
match client.export_by_file_id(file_id, actual_file_name).await {
|
||||||
|
Ok(data) => {
|
||||||
|
let output_path = format!("./{}_export.xlsx", search_name);
|
||||||
|
let mut file = File::create(&output_path)?;
|
||||||
|
file.write_all(&data)?;
|
||||||
|
println!(" ✅ Exported to: {} ({} bytes)", output_path, data.len());
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
println!(" ❌ Export failed: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
client.logout().await?;
|
||||||
|
println!("\n👋 Logged out");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user