Rust SSH service: russh async client, DashMap session registry, TOFU host key verification, CWD tracking via separate exec channel (never touches terminal stream), base64 event emission for terminal I/O. 52/52 tests passing. Vue 3 frontend: ported from Wails v3 to Tauri v2 — useTerminal composable with streaming TextDecoder + rAF batching, session store with multi-connection support, connection store/tree, sidebar, tab bar, status bar, keyboard shortcuts. All Wails imports replaced with Tauri API equivalents. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
116 lines
2.9 KiB
Rust
116 lines
2.9 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. 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,
|
|
)
|
|
.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,
|
|
)
|
|
.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.
|
|
#[tauri::command]
|
|
pub async fn disconnect_ssh(
|
|
session_id: String,
|
|
state: State<'_, AppState>,
|
|
) -> Result<(), String> {
|
|
state.ssh.disconnect(&session_id).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())
|
|
}
|