Refactor and enhance Archipelago setup and API

- Revamped GETTING_STARTED.md for clarity and completeness, detailing the Docker development environment and installation steps.
- Updated Cargo.lock and Cargo.toml to replace deprecated dependencies and add new ones, including hyper-ws-listener and env_logger.
- Improved WebSocket handling in the API to support upgrades and error management.
- Enhanced Neode UI scripts to manage Docker containers during development.
- Adjusted dummy app configurations for accurate LAN addresses.
- Sorted app entries in the UI for better organization and accessibility.
This commit is contained in:
Dorian
2026-01-27 22:47:51 +00:00
parent 4126aa0b33
commit 10fa19df66
17 changed files with 1370 additions and 450 deletions

View File

@@ -3,9 +3,10 @@ use crate::config::Config;
use anyhow::Result;
use futures_util::{SinkExt, StreamExt};
use hyper::{Method, Request, Response, StatusCode};
use hyper_tungstenite::tungstenite::Message;
use hyper_ws_listener::WsStream;
use std::sync::Arc;
use tracing::{debug, error, info};
use tokio_tungstenite::tungstenite::Message;
use tracing::{debug, info};
pub struct ApiHandler {
_config: Config,
@@ -58,40 +59,44 @@ impl ApiHandler {
async fn handle_websocket(
req: Request<hyper::Body>,
) -> Result<Response<hyper::Body>> {
if !hyper_tungstenite::is_upgrade_request(&req) {
return Ok(Response::builder()
.status(StatusCode::BAD_REQUEST)
.body(hyper::Body::from("Expected WebSocket upgrade"))?);
}
let (response, ws_fut) = hyper_tungstenite::upgrade(req, None)
let (response, ws_fut_opt) = hyper_ws_listener::create_ws(req)
.map_err(|e| anyhow::anyhow!("WebSocket upgrade failed: {}", e))?;
// Spawn a task to hold the connection open and avoid EPIPE when the UI connects
tokio::spawn(async move {
let mut ws_stream = match ws_fut.await {
Ok(s) => s,
Err(e) => {
error!("WebSocket handshake failed: {}", e);
return;
}
};
info!("WebSocket /ws/db connected");
// Keep connection open; UI may send/receive JSON patches. For now just accept and ignore.
while let Some(msg) = ws_stream.next().await {
match msg {
Ok(Message::Close(_)) => break,
Ok(Message::Ping(data)) => {
let _ = ws_stream.send(Message::Pong(data)).await;
// Spawn a task to hold the connection open if upgrade future exists
if let Some(ws_fut) = ws_fut_opt {
tokio::spawn(async move {
let ws_stream: WsStream = match ws_fut.await {
Ok(Ok(s)) => s,
Ok(Err(e)) => {
debug!("WebSocket handshake failed (hyper): {}", e);
return;
}
Ok(_) => {}
Err(e) => {
debug!("WebSocket stream error: {}", e);
break;
debug!("WebSocket task join failed: {}", e);
return;
}
};
info!("WebSocket /ws/db connected");
let (mut tx, mut rx) = ws_stream.split();
// Keep connection open; UI may send/receive JSON patches. For now just accept and ignore.
while let Some(msg) = rx.next().await {
match msg {
Ok(Message::Close(_)) => break,
Ok(Message::Ping(data)) => {
let _ = tx.send(Message::Pong(data)).await;
}
Ok(_) => {}
Err(e) => {
debug!("WebSocket stream error: {}", e);
break;
}
}
}
}
});
info!("WebSocket /ws/db disconnected");
});
}
Ok(response)
}