mod discovery; mod models; mod p2p; mod tls; mod ui; use axum::{ extract::DefaultBodyLimit, routing::{get, post}, Router, }; use axum_server::tls_rustls::RustlsConfig; use local_ip_address::local_ip; use models::{AppState, MAX_PAYLOAD_SIZE, MAX_RAM_TEXTS, MEDIA_DIR}; use std::{ collections::{HashMap, VecDeque}, net::SocketAddr, sync::{Arc, RwLock}, }; #[tokio::main] async fn main() { // 0. Регистрация глобального CryptoProvider (ring) для rustls 0.23+ let _ = rustls::crypto::ring::default_provider().install_default(); let args: Vec = std::env::args().collect(); let mut device_name = "My-Device".to_string(); let mut p2p_port: u16 = 8443; let mut ui_port: u16 = 8080; let mut custom_ip: Option = None; let mut i = 1; let mut positional_idx = 0; while i < args.len() { match args[i].as_str() { "--ip" => { if i + 1 < args.len() { custom_ip = Some(args[i + 1].clone()); i += 1; } } "--name" => { if i + 1 < args.len() { device_name = args[i + 1].clone(); i += 1; } } "--p2p-port" => { if i + 1 < args.len() { if let Ok(p) = args[i + 1].parse() { p2p_port = p; } i += 1; } } "--ui-port" => { if i + 1 < args.len() { if let Ok(p) = args[i + 1].parse() { ui_port = p; } i += 1; } } arg if !arg.starts_with("--") => { match positional_idx { 0 => device_name = arg.to_string(), 1 => { if let Ok(p) = arg.parse() { p2p_port = p; } } 2 => { if let Ok(p) = arg.parse() { ui_port = p; } } 3 => custom_ip = Some(arg.to_string()), _ => {} } positional_idx += 1; } _ => {} } i += 1; } let (bind_ip, my_ip) = match custom_ip { Some(ip) => { if ip == "0.0.0.0" { let detected = local_ip() .map(|addr| addr.to_string()) .unwrap_or_else(|_| "127.0.0.1".to_string()); ("0.0.0.0".to_string(), detected) } else { (ip.clone(), ip) } } None => { let detected = local_ip().expect("Не удалось определить локальный IP").to_string(); ("0.0.0.0".to_string(), detected) } }; tokio::fs::create_dir_all(MEDIA_DIR) .await .expect("Не удалось создать папку received_media на диске"); // 1. Генерация сертификата в ОЗУ под выбранный IP и настройка HTTPS-клиентов let (cert_der, key_der, my_fingerprint) = tls::generate_in_memory_cert(&my_ip); let trusted_fingerprints = Arc::new(RwLock::new(HashMap::new())); let http_client = tls::build_pinned_http_client(trusted_fingerprints.clone()); let probe_client = tls::build_probe_http_client(); let state = Arc::new(AppState { device_name: device_name.clone(), my_ip: my_ip.clone(), p2p_port, my_fingerprint: my_fingerprint.clone(), ram_texts: RwLock::new(VecDeque::with_capacity(MAX_RAM_TEXTS)), disk_media: RwLock::new(VecDeque::new()), peers: RwLock::new(HashMap::new()), trusted_fingerprints, http_client, probe_client, }); // 2. Запуск широковещательного UDP-маяка на порт 5354 discovery::start_discovery(state.clone()); // 3. Запуск внешнего P2P HTTPS-сервера let tls_config = RustlsConfig::from_der(vec![cert_der], key_der) .await .expect("Ошибка конфигурации TLS"); let p2p_router = Router::new() .route("/p2p/info", get(p2p::p2p_info)) .route("/p2p/text", post(p2p::p2p_receive_text)) .route("/p2p/media", post(p2p::p2p_receive_media)) .layer(DefaultBodyLimit::max(MAX_PAYLOAD_SIZE)) .with_state(state.clone()); let p2p_addr: SocketAddr = format!("{}:{}", bind_ip, p2p_port) .parse() .unwrap_or_else(|_| panic!("Некорректный сокет: {}:{}", bind_ip, p2p_port)); tokio::spawn(async move { axum_server::bind_rustls(p2p_addr, tls_config) .serve(p2p_router.into_make_service()) .await .expect("Ошибка P2P HTTPS-сервера"); }); // 4. Запуск локального UI-сервера (127.0.0.1) let ui_router = Router::new() .route("/", get(ui::ui_index)) .route("/ui/state", get(ui::ui_get_state)) .route("/ui/manual-peer", post(ui::ui_add_manual_peer)) .route("/ui/trust", post(ui::ui_trust_peer)) .route("/ui/clear-ram", post(ui::ui_clear_ram)) .route("/ui/send-text", post(ui::ui_send_text)) .route("/ui/send-media", post(ui::ui_send_media)) .route("/ui/files/:filename", get(ui::ui_serve_saved_file)) .layer(DefaultBodyLimit::max(MAX_PAYLOAD_SIZE)) .with_state(state); let ui_addr: SocketAddr = format!("127.0.0.1:{}", ui_port).parse().unwrap(); println!("=============================================================="); println!(" Устройство: {}", device_name); println!(" P2P сокет (HTTPS): https://{}:{}", bind_ip, p2p_port); println!(" Маяк обнаружения (UDP): 255.255.255.255:5354"); println!(" КОД СВЕРКИ СЕРТИФИКАТА (PIN): [ {} ]", &my_fingerprint[..8].to_uppercase()); println!(" Хранилище медиа: ./{}/", MEDIA_DIR); println!("--------------------------------------------------------------"); println!(" ОТКРОЙТЕ ИНТЕРФЕЙС В БРАУЗЕРЕ: http://127.0.0.1:{}", ui_port); println!("=============================================================="); let listener = tokio::net::TcpListener::bind(ui_addr).await.unwrap(); axum::serve(listener, ui_router).await.unwrap(); }