// Key operations module for the 4DSTAR Bundle Manager // Handles all key management operations (list, generate, add, remove, sync) const { ipcRenderer } = require('electron'); // Dependencies (injected by renderer-refactored.js) let stateManager, domManager, uiComponents; // Initialize dependencies function initializeDependencies(deps) { stateManager = deps.stateManager; domManager = deps.domManager; uiComponents = deps.uiComponents; console.log('[KEY_OPERATIONS] Dependencies initialized'); } // === KEY LISTING === async function loadKeys() { try { domManager.showSpinner(); const result = await ipcRenderer.invoke('list-keys'); domManager.hideSpinner(); if (result.success) { stateManager.setKeysState(result); displayKeys(result); return result; } else { domManager.showModal('Error', `Failed to load keys: ${result.error}`); return null; } } catch (error) { domManager.hideSpinner(); domManager.showModal('Error', `Failed to load keys: ${error.message}`); return null; } } function displayKeys(keysData) { const keysContainer = document.getElementById('keys-list-container'); if (!keysContainer) return; if (keysData.total_count === 0) { keysContainer.innerHTML = `
🔑

No Keys Found

No trusted keys are currently installed. Generate or add keys to get started.

`; return; } // Update the main header with count and add action buttons const mainHeader = document.querySelector('#keys-list-view .keys-header h3'); if (mainHeader) { mainHeader.textContent = `Trusted Keys (${keysData.total_count})`; } // Add action buttons to the header if they don't exist let keysHeader = document.querySelector('#keys-list-view .keys-header'); if (keysHeader && !keysHeader.querySelector('.keys-actions')) { const actionsDiv = document.createElement('div'); actionsDiv.className = 'keys-actions'; actionsDiv.innerHTML = ` `; keysHeader.appendChild(actionsDiv); } let html = ''; for (const [sourceName, keys] of Object.entries(keysData.keys)) { html += `

${sourceName}

${keys.length} keys
`; for (const key of keys) { const shortFingerprint = key.fingerprint.substring(0, 16) + '...'; html += ` `; } html += `
Name Fingerprint Size Actions
${key.name} ${shortFingerprint} ${key.size_bytes} bytes
`; } keysContainer.innerHTML = html; // Add event listeners for dynamically generated content setupKeyListEventListeners(); } function setupKeyListEventListeners() { // Refresh keys button const refreshBtn = document.getElementById('refresh-keys-btn'); if (refreshBtn) { refreshBtn.addEventListener('click', loadKeys); } // Sync remotes button const syncBtn = document.getElementById('sync-remotes-btn'); if (syncBtn) { syncBtn.addEventListener('click', handleSyncRemotes); } // Remove key buttons - now handled exclusively here (removed from event-handlers.js) const removeButtons = document.querySelectorAll('.remove-key-btn'); removeButtons.forEach(btn => { btn.addEventListener('click', (e) => { const fingerprint = e.target.dataset.keyFingerprint; const keyName = e.target.dataset.keyName; handleRemoveKey(fingerprint, keyName); }); }); } // === KEY GENERATION === async function handleGenerateKey() { const keyName = document.getElementById('generate-key-name')?.value || 'author_key'; const keyType = document.getElementById('generate-key-type')?.value || 'ed25519'; const outputDir = document.getElementById('generate-output-dir')?.value || '.'; if (!keyName.trim()) { domManager.showModal('Error', 'Please enter a key name.'); return; } try { domManager.showSpinner(); const result = await ipcRenderer.invoke('generate-key', { keyName: keyName.trim(), keyType: keyType, outputDir: outputDir }); domManager.hideSpinner(); if (result.success) { domManager.showModal('Success', `

Key Generated Successfully!

Key Type: ${result.key_type.toUpperCase()}

Fingerprint: ${result.fingerprint}

Private Key: ${result.private_key_path}

Public Key: ${result.public_key_path}

⚠️ Important: Keep your private key secure and never share it!
`); // Clear form document.getElementById('generate-key-name').value = ''; document.getElementById('generate-output-dir').value = '.'; } else { domManager.showModal('Error', `Failed to generate key: ${result.error}`); } } catch (error) { domManager.hideSpinner(); domManager.showModal('Error', `Failed to generate key: ${error.message}`); } } // === KEY ADDITION === async function handleAddKey() { try { // Use IPC-based file dialog instead of @electron/remote const keyPath = await ipcRenderer.invoke('select-key-file'); if (!keyPath) { return; // User canceled dialog } domManager.showSpinner(); const addResult = await ipcRenderer.invoke('add-key', keyPath); domManager.hideSpinner(); if (addResult.success) { if (addResult.already_existed) { domManager.showModal('Info', `Key '${addResult.key_name}' already exists in trust store.`); } else { domManager.showModal('Success', `

Key Added Successfully!

Your public key has been added to the trust store

