a.0.0.3
This commit is contained in:
1 parent
69e2329695
commit
8b954f757b
11 files changed
+563
-528
No files matched your search
@@ -1,5 +1,5 @@
|
||||
use crate::models::{
|
||||
sanitize_filename, AppState, IncomingTextPayload, MediaItem, PeerNode, TextItem,
|
||||
sanitize_filename, AppState, IncomingTextPayload, MediaItem, PeerDevice, TextItem,
|
||||
UiManualPeerRequest, UiSendTextRequest, UiTrustRequest, MEDIA_DIR,
|
||||
};
|
||||
use axum::{
|
||||
@@ -18,25 +18,18 @@ pub async fn ui_index() -> Html<&'static str> {
|
||||
}
|
||||
|
||||
pub async fn ui_get_state(State(state): State<Arc<AppState>>) -> Json<serde_json::Value> {
|
||||
let trusted_map = state.trusted_fingerprints.read().unwrap();
|
||||
let mut peers_list: Vec<PeerNode> = state
|
||||
let mut peers_list: Vec<PeerDevice> = state
|
||||
.peers
|
||||
.read()
|
||||
.unwrap()
|
||||
.values()
|
||||
.cloned()
|
||||
.map(|mut p| {
|
||||
p.verified = trusted_map
|
||||
.get(&p.ip)
|
||||
.map(|fp| fp.eq_ignore_ascii_case(&p.fingerprint))
|
||||
.unwrap_or(false);
|
||||
p
|
||||
})
|
||||
.collect();
|
||||
peers_list.sort_by(|a, b| a.name.cmp(&b.name));
|
||||
|
||||
let texts: Vec<TextItem> = state.ram_texts.read().unwrap().iter().cloned().collect();
|
||||
let media: Vec<MediaItem> = state.disk_media.read().unwrap().iter().cloned().collect();
|
||||
let logs: Vec<String> = state.logs.read().unwrap().iter().cloned().collect();
|
||||
|
||||
Json(serde_json::json!({
|
||||
"device_name": state.device_name,
|
||||
@@ -46,7 +39,8 @@ pub async fn ui_get_state(State(state): State<Arc<AppState>>) -> Json<serde_json
|
||||
"my_pin": state.my_fingerprint[..8].to_uppercase(),
|
||||
"peers": peers_list,
|
||||
"texts": texts,
|
||||
"media": media
|
||||
"media": media,
|
||||
"logs": logs
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -55,76 +49,73 @@ pub async fn ui_add_manual_peer(
|
||||
Json(req): Json<UiManualPeerRequest>,
|
||||
) -> Result<StatusCode, (StatusCode, String)> {
|
||||
let ip = req.ip.trim().to_string();
|
||||
let port = req.port.unwrap_or(8443);
|
||||
let port = req.port.unwrap_or(state.p2p_port);
|
||||
|
||||
let url = format!("https://{}:{}/p2p/info", ip, port);
|
||||
let resp = state
|
||||
.probe_client
|
||||
.get(&url)
|
||||
.header("X-Node-Fp", &state.my_fingerprint)
|
||||
.header("X-Node-Name", &state.device_name)
|
||||
.header("X-Node-Ip", &state.my_ip)
|
||||
.header("X-Node-Port", state.p2p_port.to_string())
|
||||
.send()
|
||||
.await
|
||||
.map_err(|e| (StatusCode::BAD_REQUEST, format!("Узел недоступен: {}", e)))?;
|
||||
.map_err(|e| (StatusCode::BAD_REQUEST, format!("Узел {} недоступен: {}", ip, e)))?;
|
||||
|
||||
if !resp.status().is_success() {
|
||||
return Err((StatusCode::BAD_GATEWAY, "Узел вернул ошибку".into()));
|
||||
return Err((StatusCode::BAD_GATEWAY, "Удаленный узел вернул ошибку".into()));
|
||||
}
|
||||
|
||||
let info: serde_json::Value = resp
|
||||
.json()
|
||||
.await
|
||||
.map_err(|_| (StatusCode::BAD_GATEWAY, "Неверный ответ узла".into()))?;
|
||||
.map_err(|_| (StatusCode::BAD_GATEWAY, "Некорректный JSON от узла".into()))?;
|
||||
|
||||
let name = info["name"].as_str().unwrap_or("Manual-Node").to_string();
|
||||
let name = info["name"].as_str().unwrap_or("Remote-Node").to_string();
|
||||
let fingerprint = info["fingerprint"].as_str().unwrap_or("").to_string();
|
||||
let remote_port = info["port"].as_u64().unwrap_or(port as u64) as u16;
|
||||
|
||||
if fingerprint.is_empty() {
|
||||
return Err((StatusCode::BAD_GATEWAY, "Отсутствует отпечаток узла".into()));
|
||||
if fingerprint.len() < 8 {
|
||||
return Err((StatusCode::BAD_GATEWAY, "Недействительный отпечаток сертификата".into()));
|
||||
}
|
||||
|
||||
let pin_code = fingerprint[..8].to_uppercase();
|
||||
let is_verified = state
|
||||
.trusted_fingerprints
|
||||
.read()
|
||||
.unwrap()
|
||||
.get(&ip)
|
||||
.map(|fp| fp.eq_ignore_ascii_case(&fingerprint))
|
||||
.unwrap_or(false);
|
||||
|
||||
state.peers.write().unwrap().insert(
|
||||
ip.clone(),
|
||||
PeerNode {
|
||||
name,
|
||||
ip,
|
||||
port,
|
||||
fingerprint,
|
||||
pin_code,
|
||||
verified: is_verified,
|
||||
},
|
||||
);
|
||||
|
||||
state.upsert_peer(name, ip, remote_port, fingerprint);
|
||||
Ok(StatusCode::OK)
|
||||
}
|
||||
|
||||
pub async fn ui_ping_peers(State(state): State<Arc<AppState>>) -> StatusCode {
|
||||
let state_clone = state.clone();
|
||||
tokio::spawn(async move {
|
||||
crate::ping_all_peers(&state_clone).await;
|
||||
});
|
||||
StatusCode::OK
|
||||
}
|
||||
|
||||
pub async fn ui_trust_peer(
|
||||
State(state): State<Arc<AppState>>,
|
||||
Json(req): Json<UiTrustRequest>,
|
||||
) -> Result<StatusCode, (StatusCode, String)> {
|
||||
let peers = state.peers.read().unwrap();
|
||||
let mut peers = state.peers.write().unwrap();
|
||||
let peer = peers
|
||||
.get(&req.target_ip)
|
||||
.ok_or((StatusCode::NOT_FOUND, "Узел не найден".to_string()))?;
|
||||
.get_mut(&req.peer_id)
|
||||
.ok_or((StatusCode::NOT_FOUND, "Устройство не найдено".to_string()))?;
|
||||
|
||||
let mut trusted = state.trusted_fingerprints.write().unwrap();
|
||||
if req.trust {
|
||||
trusted.insert(peer.ip.clone(), peer.fingerprint.clone());
|
||||
peer.verified = req.trust;
|
||||
let msg = if req.trust {
|
||||
format!("Сертификат '{}' [{}] ЗАВЕРЕН", peer.name, peer.pin_code)
|
||||
} else {
|
||||
trusted.remove(&peer.ip);
|
||||
}
|
||||
format!("Доверие устройству '{}' [{}] ОТОЗВАНО", peer.name, peer.pin_code)
|
||||
};
|
||||
drop(peers);
|
||||
state.add_log(msg);
|
||||
|
||||
Ok(StatusCode::OK)
|
||||
}
|
||||
|
||||
pub async fn ui_clear_ram(State(state): State<Arc<AppState>>) -> StatusCode {
|
||||
state.ram_texts.write().unwrap().clear();
|
||||
state.add_log("Текстовый буфер в ОЗУ очищен");
|
||||
StatusCode::OK
|
||||
}
|
||||
|
||||
@@ -132,17 +123,29 @@ pub async fn ui_send_text(
|
||||
State(state): State<Arc<AppState>>,
|
||||
Json(req): Json<UiSendTextRequest>,
|
||||
) -> Result<StatusCode, (StatusCode, String)> {
|
||||
let port = state
|
||||
.peers
|
||||
.read()
|
||||
.unwrap()
|
||||
.get(&req.target_ip)
|
||||
.map(|p| p.port)
|
||||
.unwrap_or(state.p2p_port);
|
||||
let (target_ip, port, peer_name) = {
|
||||
let peers = state.peers.read().unwrap();
|
||||
let peer = peers
|
||||
.get(&req.peer_id)
|
||||
.ok_or((StatusCode::NOT_FOUND, "Получатель не найден".to_string()))?;
|
||||
if !peer.verified {
|
||||
return Err((
|
||||
StatusCode::FORBIDDEN,
|
||||
format!("Сначала заверьте сертификат устройства '{}' [PIN: {}]!", peer.name, peer.pin_code),
|
||||
));
|
||||
}
|
||||
let ip = peer
|
||||
.ips
|
||||
.first()
|
||||
.cloned()
|
||||
.ok_or((StatusCode::BAD_REQUEST, "У получателя нет известного IP".to_string()))?;
|
||||
(ip, peer.port, peer.name.clone())
|
||||
};
|
||||
|
||||
let url = format!("https://{}:{}/p2p/text", req.target_ip, port);
|
||||
let url = format!("https://{}:{}/p2p/text", target_ip, port);
|
||||
let payload = IncomingTextPayload {
|
||||
sender: state.device_name.clone(),
|
||||
sender_fp: state.my_fingerprint.clone(),
|
||||
text: req.text,
|
||||
};
|
||||
|
||||
@@ -155,6 +158,7 @@ pub async fn ui_send_text(
|
||||
.map_err(|e| (StatusCode::BAD_GATEWAY, format!("Ошибка TLS/Сети: {}", e)))?;
|
||||
|
||||
if resp.status().is_success() {
|
||||
state.add_log(format!("Текст доставлен в ОЗУ устройства '{}'", peer_name));
|
||||
Ok(StatusCode::OK)
|
||||
} else {
|
||||
Err((resp.status(), "Удаленный узел отклонил запрос".to_string()))
|
||||
@@ -166,10 +170,10 @@ pub async fn ui_send_media(
|
||||
headers: HeaderMap,
|
||||
body: Bytes,
|
||||
) -> Result<StatusCode, (StatusCode, String)> {
|
||||
let target_ip = headers
|
||||
.get("X-Target-Ip")
|
||||
let peer_id = headers
|
||||
.get("X-Target-Id")
|
||||
.and_then(|v| v.to_str().ok())
|
||||
.ok_or((StatusCode::BAD_REQUEST, "Не указан IP получателя".to_string()))?;
|
||||
.ok_or((StatusCode::BAD_REQUEST, "Не указан ID получателя".to_string()))?;
|
||||
|
||||
let filename = headers
|
||||
.get("X-Filename")
|
||||
@@ -181,13 +185,24 @@ pub async fn ui_send_media(
|
||||
.and_then(|v| v.to_str().ok())
|
||||
.unwrap_or("application/octet-stream");
|
||||
|
||||
let port = state
|
||||
.peers
|
||||
.read()
|
||||
.unwrap()
|
||||
.get(target_ip)
|
||||
.map(|p| p.port)
|
||||
.unwrap_or(state.p2p_port);
|
||||
let (target_ip, port, peer_name) = {
|
||||
let peers = state.peers.read().unwrap();
|
||||
let peer = peers
|
||||
.get(peer_id)
|
||||
.ok_or((StatusCode::NOT_FOUND, "Получатель не найден".to_string()))?;
|
||||
if !peer.verified {
|
||||
return Err((
|
||||
StatusCode::FORBIDDEN,
|
||||
format!("Сначала заверьте сертификат устройства '{}' [PIN: {}]!", peer.name, peer.pin_code),
|
||||
));
|
||||
}
|
||||
let ip = peer
|
||||
.ips
|
||||
.first()
|
||||
.cloned()
|
||||
.ok_or((StatusCode::BAD_REQUEST, "У получателя нет IP".to_string()))?;
|
||||
(ip, peer.port, peer.name.clone())
|
||||
};
|
||||
|
||||
let url = format!("https://{}:{}/p2p/media", target_ip, port);
|
||||
|
||||
@@ -203,6 +218,7 @@ pub async fn ui_send_media(
|
||||
.map_err(|e| (StatusCode::BAD_GATEWAY, format!("Ошибка TLS/Сети: {}", e)))?;
|
||||
|
||||
if resp.status().is_success() {
|
||||
state.add_log(format!("Медиафайл отправлен на диск устройства '{}'", peer_name));
|
||||
Ok(StatusCode::OK)
|
||||
} else {
|
||||
Err((resp.status(), "Удаленный узел отклонил медиафайл".to_string()))
|
||||
|
||||
Reference in new issue
Block a user