v2
This commit is contained in:
parent
203cde1cba
commit
6f42e5ab8d
@ -6,6 +6,9 @@ let sessionTimeoutDuration = 15; // Default 15 seconds
|
|||||||
let sessionTimeoutId = null; // Background timer
|
let sessionTimeoutId = null; // Background timer
|
||||||
let popupPort = null; // Track popup connection
|
let popupPort = null; // Track popup connection
|
||||||
|
|
||||||
|
// SigSocket service instance
|
||||||
|
let sigSocketService = null;
|
||||||
|
|
||||||
// Utility function to convert Uint8Array to hex
|
// Utility function to convert Uint8Array to hex
|
||||||
function toHex(uint8Array) {
|
function toHex(uint8Array) {
|
||||||
return Array.from(uint8Array)
|
return Array.from(uint8Array)
|
||||||
@ -135,8 +138,9 @@ async function restoreSession() {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Import WASM module functions
|
// Import WASM module functions and SigSocket service
|
||||||
import init, * as wasmFunctions from './wasm/wasm_app.js';
|
import init, * as wasmFunctions from './wasm/wasm_app.js';
|
||||||
|
import SigSocketService from './background/sigsocket.js';
|
||||||
|
|
||||||
// Initialize WASM module
|
// Initialize WASM module
|
||||||
async function initVault() {
|
async function initVault() {
|
||||||
@ -151,6 +155,13 @@ async function initVault() {
|
|||||||
vault = wasmFunctions;
|
vault = wasmFunctions;
|
||||||
isInitialized = true;
|
isInitialized = true;
|
||||||
|
|
||||||
|
// Initialize SigSocket service
|
||||||
|
if (!sigSocketService) {
|
||||||
|
sigSocketService = new SigSocketService();
|
||||||
|
await sigSocketService.initialize(vault);
|
||||||
|
console.log('🔌 SigSocket service initialized');
|
||||||
|
}
|
||||||
|
|
||||||
// Try to restore previous session
|
// Try to restore previous session
|
||||||
await restoreSession();
|
await restoreSession();
|
||||||
|
|
||||||
@ -172,6 +183,20 @@ const messageHandlers = {
|
|||||||
initSession: async (request) => {
|
initSession: async (request) => {
|
||||||
await vault.init_session(request.keyspace, request.password);
|
await vault.init_session(request.keyspace, request.password);
|
||||||
await sessionManager.save(request.keyspace);
|
await sessionManager.save(request.keyspace);
|
||||||
|
|
||||||
|
// Smart auto-connect to SigSocket when session is initialized
|
||||||
|
if (sigSocketService) {
|
||||||
|
try {
|
||||||
|
// This will reuse existing connection if same workspace, or switch if different
|
||||||
|
const connected = await sigSocketService.connectToServer(request.keyspace);
|
||||||
|
if (connected) {
|
||||||
|
console.log(`🔗 SigSocket ready for workspace: ${request.keyspace}`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('Failed to auto-connect to SigSocket:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return { success: true };
|
return { success: true };
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -261,6 +286,62 @@ const messageHandlers = {
|
|||||||
await chrome.storage.local.set({ sessionTimeout: request.timeout });
|
await chrome.storage.local.set({ sessionTimeout: request.timeout });
|
||||||
resetSessionTimeout(); // Restart with new duration
|
resetSessionTimeout(); // Restart with new duration
|
||||||
return { success: true };
|
return { success: true };
|
||||||
|
},
|
||||||
|
|
||||||
|
// SigSocket handlers
|
||||||
|
connectSigSocket: async (request) => {
|
||||||
|
if (!sigSocketService) {
|
||||||
|
return { success: false, error: 'SigSocket service not initialized' };
|
||||||
|
}
|
||||||
|
const connected = await sigSocketService.connectToServer(request.workspace);
|
||||||
|
return { success: connected };
|
||||||
|
},
|
||||||
|
|
||||||
|
disconnectSigSocket: async () => {
|
||||||
|
if (!sigSocketService) {
|
||||||
|
return { success: false, error: 'SigSocket service not initialized' };
|
||||||
|
}
|
||||||
|
await sigSocketService.disconnect();
|
||||||
|
return { success: true };
|
||||||
|
},
|
||||||
|
|
||||||
|
getSigSocketStatus: async () => {
|
||||||
|
if (!sigSocketService) {
|
||||||
|
return { success: false, error: 'SigSocket service not initialized' };
|
||||||
|
}
|
||||||
|
const status = await sigSocketService.getStatus();
|
||||||
|
return { success: true, status };
|
||||||
|
},
|
||||||
|
|
||||||
|
getPendingSignRequests: async () => {
|
||||||
|
if (!sigSocketService) {
|
||||||
|
return { success: false, error: 'SigSocket service not initialized' };
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Use WASM filtered requests which handles workspace filtering
|
||||||
|
const requests = await sigSocketService.getFilteredRequests();
|
||||||
|
return { success: true, requests };
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to get pending requests:', error);
|
||||||
|
return { success: false, error: error.message };
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
approveSignRequest: async (request) => {
|
||||||
|
if (!sigSocketService) {
|
||||||
|
return { success: false, error: 'SigSocket service not initialized' };
|
||||||
|
}
|
||||||
|
const approved = await sigSocketService.approveSignRequest(request.requestId);
|
||||||
|
return { success: approved };
|
||||||
|
},
|
||||||
|
|
||||||
|
rejectSignRequest: async (request) => {
|
||||||
|
if (!sigSocketService) {
|
||||||
|
return { success: false, error: 'SigSocket service not initialized' };
|
||||||
|
}
|
||||||
|
const rejected = await sigSocketService.rejectSignRequest(request.requestId, request.reason);
|
||||||
|
return { success: rejected };
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -302,6 +383,11 @@ chrome.runtime.onConnect.addListener((port) => {
|
|||||||
// Track popup connection
|
// Track popup connection
|
||||||
popupPort = port;
|
popupPort = port;
|
||||||
|
|
||||||
|
// Connect SigSocket service to popup
|
||||||
|
if (sigSocketService) {
|
||||||
|
sigSocketService.setPopupPort(port);
|
||||||
|
}
|
||||||
|
|
||||||
// If we have an active session, ensure keep-alive is running
|
// If we have an active session, ensure keep-alive is running
|
||||||
if (currentSession) {
|
if (currentSession) {
|
||||||
startKeepAlive();
|
startKeepAlive();
|
||||||
@ -311,6 +397,11 @@ chrome.runtime.onConnect.addListener((port) => {
|
|||||||
// Popup closed, clear reference and stop keep-alive
|
// Popup closed, clear reference and stop keep-alive
|
||||||
popupPort = null;
|
popupPort = null;
|
||||||
stopKeepAlive();
|
stopKeepAlive();
|
||||||
|
|
||||||
|
// Disconnect SigSocket service from popup
|
||||||
|
if (sigSocketService) {
|
||||||
|
sigSocketService.setPopupPort(null);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
@ -1,31 +1,35 @@
|
|||||||
/**
|
/**
|
||||||
* SigSocket Service for Browser Extension
|
* SigSocket Service - Clean Implementation with New WASM APIs
|
||||||
*
|
*
|
||||||
* Handles SigSocket client functionality including:
|
* This service provides a clean interface for SigSocket functionality using
|
||||||
* - Auto-connecting to SigSocket server when workspace is created
|
* the new WASM-based APIs that handle all WebSocket management, request storage,
|
||||||
* - Managing pending sign requests
|
* and security validation internally.
|
||||||
* - Handling user approval/rejection flow
|
*
|
||||||
* - Validating keyspace matches before showing approval UI
|
* Architecture:
|
||||||
|
* - WASM handles: WebSocket connection, message parsing, request storage, security
|
||||||
|
* - Extension handles: UI notifications, badge updates, user interactions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class SigSocketService {
|
class SigSocketService {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.connection = null;
|
// Connection state
|
||||||
this.pendingRequests = new Map(); // requestId -> SignRequestData
|
|
||||||
this.connectedPublicKey = null;
|
|
||||||
this.isConnected = false;
|
this.isConnected = false;
|
||||||
|
this.currentWorkspace = null;
|
||||||
|
this.connectedPublicKey = null;
|
||||||
|
|
||||||
|
// Configuration
|
||||||
this.defaultServerUrl = "ws://localhost:8080/ws";
|
this.defaultServerUrl = "ws://localhost:8080/ws";
|
||||||
|
|
||||||
// Initialize WASM module reference
|
// WASM module reference
|
||||||
this.wasmModule = null;
|
this.wasmModule = null;
|
||||||
|
|
||||||
// Reference to popup port for communication
|
// UI communication
|
||||||
this.popupPort = null;
|
this.popupPort = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the service with WASM module
|
* Initialize the service with WASM module
|
||||||
* @param {Object} wasmModule - The loaded WASM module
|
* @param {Object} wasmModule - The loaded WASM module with SigSocketManager
|
||||||
*/
|
*/
|
||||||
async initialize(wasmModule) {
|
async initialize(wasmModule) {
|
||||||
this.wasmModule = wasmModule;
|
this.wasmModule = wasmModule;
|
||||||
@ -40,427 +44,333 @@ class SigSocketService {
|
|||||||
console.warn('Failed to load SigSocket URL from storage:', error);
|
console.warn('Failed to load SigSocket URL from storage:', error);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up global callbacks for WASM
|
console.log('🔌 SigSocket service initialized with WASM APIs');
|
||||||
globalThis.onSignRequestReceived = this.handleIncomingRequest.bind(this);
|
|
||||||
globalThis.onConnectionStateChanged = this.handleConnectionStateChange.bind(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Connect to SigSocket server for a workspace
|
* Connect to SigSocket server using WASM APIs
|
||||||
|
* WASM handles all connection logic (reuse, switching, etc.)
|
||||||
* @param {string} workspaceId - The workspace/keyspace identifier
|
* @param {string} workspaceId - The workspace/keyspace identifier
|
||||||
* @returns {Promise<boolean>} - True if connected successfully
|
* @returns {Promise<boolean>} - True if connected successfully
|
||||||
*/
|
*/
|
||||||
async connectToServer(workspaceId) {
|
async connectToServer(workspaceId) {
|
||||||
try {
|
try {
|
||||||
if (!this.wasmModule) {
|
if (!this.wasmModule?.SigSocketManager) {
|
||||||
throw new Error('WASM module not initialized');
|
throw new Error('WASM SigSocketManager not available');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if already connected to this workspace
|
console.log(`🔗 Requesting SigSocket connection for workspace: ${workspaceId}`);
|
||||||
if (this.isConnected && this.connection) {
|
|
||||||
console.log(`Already connected to SigSocket server for workspace: ${workspaceId}`);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Disconnect any existing connection first
|
// Let WASM handle all connection logic (reuse, switching, etc.)
|
||||||
if (this.connection) {
|
const connectionInfo = await this.wasmModule.SigSocketManager.connect_workspace_with_events(
|
||||||
this.disconnect();
|
workspaceId,
|
||||||
}
|
this.defaultServerUrl,
|
||||||
|
(event) => this.handleSigSocketEvent(event)
|
||||||
|
);
|
||||||
|
|
||||||
// Get the workspace default public key
|
// Parse connection info
|
||||||
const publicKeyHex = await this.wasmModule.get_workspace_default_public_key(workspaceId);
|
const info = JSON.parse(connectionInfo);
|
||||||
if (!publicKeyHex) {
|
this.currentWorkspace = info.workspace;
|
||||||
throw new Error('No public key found for workspace');
|
this.connectedPublicKey = info.public_key;
|
||||||
}
|
this.isConnected = info.is_connected;
|
||||||
|
|
||||||
console.log(`Connecting to SigSocket server for workspace: ${workspaceId} with key: ${publicKeyHex.substring(0, 16)}...`);
|
console.log(`✅ SigSocket connection result:`, {
|
||||||
|
workspace: this.currentWorkspace,
|
||||||
|
publicKey: this.connectedPublicKey?.substring(0, 16) + '...',
|
||||||
|
connected: this.isConnected
|
||||||
|
});
|
||||||
|
|
||||||
// Create new SigSocket connection
|
// Update badge to show current state
|
||||||
console.log('Creating new SigSocketConnection instance');
|
this.updateBadge();
|
||||||
this.connection = new this.wasmModule.SigSocketConnection();
|
|
||||||
console.log('SigSocketConnection instance created');
|
|
||||||
|
|
||||||
// Connect to server
|
return this.isConnected;
|
||||||
await this.connection.connect(this.defaultServerUrl, publicKeyHex);
|
|
||||||
|
|
||||||
this.connectedPublicKey = publicKeyHex;
|
|
||||||
|
|
||||||
// Clear pending requests if switching to a different workspace
|
|
||||||
if (this.currentWorkspace && this.currentWorkspace !== workspaceId) {
|
|
||||||
console.log(`Switching workspace from ${this.currentWorkspace} to ${workspaceId}, clearing pending requests`);
|
|
||||||
this.pendingRequests.clear();
|
|
||||||
this.updateBadge();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.currentWorkspace = workspaceId;
|
|
||||||
this.isConnected = true;
|
|
||||||
|
|
||||||
console.log(`Successfully connected to SigSocket server for workspace: ${workspaceId}`);
|
|
||||||
return true;
|
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to connect to SigSocket server:', error);
|
console.error('❌ SigSocket connection failed:', error);
|
||||||
this.isConnected = false;
|
this.isConnected = false;
|
||||||
this.connectedPublicKey = null;
|
|
||||||
this.currentWorkspace = null;
|
this.currentWorkspace = null;
|
||||||
if (this.connection) {
|
this.connectedPublicKey = null;
|
||||||
this.connection.disconnect();
|
|
||||||
this.connection = null;
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle incoming sign request from server
|
* Handle events from the WASM SigSocket client
|
||||||
* @param {string} requestId - Unique request identifier
|
* This is called automatically when requests arrive
|
||||||
* @param {string} messageBase64 - Message to be signed (base64-encoded)
|
* @param {Object} event - Event from WASM layer
|
||||||
*/
|
*/
|
||||||
handleIncomingRequest(requestId, messageBase64) {
|
handleSigSocketEvent(event) {
|
||||||
console.log(`Received sign request: ${requestId}`);
|
console.log('📨 Received SigSocket event:', event);
|
||||||
|
|
||||||
// Security check: Only accept requests if we have an active connection
|
if (event.type === 'sign_request') {
|
||||||
if (!this.isConnected || !this.connectedPublicKey || !this.currentWorkspace) {
|
console.log(`🔐 New sign request: ${event.request_id}`);
|
||||||
console.warn(`Rejecting sign request ${requestId}: No active workspace connection`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store the request with workspace info
|
// The request is automatically stored by WASM
|
||||||
const requestData = {
|
// We just handle UI updates
|
||||||
id: requestId,
|
this.showSignRequestNotification();
|
||||||
message: messageBase64,
|
this.updateBadge();
|
||||||
timestamp: Date.now(),
|
this.notifyPopupOfNewRequest();
|
||||||
status: 'pending',
|
|
||||||
workspace: this.currentWorkspace,
|
|
||||||
connectedPublicKey: this.connectedPublicKey
|
|
||||||
};
|
|
||||||
|
|
||||||
this.pendingRequests.set(requestId, requestData);
|
|
||||||
|
|
||||||
console.log(`Stored sign request for workspace: ${this.currentWorkspace}`);
|
|
||||||
|
|
||||||
// Show notification to user
|
|
||||||
this.showSignRequestNotification();
|
|
||||||
|
|
||||||
// Update extension badge
|
|
||||||
this.updateBadge();
|
|
||||||
|
|
||||||
// Notify popup about new request if it's open and keyspace is unlocked
|
|
||||||
this.notifyPopupOfNewRequest();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle connection state changes
|
|
||||||
* @param {boolean} connected - True if connected, false if disconnected
|
|
||||||
*/
|
|
||||||
handleConnectionStateChange(connected) {
|
|
||||||
this.isConnected = connected;
|
|
||||||
console.log(`SigSocket connection state changed: ${connected ? 'connected' : 'disconnected'}`);
|
|
||||||
|
|
||||||
if (!connected) {
|
|
||||||
this.connectedPublicKey = null;
|
|
||||||
this.currentWorkspace = null;
|
|
||||||
// Optionally attempt reconnection here
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when keyspace is unlocked - validate and show/hide approval UI
|
* Approve a sign request using WASM APIs
|
||||||
*/
|
|
||||||
async onKeypaceUnlocked() {
|
|
||||||
try {
|
|
||||||
if (!this.wasmModule) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only check keyspace match if we have a connection
|
|
||||||
if (!this.isConnected || !this.connectedPublicKey) {
|
|
||||||
console.log('No SigSocket connection to validate against');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the currently unlocked workspace name
|
|
||||||
const unlockedWorkspaceName = this.wasmModule.get_current_keyspace_name();
|
|
||||||
|
|
||||||
// Get workspace default public key for the UNLOCKED workspace (not connected workspace)
|
|
||||||
const unlockedWorkspacePublicKey = await this.wasmModule.get_workspace_default_public_key(unlockedWorkspaceName);
|
|
||||||
|
|
||||||
// Check if the unlocked workspace matches the connected workspace
|
|
||||||
const workspaceMatches = unlockedWorkspaceName === this.currentWorkspace;
|
|
||||||
const publicKeyMatches = unlockedWorkspacePublicKey === this.connectedPublicKey;
|
|
||||||
const keypaceMatches = workspaceMatches && publicKeyMatches;
|
|
||||||
|
|
||||||
console.log(`Keyspace unlock validation:`);
|
|
||||||
console.log(` Connected workspace: ${this.currentWorkspace}`);
|
|
||||||
console.log(` Unlocked workspace: ${unlockedWorkspaceName}`);
|
|
||||||
console.log(` Connected public key: ${this.connectedPublicKey}`);
|
|
||||||
console.log(` Unlocked public key: ${unlockedWorkspacePublicKey}`);
|
|
||||||
console.log(` Workspace matches: ${workspaceMatches}`);
|
|
||||||
console.log(` Public key matches: ${publicKeyMatches}`);
|
|
||||||
console.log(` Overall match: ${keypaceMatches}`);
|
|
||||||
|
|
||||||
// Always get current pending requests (filtered by connected workspace)
|
|
||||||
const currentPendingRequests = this.getPendingRequests();
|
|
||||||
|
|
||||||
// Notify popup about keyspace state
|
|
||||||
console.log(`Sending KEYSPACE_UNLOCKED message to popup: keypaceMatches=${keypaceMatches}, pendingRequests=${currentPendingRequests.length}`);
|
|
||||||
if (this.popupPort) {
|
|
||||||
this.popupPort.postMessage({
|
|
||||||
type: 'KEYSPACE_UNLOCKED',
|
|
||||||
keypaceMatches,
|
|
||||||
pendingRequests: currentPendingRequests
|
|
||||||
});
|
|
||||||
console.log('KEYSPACE_UNLOCKED message sent to popup');
|
|
||||||
} else {
|
|
||||||
console.log('No popup port available to send KEYSPACE_UNLOCKED message');
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
if (error.message && error.message.includes('Workspace not found')) {
|
|
||||||
console.log(`Keyspace unlock: Different workspace unlocked (connected to: ${this.currentWorkspace})`);
|
|
||||||
|
|
||||||
// Send message with no match and empty requests
|
|
||||||
if (this.popupPort) {
|
|
||||||
this.popupPort.postMessage({
|
|
||||||
type: 'KEYSPACE_UNLOCKED',
|
|
||||||
keypaceMatches: false,
|
|
||||||
pendingRequests: []
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.error('Error handling keyspace unlock:', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Approve a sign request
|
|
||||||
* @param {string} requestId - Request to approve
|
* @param {string} requestId - Request to approve
|
||||||
* @returns {Promise<boolean>} - True if approved successfully
|
* @returns {Promise<boolean>} - True if approved successfully
|
||||||
*/
|
*/
|
||||||
async approveSignRequest(requestId) {
|
async approveSignRequest(requestId) {
|
||||||
try {
|
try {
|
||||||
const request = this.pendingRequests.get(requestId);
|
if (!this.wasmModule?.SigSocketManager) {
|
||||||
if (!request) {
|
throw new Error('WASM SigSocketManager not available');
|
||||||
throw new Error('Request not found');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate request is for current workspace
|
console.log(`✅ Approving request: ${requestId}`);
|
||||||
if (request.workspace !== this.currentWorkspace) {
|
|
||||||
throw new Error(`Request is for workspace '${request.workspace}', but current workspace is '${this.currentWorkspace}'`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (request.connectedPublicKey !== this.connectedPublicKey) {
|
// WASM handles all validation, signing, and server communication
|
||||||
throw new Error('Request public key does not match current connection');
|
await this.wasmModule.SigSocketManager.approve_request(requestId);
|
||||||
}
|
|
||||||
|
|
||||||
// Validate keyspace is still unlocked and matches
|
console.log(`🎉 Request approved successfully: ${requestId}`);
|
||||||
if (!this.wasmModule.is_unlocked()) {
|
|
||||||
throw new Error('Keyspace is locked');
|
|
||||||
}
|
|
||||||
|
|
||||||
const currentPublicKey = await this.wasmModule.get_workspace_default_public_key(this.currentWorkspace);
|
// Update UI
|
||||||
if (currentPublicKey !== this.connectedPublicKey) {
|
|
||||||
throw new Error('Keyspace mismatch');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode message from base64
|
|
||||||
const messageBytes = atob(request.message).split('').map(c => c.charCodeAt(0));
|
|
||||||
|
|
||||||
// Sign the message with default keypair (doesn't require selected keypair)
|
|
||||||
const signatureHex = await this.wasmModule.sign_with_default_keypair(new Uint8Array(messageBytes));
|
|
||||||
|
|
||||||
// Send response to server
|
|
||||||
await this.connection.send_response(requestId, request.message, signatureHex);
|
|
||||||
|
|
||||||
// Update request status
|
|
||||||
request.status = 'approved';
|
|
||||||
request.signature = signatureHex;
|
|
||||||
|
|
||||||
// Remove from pending requests
|
|
||||||
this.pendingRequests.delete(requestId);
|
|
||||||
|
|
||||||
// Update badge
|
|
||||||
this.updateBadge();
|
this.updateBadge();
|
||||||
|
this.notifyPopupOfRequestUpdate();
|
||||||
|
|
||||||
console.log(`Approved sign request: ${requestId}`);
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to approve sign request:', error);
|
console.error(`❌ Failed to approve request ${requestId}:`, error);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reject a sign request
|
* Reject a sign request using WASM APIs
|
||||||
* @param {string} requestId - Request to reject
|
* @param {string} requestId - Request to reject
|
||||||
* @param {string} reason - Reason for rejection (optional)
|
* @param {string} reason - Reason for rejection
|
||||||
* @returns {Promise<boolean>} - True if rejected successfully
|
* @returns {Promise<boolean>} - True if rejected successfully
|
||||||
*/
|
*/
|
||||||
async rejectSignRequest(requestId, reason = 'User rejected') {
|
async rejectSignRequest(requestId, reason = 'User rejected') {
|
||||||
try {
|
try {
|
||||||
const request = this.pendingRequests.get(requestId);
|
if (!this.wasmModule?.SigSocketManager) {
|
||||||
if (!request) {
|
throw new Error('WASM SigSocketManager not available');
|
||||||
throw new Error('Request not found');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send rejection to server
|
console.log(`❌ Rejecting request: ${requestId}, reason: ${reason}`);
|
||||||
await this.connection.send_rejection(requestId, reason);
|
|
||||||
|
|
||||||
// Update request status
|
// WASM handles rejection and server communication
|
||||||
request.status = 'rejected';
|
await this.wasmModule.SigSocketManager.reject_request(requestId, reason);
|
||||||
request.reason = reason;
|
|
||||||
|
|
||||||
// Remove from pending requests
|
console.log(`✅ Request rejected successfully: ${requestId}`);
|
||||||
this.pendingRequests.delete(requestId);
|
|
||||||
|
|
||||||
// Update badge
|
// Update UI
|
||||||
this.updateBadge();
|
this.updateBadge();
|
||||||
|
this.notifyPopupOfRequestUpdate();
|
||||||
|
|
||||||
console.log(`Rejected sign request: ${requestId}, reason: ${reason}`);
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to reject sign request:', error);
|
console.error(`❌ Failed to reject request ${requestId}:`, error);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all pending requests for the current workspace
|
* Get pending requests from WASM (filtered by current workspace)
|
||||||
* @returns {Array} - Array of pending request data for current workspace
|
* @returns {Promise<Array>} - Array of pending requests for current workspace
|
||||||
*/
|
*/
|
||||||
getPendingRequests() {
|
async getPendingRequests() {
|
||||||
const allRequests = Array.from(this.pendingRequests.values());
|
return this.getFilteredRequests();
|
||||||
|
}
|
||||||
|
|
||||||
// Filter requests to only include those for the current workspace
|
/**
|
||||||
const filteredRequests = allRequests.filter(request => {
|
* Get filtered requests from WASM (workspace-aware)
|
||||||
const isCurrentWorkspace = request.workspace === this.currentWorkspace;
|
* @returns {Promise<Array>} - Array of filtered requests
|
||||||
const isCurrentPublicKey = request.connectedPublicKey === this.connectedPublicKey;
|
*/
|
||||||
|
async getFilteredRequests() {
|
||||||
if (!isCurrentWorkspace || !isCurrentPublicKey) {
|
try {
|
||||||
console.log(`Filtering out request ${request.id}: workspace=${request.workspace} (current=${this.currentWorkspace}), publicKey match=${isCurrentPublicKey}`);
|
if (!this.wasmModule?.SigSocketManager) {
|
||||||
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
return isCurrentWorkspace && isCurrentPublicKey;
|
const requestsJson = await this.wasmModule.SigSocketManager.get_filtered_requests();
|
||||||
});
|
const requests = JSON.parse(requestsJson);
|
||||||
|
|
||||||
console.log(`getPendingRequests: ${allRequests.length} total, ${filteredRequests.length} for current workspace`);
|
console.log(`📋 Retrieved ${requests.length} filtered requests for current workspace`);
|
||||||
return filteredRequests;
|
return requests;
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to get filtered requests:', error);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a request can be approved (keyspace validation)
|
||||||
|
* @param {string} requestId - Request ID to check
|
||||||
|
* @returns {Promise<boolean>} - True if can be approved
|
||||||
|
*/
|
||||||
|
async canApproveRequest(requestId) {
|
||||||
|
try {
|
||||||
|
if (!this.wasmModule?.SigSocketManager) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return await this.wasmModule.SigSocketManager.can_approve_request(requestId);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to check request approval status:', error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show notification for new sign request
|
* Show notification for new sign request
|
||||||
*/
|
*/
|
||||||
showSignRequestNotification() {
|
showSignRequestNotification() {
|
||||||
// Create notification
|
try {
|
||||||
chrome.notifications.create({
|
if (chrome.notifications && chrome.notifications.create) {
|
||||||
type: 'basic',
|
chrome.notifications.create({
|
||||||
iconUrl: 'icons/icon48.png',
|
type: 'basic',
|
||||||
title: 'Sign Request',
|
iconUrl: 'icons/icon48.png',
|
||||||
message: 'New signature request received. Click to review.'
|
title: 'SigSocket Sign Request',
|
||||||
});
|
message: 'New signature request received. Click to review.'
|
||||||
|
});
|
||||||
|
console.log('📢 Notification shown for sign request');
|
||||||
|
} else {
|
||||||
|
console.log('📢 Notifications not available, skipping notification');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('Failed to show notification:', error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notify popup about new request if popup is open and keyspace is unlocked
|
* Update extension badge with pending request count
|
||||||
|
*/
|
||||||
|
async updateBadge() {
|
||||||
|
try {
|
||||||
|
const requests = await this.getPendingRequests();
|
||||||
|
const count = requests.length;
|
||||||
|
const badgeText = count > 0 ? count.toString() : '';
|
||||||
|
|
||||||
|
console.log(`🔢 Updating badge: ${count} pending requests`);
|
||||||
|
|
||||||
|
chrome.action.setBadgeText({ text: badgeText });
|
||||||
|
chrome.action.setBadgeBackgroundColor({ color: '#ff6b6b' });
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to update badge:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify popup about new request
|
||||||
*/
|
*/
|
||||||
async notifyPopupOfNewRequest() {
|
async notifyPopupOfNewRequest() {
|
||||||
// Only notify if popup is connected
|
|
||||||
if (!this.popupPort) {
|
if (!this.popupPort) {
|
||||||
console.log('No popup port available, skipping new request notification');
|
console.log('No popup connected, skipping notification');
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if we have WASM module and can validate keyspace
|
|
||||||
if (!this.wasmModule) {
|
|
||||||
console.log('WASM module not available, skipping new request notification');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Check if keyspace is unlocked
|
const requests = await this.getPendingRequests();
|
||||||
if (!this.wasmModule.is_unlocked()) {
|
const canApprove = requests.length > 0 ? await this.canApproveRequest(requests[0].id) : false;
|
||||||
console.log('Keyspace is locked, skipping new request notification');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the currently unlocked workspace name
|
|
||||||
const unlockedWorkspaceName = this.wasmModule.get_current_keyspace_name();
|
|
||||||
|
|
||||||
// Get workspace default public key for the UNLOCKED workspace
|
|
||||||
const unlockedWorkspacePublicKey = await this.wasmModule.get_workspace_default_public_key(unlockedWorkspaceName);
|
|
||||||
|
|
||||||
// Check if the unlocked workspace matches the connected workspace
|
|
||||||
const workspaceMatches = unlockedWorkspaceName === this.currentWorkspace;
|
|
||||||
const publicKeyMatches = unlockedWorkspacePublicKey === this.connectedPublicKey;
|
|
||||||
const keypaceMatches = workspaceMatches && publicKeyMatches;
|
|
||||||
|
|
||||||
console.log(`New request notification check: keypaceMatches=${keypaceMatches}, workspace=${unlockedWorkspaceName}, connected=${this.currentWorkspace}`);
|
|
||||||
|
|
||||||
// Get current pending requests (filtered by connected workspace)
|
|
||||||
const currentPendingRequests = this.getPendingRequests();
|
|
||||||
|
|
||||||
// SECURITY: Only send requests if workspace matches, otherwise send empty array
|
|
||||||
const requestsToSend = keypaceMatches ? currentPendingRequests : [];
|
|
||||||
|
|
||||||
// Send update to popup
|
|
||||||
this.popupPort.postMessage({
|
this.popupPort.postMessage({
|
||||||
type: 'NEW_SIGN_REQUEST',
|
type: 'NEW_SIGN_REQUEST',
|
||||||
keypaceMatches,
|
canApprove,
|
||||||
pendingRequests: requestsToSend
|
pendingRequests: requests
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(`Sent NEW_SIGN_REQUEST message to popup: keypaceMatches=${keypaceMatches}, ${requestsToSend.length} requests (${currentPendingRequests.length} total for connected workspace)`);
|
console.log(`📤 Notified popup: ${requests.length} requests, canApprove: ${canApprove}`);
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log('Error in notifyPopupOfNewRequest:', error);
|
console.error('Failed to notify popup:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update extension badge with pending request count for current workspace
|
* Notify popup about request updates
|
||||||
*/
|
*/
|
||||||
updateBadge() {
|
async notifyPopupOfRequestUpdate() {
|
||||||
// Only count requests for the current workspace
|
if (!this.popupPort) return;
|
||||||
const currentWorkspaceRequests = this.getPendingRequests();
|
|
||||||
const count = currentWorkspaceRequests.length;
|
|
||||||
const badgeText = count > 0 ? count.toString() : '';
|
|
||||||
|
|
||||||
console.log(`Updating badge: ${this.pendingRequests.size} total requests, ${count} for current workspace, badge text: "${badgeText}"`);
|
try {
|
||||||
|
const requests = await this.getPendingRequests();
|
||||||
|
|
||||||
chrome.action.setBadgeText({ text: badgeText });
|
this.popupPort.postMessage({
|
||||||
chrome.action.setBadgeBackgroundColor({ color: '#ff6b6b' });
|
type: 'REQUESTS_UPDATED',
|
||||||
|
pendingRequests: requests
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to notify popup of update:', error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disconnect from SigSocket server
|
* Disconnect from SigSocket server
|
||||||
|
* WASM handles all disconnection logic
|
||||||
*/
|
*/
|
||||||
disconnect() {
|
async disconnect() {
|
||||||
if (this.connection) {
|
try {
|
||||||
this.connection.disconnect();
|
if (this.wasmModule?.SigSocketManager) {
|
||||||
this.connection = null;
|
await this.wasmModule.SigSocketManager.disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.isConnected = false;
|
// Clear local state
|
||||||
this.connectedPublicKey = null;
|
this.isConnected = false;
|
||||||
this.currentWorkspace = null;
|
this.currentWorkspace = null;
|
||||||
this.pendingRequests.clear();
|
this.connectedPublicKey = null;
|
||||||
this.updateBadge();
|
|
||||||
|
this.updateBadge();
|
||||||
|
|
||||||
|
console.log('🔌 SigSocket disconnection requested');
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to disconnect:', error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get connection status
|
* Get connection status from WASM
|
||||||
* @returns {Object} - Connection status information
|
* @returns {Promise<Object>} - Connection status information
|
||||||
*/
|
*/
|
||||||
getStatus() {
|
async getStatus() {
|
||||||
return {
|
try {
|
||||||
isConnected: this.isConnected,
|
if (!this.wasmModule?.SigSocketManager) {
|
||||||
connectedPublicKey: this.connectedPublicKey,
|
return {
|
||||||
pendingRequestCount: this.getPendingRequests().length,
|
isConnected: false,
|
||||||
serverUrl: this.defaultServerUrl
|
workspace: null,
|
||||||
};
|
publicKey: null,
|
||||||
|
pendingRequestCount: 0,
|
||||||
|
serverUrl: this.defaultServerUrl
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Let WASM provide the authoritative status
|
||||||
|
const statusJson = await this.wasmModule.SigSocketManager.get_connection_status();
|
||||||
|
const status = JSON.parse(statusJson);
|
||||||
|
const requests = await this.getPendingRequests();
|
||||||
|
|
||||||
|
return {
|
||||||
|
isConnected: status.is_connected,
|
||||||
|
workspace: status.workspace,
|
||||||
|
publicKey: status.public_key,
|
||||||
|
pendingRequestCount: requests.length,
|
||||||
|
serverUrl: this.defaultServerUrl
|
||||||
|
};
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to get status:', error);
|
||||||
|
return {
|
||||||
|
isConnected: false,
|
||||||
|
workspace: null,
|
||||||
|
publicKey: null,
|
||||||
|
pendingRequestCount: 0,
|
||||||
|
serverUrl: this.defaultServerUrl
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -469,6 +379,30 @@ class SigSocketService {
|
|||||||
*/
|
*/
|
||||||
setPopupPort(port) {
|
setPopupPort(port) {
|
||||||
this.popupPort = port;
|
this.popupPort = port;
|
||||||
|
console.log('📱 Popup connected to SigSocket service');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when keyspace is unlocked - notify popup of current state
|
||||||
|
*/
|
||||||
|
async onKeypaceUnlocked() {
|
||||||
|
if (!this.popupPort) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const requests = await this.getPendingRequests();
|
||||||
|
const canApprove = requests.length > 0 ? await this.canApproveRequest(requests[0].id) : false;
|
||||||
|
|
||||||
|
this.popupPort.postMessage({
|
||||||
|
type: 'KEYSPACE_UNLOCKED',
|
||||||
|
canApprove,
|
||||||
|
pendingRequests: requests
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`🔓 Keyspace unlocked notification sent: ${requests.length} requests, canApprove: ${canApprove}`);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to handle keyspace unlock:', error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,7 +6,8 @@
|
|||||||
|
|
||||||
"permissions": [
|
"permissions": [
|
||||||
"storage",
|
"storage",
|
||||||
"activeTab"
|
"activeTab",
|
||||||
|
"notifications"
|
||||||
],
|
],
|
||||||
|
|
||||||
"icons": {
|
"icons": {
|
||||||
|
@ -73,6 +73,50 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- SigSocket Requests Section -->
|
||||||
|
<div class="card sigsocket-section" id="sigSocketSection">
|
||||||
|
<div class="section-header">
|
||||||
|
<h3>🔌 SigSocket Requests</h3>
|
||||||
|
<div class="connection-status" id="connectionStatus">
|
||||||
|
<span class="status-dot" id="connectionDot"></span>
|
||||||
|
<span id="connectionText">Disconnected</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="requests-container" id="requestsContainer">
|
||||||
|
<div class="no-requests" id="noRequestsMessage">
|
||||||
|
<div class="empty-state">
|
||||||
|
<div class="empty-icon">📝</div>
|
||||||
|
<p>No pending sign requests</p>
|
||||||
|
<small>Requests will appear here when received from SigSocket server</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="requests-list hidden" id="requestsList">
|
||||||
|
<!-- Requests will be populated here -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sigsocket-actions">
|
||||||
|
<button id="refreshRequestsBtn" class="btn btn-ghost btn-small">
|
||||||
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<polyline points="23 4 23 10 17 10"></polyline>
|
||||||
|
<polyline points="1 20 1 14 7 14"></polyline>
|
||||||
|
<path d="M20.49 9A9 9 0 0 0 5.64 5.64L1 10m22 4l-4.64 4.36A9 9 0 0 1 3.51 15"></path>
|
||||||
|
</svg>
|
||||||
|
Refresh
|
||||||
|
</button>
|
||||||
|
<button id="sigSocketStatusBtn" class="btn btn-ghost btn-small">
|
||||||
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<circle cx="12" cy="12" r="10"></circle>
|
||||||
|
<line x1="12" y1="6" x2="12" y2="12"></line>
|
||||||
|
<line x1="16" y1="16" x2="12" y2="12"></line>
|
||||||
|
</svg>
|
||||||
|
Status
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="vault-header">
|
<div class="vault-header">
|
||||||
<h2>Your Keypairs</h2>
|
<h2>Your Keypairs</h2>
|
||||||
<button id="toggleAddKeypairBtn" class="btn btn-primary">
|
<button id="toggleAddKeypairBtn" class="btn btn-primary">
|
||||||
|
@ -932,3 +932,292 @@ const verifySignature = () => performCryptoOperation({
|
|||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// SigSocket functionality
|
||||||
|
let sigSocketRequests = [];
|
||||||
|
let sigSocketStatus = { isConnected: false, workspace: null };
|
||||||
|
|
||||||
|
// Initialize SigSocket UI elements
|
||||||
|
const sigSocketElements = {
|
||||||
|
connectionStatus: document.getElementById('connectionStatus'),
|
||||||
|
connectionDot: document.getElementById('connectionDot'),
|
||||||
|
connectionText: document.getElementById('connectionText'),
|
||||||
|
requestsContainer: document.getElementById('requestsContainer'),
|
||||||
|
noRequestsMessage: document.getElementById('noRequestsMessage'),
|
||||||
|
requestsList: document.getElementById('requestsList'),
|
||||||
|
refreshRequestsBtn: document.getElementById('refreshRequestsBtn'),
|
||||||
|
sigSocketStatusBtn: document.getElementById('sigSocketStatusBtn')
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add SigSocket event listeners
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
// Add SigSocket button listeners
|
||||||
|
sigSocketElements.refreshRequestsBtn?.addEventListener('click', refreshSigSocketRequests);
|
||||||
|
sigSocketElements.sigSocketStatusBtn?.addEventListener('click', showSigSocketStatus);
|
||||||
|
|
||||||
|
// Load initial SigSocket state
|
||||||
|
loadSigSocketState();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Listen for messages from background script about SigSocket events
|
||||||
|
if (backgroundPort) {
|
||||||
|
backgroundPort.onMessage.addListener((message) => {
|
||||||
|
if (message.type === 'NEW_SIGN_REQUEST') {
|
||||||
|
handleNewSignRequest(message);
|
||||||
|
} else if (message.type === 'REQUESTS_UPDATED') {
|
||||||
|
updateRequestsList(message.pendingRequests);
|
||||||
|
} else if (message.type === 'KEYSPACE_UNLOCKED') {
|
||||||
|
handleKeypaceUnlocked(message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load SigSocket state when popup opens
|
||||||
|
async function loadSigSocketState() {
|
||||||
|
try {
|
||||||
|
// Get SigSocket status
|
||||||
|
const statusResponse = await sendMessage('getSigSocketStatus');
|
||||||
|
if (statusResponse?.success) {
|
||||||
|
updateConnectionStatus(statusResponse.status);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get pending requests
|
||||||
|
const requestsResponse = await sendMessage('getPendingSignRequests');
|
||||||
|
if (requestsResponse?.success) {
|
||||||
|
updateRequestsList(requestsResponse.requests);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('Failed to load SigSocket state:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update connection status display
|
||||||
|
function updateConnectionStatus(status) {
|
||||||
|
sigSocketStatus = status;
|
||||||
|
|
||||||
|
if (sigSocketElements.connectionDot && sigSocketElements.connectionText) {
|
||||||
|
if (status.isConnected) {
|
||||||
|
sigSocketElements.connectionDot.classList.add('connected');
|
||||||
|
sigSocketElements.connectionText.textContent = `Connected (${status.workspace || 'Unknown'})`;
|
||||||
|
} else {
|
||||||
|
sigSocketElements.connectionDot.classList.remove('connected');
|
||||||
|
sigSocketElements.connectionText.textContent = 'Disconnected';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update requests list display
|
||||||
|
function updateRequestsList(requests) {
|
||||||
|
sigSocketRequests = requests || [];
|
||||||
|
|
||||||
|
if (!sigSocketElements.requestsContainer) return;
|
||||||
|
|
||||||
|
if (sigSocketRequests.length === 0) {
|
||||||
|
sigSocketElements.noRequestsMessage?.classList.remove('hidden');
|
||||||
|
sigSocketElements.requestsList?.classList.add('hidden');
|
||||||
|
} else {
|
||||||
|
sigSocketElements.noRequestsMessage?.classList.add('hidden');
|
||||||
|
sigSocketElements.requestsList?.classList.remove('hidden');
|
||||||
|
|
||||||
|
if (sigSocketElements.requestsList) {
|
||||||
|
sigSocketElements.requestsList.innerHTML = sigSocketRequests.map(request =>
|
||||||
|
createRequestItem(request)
|
||||||
|
).join('');
|
||||||
|
|
||||||
|
// Add event listeners to approve/reject buttons
|
||||||
|
addRequestEventListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create HTML for a single request item
|
||||||
|
function createRequestItem(request) {
|
||||||
|
const requestTime = new Date(request.timestamp || Date.now()).toLocaleTimeString();
|
||||||
|
const shortId = request.id.substring(0, 8) + '...';
|
||||||
|
const decodedMessage = request.message ? atob(request.message) : 'No message';
|
||||||
|
|
||||||
|
return `
|
||||||
|
<div class="request-item" data-request-id="${request.id}">
|
||||||
|
<div class="request-header">
|
||||||
|
<div class="request-id" title="${request.id}">${shortId}</div>
|
||||||
|
<div class="request-time">${requestTime}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="request-message" title="${decodedMessage}">
|
||||||
|
${decodedMessage.length > 100 ? decodedMessage.substring(0, 100) + '...' : decodedMessage}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="request-actions">
|
||||||
|
<button class="btn-approve" data-request-id="${request.id}">
|
||||||
|
✓ Approve
|
||||||
|
</button>
|
||||||
|
<button class="btn-reject" data-request-id="${request.id}">
|
||||||
|
✗ Reject
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add event listeners to request action buttons
|
||||||
|
function addRequestEventListeners() {
|
||||||
|
// Approve buttons
|
||||||
|
document.querySelectorAll('.btn-approve').forEach(btn => {
|
||||||
|
btn.addEventListener('click', async (e) => {
|
||||||
|
const requestId = e.target.getAttribute('data-request-id');
|
||||||
|
await approveSignRequest(requestId);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Reject buttons
|
||||||
|
document.querySelectorAll('.btn-reject').forEach(btn => {
|
||||||
|
btn.addEventListener('click', async (e) => {
|
||||||
|
const requestId = e.target.getAttribute('data-request-id');
|
||||||
|
await rejectSignRequest(requestId);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle new sign request notification
|
||||||
|
function handleNewSignRequest(message) {
|
||||||
|
// Update requests list
|
||||||
|
if (message.pendingRequests) {
|
||||||
|
updateRequestsList(message.pendingRequests);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show notification if workspace doesn't match
|
||||||
|
if (!message.canApprove) {
|
||||||
|
showWorkspaceMismatchWarning();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle keyspace unlocked event
|
||||||
|
function handleKeypaceUnlocked(message) {
|
||||||
|
// Update requests list
|
||||||
|
if (message.pendingRequests) {
|
||||||
|
updateRequestsList(message.pendingRequests);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update button states based on whether requests can be approved
|
||||||
|
updateRequestButtonStates(message.canApprove);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show workspace mismatch warning
|
||||||
|
function showWorkspaceMismatchWarning() {
|
||||||
|
const existingWarning = document.querySelector('.workspace-mismatch');
|
||||||
|
if (existingWarning) return; // Don't show multiple warnings
|
||||||
|
|
||||||
|
const warning = document.createElement('div');
|
||||||
|
warning.className = 'workspace-mismatch';
|
||||||
|
warning.innerHTML = `
|
||||||
|
⚠️ Sign requests received for a different workspace.
|
||||||
|
Switch to the correct workspace to approve requests.
|
||||||
|
`;
|
||||||
|
|
||||||
|
if (sigSocketElements.requestsContainer) {
|
||||||
|
sigSocketElements.requestsContainer.insertBefore(warning, sigSocketElements.requestsContainer.firstChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auto-remove warning after 10 seconds
|
||||||
|
setTimeout(() => {
|
||||||
|
warning.remove();
|
||||||
|
}, 10000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update request button states
|
||||||
|
function updateRequestButtonStates(canApprove) {
|
||||||
|
document.querySelectorAll('.btn-approve, .btn-reject').forEach(btn => {
|
||||||
|
btn.disabled = !canApprove;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Approve a sign request
|
||||||
|
async function approveSignRequest(requestId) {
|
||||||
|
try {
|
||||||
|
const button = document.querySelector(`[data-request-id="${requestId}"].btn-approve`);
|
||||||
|
setButtonLoading(button, true);
|
||||||
|
|
||||||
|
const response = await sendMessage('approveSignRequest', { requestId });
|
||||||
|
|
||||||
|
if (response?.success) {
|
||||||
|
showToast('Request approved and signed!', 'success');
|
||||||
|
await refreshSigSocketRequests();
|
||||||
|
} else {
|
||||||
|
throw new Error(getResponseError(response, 'approve request'));
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
showToast(`Failed to approve request: ${error.message}`, 'error');
|
||||||
|
} finally {
|
||||||
|
const button = document.querySelector(`[data-request-id="${requestId}"].btn-approve`);
|
||||||
|
setButtonLoading(button, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reject a sign request
|
||||||
|
async function rejectSignRequest(requestId) {
|
||||||
|
try {
|
||||||
|
const button = document.querySelector(`[data-request-id="${requestId}"].btn-reject`);
|
||||||
|
setButtonLoading(button, true);
|
||||||
|
|
||||||
|
const response = await sendMessage('rejectSignRequest', {
|
||||||
|
requestId,
|
||||||
|
reason: 'User rejected via extension'
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response?.success) {
|
||||||
|
showToast('Request rejected', 'info');
|
||||||
|
await refreshSigSocketRequests();
|
||||||
|
} else {
|
||||||
|
throw new Error(getResponseError(response, 'reject request'));
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
showToast(`Failed to reject request: ${error.message}`, 'error');
|
||||||
|
} finally {
|
||||||
|
const button = document.querySelector(`[data-request-id="${requestId}"].btn-reject`);
|
||||||
|
setButtonLoading(button, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Refresh SigSocket requests
|
||||||
|
async function refreshSigSocketRequests() {
|
||||||
|
try {
|
||||||
|
setButtonLoading(sigSocketElements.refreshRequestsBtn, true);
|
||||||
|
|
||||||
|
const response = await sendMessage('getPendingSignRequests');
|
||||||
|
if (response?.success) {
|
||||||
|
updateRequestsList(response.requests);
|
||||||
|
showToast('Requests refreshed', 'success');
|
||||||
|
} else {
|
||||||
|
throw new Error(getResponseError(response, 'refresh requests'));
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
showToast(`Failed to refresh requests: ${error.message}`, 'error');
|
||||||
|
} finally {
|
||||||
|
setButtonLoading(sigSocketElements.refreshRequestsBtn, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show SigSocket status
|
||||||
|
async function showSigSocketStatus() {
|
||||||
|
try {
|
||||||
|
const response = await sendMessage('getSigSocketStatus');
|
||||||
|
if (response?.success) {
|
||||||
|
const status = response.status;
|
||||||
|
const statusText = `
|
||||||
|
SigSocket Status:
|
||||||
|
• Connected: ${status.isConnected ? 'Yes' : 'No'}
|
||||||
|
• Workspace: ${status.workspace || 'None'}
|
||||||
|
• Public Key: ${status.publicKey ? status.publicKey.substring(0, 16) + '...' : 'None'}
|
||||||
|
• Pending Requests: ${status.pendingRequestCount || 0}
|
||||||
|
• Server URL: ${status.serverUrl}
|
||||||
|
`.trim();
|
||||||
|
|
||||||
|
showToast(statusText, 'info');
|
||||||
|
updateConnectionStatus(status);
|
||||||
|
} else {
|
||||||
|
throw new Error(getResponseError(response, 'get status'));
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
showToast(`Failed to get status: ${error.message}`, 'error');
|
||||||
|
}
|
||||||
|
}
|
@ -1070,3 +1070,182 @@ input::placeholder, textarea::placeholder {
|
|||||||
width: 20px;
|
width: 20px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* SigSocket Requests Styles */
|
||||||
|
.sigsocket-section {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-header h3 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.connection-status {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-dot {
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: var(--accent-error);
|
||||||
|
transition: background-color 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-dot.connected {
|
||||||
|
background: var(--accent-success);
|
||||||
|
}
|
||||||
|
|
||||||
|
.requests-container {
|
||||||
|
min-height: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-state {
|
||||||
|
text-align: center;
|
||||||
|
padding: 20px;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-icon {
|
||||||
|
font-size: 24px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-state p {
|
||||||
|
margin: 0 0 4px 0;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-state small {
|
||||||
|
font-size: 11px;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.request-item {
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 12px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.request-item:hover {
|
||||||
|
border-color: var(--border-focus);
|
||||||
|
box-shadow: 0 2px 8px hsla(var(--primary-hue), var(--primary-saturation), 55%, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.request-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: flex-start;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.request-id {
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
background: var(--bg-input);
|
||||||
|
padding: 2px 6px;
|
||||||
|
border-radius: 4px;
|
||||||
|
max-width: 120px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.request-time {
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.request-message {
|
||||||
|
margin: 8px 0;
|
||||||
|
padding: 8px;
|
||||||
|
background: var(--bg-input);
|
||||||
|
border-radius: 4px;
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
font-size: 11px;
|
||||||
|
word-break: break-all;
|
||||||
|
max-height: 60px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.request-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
margin-top: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-approve {
|
||||||
|
background: var(--accent-success);
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
padding: 6px 12px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-approve:hover {
|
||||||
|
background: hsl(var(--accent-hue), 65%, 40%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-reject {
|
||||||
|
background: var(--accent-error);
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
padding: 6px 12px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-reject:hover {
|
||||||
|
background: hsl(0, 70%, 50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-approve:disabled,
|
||||||
|
.btn-reject:disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sigsocket-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
margin-top: 12px;
|
||||||
|
padding-top: 12px;
|
||||||
|
border-top: 1px solid var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-mismatch {
|
||||||
|
background: hsla(35, 85%, 85%, 0.8);
|
||||||
|
border: 1px solid hsla(35, 85%, 70%, 0.5);
|
||||||
|
color: hsl(35, 70%, 30%);
|
||||||
|
padding: 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .workspace-mismatch {
|
||||||
|
background: hsla(35, 60%, 15%, 0.8);
|
||||||
|
border-color: hsla(35, 60%, 30%, 0.5);
|
||||||
|
color: hsl(35, 70%, 70%);
|
||||||
|
}
|
@ -277,6 +277,42 @@ export function is_unlocked() {
|
|||||||
return ret !== 0;
|
return ret !== 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the default public key for a workspace (keyspace)
|
||||||
|
* This returns the public key of the first keypair in the keyspace
|
||||||
|
* @param {string} workspace_id
|
||||||
|
* @returns {Promise<any>}
|
||||||
|
*/
|
||||||
|
export function get_workspace_default_public_key(workspace_id) {
|
||||||
|
const ptr0 = passStringToWasm0(workspace_id, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||||
|
const len0 = WASM_VECTOR_LEN;
|
||||||
|
const ret = wasm.get_workspace_default_public_key(ptr0, len0);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current unlocked public key as hex string
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
export function get_current_unlocked_public_key() {
|
||||||
|
let deferred2_0;
|
||||||
|
let deferred2_1;
|
||||||
|
try {
|
||||||
|
const ret = wasm.get_current_unlocked_public_key();
|
||||||
|
var ptr1 = ret[0];
|
||||||
|
var len1 = ret[1];
|
||||||
|
if (ret[3]) {
|
||||||
|
ptr1 = 0; len1 = 0;
|
||||||
|
throw takeFromExternrefTable0(ret[2]);
|
||||||
|
}
|
||||||
|
deferred2_0 = ptr1;
|
||||||
|
deferred2_1 = len1;
|
||||||
|
return getStringFromWasm0(ptr1, len1);
|
||||||
|
} finally {
|
||||||
|
wasm.__wbindgen_free(deferred2_0, deferred2_1, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all keypairs from the current session
|
* Get all keypairs from the current session
|
||||||
* Returns an array of keypair objects with id, type, and metadata
|
* Returns an array of keypair objects with id, type, and metadata
|
||||||
@ -323,7 +359,7 @@ function passArray8ToWasm0(arg, malloc) {
|
|||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Sign message with current session
|
* Sign message with current session (requires selected keypair)
|
||||||
* @param {Uint8Array} message
|
* @param {Uint8Array} message
|
||||||
* @returns {Promise<any>}
|
* @returns {Promise<any>}
|
||||||
*/
|
*/
|
||||||
@ -334,6 +370,41 @@ export function sign(message) {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current keyspace name
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
export function get_current_keyspace_name() {
|
||||||
|
let deferred2_0;
|
||||||
|
let deferred2_1;
|
||||||
|
try {
|
||||||
|
const ret = wasm.get_current_keyspace_name();
|
||||||
|
var ptr1 = ret[0];
|
||||||
|
var len1 = ret[1];
|
||||||
|
if (ret[3]) {
|
||||||
|
ptr1 = 0; len1 = 0;
|
||||||
|
throw takeFromExternrefTable0(ret[2]);
|
||||||
|
}
|
||||||
|
deferred2_0 = ptr1;
|
||||||
|
deferred2_1 = len1;
|
||||||
|
return getStringFromWasm0(ptr1, len1);
|
||||||
|
} finally {
|
||||||
|
wasm.__wbindgen_free(deferred2_0, deferred2_1, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sign message with default keypair (first keypair in keyspace) without changing session state
|
||||||
|
* @param {Uint8Array} message
|
||||||
|
* @returns {Promise<any>}
|
||||||
|
*/
|
||||||
|
export function sign_with_default_keypair(message) {
|
||||||
|
const ptr0 = passArray8ToWasm0(message, wasm.__wbindgen_malloc);
|
||||||
|
const len0 = WASM_VECTOR_LEN;
|
||||||
|
const ret = wasm.sign_with_default_keypair(ptr0, len0);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verify a signature with the current session's selected keypair
|
* Verify a signature with the current session's selected keypair
|
||||||
* @param {Uint8Array} message
|
* @param {Uint8Array} message
|
||||||
@ -395,24 +466,391 @@ export function run_rhai(script) {
|
|||||||
return takeFromExternrefTable0(ret[0]);
|
return takeFromExternrefTable0(ret[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function __wbg_adapter_32(arg0, arg1, arg2) {
|
function __wbg_adapter_34(arg0, arg1, arg2) {
|
||||||
wasm.closure121_externref_shim(arg0, arg1, arg2);
|
wasm.closure174_externref_shim(arg0, arg1, arg2);
|
||||||
}
|
}
|
||||||
|
|
||||||
function __wbg_adapter_35(arg0, arg1, arg2) {
|
function __wbg_adapter_39(arg0, arg1) {
|
||||||
wasm.closure150_externref_shim(arg0, arg1, arg2);
|
wasm._dyn_core__ops__function__FnMut_____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__ha4436a3f79fb1a0f(arg0, arg1);
|
||||||
}
|
}
|
||||||
|
|
||||||
function __wbg_adapter_38(arg0, arg1, arg2) {
|
function __wbg_adapter_44(arg0, arg1, arg2) {
|
||||||
wasm.closure227_externref_shim(arg0, arg1, arg2);
|
wasm.closure237_externref_shim(arg0, arg1, arg2);
|
||||||
}
|
}
|
||||||
|
|
||||||
function __wbg_adapter_138(arg0, arg1, arg2, arg3) {
|
function __wbg_adapter_49(arg0, arg1) {
|
||||||
wasm.closure1879_externref_shim(arg0, arg1, arg2, arg3);
|
wasm._dyn_core__ops__function__FnMut_____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__hf148c54a4a246cea(arg0, arg1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function __wbg_adapter_52(arg0, arg1, arg2) {
|
||||||
|
wasm.closure308_externref_shim(arg0, arg1, arg2);
|
||||||
|
}
|
||||||
|
|
||||||
|
function __wbg_adapter_55(arg0, arg1, arg2) {
|
||||||
|
wasm.closure392_externref_shim(arg0, arg1, arg2);
|
||||||
|
}
|
||||||
|
|
||||||
|
function __wbg_adapter_207(arg0, arg1, arg2, arg3) {
|
||||||
|
wasm.closure2046_externref_shim(arg0, arg1, arg2, arg3);
|
||||||
|
}
|
||||||
|
|
||||||
|
const __wbindgen_enum_BinaryType = ["blob", "arraybuffer"];
|
||||||
|
|
||||||
const __wbindgen_enum_IdbTransactionMode = ["readonly", "readwrite", "versionchange", "readwriteflush", "cleanup"];
|
const __wbindgen_enum_IdbTransactionMode = ["readonly", "readwrite", "versionchange", "readwriteflush", "cleanup"];
|
||||||
|
|
||||||
|
const SigSocketConnectionFinalization = (typeof FinalizationRegistry === 'undefined')
|
||||||
|
? { register: () => {}, unregister: () => {} }
|
||||||
|
: new FinalizationRegistry(ptr => wasm.__wbg_sigsocketconnection_free(ptr >>> 0, 1));
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
export class SigSocketConnection {
|
||||||
|
|
||||||
|
__destroy_into_raw() {
|
||||||
|
const ptr = this.__wbg_ptr;
|
||||||
|
this.__wbg_ptr = 0;
|
||||||
|
SigSocketConnectionFinalization.unregister(this);
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
free() {
|
||||||
|
const ptr = this.__destroy_into_raw();
|
||||||
|
wasm.__wbg_sigsocketconnection_free(ptr, 0);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Create a new SigSocket connection
|
||||||
|
*/
|
||||||
|
constructor() {
|
||||||
|
const ret = wasm.sigsocketconnection_new();
|
||||||
|
this.__wbg_ptr = ret >>> 0;
|
||||||
|
SigSocketConnectionFinalization.register(this, this.__wbg_ptr, this);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
* @param {string} server_url
|
||||||
|
* @param {string} public_key_hex
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
connect(server_url, public_key_hex) {
|
||||||
|
const ptr0 = passStringToWasm0(server_url, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||||
|
const len0 = WASM_VECTOR_LEN;
|
||||||
|
const ptr1 = passStringToWasm0(public_key_hex, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||||
|
const len1 = WASM_VECTOR_LEN;
|
||||||
|
const ret = wasm.sigsocketconnection_connect(this.__wbg_ptr, ptr0, len0, ptr1, len1);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
* @param {string} request_id
|
||||||
|
* @param {string} message_base64
|
||||||
|
* @param {string} signature_hex
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
send_response(request_id, message_base64, signature_hex) {
|
||||||
|
const ptr0 = passStringToWasm0(request_id, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||||
|
const len0 = WASM_VECTOR_LEN;
|
||||||
|
const ptr1 = passStringToWasm0(message_base64, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||||
|
const len1 = WASM_VECTOR_LEN;
|
||||||
|
const ptr2 = passStringToWasm0(signature_hex, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||||
|
const len2 = WASM_VECTOR_LEN;
|
||||||
|
const ret = wasm.sigsocketconnection_send_response(this.__wbg_ptr, ptr0, len0, ptr1, len1, ptr2, len2);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
* @param {string} request_id
|
||||||
|
* @param {string} reason
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
send_rejection(request_id, reason) {
|
||||||
|
const ptr0 = passStringToWasm0(request_id, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||||
|
const len0 = WASM_VECTOR_LEN;
|
||||||
|
const ptr1 = passStringToWasm0(reason, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||||
|
const len1 = WASM_VECTOR_LEN;
|
||||||
|
const ret = wasm.sigsocketconnection_send_rejection(this.__wbg_ptr, ptr0, len0, ptr1, len1);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Disconnect from the SigSocket server
|
||||||
|
*/
|
||||||
|
disconnect() {
|
||||||
|
wasm.sigsocketconnection_disconnect(this.__wbg_ptr);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Check if connected to the server
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
is_connected() {
|
||||||
|
const ret = wasm.sigsocketconnection_is_connected(this.__wbg_ptr);
|
||||||
|
return ret !== 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const SigSocketManagerFinalization = (typeof FinalizationRegistry === 'undefined')
|
||||||
|
? { register: () => {}, unregister: () => {} }
|
||||||
|
: new FinalizationRegistry(ptr => wasm.__wbg_sigsocketmanager_free(ptr >>> 0, 1));
|
||||||
|
/**
|
||||||
|
* SigSocket manager for high-level operations
|
||||||
|
*/
|
||||||
|
export class SigSocketManager {
|
||||||
|
|
||||||
|
__destroy_into_raw() {
|
||||||
|
const ptr = this.__wbg_ptr;
|
||||||
|
this.__wbg_ptr = 0;
|
||||||
|
SigSocketManagerFinalization.unregister(this);
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
free() {
|
||||||
|
const ptr = this.__destroy_into_raw();
|
||||||
|
wasm.__wbg_sigsocketmanager_free(ptr, 0);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Connect to SigSocket server with smart connection management
|
||||||
|
*
|
||||||
|
* This handles all connection logic:
|
||||||
|
* - Reuses existing connection if same workspace
|
||||||
|
* - Switches connection if different workspace
|
||||||
|
* - Creates new connection if none exists
|
||||||
|
*
|
||||||
|
* # Arguments
|
||||||
|
* * `workspace` - The workspace name to connect with
|
||||||
|
* * `server_url` - The SigSocket server URL (e.g., "ws://localhost:8080/ws")
|
||||||
|
* * `event_callback` - JavaScript function to call when events occur
|
||||||
|
*
|
||||||
|
* # Returns
|
||||||
|
* * `Ok(connection_info)` - JSON string with connection details
|
||||||
|
* * `Err(error)` - If connection failed or workspace is invalid
|
||||||
|
* @param {string} workspace
|
||||||
|
* @param {string} server_url
|
||||||
|
* @param {Function} event_callback
|
||||||
|
* @returns {Promise<string>}
|
||||||
|
*/
|
||||||
|
static connect_workspace_with_events(workspace, server_url, event_callback) {
|
||||||
|
const ptr0 = passStringToWasm0(workspace, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||||
|
const len0 = WASM_VECTOR_LEN;
|
||||||
|
const ptr1 = passStringToWasm0(server_url, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||||
|
const len1 = WASM_VECTOR_LEN;
|
||||||
|
const ret = wasm.sigsocketmanager_connect_workspace_with_events(ptr0, len0, ptr1, len1, event_callback);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Connect to SigSocket server with a specific workspace (backward compatibility)
|
||||||
|
*
|
||||||
|
* This is a simpler version that doesn't set up event callbacks.
|
||||||
|
* Use connect_workspace_with_events for full functionality.
|
||||||
|
*
|
||||||
|
* # Arguments
|
||||||
|
* * `workspace` - The workspace name to connect with
|
||||||
|
* * `server_url` - The SigSocket server URL (e.g., "ws://localhost:8080/ws")
|
||||||
|
*
|
||||||
|
* # Returns
|
||||||
|
* * `Ok(connection_info)` - JSON string with connection details
|
||||||
|
* * `Err(error)` - If connection failed or workspace is invalid
|
||||||
|
* @param {string} workspace
|
||||||
|
* @param {string} server_url
|
||||||
|
* @returns {Promise<string>}
|
||||||
|
*/
|
||||||
|
static connect_workspace(workspace, server_url) {
|
||||||
|
const ptr0 = passStringToWasm0(workspace, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||||
|
const len0 = WASM_VECTOR_LEN;
|
||||||
|
const ptr1 = passStringToWasm0(server_url, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||||
|
const len1 = WASM_VECTOR_LEN;
|
||||||
|
const ret = wasm.sigsocketmanager_connect_workspace(ptr0, len0, ptr1, len1);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Disconnect from SigSocket server
|
||||||
|
*
|
||||||
|
* # Returns
|
||||||
|
* * `Ok(())` - Successfully disconnected
|
||||||
|
* * `Err(error)` - If disconnect failed
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
static disconnect() {
|
||||||
|
const ret = wasm.sigsocketmanager_disconnect();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Check if we can approve a specific sign request
|
||||||
|
*
|
||||||
|
* This validates that:
|
||||||
|
* 1. The request exists
|
||||||
|
* 2. The vault session is unlocked
|
||||||
|
* 3. The current workspace matches the request's target
|
||||||
|
*
|
||||||
|
* # Arguments
|
||||||
|
* * `request_id` - The ID of the request to validate
|
||||||
|
*
|
||||||
|
* # Returns
|
||||||
|
* * `Ok(true)` - Request can be approved
|
||||||
|
* * `Ok(false)` - Request cannot be approved
|
||||||
|
* * `Err(error)` - Validation error
|
||||||
|
* @param {string} request_id
|
||||||
|
* @returns {Promise<boolean>}
|
||||||
|
*/
|
||||||
|
static can_approve_request(request_id) {
|
||||||
|
const ptr0 = passStringToWasm0(request_id, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||||
|
const len0 = WASM_VECTOR_LEN;
|
||||||
|
const ret = wasm.sigsocketmanager_can_approve_request(ptr0, len0);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Approve a sign request and send the signature to the server
|
||||||
|
*
|
||||||
|
* This performs the complete approval flow:
|
||||||
|
* 1. Validates the request can be approved
|
||||||
|
* 2. Signs the message using the vault
|
||||||
|
* 3. Sends the signature to the SigSocket server
|
||||||
|
* 4. Removes the request from pending list
|
||||||
|
*
|
||||||
|
* # Arguments
|
||||||
|
* * `request_id` - The ID of the request to approve
|
||||||
|
*
|
||||||
|
* # Returns
|
||||||
|
* * `Ok(signature)` - Base64-encoded signature that was sent
|
||||||
|
* * `Err(error)` - If approval failed
|
||||||
|
* @param {string} request_id
|
||||||
|
* @returns {Promise<string>}
|
||||||
|
*/
|
||||||
|
static approve_request(request_id) {
|
||||||
|
const ptr0 = passStringToWasm0(request_id, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||||
|
const len0 = WASM_VECTOR_LEN;
|
||||||
|
const ret = wasm.sigsocketmanager_approve_request(ptr0, len0);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Reject a sign request
|
||||||
|
*
|
||||||
|
* # Arguments
|
||||||
|
* * `request_id` - The ID of the request to reject
|
||||||
|
* * `reason` - The reason for rejection
|
||||||
|
*
|
||||||
|
* # Returns
|
||||||
|
* * `Ok(())` - Request rejected successfully
|
||||||
|
* * `Err(error)` - If rejection failed
|
||||||
|
* @param {string} request_id
|
||||||
|
* @param {string} reason
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
static reject_request(request_id, reason) {
|
||||||
|
const ptr0 = passStringToWasm0(request_id, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||||
|
const len0 = WASM_VECTOR_LEN;
|
||||||
|
const ptr1 = passStringToWasm0(reason, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||||
|
const len1 = WASM_VECTOR_LEN;
|
||||||
|
const ret = wasm.sigsocketmanager_reject_request(ptr0, len0, ptr1, len1);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Get pending requests filtered by current workspace
|
||||||
|
*
|
||||||
|
* This returns only the requests that the current vault session can handle,
|
||||||
|
* based on the unlocked workspace and its public key.
|
||||||
|
*
|
||||||
|
* # Returns
|
||||||
|
* * `Ok(requests_json)` - JSON array of filtered requests
|
||||||
|
* * `Err(error)` - If filtering failed
|
||||||
|
* @returns {Promise<string>}
|
||||||
|
*/
|
||||||
|
static get_filtered_requests() {
|
||||||
|
const ret = wasm.sigsocketmanager_get_filtered_requests();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Add a pending sign request (called when request arrives from server)
|
||||||
|
*
|
||||||
|
* # Arguments
|
||||||
|
* * `request_json` - JSON string containing the sign request
|
||||||
|
*
|
||||||
|
* # Returns
|
||||||
|
* * `Ok(())` - Request added successfully
|
||||||
|
* * `Err(error)` - If adding failed
|
||||||
|
* @param {string} request_json
|
||||||
|
*/
|
||||||
|
static add_pending_request(request_json) {
|
||||||
|
const ptr0 = passStringToWasm0(request_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||||
|
const len0 = WASM_VECTOR_LEN;
|
||||||
|
const ret = wasm.sigsocketmanager_add_pending_request(ptr0, len0);
|
||||||
|
if (ret[1]) {
|
||||||
|
throw takeFromExternrefTable0(ret[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Get connection status
|
||||||
|
*
|
||||||
|
* # Returns
|
||||||
|
* * `Ok(status_json)` - JSON object with connection status
|
||||||
|
* * `Err(error)` - If getting status failed
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
static get_connection_status() {
|
||||||
|
let deferred2_0;
|
||||||
|
let deferred2_1;
|
||||||
|
try {
|
||||||
|
const ret = wasm.sigsocketmanager_get_connection_status();
|
||||||
|
var ptr1 = ret[0];
|
||||||
|
var len1 = ret[1];
|
||||||
|
if (ret[3]) {
|
||||||
|
ptr1 = 0; len1 = 0;
|
||||||
|
throw takeFromExternrefTable0(ret[2]);
|
||||||
|
}
|
||||||
|
deferred2_0 = ptr1;
|
||||||
|
deferred2_1 = len1;
|
||||||
|
return getStringFromWasm0(ptr1, len1);
|
||||||
|
} finally {
|
||||||
|
wasm.__wbindgen_free(deferred2_0, deferred2_1, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Clear all pending requests
|
||||||
|
*
|
||||||
|
* # Returns
|
||||||
|
* * `Ok(())` - Requests cleared successfully
|
||||||
|
*/
|
||||||
|
static clear_pending_requests() {
|
||||||
|
const ret = wasm.sigsocketmanager_clear_pending_requests();
|
||||||
|
if (ret[1]) {
|
||||||
|
throw takeFromExternrefTable0(ret[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function __wbg_load(module, imports) {
|
async function __wbg_load(module, imports) {
|
||||||
if (typeof Response === 'function' && module instanceof Response) {
|
if (typeof Response === 'function' && module instanceof Response) {
|
||||||
if (typeof WebAssembly.instantiateStreaming === 'function') {
|
if (typeof WebAssembly.instantiateStreaming === 'function') {
|
||||||
@ -459,6 +897,9 @@ function __wbg_get_imports() {
|
|||||||
const ret = arg0.call(arg1, arg2);
|
const ret = arg0.call(arg1, arg2);
|
||||||
return ret;
|
return ret;
|
||||||
}, arguments) };
|
}, arguments) };
|
||||||
|
imports.wbg.__wbg_close_2893b7d056a0627d = function() { return handleError(function (arg0) {
|
||||||
|
arg0.close();
|
||||||
|
}, arguments) };
|
||||||
imports.wbg.__wbg_createObjectStore_d2f9e1016f4d81b9 = function() { return handleError(function (arg0, arg1, arg2, arg3) {
|
imports.wbg.__wbg_createObjectStore_d2f9e1016f4d81b9 = function() { return handleError(function (arg0, arg1, arg2, arg3) {
|
||||||
const ret = arg0.createObjectStore(getStringFromWasm0(arg1, arg2), arg3);
|
const ret = arg0.createObjectStore(getStringFromWasm0(arg1, arg2), arg3);
|
||||||
return ret;
|
return ret;
|
||||||
@ -467,6 +908,10 @@ function __wbg_get_imports() {
|
|||||||
const ret = arg0.crypto;
|
const ret = arg0.crypto;
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
imports.wbg.__wbg_data_432d9c3df2630942 = function(arg0) {
|
||||||
|
const ret = arg0.data;
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
imports.wbg.__wbg_error_524f506f44df1645 = function(arg0) {
|
imports.wbg.__wbg_error_524f506f44df1645 = function(arg0) {
|
||||||
console.error(arg0);
|
console.error(arg0);
|
||||||
};
|
};
|
||||||
@ -539,10 +984,23 @@ function __wbg_get_imports() {
|
|||||||
const ret = result;
|
const ret = result;
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
imports.wbg.__wbg_instanceof_Window_def73ea0955fc569 = function(arg0) {
|
||||||
|
let result;
|
||||||
|
try {
|
||||||
|
result = arg0 instanceof Window;
|
||||||
|
} catch (_) {
|
||||||
|
result = false;
|
||||||
|
}
|
||||||
|
const ret = result;
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
imports.wbg.__wbg_length_52b6c4580c5ec934 = function(arg0) {
|
imports.wbg.__wbg_length_52b6c4580c5ec934 = function(arg0) {
|
||||||
const ret = arg0.length;
|
const ret = arg0.length;
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
imports.wbg.__wbg_log_c222819a41e063d3 = function(arg0) {
|
||||||
|
console.log(arg0);
|
||||||
|
};
|
||||||
imports.wbg.__wbg_msCrypto_a61aeb35a24c1329 = function(arg0) {
|
imports.wbg.__wbg_msCrypto_a61aeb35a24c1329 = function(arg0) {
|
||||||
const ret = arg0.msCrypto;
|
const ret = arg0.msCrypto;
|
||||||
return ret;
|
return ret;
|
||||||
@ -558,7 +1016,7 @@ function __wbg_get_imports() {
|
|||||||
const a = state0.a;
|
const a = state0.a;
|
||||||
state0.a = 0;
|
state0.a = 0;
|
||||||
try {
|
try {
|
||||||
return __wbg_adapter_138(a, state0.b, arg0, arg1);
|
return __wbg_adapter_207(a, state0.b, arg0, arg1);
|
||||||
} finally {
|
} finally {
|
||||||
state0.a = a;
|
state0.a = a;
|
||||||
}
|
}
|
||||||
@ -577,6 +1035,10 @@ function __wbg_get_imports() {
|
|||||||
const ret = new Array();
|
const ret = new Array();
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
imports.wbg.__wbg_new_92c54fc74574ef55 = function() { return handleError(function (arg0, arg1) {
|
||||||
|
const ret = new WebSocket(getStringFromWasm0(arg0, arg1));
|
||||||
|
return ret;
|
||||||
|
}, arguments) };
|
||||||
imports.wbg.__wbg_new_a12002a7f91c75be = function(arg0) {
|
imports.wbg.__wbg_new_a12002a7f91c75be = function(arg0) {
|
||||||
const ret = new Uint8Array(arg0);
|
const ret = new Uint8Array(arg0);
|
||||||
return ret;
|
return ret;
|
||||||
@ -609,6 +1071,12 @@ function __wbg_get_imports() {
|
|||||||
const ret = arg0.objectStore(getStringFromWasm0(arg1, arg2));
|
const ret = arg0.objectStore(getStringFromWasm0(arg1, arg2));
|
||||||
return ret;
|
return ret;
|
||||||
}, arguments) };
|
}, arguments) };
|
||||||
|
imports.wbg.__wbg_onConnectionStateChanged_b0dc098522afadba = function(arg0) {
|
||||||
|
onConnectionStateChanged(arg0 !== 0);
|
||||||
|
};
|
||||||
|
imports.wbg.__wbg_onSignRequestReceived_93232ba7a0919705 = function(arg0, arg1, arg2, arg3) {
|
||||||
|
onSignRequestReceived(getStringFromWasm0(arg0, arg1), getStringFromWasm0(arg2, arg3));
|
||||||
|
};
|
||||||
imports.wbg.__wbg_open_88b1390d99a7c691 = function() { return handleError(function (arg0, arg1, arg2) {
|
imports.wbg.__wbg_open_88b1390d99a7c691 = function() { return handleError(function (arg0, arg1, arg2) {
|
||||||
const ret = arg0.open(getStringFromWasm0(arg1, arg2));
|
const ret = arg0.open(getStringFromWasm0(arg1, arg2));
|
||||||
return ret;
|
return ret;
|
||||||
@ -643,6 +1111,10 @@ function __wbg_get_imports() {
|
|||||||
imports.wbg.__wbg_randomFillSync_ac0988aba3254290 = function() { return handleError(function (arg0, arg1) {
|
imports.wbg.__wbg_randomFillSync_ac0988aba3254290 = function() { return handleError(function (arg0, arg1) {
|
||||||
arg0.randomFillSync(arg1);
|
arg0.randomFillSync(arg1);
|
||||||
}, arguments) };
|
}, arguments) };
|
||||||
|
imports.wbg.__wbg_readyState_7ef6e63c349899ed = function(arg0) {
|
||||||
|
const ret = arg0.readyState;
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
imports.wbg.__wbg_require_60cc747a6bc5215a = function() { return handleError(function () {
|
imports.wbg.__wbg_require_60cc747a6bc5215a = function() { return handleError(function () {
|
||||||
const ret = module.require;
|
const ret = module.require;
|
||||||
return ret;
|
return ret;
|
||||||
@ -655,12 +1127,38 @@ function __wbg_get_imports() {
|
|||||||
const ret = arg0.result;
|
const ret = arg0.result;
|
||||||
return ret;
|
return ret;
|
||||||
}, arguments) };
|
}, arguments) };
|
||||||
|
imports.wbg.__wbg_send_0293179ba074ffb4 = function() { return handleError(function (arg0, arg1, arg2) {
|
||||||
|
arg0.send(getStringFromWasm0(arg1, arg2));
|
||||||
|
}, arguments) };
|
||||||
|
imports.wbg.__wbg_setTimeout_f2fe5af8e3debeb3 = function() { return handleError(function (arg0, arg1, arg2) {
|
||||||
|
const ret = arg0.setTimeout(arg1, arg2);
|
||||||
|
return ret;
|
||||||
|
}, arguments) };
|
||||||
imports.wbg.__wbg_set_65595bdd868b3009 = function(arg0, arg1, arg2) {
|
imports.wbg.__wbg_set_65595bdd868b3009 = function(arg0, arg1, arg2) {
|
||||||
arg0.set(arg1, arg2 >>> 0);
|
arg0.set(arg1, arg2 >>> 0);
|
||||||
};
|
};
|
||||||
|
imports.wbg.__wbg_set_bb8cecf6a62b9f46 = function() { return handleError(function (arg0, arg1, arg2) {
|
||||||
|
const ret = Reflect.set(arg0, arg1, arg2);
|
||||||
|
return ret;
|
||||||
|
}, arguments) };
|
||||||
|
imports.wbg.__wbg_setbinaryType_92fa1ffd873b327c = function(arg0, arg1) {
|
||||||
|
arg0.binaryType = __wbindgen_enum_BinaryType[arg1];
|
||||||
|
};
|
||||||
|
imports.wbg.__wbg_setonclose_14fc475a49d488fc = function(arg0, arg1) {
|
||||||
|
arg0.onclose = arg1;
|
||||||
|
};
|
||||||
|
imports.wbg.__wbg_setonerror_8639efe354b947cd = function(arg0, arg1) {
|
||||||
|
arg0.onerror = arg1;
|
||||||
|
};
|
||||||
imports.wbg.__wbg_setonerror_d7e3056cc6e56085 = function(arg0, arg1) {
|
imports.wbg.__wbg_setonerror_d7e3056cc6e56085 = function(arg0, arg1) {
|
||||||
arg0.onerror = arg1;
|
arg0.onerror = arg1;
|
||||||
};
|
};
|
||||||
|
imports.wbg.__wbg_setonmessage_6eccab530a8fb4c7 = function(arg0, arg1) {
|
||||||
|
arg0.onmessage = arg1;
|
||||||
|
};
|
||||||
|
imports.wbg.__wbg_setonopen_2da654e1f39745d5 = function(arg0, arg1) {
|
||||||
|
arg0.onopen = arg1;
|
||||||
|
};
|
||||||
imports.wbg.__wbg_setonsuccess_afa464ee777a396d = function(arg0, arg1) {
|
imports.wbg.__wbg_setonsuccess_afa464ee777a396d = function(arg0, arg1) {
|
||||||
arg0.onsuccess = arg1;
|
arg0.onsuccess = arg1;
|
||||||
};
|
};
|
||||||
@ -695,6 +1193,10 @@ function __wbg_get_imports() {
|
|||||||
const ret = arg0.then(arg1);
|
const ret = arg0.then(arg1);
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
imports.wbg.__wbg_then_48b406749878a531 = function(arg0, arg1, arg2) {
|
||||||
|
const ret = arg0.then(arg1, arg2);
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
imports.wbg.__wbg_transaction_d6d07c3c9963c49e = function() { return handleError(function (arg0, arg1, arg2) {
|
imports.wbg.__wbg_transaction_d6d07c3c9963c49e = function() { return handleError(function (arg0, arg1, arg2) {
|
||||||
const ret = arg0.transaction(arg1, __wbindgen_enum_IdbTransactionMode[arg2]);
|
const ret = arg0.transaction(arg1, __wbindgen_enum_IdbTransactionMode[arg2]);
|
||||||
return ret;
|
return ret;
|
||||||
@ -703,6 +1205,9 @@ function __wbg_get_imports() {
|
|||||||
const ret = arg0.versions;
|
const ret = arg0.versions;
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
imports.wbg.__wbg_warn_4ca3906c248c47c4 = function(arg0) {
|
||||||
|
console.warn(arg0);
|
||||||
|
};
|
||||||
imports.wbg.__wbindgen_cb_drop = function(arg0) {
|
imports.wbg.__wbindgen_cb_drop = function(arg0) {
|
||||||
const obj = arg0.original;
|
const obj = arg0.original;
|
||||||
if (obj.cnt-- == 1) {
|
if (obj.cnt-- == 1) {
|
||||||
@ -712,16 +1217,40 @@ function __wbg_get_imports() {
|
|||||||
const ret = false;
|
const ret = false;
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
imports.wbg.__wbindgen_closure_wrapper378 = function(arg0, arg1, arg2) {
|
imports.wbg.__wbindgen_closure_wrapper1015 = function(arg0, arg1, arg2) {
|
||||||
const ret = makeMutClosure(arg0, arg1, 122, __wbg_adapter_32);
|
const ret = makeMutClosure(arg0, arg1, 309, __wbg_adapter_52);
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
imports.wbg.__wbindgen_closure_wrapper549 = function(arg0, arg1, arg2) {
|
imports.wbg.__wbindgen_closure_wrapper1320 = function(arg0, arg1, arg2) {
|
||||||
const ret = makeMutClosure(arg0, arg1, 151, __wbg_adapter_35);
|
const ret = makeMutClosure(arg0, arg1, 393, __wbg_adapter_55);
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
imports.wbg.__wbindgen_closure_wrapper857 = function(arg0, arg1, arg2) {
|
imports.wbg.__wbindgen_closure_wrapper423 = function(arg0, arg1, arg2) {
|
||||||
const ret = makeMutClosure(arg0, arg1, 228, __wbg_adapter_38);
|
const ret = makeMutClosure(arg0, arg1, 172, __wbg_adapter_34);
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
imports.wbg.__wbindgen_closure_wrapper424 = function(arg0, arg1, arg2) {
|
||||||
|
const ret = makeMutClosure(arg0, arg1, 172, __wbg_adapter_34);
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
imports.wbg.__wbindgen_closure_wrapper425 = function(arg0, arg1, arg2) {
|
||||||
|
const ret = makeMutClosure(arg0, arg1, 172, __wbg_adapter_39);
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
imports.wbg.__wbindgen_closure_wrapper428 = function(arg0, arg1, arg2) {
|
||||||
|
const ret = makeMutClosure(arg0, arg1, 172, __wbg_adapter_34);
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
imports.wbg.__wbindgen_closure_wrapper766 = function(arg0, arg1, arg2) {
|
||||||
|
const ret = makeMutClosure(arg0, arg1, 238, __wbg_adapter_44);
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
imports.wbg.__wbindgen_closure_wrapper767 = function(arg0, arg1, arg2) {
|
||||||
|
const ret = makeMutClosure(arg0, arg1, 238, __wbg_adapter_44);
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
imports.wbg.__wbindgen_closure_wrapper770 = function(arg0, arg1, arg2) {
|
||||||
|
const ret = makeMutClosure(arg0, arg1, 238, __wbg_adapter_49);
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
imports.wbg.__wbindgen_debug_string = function(arg0, arg1) {
|
imports.wbg.__wbindgen_debug_string = function(arg0, arg1) {
|
||||||
@ -778,6 +1307,14 @@ function __wbg_get_imports() {
|
|||||||
const ret = wasm.memory;
|
const ret = wasm.memory;
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
imports.wbg.__wbindgen_string_get = function(arg0, arg1) {
|
||||||
|
const obj = arg1;
|
||||||
|
const ret = typeof(obj) === 'string' ? obj : undefined;
|
||||||
|
var ptr1 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||||
|
var len1 = WASM_VECTOR_LEN;
|
||||||
|
getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
|
||||||
|
getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
|
||||||
|
};
|
||||||
imports.wbg.__wbindgen_string_new = function(arg0, arg1) {
|
imports.wbg.__wbindgen_string_new = function(arg0, arg1) {
|
||||||
const ret = getStringFromWasm0(arg0, arg1);
|
const ret = getStringFromWasm0(arg0, arg1);
|
||||||
return ret;
|
return ret;
|
||||||
|
Binary file not shown.
@ -525,7 +525,13 @@ impl WasmClient {
|
|||||||
|
|
||||||
impl Drop for WasmClient {
|
impl Drop for WasmClient {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
// Cleanup will be handled by the WebSocket close
|
// Close WebSocket connection if it exists
|
||||||
|
if let Some(ws) = self.websocket.take() {
|
||||||
|
ws.close().unwrap_or_else(|e| {
|
||||||
|
web_sys::console::warn_1(&format!("Failed to close WebSocket: {:?}", e).into());
|
||||||
|
});
|
||||||
|
web_sys::console::log_1(&"🔌 WebSocket connection closed on drop".into());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ use wasm_bindgen::prelude::*;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use sigsocket_client::{SigSocketClient, SignRequest, SignRequestHandler, Result as SigSocketResult, SigSocketError};
|
use sigsocket_client::{SigSocketClient, SignRequest, SignRequestHandler, Result as SigSocketResult, SigSocketError};
|
||||||
use web_sys::console;
|
use web_sys::console;
|
||||||
|
use base64::prelude::*;
|
||||||
|
|
||||||
use crate::vault_bindings::{get_workspace_default_public_key, get_current_keyspace_name, is_unlocked, sign_with_default_keypair};
|
use crate::vault_bindings::{get_workspace_default_public_key, get_current_keyspace_name, is_unlocked, sign_with_default_keypair};
|
||||||
|
|
||||||
@ -31,7 +32,32 @@ impl ExtensionNotificationHandler {
|
|||||||
|
|
||||||
impl SignRequestHandler for ExtensionNotificationHandler {
|
impl SignRequestHandler for ExtensionNotificationHandler {
|
||||||
fn handle_sign_request(&self, request: &SignRequest) -> SigSocketResult<Vec<u8>> {
|
fn handle_sign_request(&self, request: &SignRequest) -> SigSocketResult<Vec<u8>> {
|
||||||
// Create event object for JavaScript
|
console_log!("📨 WASM: Handling sign request: {}", request.id);
|
||||||
|
|
||||||
|
// First, store the request in the WASM client
|
||||||
|
let store_result = SIGSOCKET_CLIENT.with(|c| {
|
||||||
|
let mut client_opt = c.borrow_mut();
|
||||||
|
if let Some(client) = client_opt.as_mut() {
|
||||||
|
// Get the connected public key as the target
|
||||||
|
if let Some(target_public_key) = client.connected_public_key() {
|
||||||
|
client.add_pending_request(request.clone(), target_public_key.to_string());
|
||||||
|
console_log!("✅ WASM: Stored sign request: {}", request.id);
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(SigSocketError::Other("No connected public key".to_string()))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(SigSocketError::Other("No SigSocket client available".to_string()))
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// If storage failed, return error
|
||||||
|
if let Err(e) = store_result {
|
||||||
|
console_log!("❌ WASM: Failed to store request: {:?}", e);
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create event object for JavaScript notification
|
||||||
let event = js_sys::Object::new();
|
let event = js_sys::Object::new();
|
||||||
js_sys::Reflect::set(&event, &"type".into(), &"sign_request".into())
|
js_sys::Reflect::set(&event, &"type".into(), &"sign_request".into())
|
||||||
.map_err(|_| SigSocketError::Other("Failed to set event type".to_string()))?;
|
.map_err(|_| SigSocketError::Other("Failed to set event type".to_string()))?;
|
||||||
@ -40,17 +66,16 @@ impl SignRequestHandler for ExtensionNotificationHandler {
|
|||||||
js_sys::Reflect::set(&event, &"message".into(), &request.message.clone().into())
|
js_sys::Reflect::set(&event, &"message".into(), &request.message.clone().into())
|
||||||
.map_err(|_| SigSocketError::Other("Failed to set message".to_string()))?;
|
.map_err(|_| SigSocketError::Other("Failed to set message".to_string()))?;
|
||||||
|
|
||||||
// Store the request in our pending requests (this will be done by the client)
|
// Notify the extension
|
||||||
// and notify the extension
|
|
||||||
match self.callback.call1(&wasm_bindgen::JsValue::NULL, &event) {
|
match self.callback.call1(&wasm_bindgen::JsValue::NULL, &event) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
console_log!("Notified extension about sign request: {}", request.id);
|
console_log!("✅ WASM: Notified extension about sign request: {}", request.id);
|
||||||
// Return an error to indicate this request should not be auto-signed
|
// Return an error to indicate this request should not be auto-signed
|
||||||
// The extension will handle the approval flow
|
// The extension will handle the approval flow
|
||||||
Err(SigSocketError::Other("Request forwarded to extension for approval".to_string()))
|
Err(SigSocketError::Other("Request forwarded to extension for approval".to_string()))
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
console_log!("Failed to notify extension: {:?}", e);
|
console_log!("❌ WASM: Failed to notify extension: {:?}", e);
|
||||||
Err(SigSocketError::Other("Extension notification failed".to_string()))
|
Err(SigSocketError::Other("Extension notification failed".to_string()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -72,10 +97,12 @@ pub struct SigSocketManager;
|
|||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
impl SigSocketManager {
|
impl SigSocketManager {
|
||||||
/// Connect to SigSocket server with a specific workspace and event callback
|
/// Connect to SigSocket server with smart connection management
|
||||||
///
|
///
|
||||||
/// This establishes a real WebSocket connection using the workspace's default public key
|
/// This handles all connection logic:
|
||||||
/// and integrates with the vault system for security validation.
|
/// - Reuses existing connection if same workspace
|
||||||
|
/// - Switches connection if different workspace
|
||||||
|
/// - Creates new connection if none exists
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
/// * `workspace` - The workspace name to connect with
|
/// * `workspace` - The workspace name to connect with
|
||||||
@ -98,25 +125,56 @@ impl SigSocketManager {
|
|||||||
let public_key_bytes = hex::decode(&public_key_hex)
|
let public_key_bytes = hex::decode(&public_key_hex)
|
||||||
.map_err(|e| JsValue::from_str(&format!("Invalid public key format: {}", e)))?;
|
.map_err(|e| JsValue::from_str(&format!("Invalid public key format: {}", e)))?;
|
||||||
|
|
||||||
// 3. Create SigSocket client with extension notification handler
|
// 3. Check if already connected to same workspace and handle disconnection
|
||||||
let mut client = SigSocketClient::new(server_url, public_key_bytes)
|
let should_connect = SIGSOCKET_CLIENT.with(|c| {
|
||||||
.map_err(|e| JsValue::from_str(&format!("Failed to create client: {:?}", e)))?;
|
let mut client_opt = c.borrow_mut();
|
||||||
|
|
||||||
// Set up extension notification handler using existing API
|
// Check if we already have a client for this workspace
|
||||||
let handler = ExtensionNotificationHandler::new(event_callback.clone());
|
if let Some(existing_client) = client_opt.as_ref() {
|
||||||
client.set_sign_handler(handler);
|
if let Some(existing_key) = existing_client.connected_public_key() {
|
||||||
|
if existing_key == hex::encode(&public_key_bytes) && existing_client.is_connected() {
|
||||||
|
console_log!("🔄 WASM: Already connected to workspace: {}", workspace);
|
||||||
|
return false; // Reuse existing connection
|
||||||
|
} else {
|
||||||
|
console_log!("🔄 WASM: Switching workspace from {} to {}",
|
||||||
|
existing_key, hex::encode(&public_key_bytes));
|
||||||
|
|
||||||
// 4. Connect to the WebSocket server
|
// Disconnect the old client
|
||||||
client.connect().await
|
*client_opt = None; // This will drop the old client and close WebSocket
|
||||||
.map_err(|e| JsValue::from_str(&format!("Connection failed: {:?}", e)))?;
|
console_log!("🔌 WASM: Disconnected from old workspace");
|
||||||
|
|
||||||
console_log!("SigSocket connected successfully to {}", server_url);
|
return true; // Need new connection
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 5. Store the connected client
|
true // Need new connection, no old one to disconnect
|
||||||
SIGSOCKET_CLIENT.with(|c| {
|
|
||||||
*c.borrow_mut() = Some(client);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 4. Create and connect if needed
|
||||||
|
if should_connect {
|
||||||
|
console_log!("🔗 WASM: Creating new connection for workspace: {}", workspace);
|
||||||
|
|
||||||
|
// Create new client
|
||||||
|
let mut client = SigSocketClient::new(server_url, public_key_bytes.clone())
|
||||||
|
.map_err(|e| JsValue::from_str(&format!("Failed to create client: {:?}", e)))?;
|
||||||
|
|
||||||
|
// Set up extension notification handler
|
||||||
|
let handler = ExtensionNotificationHandler::new(event_callback.clone());
|
||||||
|
client.set_sign_handler(handler);
|
||||||
|
|
||||||
|
// Connect to the WebSocket server
|
||||||
|
client.connect().await
|
||||||
|
.map_err(|e| JsValue::from_str(&format!("Connection failed: {:?}", e)))?;
|
||||||
|
|
||||||
|
console_log!("✅ WASM: Connected to SigSocket server for workspace: {}", workspace);
|
||||||
|
|
||||||
|
// Store the connected client
|
||||||
|
SIGSOCKET_CLIENT.with(|c| {
|
||||||
|
*c.borrow_mut() = Some(client);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// 6. Return connection info
|
// 6. Return connection info
|
||||||
let connection_info = SigSocketConnectionInfo {
|
let connection_info = SigSocketConnectionInfo {
|
||||||
workspace: workspace.to_string(),
|
workspace: workspace.to_string(),
|
||||||
@ -158,9 +216,16 @@ impl SigSocketManager {
|
|||||||
pub async fn disconnect() -> Result<(), JsValue> {
|
pub async fn disconnect() -> Result<(), JsValue> {
|
||||||
SIGSOCKET_CLIENT.with(|c| {
|
SIGSOCKET_CLIENT.with(|c| {
|
||||||
let mut client_opt = c.borrow_mut();
|
let mut client_opt = c.borrow_mut();
|
||||||
if let Some(_client) = client_opt.take() {
|
if let Some(client) = client_opt.take() {
|
||||||
// client.disconnect().await?; // Will be async in real implementation
|
let workspace_info = client.connected_public_key()
|
||||||
console_log!("SigSocket client disconnected");
|
.map(|key| key[..16].to_string())
|
||||||
|
.unwrap_or_else(|| "unknown".to_string());
|
||||||
|
|
||||||
|
// Dropping the client will close the WebSocket connection
|
||||||
|
drop(client);
|
||||||
|
console_log!("🔌 WASM: Disconnected SigSocket client (was: {}...)", workspace_info);
|
||||||
|
} else {
|
||||||
|
console_log!("🔌 WASM: No SigSocket client to disconnect");
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
@ -254,27 +319,51 @@ impl SigSocketManager {
|
|||||||
|
|
||||||
// 3. Sign with vault
|
// 3. Sign with vault
|
||||||
let signature_result = sign_with_default_keypair(&message_bytes).await?;
|
let signature_result = sign_with_default_keypair(&message_bytes).await?;
|
||||||
let signature_obj: serde_json::Value = serde_json::from_str(&signature_result.as_string().unwrap())
|
let signature_hex = signature_result.as_string()
|
||||||
.map_err(|e| JsValue::from_str(&format!("Failed to parse signature: {}", e)))?;
|
.ok_or_else(|| JsValue::from_str("Signature result is not a string"))?;
|
||||||
|
|
||||||
let signature_base64 = signature_obj["signature"].as_str()
|
// Convert hex signature to base64 for SigSocket protocol
|
||||||
.ok_or_else(|| JsValue::from_str("Invalid signature format"))?;
|
let signature_bytes = hex::decode(&signature_hex)
|
||||||
|
.map_err(|e| JsValue::from_str(&format!("Invalid hex signature: {}", e)))?;
|
||||||
|
let signature_base64 = base64::prelude::BASE64_STANDARD.encode(&signature_bytes);
|
||||||
|
|
||||||
// 4. Send response to server and remove request
|
// 4. Get original message for response
|
||||||
|
let original_message = SIGSOCKET_CLIENT.with(|c| {
|
||||||
|
let client = c.borrow();
|
||||||
|
let client = client.as_ref().ok_or_else(|| JsValue::from_str("Not connected"))?;
|
||||||
|
|
||||||
|
let request = client.get_pending_request(request_id)
|
||||||
|
.ok_or_else(|| JsValue::from_str("Request not found"))?;
|
||||||
|
|
||||||
|
Ok::<String, JsValue>(request.request.message.clone())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// 5. Send response to server (create a new scope to avoid borrowing issues)
|
||||||
|
{
|
||||||
|
let client_ref = SIGSOCKET_CLIENT.with(|c| {
|
||||||
|
c.borrow().as_ref().map(|client| client as *const SigSocketClient)
|
||||||
|
}).ok_or_else(|| JsValue::from_str("Not connected"))?;
|
||||||
|
|
||||||
|
// SAFETY: We know the client exists and we're using it synchronously
|
||||||
|
let client = unsafe { &*client_ref };
|
||||||
|
|
||||||
|
client.send_response(request_id, &original_message, &signature_base64).await
|
||||||
|
.map_err(|e| JsValue::from_str(&format!("Failed to send response: {:?}", e)))?;
|
||||||
|
|
||||||
|
console_log!("✅ WASM: Sent signature response to server for request: {}", request_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. Remove the request after successful send
|
||||||
SIGSOCKET_CLIENT.with(|c| {
|
SIGSOCKET_CLIENT.with(|c| {
|
||||||
let mut client = c.borrow_mut();
|
let mut client = c.borrow_mut();
|
||||||
let client = client.as_mut().ok_or_else(|| JsValue::from_str("Not connected"))?;
|
if let Some(client) = client.as_mut() {
|
||||||
|
client.remove_pending_request(request_id);
|
||||||
|
console_log!("✅ WASM: Removed request from pending list: {}", request_id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Send response (will be async in real implementation)
|
console_log!("🎉 WASM: Successfully approved and sent signature for request: {}", request_id);
|
||||||
// client.send_response(request_id, &original_request.message, signature_base64).await?;
|
Ok(signature_base64)
|
||||||
|
|
||||||
// Remove the request
|
|
||||||
client.remove_pending_request(request_id);
|
|
||||||
|
|
||||||
console_log!("Approved and sent signature for request: {}", request_id);
|
|
||||||
|
|
||||||
Ok(signature_base64.to_string())
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reject a sign request
|
/// Reject a sign request
|
||||||
@ -288,20 +377,32 @@ impl SigSocketManager {
|
|||||||
/// * `Err(error)` - If rejection failed
|
/// * `Err(error)` - If rejection failed
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
pub async fn reject_request(request_id: &str, reason: &str) -> Result<(), JsValue> {
|
pub async fn reject_request(request_id: &str, reason: &str) -> Result<(), JsValue> {
|
||||||
|
// Send rejection to server first
|
||||||
|
{
|
||||||
|
let client_ref = SIGSOCKET_CLIENT.with(|c| {
|
||||||
|
c.borrow().as_ref().map(|client| client as *const SigSocketClient)
|
||||||
|
}).ok_or_else(|| JsValue::from_str("Not connected"))?;
|
||||||
|
|
||||||
|
// SAFETY: We know the client exists and we're using it synchronously
|
||||||
|
let client = unsafe { &*client_ref };
|
||||||
|
|
||||||
|
client.send_rejection(request_id, reason).await
|
||||||
|
.map_err(|e| JsValue::from_str(&format!("Failed to send rejection: {:?}", e)))?;
|
||||||
|
|
||||||
|
console_log!("✅ WASM: Sent rejection to server for request: {}", request_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the request after successful send
|
||||||
SIGSOCKET_CLIENT.with(|c| {
|
SIGSOCKET_CLIENT.with(|c| {
|
||||||
let mut client = c.borrow_mut();
|
let mut client = c.borrow_mut();
|
||||||
let client = client.as_mut().ok_or_else(|| JsValue::from_str("Not connected"))?;
|
if let Some(client) = client.as_mut() {
|
||||||
|
client.remove_pending_request(request_id);
|
||||||
|
console_log!("✅ WASM: Removed rejected request from pending list: {}", request_id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Send rejection (will be async in real implementation)
|
console_log!("🚫 WASM: Successfully rejected request: {} (reason: {})", request_id, reason);
|
||||||
// client.send_rejection(request_id, reason).await?;
|
Ok(())
|
||||||
|
|
||||||
// Remove the request
|
|
||||||
client.remove_pending_request(request_id);
|
|
||||||
|
|
||||||
console_log!("Rejected request {}: {}", request_id, reason);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get pending requests filtered by current workspace
|
/// Get pending requests filtered by current workspace
|
||||||
|
Loading…
Reference in New Issue
Block a user