This commit is contained in:
netadmin committed 2026-10-23 14:51:33 +03:00
1 parent 5723bfc532
commit e88dc07696
5 files changed
+613 -104

No files matched your search

+143 -6
View File
@@ -1,6 +1,7 @@
use serde::{Deserialize, Serialize};
use std::{
collections::{HashMap, VecDeque},
net::Ipv4Addr,
sync::{Arc, RwLock},
time::{SystemTime, UNIX_EPOCH},
};
@@ -56,6 +57,30 @@ pub struct BeaconPacket {
pub fingerprint: String,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct TransferProgress {
pub id: String,
pub filename: String,
pub peer_name: String,
/// "out" (отправка) или "in" (прием)
pub direction: String,
pub transferred_bytes: usize,
pub total_bytes: usize,
pub percent: u8,
pub done: bool,
pub error: Option<String>,
pub updated_at: u64,
}
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct ScanProgress {
pub active: bool,
pub range: String,
pub scanned: usize,
pub total: usize,
pub found: usize,
}
pub struct AppState {
pub device_name: String,
pub my_ip: String,
@@ -64,11 +89,11 @@ pub struct AppState {
pub ping_interval_sec: u64,
pub ram_texts: RwLock<VecDeque<TextItem>>,
pub disk_media: RwLock<VecDeque<MediaItem>>,
/// Ключ таблицы — постоянный fingerprint (ID) устройства
pub peers: Arc<RwLock<HashMap<String, PeerDevice>>>,
pub http_client: reqwest::Client,
pub probe_client: reqwest::Client,
pub transfers: RwLock<Vec<String>>,
pub transfers: RwLock<Vec<TransferProgress>>,
pub scan_status: RwLock<ScanProgress>,
pub logs: RwLock<VecDeque<String>>,
}
@@ -83,13 +108,26 @@ impl AppState {
logs.push_front(format!("[{}] {}", now_sec(), text));
}
/// Добавляет или обновляет найденное устройство по его постоянному fingerprint
pub fn upsert_peer(&self, name: String, ip: String, port: u16, fingerprint: String) {
pub fn upsert_transfer(&self, item: TransferProgress) {
let mut list = self.transfers.write().unwrap();
if let Some(existing) = list.iter_mut().find(|t| t.id == item.id) {
*existing = item;
} else {
list.insert(0, item);
}
}
pub fn remove_transfer(&self, id: &str) {
let mut list = self.transfers.write().unwrap();
list.retain(|t| t.id != id);
}
pub fn upsert_peer(&self, name: String, ip: String, port: u16, fingerprint: String) -> bool {
if fingerprint == self.my_fingerprint || fingerprint.len() < 8 {
return;
return false;
}
if ip.is_empty() || ip == "0.0.0.0" || ip == "127.0.0.1" {
return;
return false;
}
let pin_code = fingerprint[..8].to_uppercase();
@@ -105,6 +143,7 @@ impl AppState {
existing.ips.remove(pos);
}
existing.ips.insert(0, ip);
false
} else {
drop(peers);
self.add_log(format!(
@@ -126,6 +165,7 @@ impl AppState {
last_seen: now,
},
);
true
}
}
}
@@ -155,6 +195,12 @@ pub struct UiManualPeerRequest {
pub port: Option<u16>,
}
#[derive(Deserialize)]
pub struct UiScanRangeRequest {
pub range: String,
pub port: Option<u16>,
}
pub fn now_sec() -> u64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
@@ -200,3 +246,94 @@ pub fn sanitize_filename(name: &str) -> String {
trimmed.to_string()
}
}
/// Парсер диапазонов IP-адресов:
/// Поддерживает: "all", "192.168.1.1-254", "192.168.1.10-50", "192.168.1.10-192.168.1.80", "192.168.1.0/24"
pub fn parse_ip_range(input: &str, my_ip: &str) -> Result<(String, Vec<String>), String> {
let raw = input.trim();
let effective = if raw.is_empty() || raw.eq_ignore_ascii_case("all") || raw.eq_ignore_ascii_case("full") {
let base: Ipv4Addr = my_ip
.parse()
.map_err(|_| "Не удалось определить подсеть автоматически. Введите диапазон вручную.".to_string())?;
let o = base.octets();
format!("{}.{}.{}.1-254", o[0], o[1], o[2])
} else {
raw.to_string()
};
let mut result = Vec::new();
for part in effective.split(',') {
let token = part.trim();
if token.is_empty() {
continue;
}
if let Some(cidr_base) = token.strip_suffix("/24") {
let ip: Ipv4Addr = cidr_base
.trim()
.parse()
.map_err(|_| format!("Некорректный CIDR адрес: {}", token))?;
let o = ip.octets();
for last in 1..=254u8 {
result.push(format!("{}.{}.{}.{}", o[0], o[1], o[2], last));
}
} else if let Some((left, right)) = token.split_once('-') {
let left = left.trim();
let right = right.trim();
let start_ip: Ipv4Addr = left
.parse()
.map_err(|_| format!("Некорректный начальный IP: {}", left))?;
if right.contains('.') {
let end_ip: Ipv4Addr = right
.parse()
.map_err(|_| format!("Некорректный конечный IP: {}", right))?;
let start_u = u32::from(start_ip);
let end_u = u32::from(end_ip);
if end_u < start_u {
return Err("Конечный IP меньше начального".into());
}
if end_u - start_u > 1024 {
return Err("Диапазон слишком велик (максимум 1024 адреса за раз)".into());
}
for val in start_u..=end_u {
let ip = Ipv4Addr::from(val);
let last = ip.octets()[3];
if last != 0 && last != 255 {
result.push(ip.to_string());
}
}
} else {
let end_last: u8 = right
.parse()
.map_err(|_| format!("Некорректный конец диапазона: {}", right))?;
let o = start_ip.octets();
let start_last = o[3];
if end_last < start_last {
return Err(format!("Конец диапазона ({}) меньше начала ({})", end_last, start_last));
}
for last in start_last..=end_last {
if last != 0 && last != 255 {
result.push(format!("{}.{}.{}.{}", o[0], o[1], o[2], last));
}
}
}
} else {
let ip: Ipv4Addr = token
.parse()
.map_err(|_| format!("Некорректный IP-адрес: {}", token))?;
result.push(ip.to_string());
}
}
result.retain(|ip| ip != my_ip && ip != "127.0.0.1" && ip != "0.0.0.0");
result.dedup();
if result.is_empty() {
return Err("Диапазон не содержит доступных для сканирования IP-адресов".into());
}
Ok((effective, result))
}