Files
opsxshell/src-tauri/src/domain/ssh.rs
T

345 lines
8.8 KiB
Rust

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<String>,
pub private_key: Option<String>,
pub passphrase: Option<String>,
pub proxy: Option<serde_json::Value>,
pub jump_host: Option<serde_json::Value>,
pub jump_hosts: Option<Vec<serde_json::Value>>,
pub timeout_seconds: Option<u64>,
pub keep_alive: Option<bool>,
}
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<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error_code: Option<String>,
}
impl OperationResponse {
pub fn ok() -> Self {
Self {
success: true,
error: None,
error_code: None,
}
}
pub fn error(error: impl Into<String>) -> Self {
Self {
success: false,
error: Some(error.into()),
error_code: None,
}
}
pub fn coded(code: &'static str, error: impl Into<String>) -> 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<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub shell_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub sftp_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error_code: Option<String>,
}
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<String>) -> 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<String>) -> 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<Vec<u8>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
}
impl ShellOutputResponse {
pub fn ok(data: Vec<u8>) -> Self {
Self {
success: true,
data: Some(data),
error: None,
}
}
pub fn error(error: impl Into<String>) -> 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<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub exit_code: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
}
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ForwardResponse {
pub success: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub forward_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub bound_port: Option<u16>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
}
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<String>) -> 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<String>,
pub target_port: Option<u16>,
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<u8>,
}
#[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<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub timezone: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub timezone_offset: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub uptime_sec: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub cpu_percent: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub mem_used_kb: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub mem_total_kb: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub mem_percent: Option<u8>,
pub disks: Vec<HostDiskMetric>,
}
#[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<HostMetrics>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
}
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<String>) -> Self {
Self {
success: false,
skipped: false,
metrics: None,
error: Some(error.into()),
}
}
}