remove providerSettings firebaseRepository + minor refactor

This commit is contained in:
samtiz 2025-07-15 16:10:32 +09:00
parent ab23c10006
commit 8592d1c4ed
5 changed files with 23 additions and 114 deletions

View File

@ -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
};

View File

@ -1,4 +1,3 @@
const firebaseRepository = require('./firebase.repository');
const sqliteRepository = require('./sqlite.repository'); const sqliteRepository = require('./sqlite.repository');
let authService = null; let authService = null;
@ -8,12 +7,7 @@ function setAuthService(service) {
} }
function getBaseRepository() { function getBaseRepository() {
if (!authService) { return sqliteRepository;
throw new Error('AuthService not set for providerSettings repository');
}
const user = authService.getCurrentUser();
return user.isLoggedIn ? firebaseRepository : sqliteRepository;
} }
const providerSettingsRepositoryAdapter = { const providerSettingsRepositoryAdapter = {

View File

@ -72,7 +72,7 @@ class AuthService {
// ** Initialize encryption key for the logged-in user if permissions are already granted ** // ** Initialize encryption key for the logged-in user if permissions are already granted **
if (process.platform === 'darwin' && !(await permissionService.checkKeychainCompleted(this.currentUserId))) { if (process.platform === 'darwin' && !(await permissionService.checkKeychainCompleted(this.currentUserId))) {
console.warn('[AuthService] Keychain permission not yet completed for this user. Deferring key initialization.'); 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); await encryptionService.initializeKey(user.uid);
} }
@ -113,12 +113,7 @@ class AuthService {
// End active sessions for the local/default user as well. // End active sessions for the local/default user as well.
await sessionRepository.endAllActiveSessions(); await sessionRepository.endAllActiveSessions();
// ** Initialize encryption key for the default/local user if permissions are already granted ** encryptionService.resetSessionKey();
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);
}
} }
this.broadcastUserState(); this.broadcastUserState();

View File

@ -76,6 +76,10 @@ async function initializeKey(userId) {
} }
} }
function resetSessionKey() {
sessionKey = null;
}
/** /**
* Encrypts a given text using AES-256-GCM. * Encrypts a given text using AES-256-GCM.
* @param {string} text The text to encrypt. * @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 = { module.exports = {
initializeKey, initializeKey,
resetSessionKey,
encrypt, encrypt,
decrypt, decrypt,
looksEncrypted,
}; };

View File

@ -6,23 +6,7 @@ const { PROVIDERS, getProviderClass } = require('../ai/factory');
const encryptionService = require('./encryptionService'); const encryptionService = require('./encryptionService');
const providerSettingsRepository = require('../repositories/providerSettings'); const providerSettingsRepository = require('../repositories/providerSettings');
const userModelSelectionsRepository = require('../repositories/userModelSelections'); const userModelSelectionsRepository = require('../repositories/userModelSelections');
// Import authService directly (singleton)
const authService = require('./authService'); 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 { class ModelStateService extends EventEmitter {
constructor() { constructor() {
@ -223,7 +207,7 @@ class ModelStateService extends EventEmitter {
// Conditionally initialize encryption if old encrypted keys are detected // Conditionally initialize encryption if old encrypted keys are detected
try { try {
const rows = await providerSettingsRepository.getRawApiKeysByUid(); 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...'); console.log('[ModelStateService] Encrypted keys detected, initializing encryption...');
await encryptionService.initializeKey(userId); await encryptionService.initializeKey(userId);
} else { } else {