mirror of
https://github.com/henrygd/beszel.git
synced 2026-03-22 21:46:18 +01:00
- Introduce `Transport` interface to abstract WebSocket and SSH communication - Add generic `Data` field to `AgentResponse` for streamlined future endpoints - Maintain backward compatibility with legacy hubs and agents using typed fields - Unify fetch operations (SMART, systemd, containers) under a single `request` method - Improve `RequestManager` with deadline awareness and legacy response support - Refactor agent response routing into dedicated `agent/response.go` - Update version to 0.18.0-beta.2
75 lines
2.0 KiB
Go
75 lines
2.0 KiB
Go
package transport
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
|
|
"github.com/fxamacker/cbor/v2"
|
|
"github.com/henrygd/beszel"
|
|
"github.com/henrygd/beszel/internal/common"
|
|
"github.com/henrygd/beszel/internal/hub/ws"
|
|
)
|
|
|
|
// ErrWebSocketNotConnected indicates a WebSocket transport is not currently connected.
|
|
var ErrWebSocketNotConnected = errors.New("websocket not connected")
|
|
|
|
// WebSocketTransport implements Transport over WebSocket connections.
|
|
type WebSocketTransport struct {
|
|
wsConn *ws.WsConn
|
|
}
|
|
|
|
// NewWebSocketTransport creates a new WebSocket transport wrapper.
|
|
func NewWebSocketTransport(wsConn *ws.WsConn) *WebSocketTransport {
|
|
return &WebSocketTransport{wsConn: wsConn}
|
|
}
|
|
|
|
// Request sends a request to the agent via WebSocket and unmarshals the response.
|
|
func (t *WebSocketTransport) Request(ctx context.Context, action common.WebSocketAction, req any, dest any) error {
|
|
if !t.IsConnected() {
|
|
return ErrWebSocketNotConnected
|
|
}
|
|
|
|
pendingReq, err := t.wsConn.SendRequest(ctx, action, req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Wait for response
|
|
select {
|
|
case message := <-pendingReq.ResponseCh:
|
|
defer message.Close()
|
|
defer pendingReq.Cancel()
|
|
|
|
// Legacy agents (< MinVersionAgentResponse) respond with a raw payload instead of an AgentResponse wrapper.
|
|
if t.wsConn.AgentVersion().LT(beszel.MinVersionAgentResponse) {
|
|
return cbor.Unmarshal(message.Data.Bytes(), dest)
|
|
}
|
|
|
|
var agentResponse common.AgentResponse
|
|
if err := cbor.Unmarshal(message.Data.Bytes(), &agentResponse); err != nil {
|
|
return err
|
|
}
|
|
|
|
if agentResponse.Error != "" {
|
|
return errors.New(agentResponse.Error)
|
|
}
|
|
|
|
return UnmarshalResponse(agentResponse, action, dest)
|
|
|
|
case <-pendingReq.Context.Done():
|
|
return pendingReq.Context.Err()
|
|
}
|
|
}
|
|
|
|
// IsConnected returns true if the WebSocket connection is active.
|
|
func (t *WebSocketTransport) IsConnected() bool {
|
|
return t.wsConn != nil && t.wsConn.IsConnected()
|
|
}
|
|
|
|
// Close terminates the WebSocket connection.
|
|
func (t *WebSocketTransport) Close() {
|
|
if t.wsConn != nil {
|
|
t.wsConn.Close(nil)
|
|
}
|
|
}
|