feat: HELIOS Remote v5.0.0 Final Release & Systemd Auto-Start Daemon
This commit is contained in:
124
viewer/src/main.rs
Normal file
124
viewer/src/main.rs
Normal file
@@ -0,0 +1,124 @@
|
||||
use axum::{
|
||||
extract::ws::{Message, WebSocket, WebSocketUpgrade},
|
||||
response::{Html, IntoResponse},
|
||||
routing::get,
|
||||
Router,
|
||||
};
|
||||
use common::ProtocolMessage;
|
||||
use futures_util::{SinkExt, StreamExt};
|
||||
use std::env;
|
||||
use std::net::SocketAddr;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
|
||||
use tokio::net::TcpStream;
|
||||
use tokio::sync::Mutex;
|
||||
use tower_http::services::ServeDir;
|
||||
use tracing::{error, info, warn};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
tracing_subscriber::fmt::init();
|
||||
|
||||
let args: Vec<String> = env::args().collect();
|
||||
let device_id = if args.len() > 1 {
|
||||
Uuid::parse_str(&args[1]).unwrap_or_else(|_| Uuid::new_v4())
|
||||
} else {
|
||||
Uuid::parse_str("8201b455-673b-4515-8258-096fa9a8362b").unwrap_or_else(|_| Uuid::new_v4())
|
||||
};
|
||||
|
||||
let ui_path = PathBuf::from("viewer/ui");
|
||||
|
||||
info!("HELIOS GUI Viewer (Webview App) launching...");
|
||||
info!("Target Remote Device ID: {}", device_id);
|
||||
|
||||
// Build Axum Router for Webview UI & WebSocket Bridge
|
||||
let app = Router::new()
|
||||
.route("/", get(serve_index))
|
||||
.route("/ws", get(move |ws| ws_handler(ws, device_id)))
|
||||
.nest_service("/static", ServeDir::new(&ui_path))
|
||||
.fallback_service(ServeDir::new(&ui_path));
|
||||
|
||||
let gui_addr = SocketAddr::from(([0, 0, 0, 0], 8085));
|
||||
info!("HELIOS Viewer Webview Dashboard serving at http://localhost:8085");
|
||||
|
||||
|
||||
let listener = tokio::net::TcpListener::bind(gui_addr).await?;
|
||||
axum::serve(listener, app).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn serve_index() -> Html<&'static str> {
|
||||
Html(include_str!("../ui/index.html"))
|
||||
}
|
||||
|
||||
async fn ws_handler(ws: WebSocketUpgrade, device_id: Uuid) -> impl IntoResponse {
|
||||
ws.on_upgrade(move |socket| handle_ws_socket(socket, device_id))
|
||||
}
|
||||
|
||||
async fn handle_ws_socket(ws_socket: WebSocket, device_id: Uuid) {
|
||||
info!("New Webview GUI client connected to Local WebSocket Bridge");
|
||||
|
||||
let relay_addr = "127.0.0.1:4000";
|
||||
let tcp_stream = match TcpStream::connect(relay_addr).await {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
error!("Failed to connect to Relay Server at {}: {}", relay_addr, e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let (relay_reader, mut relay_writer) = tcp_stream.into_split();
|
||||
|
||||
// Handshake with Relay Server
|
||||
let handshake = ProtocolMessage::Handshake {
|
||||
device_id,
|
||||
session_token: "viewer_session_token".to_string(),
|
||||
is_agent: false,
|
||||
};
|
||||
|
||||
if let Ok(mut json) = serde_json::to_string(&handshake) {
|
||||
json.push('\n');
|
||||
if let Err(e) = relay_writer.write_all(json.as_bytes()).await {
|
||||
error!("Failed handshake with Relay: {}", e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let (ws_sender, mut ws_receiver) = ws_socket.split();
|
||||
let ws_sender = Arc::new(Mutex::new(ws_sender));
|
||||
|
||||
// Relay -> Webview GUI Forwarder Task
|
||||
let ws_sender_clone = ws_sender.clone();
|
||||
tokio::spawn(async move {
|
||||
let mut lines = BufReader::new(relay_reader).lines();
|
||||
while let Ok(Some(line)) = lines.next_line().await {
|
||||
if line.trim().is_empty() {
|
||||
continue;
|
||||
}
|
||||
let mut ws_guard = ws_sender_clone.lock().await;
|
||||
if let Err(e) = ws_guard.send(Message::Text(line)).await {
|
||||
error!("Error forwarding Relay frame to Webview GUI: {}", e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Webview GUI -> Relay Forwarder Task
|
||||
let relay_writer = Arc::new(Mutex::new(relay_writer));
|
||||
while let Some(Ok(msg)) = ws_receiver.next().await {
|
||||
if let Message::Text(text) = msg {
|
||||
if let Ok(protocol_msg) = serde_json::from_str::<ProtocolMessage>(&text) {
|
||||
if let Ok(mut json) = serde_json::to_string(&protocol_msg) {
|
||||
json.push('\n');
|
||||
let mut writer_guard = relay_writer.lock().await;
|
||||
let _ = writer_guard.write_all(json.as_bytes()).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
warn!("Webview GUI client disconnected");
|
||||
}
|
||||
Reference in New Issue
Block a user