sal-modular/wasm_app/src/sigsocket/connection.rs

169 lines
6.0 KiB
Rust

//! SigSocket connection wrapper for WASM
//!
//! This module provides a WASM-bindgen compatible wrapper around the
//! SigSocket client that can be used from JavaScript in the browser extension.
use wasm_bindgen::prelude::*;
use sigsocket_client::{SigSocketClient, SignResponse};
use crate::sigsocket::handler::JavaScriptSignHandler;
/// WASM-bindgen wrapper for SigSocket client
///
/// This provides a clean JavaScript API for the browser extension to:
/// - Connect to SigSocket servers
/// - Send responses to sign requests
/// - Manage connection state
#[wasm_bindgen]
pub struct SigSocketConnection {
client: Option<SigSocketClient>,
connected: bool,
}
#[wasm_bindgen]
impl SigSocketConnection {
/// Create a new SigSocket connection
#[wasm_bindgen(constructor)]
pub fn new() -> Self {
Self {
client: None,
connected: false,
}
}
/// Connect to a SigSocket server
///
/// # Arguments
/// * `server_url` - WebSocket server URL (e.g., "ws://localhost:8080/ws")
/// * `public_key_hex` - Client's public key as hex string
///
/// # Returns
/// * `Ok(())` - Successfully connected
/// * `Err(error)` - Connection failed
#[wasm_bindgen]
pub async fn connect(&mut self, server_url: &str, public_key_hex: &str) -> Result<(), JsValue> {
web_sys::console::log_1(&format!("SigSocketConnection::connect called with URL: {}", server_url).into());
web_sys::console::log_1(&format!("Public key (first 16 chars): {}", &public_key_hex[..16]).into());
// Decode public key from hex
let public_key = hex::decode(public_key_hex)
.map_err(|e| JsValue::from_str(&format!("Invalid public key hex: {}", e)))?;
web_sys::console::log_1(&"Creating SigSocketClient...".into());
// Create client
let mut client = SigSocketClient::new(server_url, public_key)
.map_err(|e| JsValue::from_str(&format!("Failed to create client: {}", e)))?;
web_sys::console::log_1(&"SigSocketClient created, attempting connection...".into());
// Set up JavaScript handler
client.set_sign_handler(JavaScriptSignHandler);
// Connect to server
web_sys::console::log_1(&"Calling client.connect()...".into());
client.connect().await
.map_err(|e| {
web_sys::console::error_1(&format!("Client connection failed: {}", e).into());
JsValue::from_str(&format!("Failed to connect: {}", e))
})?;
web_sys::console::log_1(&"Client connection successful!".into());
self.client = Some(client);
self.connected = true;
web_sys::console::log_1(&"SigSocketConnection state updated to connected".into());
// Notify JavaScript of connection state change
super::handler::on_connection_state_changed(true);
Ok(())
}
/// Send a response to a sign request
///
/// This should be called by the extension after the user has approved
/// a sign request and the message has been signed.
///
/// # Arguments
/// * `request_id` - ID of the original request
/// * `message_base64` - Original message (base64-encoded)
/// * `signature_hex` - Signature as hex string
///
/// # Returns
/// * `Ok(())` - Response sent successfully
/// * `Err(error)` - Failed to send response
#[wasm_bindgen]
pub async fn send_response(&self, request_id: &str, message_base64: &str, signature_hex: &str) -> Result<(), JsValue> {
let client = self.client.as_ref()
.ok_or_else(|| JsValue::from_str("Not connected"))?;
// Decode signature from hex
let signature = hex::decode(signature_hex)
.map_err(|e| JsValue::from_str(&format!("Invalid signature hex: {}", e)))?;
// Create response
let response = SignResponse::new(request_id, message_base64,
base64::Engine::encode(&base64::engine::general_purpose::STANDARD, &signature));
// Send response
client.send_sign_response(&response).await
.map_err(|e| JsValue::from_str(&format!("Failed to send response: {}", e)))?;
Ok(())
}
/// Send a rejection for a sign request
///
/// This should be called when the user rejects a sign request.
///
/// # Arguments
/// * `request_id` - ID of the request to reject
/// * `reason` - Reason for rejection (optional)
///
/// # Returns
/// * `Ok(())` - Rejection sent successfully
/// * `Err(error)` - Failed to send rejection
#[wasm_bindgen]
pub async fn send_rejection(&self, request_id: &str, reason: &str) -> Result<(), JsValue> {
// For now, we'll just log the rejection
// In a full implementation, the server might support rejection messages
web_sys::console::log_1(&format!("Sign request {} rejected: {}", request_id, reason).into());
// TODO: If the server supports rejection messages, send them here
// For now, we just ignore the request (timeout on server side)
Ok(())
}
/// Disconnect from the SigSocket server
#[wasm_bindgen]
pub fn disconnect(&mut self) {
if let Some(_client) = self.client.take() {
// Note: We can't await in a non-async function, so we'll just drop the client
// The Drop implementation should handle cleanup
self.connected = false;
// Notify JavaScript of connection state change
super::handler::on_connection_state_changed(false);
}
}
/// Check if connected to the server
#[wasm_bindgen]
pub fn is_connected(&self) -> bool {
// Check if we have a client and if it reports as connected
if let Some(ref client) = self.client {
client.is_connected()
} else {
false
}
}
}
impl Default for SigSocketConnection {
fn default() -> Self {
Self::new()
}
}