Files
obsidian-mcp-server/src/utils/encryption-utils.ts
Bill 779b3d6e8c fix: handle undefined safeStorage and remove diagnostic logging
Root cause: electron.safeStorage was undefined (not null) when the
property doesn't exist, causing "Cannot read properties of undefined"
error when accessing isEncryptionAvailable.

Fix: Normalize undefined to null with `|| null` operator when importing
safeStorage, ensuring consistent null checks throughout the code.

Changes:
- Set safeStorage to null if electron.safeStorage is undefined
- Remove all diagnostic try-catch blocks from settings UI
- Remove console.log debugging statements
- Restore clean code that now works correctly

This resolves the settings UI crash that prevented the API key
management section from displaying.
2025-10-26 00:16:35 -04:00

77 lines
2.3 KiB
TypeScript

// Safely import safeStorage - may not be available in all environments
let safeStorage: any = null;
try {
const electron = require('electron');
safeStorage = electron.safeStorage || null;
} catch (error) {
console.warn('Electron safeStorage not available, API keys will be stored in plaintext');
}
/**
* Checks if encryption is available on the current platform
* @returns true if safeStorage encryption is available
*/
export function isEncryptionAvailable(): boolean {
return safeStorage !== null &&
typeof safeStorage.isEncryptionAvailable === 'function' &&
safeStorage.isEncryptionAvailable();
}
/**
* Encrypts an API key using Electron's safeStorage API
* Falls back to plaintext if encryption is not available (e.g., Linux without keyring)
* @param apiKey The plaintext API key to encrypt
* @returns Encrypted API key with "encrypted:" prefix, or plaintext if encryption unavailable
*/
export function encryptApiKey(apiKey: string): string {
if (!apiKey) {
return '';
}
// Check if safeStorage is available and encryption is enabled
if (!isEncryptionAvailable()) {
console.warn('Encryption not available, storing API key in plaintext');
return apiKey;
}
try {
const encrypted = safeStorage.encryptString(apiKey);
return `encrypted:${encrypted.toString('base64')}`;
} catch (error) {
console.error('Failed to encrypt API key, falling back to plaintext:', error);
return apiKey;
}
}
/**
* Decrypts an API key encrypted with encryptApiKey
* @param stored The stored API key (encrypted or plaintext)
* @returns Decrypted API key
*/
export function decryptApiKey(stored: string): string {
if (!stored) {
return '';
}
// Check if this is an encrypted key
if (!stored.startsWith('encrypted:')) {
// Legacy plaintext key or fallback
return stored;
}
// If safeStorage is not available, we can't decrypt
if (!safeStorage) {
console.error('Cannot decrypt API key: safeStorage not available');
throw new Error('Failed to decrypt API key. You may need to regenerate it.');
}
try {
const encryptedData = stored.substring(10); // Remove "encrypted:" prefix
const buffer = Buffer.from(encryptedData, 'base64');
return safeStorage.decryptString(buffer);
} catch (error) {
console.error('Failed to decrypt API key:', error);
throw new Error('Failed to decrypt API key. You may need to regenerate it.');
}
}