1→//! REST API endpoints 2→ 3→pub mod auth; 4→pub mod users; 5→ 6→use axum::{ 7→ extract::{Path, State, Query}, 8→ Json, 9→}; 10→use serde::{Deserialize, Serialize}; 11→use uuid::Uuid; 12→ 13→use crate::session::SessionManager; 14→use crate::db; 15→ 16→/// Viewer info returned by API 17→#[derive(Debug, Serialize)] 18→pub struct ViewerInfoApi { 19→ pub id: String, 20→ pub name: String, 21→ pub connected_at: String, 22→} 23→ 24→impl From for ViewerInfoApi { 25→ fn from(v: crate::session::ViewerInfo) -> Self { 26→ Self { 27→ id: v.id, 28→ name: v.name, 29→ connected_at: v.connected_at.to_rfc3339(), 30→ } 31→ } 32→} 33→ 34→/// Session info returned by API 35→#[derive(Debug, Serialize)] 36→pub struct SessionInfo { 37→ pub id: String, 38→ pub agent_id: String, 39→ pub agent_name: String, 40→ pub started_at: String, 41→ pub viewer_count: usize, 42→ pub viewers: Vec, 43→ pub is_streaming: bool, 44→ pub is_online: bool, 45→ pub is_persistent: bool, 46→ pub last_heartbeat: String, 47→ pub os_version: Option, 48→ pub is_elevated: bool, 49→ pub uptime_secs: i64, 50→ pub display_count: i32, 51→} 52→ 53→impl From for SessionInfo { 54→ fn from(s: crate::session::Session) -> Self { 55→ Self { 56→ id: s.id.to_string(), 57→ agent_id: s.agent_id, 58→ agent_name: s.agent_name, 59→ started_at: s.started_at.to_rfc3339(), 60→ viewer_count: s.viewer_count, 61→ viewers: s.viewers.into_iter().map(ViewerInfoApi::from).collect(), 62→ is_streaming: s.is_streaming, 63→ is_online: s.is_online, 64→ is_persistent: s.is_persistent, 65→ last_heartbeat: s.last_heartbeat.to_rfc3339(), 66→ os_version: s.os_version, 67→ is_elevated: s.is_elevated, 68→ uptime_secs: s.uptime_secs, 69→ display_count: s.display_count, 70→ } 71→ } 72→} 73→ 74→/// List all active sessions 75→pub async fn list_sessions( 76→ State(sessions): State, 77→) -> Json> { 78→ let sessions = sessions.list_sessions().await; 79→ Json(sessions.into_iter().map(SessionInfo::from).collect()) 80→} 81→ 82→/// Get a specific session by ID 83→pub async fn get_session( 84→ State(sessions): State, 85→ Path(id): Path, 86→) -> Result, (axum::http::StatusCode, &'static str)> { 87→ let session_id = Uuid::parse_str(&id) 88→ .map_err(|_| (axum::http::StatusCode::BAD_REQUEST, "Invalid session ID"))?; 89→ 90→ let session = sessions.get_session(session_id).await 91→ .ok_or((axum::http::StatusCode::NOT_FOUND, "Session not found"))?; 92→ 93→ Ok(Json(SessionInfo::from(session))) 94→} 95→ 96→// ============================================================================ 97→// Machine API Types 98→// ============================================================================ 99→ 100→/// Machine info returned by API 101→#[derive(Debug, Serialize)] 102→pub struct MachineInfo { 103→ pub id: String, 104→ pub agent_id: String, 105→ pub hostname: String, 106→ pub os_version: Option, 107→ pub is_elevated: bool, 108→ pub is_persistent: bool, 109→ pub first_seen: String, 110→ pub last_seen: String, 111→ pub status: String, 112→} 113→ 114→impl From for MachineInfo { 115→ fn from(m: db::machines::Machine) -> Self { 116→ Self { 117→ id: m.id.to_string(), 118→ agent_id: m.agent_id, 119→ hostname: m.hostname, 120→ os_version: m.os_version, 121→ is_elevated: m.is_elevated, 122→ is_persistent: m.is_persistent, 123→ first_seen: m.first_seen.to_rfc3339(), 124→ last_seen: m.last_seen.to_rfc3339(), 125→ status: m.status, 126→ } 127→ } 128→} 129→ 130→/// Session record for history 131→#[derive(Debug, Serialize)] 132→pub struct SessionRecord { 133→ pub id: String, 134→ pub started_at: String, 135→ pub ended_at: Option, 136→ pub duration_secs: Option, 137→ pub is_support_session: bool, 138→ pub support_code: Option, 139→ pub status: String, 140→} 141→ 142→impl From for SessionRecord { 143→ fn from(s: db::sessions::DbSession) -> Self { 144→ Self { 145→ id: s.id.to_string(), 146→ started_at: s.started_at.to_rfc3339(), 147→ ended_at: s.ended_at.map(|t| t.to_rfc3339()), 148→ duration_secs: s.duration_secs, 149→ is_support_session: s.is_support_session, 150→ support_code: s.support_code, 151→ status: s.status, 152→ } 153→ } 154→} 155→ 156→/// Event record for history 157→#[derive(Debug, Serialize)] 158→pub struct EventRecord { 159→ pub id: i64, 160→ pub session_id: String, 161→ pub event_type: String, 162→ pub timestamp: String, 163→ pub viewer_id: Option, 164→ pub viewer_name: Option, 165→ pub details: Option, 166→ pub ip_address: Option, 167→} 168→ 169→impl From for EventRecord { 170→ fn from(e: db::events::SessionEvent) -> Self { 171→ Self { 172→ id: e.id, 173→ session_id: e.session_id.to_string(), 174→ event_type: e.event_type, 175→ timestamp: e.timestamp.to_rfc3339(), 176→ viewer_id: e.viewer_id, 177→ viewer_name: e.viewer_name, 178→ details: e.details, 179→ ip_address: e.ip_address, 180→ } 181→ } 182→} 183→ 184→/// Full machine history (for export) 185→#[derive(Debug, Serialize)] 186→pub struct MachineHistory { 187→ pub machine: MachineInfo, 188→ pub sessions: Vec, 189→ pub events: Vec, 190→ pub exported_at: String, 191→} 192→ 193→/// Query parameters for machine deletion 194→#[derive(Debug, Deserialize)] 195→pub struct DeleteMachineParams { 196→ /// If true, send uninstall command to agent (if online) 197→ #[serde(default)] 198→ pub uninstall: bool, 199→ /// If true, include history in response before deletion 200→ #[serde(default)] 201→ pub export: bool, 202→} 203→ 204→/// Response for machine deletion 205→#[derive(Debug, Serialize)] 206→pub struct DeleteMachineResponse { 207→ pub success: bool, 208→ pub message: String, 209→ pub uninstall_sent: bool, 210→ pub history: Option, 211→} 212→ Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.