wraith/src-tauri/src/commands/ssh_commands.rs
Vantz Stockwell a8656b0812 feat: Phase 3 complete — SFTP sidebar with full file operations
Rust SFTP service: russh-sftp client on same SSH connection,
DashMap storage, list/read/write/mkdir/delete/rename/stat ops.
5MB file size guard. Non-fatal SFTP failure (terminal still works).

Vue frontend: FileTree with all toolbar buttons wired (upload,
download, delete, mkdir, refresh), TransferProgress panel,
useSftp composable with CWD following via Tauri events.
MainLayout wired with SFTP sidebar.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 15:46:35 -04:00

121 lines
3.1 KiB
Rust

//! Tauri commands for SSH session management.
//!
//! All commands are async because russh operations are inherently asynchronous.
//! The SSH service is accessed via `State<AppState>` and the `AppHandle` is
//! used for event emission.
use tauri::{AppHandle, State};
use crate::ssh::session::{AuthMethod, SessionInfo};
use crate::AppState;
/// Connect to an SSH server with password authentication.
///
/// Opens a PTY, starts a shell, and begins streaming output via
/// `ssh:data:{session_id}` events. Also opens an SFTP subsystem channel on
/// the same connection. Returns the session UUID.
#[tauri::command]
pub async fn connect_ssh(
hostname: String,
port: u16,
username: String,
password: String,
cols: u32,
rows: u32,
app_handle: AppHandle,
state: State<'_, AppState>,
) -> Result<String, String> {
state
.ssh
.connect(
app_handle,
&hostname,
port,
&username,
AuthMethod::Password(password),
cols,
rows,
&state.sftp,
)
.await
}
/// Connect to an SSH server with private key authentication.
///
/// The `private_key_pem` should be the PEM-encoded private key content.
/// `passphrase` is `None` if the key is not encrypted.
///
/// Opens a PTY, starts a shell, and begins streaming output via
/// `ssh:data:{session_id}` events. Returns the session UUID.
#[tauri::command]
pub async fn connect_ssh_with_key(
hostname: String,
port: u16,
username: String,
private_key_pem: String,
passphrase: Option<String>,
cols: u32,
rows: u32,
app_handle: AppHandle,
state: State<'_, AppState>,
) -> Result<String, String> {
state
.ssh
.connect(
app_handle,
&hostname,
port,
&username,
AuthMethod::Key {
private_key_pem,
passphrase,
},
cols,
rows,
&state.sftp,
)
.await
}
/// Write data to a session's PTY stdin.
///
/// The `data` parameter is a string that will be sent as UTF-8 bytes.
#[tauri::command]
pub async fn ssh_write(
session_id: String,
data: String,
state: State<'_, AppState>,
) -> Result<(), String> {
state.ssh.write(&session_id, data.as_bytes()).await
}
/// Resize the PTY window for a session.
#[tauri::command]
pub async fn ssh_resize(
session_id: String,
cols: u32,
rows: u32,
state: State<'_, AppState>,
) -> Result<(), String> {
state.ssh.resize(&session_id, cols, rows).await
}
/// Disconnect an SSH session — closes the channel and removes it.
///
/// Also removes the associated SFTP client.
#[tauri::command]
pub async fn disconnect_ssh(
session_id: String,
state: State<'_, AppState>,
) -> Result<(), String> {
state.ssh.disconnect(&session_id, &state.sftp).await
}
/// List all active SSH sessions (metadata only).
#[tauri::command]
pub async fn list_ssh_sessions(
state: State<'_, AppState>,
) -> Result<Vec<SessionInfo>, String> {
Ok(state.ssh.list_sessions())
}