Key Name
${addResult.key_name}
Fingerprint
${addResult.fingerprint}
`); } // Refresh keys list await loadKeys(); } else { domManager.showModal('Error', `Failed to add key: ${addResult.error}`); } } catch (error) { domManager.hideSpinner(); domManager.showModal('Error', `Failed to add key: ${error.message}`); } } // === KEY REMOVAL === async function handleRemoveKey(fingerprint, keyName) { const confirmed = await uiComponents.showConfirmDialog( 'Remove Key', `Are you sure you want to remove the key "${keyName}"?\n\nThis action cannot be undone.` ); if (!confirmed) return; try { domManager.showSpinner(); const result = await ipcRenderer.invoke('remove-key', fingerprint); domManager.hideSpinner(); if (result.success) { domManager.showModal('Success', `Removed ${result.removed_count} key(s) successfully.`); // Refresh keys list await loadKeys(); } else { domManager.showModal('Error', `Failed to remove key: ${result.error}`); } } catch (error) { domManager.hideSpinner(); domManager.showModal('Error', `Failed to remove key: ${error.message}`); } } // === REMOTE SYNC === async function handleSyncRemotes() { try { domManager.showSpinner(); const result = await ipcRenderer.invoke('sync-remotes'); domManager.hideSpinner(); if (result.success) { const successCount = result.synced_remotes.filter(r => r.status === 'success').length; const failedCount = result.synced_remotes.filter(r => r.status === 'failed').length; let message = `

Remote Sync Completed

✅ Successful: ${successCount}

❌ Failed: ${failedCount}

📦 Total keys synced: ${result.total_keys_synced}

`; if (result.removed_remotes.length > 0) { message += `

Removed failing remotes: ${result.removed_remotes.join(', ')}

`; } domManager.showModal('Sync Results', message); // Refresh keys list await loadKeys(); } else { domManager.showModal('Error', `Failed to sync remotes: ${result.error}`); } } catch (error) { domManager.hideSpinner(); domManager.showModal('Error', `Failed to sync remotes: ${error.message}`); } } // === REMOTE MANAGEMENT === async function loadRemoteSources() { try { const result = await ipcRenderer.invoke('get-remote-sources'); if (result.success) { displayRemoteSources(result.remotes); return result.remotes; } else { domManager.showModal('Error', `Failed to load remote sources: ${result.error}`); return []; } } catch (error) { domManager.showModal('Error', `Failed to load remote sources: ${error.message}`); return []; } } function displayRemoteSources(remotes) { const remotesContainer = document.getElementById('remotes-list-container'); if (!remotesContainer) return; if (remotes.length === 0) { remotesContainer.innerHTML = `
🌐

No Remote Sources

No remote key sources are configured. Add remote repositories to sync keys automatically.

`; return; } let html = `

Remote Key Sources (${remotes.length})

`; for (const remote of remotes) { const status = remote.exists ? '✅' : '❌'; const statusText = remote.exists ? 'Synced' : 'Not synced'; html += ` `; } html += `
Status Name URL Keys Actions
${status} ${remote.name} ${remote.url} ${remote.keys_count}
`; remotesContainer.innerHTML = html; // Add event listeners for remove buttons const removeButtons = document.querySelectorAll('.remove-remote-btn'); removeButtons.forEach(btn => { btn.addEventListener('click', (e) => { const remoteName = e.target.dataset.remoteName; handleRemoveRemoteSource(remoteName); }); }); } async function handleAddRemoteSource() { const name = document.getElementById('remote-name')?.value; const url = document.getElementById('remote-url')?.value; if (!name || !url) { domManager.showModal('Error', 'Please enter both name and URL for the remote source.'); return; } try { domManager.showSpinner(); const result = await ipcRenderer.invoke('add-remote-source', { name: name.trim(), url: url.trim() }); domManager.hideSpinner(); if (result.success) { domManager.showModal('Success', `Remote source '${result.name}' added successfully.`); // Clear form document.getElementById('remote-name').value = ''; document.getElementById('remote-url').value = ''; // Refresh remotes list await loadRemoteSources(); } else { domManager.showModal('Error', `Failed to add remote source: ${result.error}`); } } catch (error) { domManager.hideSpinner(); domManager.showModal('Error', `Failed to add remote source: ${error.message}`); } } async function handleRemoveRemoteSource(remoteName) { const confirmed = await uiComponents.showConfirmDialog( 'Remove Remote Source', `Are you sure you want to remove the remote source "${remoteName}"?\n\nThis will also remove all keys synced from this source.` ); if (!confirmed) return; try { domManager.showSpinner(); const result = await ipcRenderer.invoke('remove-remote-source', remoteName); domManager.hideSpinner(); if (result.success) { domManager.showModal('Success', `Remote source '${result.name}' removed successfully.`); // Refresh remotes list and keys list await loadRemoteSources(); await loadKeys(); } else { domManager.showModal('Error', `Failed to remove remote source: ${result.error}`); } } catch (error) { domManager.hideSpinner(); domManager.showModal('Error', `Failed to remove remote source: ${error.message}`); } } // Export functions module.exports = { initializeDependencies, loadKeys, loadTrustedKeys: loadKeys, // Alias for compatibility handleGenerateKey, handleAddKey, handleRemoveKey, handleSyncRemotes, loadRemoteSources, handleAddRemoteSource, handleRemoveRemoteSource };