//! REST API endpoints pub mod auth; pub mod auth_logout; pub mod users; pub mod releases; pub mod downloads; use axum::{ extract::{Path, State, Query}, Json, }; use serde::{Deserialize, Serialize}; use uuid::Uuid; use crate::session::SessionManager; use crate::db; /// Viewer info returned by API #[derive(Debug, Serialize)] pub struct ViewerInfoApi { pub id: String, pub name: String, pub connected_at: String, } impl From for ViewerInfoApi { fn from(v: crate::session::ViewerInfo) -> Self { Self { id: v.id, name: v.name, connected_at: v.connected_at.to_rfc3339(), } } } /// Session info returned by API #[derive(Debug, Serialize)] pub struct SessionInfo { pub id: String, pub agent_id: String, pub agent_name: String, pub started_at: String, pub viewer_count: usize, pub viewers: Vec, pub is_streaming: bool, pub is_online: bool, pub is_persistent: bool, pub last_heartbeat: String, pub os_version: Option, pub is_elevated: bool, pub uptime_secs: i64, pub display_count: i32, pub agent_version: Option, } impl From for SessionInfo { fn from(s: crate::session::Session) -> Self { Self { id: s.id.to_string(), agent_id: s.agent_id, agent_name: s.agent_name, started_at: s.started_at.to_rfc3339(), viewer_count: s.viewer_count, viewers: s.viewers.into_iter().map(ViewerInfoApi::from).collect(), is_streaming: s.is_streaming, is_online: s.is_online, is_persistent: s.is_persistent, last_heartbeat: s.last_heartbeat.to_rfc3339(), os_version: s.os_version, is_elevated: s.is_elevated, uptime_secs: s.uptime_secs, display_count: s.display_count, agent_version: s.agent_version, } } } /// List all active sessions pub async fn list_sessions( State(sessions): State, ) -> Json> { let sessions = sessions.list_sessions().await; Json(sessions.into_iter().map(SessionInfo::from).collect()) } /// Get a specific session by ID pub async fn get_session( State(sessions): State, Path(id): Path, ) -> Result, (axum::http::StatusCode, &'static str)> { let session_id = Uuid::parse_str(&id) .map_err(|_| (axum::http::StatusCode::BAD_REQUEST, "Invalid session ID"))?; let session = sessions.get_session(session_id).await .ok_or((axum::http::StatusCode::NOT_FOUND, "Session not found"))?; Ok(Json(SessionInfo::from(session))) } // ============================================================================ // Machine API Types // ============================================================================ /// Machine info returned by API #[derive(Debug, Serialize)] pub struct MachineInfo { pub id: String, pub agent_id: String, pub hostname: String, pub os_version: Option, pub is_elevated: bool, pub is_persistent: bool, pub first_seen: String, pub last_seen: String, pub status: String, } impl From for MachineInfo { fn from(m: db::machines::Machine) -> Self { Self { id: m.id.to_string(), agent_id: m.agent_id, hostname: m.hostname, os_version: m.os_version, is_elevated: m.is_elevated, is_persistent: m.is_persistent, first_seen: m.first_seen.to_rfc3339(), last_seen: m.last_seen.to_rfc3339(), status: m.status, } } } /// Session record for history #[derive(Debug, Serialize)] pub struct SessionRecord { pub id: String, pub started_at: String, pub ended_at: Option, pub duration_secs: Option, pub is_support_session: bool, pub support_code: Option, pub status: String, } impl From for SessionRecord { fn from(s: db::sessions::DbSession) -> Self { Self { id: s.id.to_string(), started_at: s.started_at.to_rfc3339(), ended_at: s.ended_at.map(|t| t.to_rfc3339()), duration_secs: s.duration_secs, is_support_session: s.is_support_session, support_code: s.support_code, status: s.status, } } } /// Event record for history #[derive(Debug, Serialize)] pub struct EventRecord { pub id: i64, pub session_id: String, pub event_type: String, pub timestamp: String, pub viewer_id: Option, pub viewer_name: Option, pub details: Option, pub ip_address: Option, } impl From for EventRecord { fn from(e: db::events::SessionEvent) -> Self { Self { id: e.id, session_id: e.session_id.to_string(), event_type: e.event_type, timestamp: e.timestamp.to_rfc3339(), viewer_id: e.viewer_id, viewer_name: e.viewer_name, details: e.details, ip_address: e.ip_address, } } } /// Full machine history (for export) #[derive(Debug, Serialize)] pub struct MachineHistory { pub machine: MachineInfo, pub sessions: Vec, pub events: Vec, pub exported_at: String, } /// Query parameters for machine deletion #[derive(Debug, Deserialize)] pub struct DeleteMachineParams { /// If true, send uninstall command to agent (if online) #[serde(default)] pub uninstall: bool, /// If true, include history in response before deletion #[serde(default)] pub export: bool, } /// Response for machine deletion #[derive(Debug, Serialize)] pub struct DeleteMachineResponse { pub success: bool, pub message: String, pub uninstall_sent: bool, pub history: Option, }