use crate::models::{ now_sec, sanitize_filename, url_decode, AppState, IncomingTextPayload, MediaItem, TextItem, MAX_RAM_TEXTS, MEDIA_DIR, }; use axum::{ body::Bytes, extract::State, http::{header, HeaderMap, StatusCode}, Json, }; use std::{path::PathBuf, sync::Arc}; /// Прием текста от другого устройства -> сохраняется СТРОГО В ОЗУ pub async fn p2p_receive_text( State(state): State>, Json(payload): Json, ) -> StatusCode { let mut ram_texts = state.ram_texts.write().unwrap(); if ram_texts.len() >= MAX_RAM_TEXTS { ram_texts.pop_back(); } ram_texts.push_front(TextItem { sender: payload.sender, text: payload.text, timestamp: now_sec(), }); StatusCode::OK } /// Прием медиаконтента от другого устройства -> сохраняется НА ДИСК pub async fn p2p_receive_media( State(state): State>, headers: HeaderMap, body: Bytes, ) -> Result { let sender = headers .get("X-Sender") .and_then(|v| v.to_str().ok()) .map(url_decode) .unwrap_or_else(|| "Unknown".to_string()); let raw_filename = headers .get("X-Filename") .and_then(|v| v.to_str().ok()) .unwrap_or("file.bin"); let mime = headers .get(header::CONTENT_TYPE) .and_then(|v| v.to_str().ok()) .unwrap_or("application/octet-stream") .to_string(); tokio::fs::create_dir_all(MEDIA_DIR) .await .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?; let safe_name = sanitize_filename(raw_filename); let final_filename = format!("{}_{}", now_sec(), safe_name); let file_path = PathBuf::from(MEDIA_DIR).join(&final_filename); tokio::fs::write(&file_path, &body) .await .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?; let abs_path = std::fs::canonicalize(&file_path) .unwrap_or_else(|_| file_path.clone()) .display() .to_string(); let mut media_list = state.disk_media.write().unwrap(); media_list.push_front(MediaItem { sender, filename: final_filename, saved_path: abs_path, mime, size: body.len(), timestamp: now_sec(), }); Ok(StatusCode::OK) }