diff --git a/src/features/common/repositories/providerSettings/firebase.repository.js b/src/features/common/repositories/providerSettings/firebase.repository.js deleted file mode 100644 index f7fed8f..0000000 --- a/src/features/common/repositories/providerSettings/firebase.repository.js +++ /dev/null @@ -1,83 +0,0 @@ -const { collection, doc, getDoc, getDocs, setDoc, deleteDoc, query, where } = require('firebase/firestore'); -const { getFirestoreInstance: getFirestore } = require('../../services/firebaseClient'); -const { createEncryptedConverter } = require('../firestoreConverter'); - -// Create encrypted converter for provider settings -const providerSettingsConverter = createEncryptedConverter([ - 'api_key', // Encrypt API keys - 'selected_llm_model', // Encrypt model selections for privacy - 'selected_stt_model' -]); - -function providerSettingsCol() { - const db = getFirestore(); - return collection(db, 'provider_settings').withConverter(providerSettingsConverter); -} - -async function getByProvider(uid, provider) { - try { - const docRef = doc(providerSettingsCol(), `${uid}_${provider}`); - const docSnap = await getDoc(docRef); - return docSnap.exists() ? { id: docSnap.id, ...docSnap.data() } : null; - } catch (error) { - console.error('[ProviderSettings Firebase] Error getting provider settings:', error); - return null; - } -} - -async function getAllByUid(uid) { - try { - const q = query(providerSettingsCol(), where('uid', '==', uid)); - const querySnapshot = await getDocs(q); - return querySnapshot.docs.map(doc => ({ id: doc.id, ...doc.data() })); - } catch (error) { - console.error('[ProviderSettings Firebase] Error getting all provider settings:', error); - return []; - } -} - -async function upsert(uid, provider, settings) { - try { - const docRef = doc(providerSettingsCol(), `${uid}_${provider}`); - await setDoc(docRef, settings, { merge: true }); - return { changes: 1 }; - } catch (error) { - console.error('[ProviderSettings Firebase] Error upserting provider settings:', error); - throw error; - } -} - -async function remove(uid, provider) { - try { - const docRef = doc(providerSettingsCol(), `${uid}_${provider}`); - await deleteDoc(docRef); - return { changes: 1 }; - } catch (error) { - console.error('[ProviderSettings Firebase] Error removing provider settings:', error); - throw error; - } -} - -async function removeAllByUid(uid) { - try { - const settings = await getAllByUid(uid); - const deletePromises = settings.map(setting => { - const docRef = doc(providerSettingsCol(), setting.id); - return deleteDoc(docRef); - }); - - await Promise.all(deletePromises); - return { changes: settings.length }; - } catch (error) { - console.error('[ProviderSettings Firebase] Error removing all provider settings:', error); - throw error; - } -} - -module.exports = { - getByProvider, - getAllByUid, - upsert, - remove, - removeAllByUid -}; \ No newline at end of file diff --git a/src/features/common/repositories/providerSettings/index.js b/src/features/common/repositories/providerSettings/index.js index 93d10af..cbb9119 100644 --- a/src/features/common/repositories/providerSettings/index.js +++ b/src/features/common/repositories/providerSettings/index.js @@ -1,4 +1,3 @@ -const firebaseRepository = require('./firebase.repository'); const sqliteRepository = require('./sqlite.repository'); let authService = null; @@ -8,12 +7,7 @@ function setAuthService(service) { } function getBaseRepository() { - if (!authService) { - throw new Error('AuthService not set for providerSettings repository'); - } - - const user = authService.getCurrentUser(); - return user.isLoggedIn ? firebaseRepository : sqliteRepository; + return sqliteRepository; } const providerSettingsRepositoryAdapter = { diff --git a/src/features/common/services/authService.js b/src/features/common/services/authService.js index 9d821a1..7490166 100644 --- a/src/features/common/services/authService.js +++ b/src/features/common/services/authService.js @@ -72,7 +72,7 @@ class AuthService { // ** Initialize encryption key for the logged-in user if permissions are already granted ** if (process.platform === 'darwin' && !(await permissionService.checkKeychainCompleted(this.currentUserId))) { console.warn('[AuthService] Keychain permission not yet completed for this user. Deferring key initialization.'); - } else if (process.platform === 'darwin') { + } else { await encryptionService.initializeKey(user.uid); } @@ -113,12 +113,7 @@ class AuthService { // End active sessions for the local/default user as well. await sessionRepository.endAllActiveSessions(); - // ** Initialize encryption key for the default/local user if permissions are already granted ** - if (process.platform === 'darwin' && !(await permissionService.checkKeychainCompleted(this.currentUserId))) { - console.warn('[AuthService] Keychain permission not yet completed for default user. Deferring key initialization.'); - } else if (process.platform === 'darwin') { - await encryptionService.initializeKey(this.currentUserId); - } + encryptionService.resetSessionKey(); } this.broadcastUserState(); diff --git a/src/features/common/services/encryptionService.js b/src/features/common/services/encryptionService.js index 68fc224..8a4612d 100644 --- a/src/features/common/services/encryptionService.js +++ b/src/features/common/services/encryptionService.js @@ -76,6 +76,10 @@ async function initializeKey(userId) { } } +function resetSessionKey() { + sessionKey = null; +} + /** * Encrypts a given text using AES-256-GCM. * @param {string} text The text to encrypt. @@ -149,8 +153,23 @@ function decrypt(encryptedText) { } } +function looksEncrypted(str) { + if (!str || typeof str !== 'string') return false; + // Base64 chars + optional '=' padding + if (!/^[A-Za-z0-9+/]+={0,2}$/.test(str)) return false; + try { + const buf = Buffer.from(str, 'base64'); + // Our AES-GCM cipher text must be at least 32 bytes (IV 16 + TAG 16) + return buf.length >= 32; + } catch { + return false; + } +} + module.exports = { initializeKey, + resetSessionKey, encrypt, decrypt, + looksEncrypted, }; \ No newline at end of file diff --git a/src/features/common/services/modelStateService.js b/src/features/common/services/modelStateService.js index 98a9b7a..c0a1ce2 100644 --- a/src/features/common/services/modelStateService.js +++ b/src/features/common/services/modelStateService.js @@ -6,23 +6,7 @@ const { PROVIDERS, getProviderClass } = require('../ai/factory'); const encryptionService = require('./encryptionService'); const providerSettingsRepository = require('../repositories/providerSettings'); const userModelSelectionsRepository = require('../repositories/userModelSelections'); - -// Import authService directly (singleton) const authService = require('./authService'); -const permissionService = require('./permissionService'); - -function looksEncrypted(str) { - if (!str || typeof str !== 'string') return false; - // Base64 chars + optional '=' padding - if (!/^[A-Za-z0-9+/]+={0,2}$/.test(str)) return false; - try { - const buf = Buffer.from(str, 'base64'); - // Our AES-GCM cipher text must be at least 32 bytes (IV 16 + TAG 16) - return buf.length >= 32; - } catch { - return false; - } -} class ModelStateService extends EventEmitter { constructor() { @@ -223,7 +207,7 @@ class ModelStateService extends EventEmitter { // Conditionally initialize encryption if old encrypted keys are detected try { const rows = await providerSettingsRepository.getRawApiKeysByUid(); - if (rows.some(r => looksEncrypted(r.api_key))) { + if (rows.some(r => encryptionService.looksEncrypted(r.api_key))) { console.log('[ModelStateService] Encrypted keys detected, initializing encryption...'); await encryptionService.initializeKey(userId); } else {