server: models.rs — Session, Agent, WsMessage types, API response helpers
This commit is contained in:
parent
bf8e9f79f8
commit
5c8f84848f
145
server/src/models.rs
Normal file
145
server/src/models.rs
Normal file
@ -0,0 +1,145 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use chrono::{DateTime, Utc};
|
||||
|
||||
/// Status of a remote desktop session.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum SessionStatus {
|
||||
/// Session created, waiting for an agent to connect.
|
||||
Waiting,
|
||||
/// Agent is streaming display/audio.
|
||||
Active,
|
||||
/// Session ended or agent disconnected.
|
||||
Disconnected,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for SessionStatus {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
SessionStatus::Waiting => write!(f, "waiting"),
|
||||
SessionStatus::Active => write!(f, "active"),
|
||||
SessionStatus::Disconnected => write!(f, "disconnected"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A remote desktop session. Each session corresponds to one VM / remote machine.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Session {
|
||||
pub id: String,
|
||||
pub status: SessionStatus,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub display_active: bool,
|
||||
pub audio_active: bool,
|
||||
/// Resolution reported by the agent (e.g. "1920x1080").
|
||||
pub resolution: Option<String>,
|
||||
/// Arbitrary key-value metadata attached to this session.
|
||||
pub metadata: HashMap<String, String>,
|
||||
}
|
||||
|
||||
impl Session {
|
||||
pub fn new(id: String) -> Self {
|
||||
Self {
|
||||
id,
|
||||
status: SessionStatus::Waiting,
|
||||
created_at: Utc::now(),
|
||||
display_active: false,
|
||||
audio_active: false,
|
||||
resolution: None,
|
||||
metadata: HashMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Connection metadata for a registered desktop agent.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct AgentConnection {
|
||||
pub agent_id: String,
|
||||
pub session_id: String,
|
||||
pub connected_at: DateTime<Utc>,
|
||||
pub ip_address: String,
|
||||
pub display_active: bool,
|
||||
pub audio_active: bool,
|
||||
}
|
||||
|
||||
/// Client type for WebSocket connections.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum ClientType {
|
||||
/// Browser-based viewer watching the remote desktop.
|
||||
Viewer,
|
||||
/// Native agent executable running inside the VM.
|
||||
Agent,
|
||||
}
|
||||
|
||||
// ── WebSocket message types ──────────────────────────────────────────────────
|
||||
|
||||
/// Every WebSocket frame carries a JSON envelope with a `msg_type` discriminator.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(tag = "msg_type", rename_all = "snake_case")]
|
||||
pub enum WsMessage {
|
||||
// ── Agent → Server ───────────────────────────────────────────────────────
|
||||
/// A single display frame, base64-encoded JPEG or PNG.
|
||||
DisplayFrame { session_id: String, data: String, timestamp: Option<f64> },
|
||||
/// An audio chunk, base64-encoded PCM / Opus / AAC.
|
||||
AudioFrame { session_id: String, data: String, timestamp: Option<f64> },
|
||||
/// Agent announces its capabilities and info on connect.
|
||||
AgentInfo { session_id: String, agent_id: String, resolution: Option<String> },
|
||||
/// Keep-alive ping from agent.
|
||||
Heartbeat,
|
||||
|
||||
// ── Viewer → Server ──────────────────────────────────────────────────────
|
||||
/// Viewer sends a HUD (heads-up display) command to the agent.
|
||||
HudCommand { session_id: String, command: String, params: serde_json::Value },
|
||||
/// Viewer requests display resize.
|
||||
Resize { session_id: String, width: u32, height: u32 },
|
||||
|
||||
// ── Server → Viewer ──────────────────────────────────────────────────────
|
||||
/// Server relays a display frame to viewers.
|
||||
FrameBroadcast { data: String, content_type: String },
|
||||
/// Server relays an audio chunk to viewers.
|
||||
AudioBroadcast { data: String, content_type: String },
|
||||
/// Session state update pushed to viewers.
|
||||
SessionUpdate { session_id: String, status: SessionStatus, resolution: Option<String> },
|
||||
|
||||
// ── Server → Agent ───────────────────────────────────────────────────────
|
||||
/// Server forwards a HUD command from a viewer to the agent.
|
||||
ForwardHudCommand { command: String, params: serde_json::Value },
|
||||
/// Server forwards a resize request from a viewer.
|
||||
ForwardResize { width: u32, height: u32 },
|
||||
/// Server tells the agent to start / stop streaming.
|
||||
StreamControl { action: String },
|
||||
|
||||
// ── Generic ──────────────────────────────────────────────────────────────
|
||||
Error { message: String },
|
||||
Ack { message: String },
|
||||
}
|
||||
|
||||
/// Response body for health-check and generic API responses.
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct ApiResponse<T: Serialize> {
|
||||
pub ok: bool,
|
||||
pub data: Option<T>,
|
||||
pub error: Option<String>,
|
||||
}
|
||||
|
||||
impl<T: Serialize> ApiResponse<T> {
|
||||
pub fn ok(data: T) -> Self {
|
||||
Self { ok: true, data: Some(data), error: None }
|
||||
}
|
||||
|
||||
pub fn err(msg: impl Into<String>) -> Self {
|
||||
Self { ok: false, data: None, error: Some(msg.into()) }
|
||||
}
|
||||
}
|
||||
|
||||
/// Health-check payload.
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct HealthInfo {
|
||||
pub status: String,
|
||||
pub uptime_secs: u64,
|
||||
pub active_sessions: usize,
|
||||
pub connected_agents: usize,
|
||||
pub version: String,
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user