a.0.0.4
This commit is contained in:
1 parent
5723bfc532
commit
e88dc07696
5 files changed
+613
-104
No files matched your search
+143
-6
@@ -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))
|
||||
}
|
||||
Reference in new issue
Block a user