version a0.0.1

This commit is contained in:
netadmin committed 2026-10-22 20:45:13 +03:00
commit 9469ef10b6
19 files changed
+4281

No files matched your search

+123
View File
@@ -0,0 +1,123 @@
use serde::{Deserialize, Serialize};
use std::{
collections::{HashMap, VecDeque},
sync::{Arc, RwLock},
time::{SystemTime, UNIX_EPOCH},
};
/// Лимит одного передаваемого медиафайла (200 МБ)
pub const MAX_PAYLOAD_SIZE: usize = 200 * 1024 * 1024;
/// Максимум текстовых сообщений, хранимых в ОЗУ
pub const MAX_RAM_TEXTS: usize = 50;
/// Папка на диске для сохранения входящего медиаконтента
pub const MEDIA_DIR: &str = "received_media";
/// Тип сервиса для обнаружения через mDNS
pub const SERVICE_TYPE: &str = "_ramshare._tcp.local.";
/// Текстовое сообщение (хранится ТОЛЬКО в ОЗУ)
#[derive(Clone, Serialize, Deserialize)]
pub struct TextItem {
pub sender: String,
pub text: String,
pub timestamp: u64,
}
/// Метаданные медиафайла (сам файл сохраняется НА ДИСК)
#[derive(Clone, Serialize, Deserialize)]
pub struct MediaItem {
pub sender: String,
pub filename: String,
pub saved_path: String,
pub mime: String,
pub size: usize,
pub timestamp: u64,
}
/// Устройство в локальной сети, найденное через mDNS
#[derive(Clone, Serialize, Deserialize)]
pub struct PeerNode {
pub name: String,
pub ip: String,
pub port: u16,
pub fingerprint: String,
pub pin_code: String,
pub verified: bool,
}
/// Глобальное состояние приложения
pub struct AppState {
pub device_name: String,
pub my_ip: String,
pub p2p_port: u16,
pub my_fingerprint: String,
pub ram_texts: RwLock<VecDeque<TextItem>>,
pub disk_media: RwLock<VecDeque<MediaItem>>,
pub peers: RwLock<HashMap<String, PeerNode>>,
pub trusted_fingerprints: Arc<RwLock<HashMap<String, String>>>,
pub http_client: reqwest::Client,
}
#[derive(Serialize, Deserialize)]
pub struct IncomingTextPayload {
pub sender: String,
pub text: String,
}
#[derive(Deserialize)]
pub struct UiSendTextRequest {
pub target_ip: String,
pub text: String,
}
#[derive(Deserialize)]
pub struct UiTrustRequest {
pub target_ip: String,
pub trust: bool,
}
pub fn now_sec() -> u64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_secs()
}
/// Декодирование URL-encoded имени файла (для поддержки кириллицы и пробелов)
pub fn url_decode(input: &str) -> String {
let mut bytes = Vec::with_capacity(input.len());
let mut chars = input.as_bytes().iter().copied();
while let Some(b) = chars.next() {
if b == b'%' {
if let (Some(c1), Some(c2)) = (chars.next(), chars.next()) {
if let Ok(decoded) = u8::from_str_radix(&format!("{}{}", c1 as char, c2 as char), 16)
{
bytes.push(decoded);
continue;
}
}
} else if b == b'+' {
bytes.push(b' ');
} else {
bytes.push(b);
}
}
String::from_utf8_lossy(&bytes).into_owned()
}
/// Защита от Path Traversal при сохранении медиа на диск
pub fn sanitize_filename(name: &str) -> String {
let decoded = url_decode(name);
let clean: String = decoded
.chars()
.map(|c| match c {
'/' | '\\' | ':' | '*' | '?' | '"' | '<' | '>' | '|' | '\0' => '_',
_ => c,
})
.collect();
let trimmed = clean.trim_matches('.').trim();
if trimmed.is_empty() {
"media_file.bin".to_string()
} else {
trimmed.to_string()
}
}