use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Deserialize)] #[serde(rename_all = "camelCase")] pub struct SshConnectRequest { pub host: String, #[serde(default = "default_ssh_port")] pub port: u16, pub username: String, pub password: Option, pub private_key: Option, pub passphrase: Option, pub proxy: Option, pub jump_host: Option, pub jump_hosts: Option>, pub timeout_seconds: Option, pub keep_alive: Option, } fn default_ssh_port() -> u16 { 22 } #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct OperationResponse { pub success: bool, #[serde(skip_serializing_if = "Option::is_none")] pub error: Option, #[serde(skip_serializing_if = "Option::is_none")] pub error_code: Option, } impl OperationResponse { pub fn ok() -> Self { Self { success: true, error: None, error_code: None, } } pub fn error(error: impl Into) -> Self { Self { success: false, error: Some(error.into()), error_code: None, } } pub fn coded(code: &'static str, error: impl Into) -> Self { Self { success: false, error: Some(error.into()), error_code: Some(code.to_string()), } } } #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct IdResponse { pub success: bool, #[serde(skip_serializing_if = "Option::is_none")] pub connection_id: Option, #[serde(skip_serializing_if = "Option::is_none")] pub shell_id: Option, #[serde(skip_serializing_if = "Option::is_none")] pub sftp_id: Option, #[serde(skip_serializing_if = "Option::is_none")] pub error: Option, #[serde(skip_serializing_if = "Option::is_none")] pub error_code: Option, } impl IdResponse { pub fn connection(id: String) -> Self { Self { success: true, connection_id: Some(id), shell_id: None, sftp_id: None, error: None, error_code: None, } } pub fn shell(id: String) -> Self { Self { success: true, connection_id: None, shell_id: Some(id), sftp_id: None, error: None, error_code: None, } } pub fn sftp(id: String) -> Self { Self { success: true, connection_id: None, shell_id: None, sftp_id: Some(id), error: None, error_code: None, } } pub fn error(error: impl Into) -> Self { Self { success: false, connection_id: None, shell_id: None, sftp_id: None, error: Some(error.into()), error_code: None, } } pub fn coded(code: &'static str, error: impl Into) -> Self { Self { success: false, connection_id: None, shell_id: None, sftp_id: None, error: Some(error.into()), error_code: Some(code.to_string()), } } } #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct ShellOutputResponse { pub success: bool, #[serde(skip_serializing_if = "Option::is_none")] pub data: Option>, #[serde(skip_serializing_if = "Option::is_none")] pub error: Option, } impl ShellOutputResponse { pub fn ok(data: Vec) -> Self { Self { success: true, data: Some(data), error: None, } } pub fn error(error: impl Into) -> Self { Self { success: false, data: None, error: Some(error.into()), } } } #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct ExecResponse { pub success: bool, #[serde(skip_serializing_if = "Option::is_none")] pub result: Option, #[serde(skip_serializing_if = "Option::is_none")] pub exit_code: Option, #[serde(skip_serializing_if = "Option::is_none")] pub error: Option, } #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct ForwardResponse { pub success: bool, #[serde(skip_serializing_if = "Option::is_none")] pub forward_id: Option, #[serde(skip_serializing_if = "Option::is_none")] pub bound_port: Option, #[serde(skip_serializing_if = "Option::is_none")] pub error: Option, } impl ForwardResponse { pub fn ok(forward_id: String, port: u16) -> Self { Self { success: true, forward_id: Some(forward_id), bound_port: Some(port), error: None, } } pub fn error(error: impl Into) -> Self { Self { success: false, forward_id: None, bound_port: None, error: Some(error.into()), } } } /// Runtime state for a single forwarding listener. Persisted rules live in the /// renderer settings; this structure deliberately represents process-local /// state only. #[derive(Debug, Clone, Serialize)] #[serde(rename_all = "camelCase")] pub struct ForwardStatus { pub forward_id: String, pub connection_id: String, pub kind: String, pub bind_host: String, pub bound_port: u16, pub target_host: Option, pub target_port: Option, pub started_at: u64, pub running: bool, pub active_connections: u64, pub bytes_up: u64, pub bytes_down: u64, } #[derive(Debug, Serialize, Clone)] #[serde(rename_all = "camelCase")] pub struct ShellDataEvent { pub connection_id: String, pub shell_id: String, pub data: Vec, } #[derive(Debug, Serialize, Clone)] #[serde(rename_all = "camelCase")] pub struct ShellClosedEvent { pub connection_id: String, pub shell_id: String, pub reason: String, pub idle_timeout: bool, } #[derive(Debug, Serialize, Clone)] #[serde(rename_all = "camelCase")] pub struct ConnectionClosedEvent { pub connection_id: String, pub reason: String, } #[derive(Debug, Serialize)] pub struct SftpFileAttrs { pub mode: u32, pub size: u64, pub mtime: u64, pub atime: u64, } #[derive(Debug, Serialize)] pub struct SftpDirEntry { pub filename: String, pub longname: String, pub attrs: SftpFileAttrs, } #[derive(Debug, Serialize, Clone)] #[serde(rename_all = "camelCase")] pub struct SftpProgressEvent { pub sftp_id: String, pub transferred: u64, pub total: u64, pub percentage: u8, } #[derive(Debug, Serialize, Clone)] #[serde(rename_all = "camelCase")] pub struct HostDiskMetric { pub mount: String, pub percent: u8, pub used_kb: u64, pub total_kb: u64, } #[derive(Debug, Serialize, Clone)] #[serde(rename_all = "camelCase")] pub struct HostMetrics { #[serde(skip_serializing_if = "Option::is_none")] pub os_pretty_name: Option, #[serde(skip_serializing_if = "Option::is_none")] pub timezone: Option, #[serde(skip_serializing_if = "Option::is_none")] pub timezone_offset: Option, #[serde(skip_serializing_if = "Option::is_none")] pub uptime_sec: Option, #[serde(skip_serializing_if = "Option::is_none")] pub cpu_percent: Option, #[serde(skip_serializing_if = "Option::is_none")] pub mem_used_kb: Option, #[serde(skip_serializing_if = "Option::is_none")] pub mem_total_kb: Option, #[serde(skip_serializing_if = "Option::is_none")] pub mem_percent: Option, pub disks: Vec, } #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct HostMetricsResponse { pub success: bool, /// True when another ssh_exec (e.g. Agent) holds the connection — caller should keep stale UI. #[serde(default, skip_serializing_if = "std::ops::Not::not")] pub skipped: bool, #[serde(skip_serializing_if = "Option::is_none")] pub metrics: Option, #[serde(skip_serializing_if = "Option::is_none")] pub error: Option, } impl HostMetricsResponse { pub fn ok(metrics: HostMetrics) -> Self { Self { success: true, skipped: false, metrics: Some(metrics), error: None, } } pub fn skipped() -> Self { Self { success: true, skipped: true, metrics: None, error: None, } } pub fn error(error: impl Into) -> Self { Self { success: false, skipped: false, metrics: None, error: Some(error.into()), } } }