fix: Implement Phase 2 major fixes
Database: - Add missing indexes for api_key_hash, status, metrics queries - New migration: 005_add_missing_indexes.sql Server: - Fix WebSocket Ping/Pong protocol (RFC 6455 compliance) - Use separate channel for Pong responses Agent: - Replace format!() path construction with PathBuf::join() - Replace todo!() macros with proper errors for macOS support Dashboard: - Fix duplicate filter values in Agents page (__unassigned__ sentinel) - Add onError handlers to all mutations in Agents, Clients, Sites pages All changes reviewed and approved. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -7,7 +7,6 @@
|
||||
//! - Watchdog event handling
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
use axum::{
|
||||
extract::{
|
||||
@@ -18,7 +17,7 @@ use axum::{
|
||||
};
|
||||
use futures_util::{SinkExt, StreamExt};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::sync::{mpsc, RwLock};
|
||||
use tokio::sync::mpsc;
|
||||
use tracing::{debug, error, info, warn};
|
||||
use uuid::Uuid;
|
||||
|
||||
@@ -253,9 +252,13 @@ pub async fn ws_handler(ws: WebSocketUpgrade, State(state): State<AppState>) ->
|
||||
async fn handle_socket(socket: WebSocket, state: AppState) {
|
||||
let (mut sender, mut receiver) = socket.split();
|
||||
|
||||
// Create channel for outgoing messages
|
||||
// Create channel for outgoing protocol messages (ServerMessage)
|
||||
let (tx, mut rx) = mpsc::channel::<ServerMessage>(100);
|
||||
|
||||
// Create separate channel for raw WebSocket frames (Pong responses)
|
||||
// This allows proper WebSocket protocol compliance without changing the public API
|
||||
let (pong_tx, mut pong_rx) = mpsc::channel::<Vec<u8>>(16);
|
||||
|
||||
// Wait for authentication message
|
||||
let auth_result = match authenticate(&mut receiver, &mut sender, &state).await {
|
||||
Ok(result) => {
|
||||
@@ -358,12 +361,26 @@ async fn handle_socket(socket: WebSocket, state: AppState) {
|
||||
let agent_id = auth_result.agent_id;
|
||||
|
||||
// Spawn task to forward outgoing messages
|
||||
// Handles both protocol messages (ServerMessage) and raw Pong frames
|
||||
let send_task = tokio::spawn(async move {
|
||||
while let Some(msg) = rx.recv().await {
|
||||
if let Ok(json) = serde_json::to_string(&msg) {
|
||||
if sender.send(Message::Text(json)).await.is_err() {
|
||||
break;
|
||||
loop {
|
||||
tokio::select! {
|
||||
// Handle protocol messages (ServerMessage -> JSON text)
|
||||
Some(msg) = rx.recv() => {
|
||||
if let Ok(json) = serde_json::to_string(&msg) {
|
||||
if sender.send(Message::Text(json)).await.is_err() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Handle Pong responses (WebSocket protocol compliance)
|
||||
Some(data) = pong_rx.recv() => {
|
||||
if sender.send(Message::Pong(data)).await.is_err() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Both channels closed
|
||||
else => break,
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -377,11 +394,10 @@ async fn handle_socket(socket: WebSocket, state: AppState) {
|
||||
}
|
||||
}
|
||||
Ok(Message::Ping(data)) => {
|
||||
if tx
|
||||
.send(ServerMessage::Ack { message_id: None })
|
||||
.await
|
||||
.is_err()
|
||||
{
|
||||
// WebSocket protocol requires Pong response with same payload
|
||||
// Send via pong channel to the send task
|
||||
if pong_tx.send(data).await.is_err() {
|
||||
warn!("Failed to send Pong response for agent {}", agent_id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user