From dd21bcbda221aa4a323c2fabdb40fd38782be354 Mon Sep 17 00:00:00 2001 From: ht Date: Sun, 6 Sep 2026 19:05:55 +0300 Subject: [PATCH] scope AI model settings per account --- docs/quality/uygulama-durumu.md | 6 ++++++ src/context/AppContext.jsx | 24 ++++++++++++++++++++---- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/docs/quality/uygulama-durumu.md b/docs/quality/uygulama-durumu.md index d11415c..035068d 100644 --- a/docs/quality/uygulama-durumu.md +++ b/docs/quality/uygulama-durumu.md @@ -2152,3 +2152,9 @@ Doğrulama: yerel sentetik hazır taslakta çıktı penceresi başlığı, Türk Analitik ekranındaki ciro hedefi daha önce ortak `localStorage` anahtarına yazılıyordu; bu, aynı tarayıcıda hesap değiştirildiğinde hedefin yanlış hesaba görünmesine neden olabilirdi. Hedef anahtarı artık normalize edilmiş hesap e-postasıyla ayrılıyor, depolama hataları güvenle ele alınıyor ve hesap değişiminde eski hedef yeni hesaba yazılmıyor. Doğrulama: 293 otomatik test ve üretim derlemesi başarılı. Gerçek iki hesaplı tarayıcı kabulü ve canlı deployment hâlâ ayrıca yapılmalı. + +### 2026-09-06 · AI model ayarlarının hesap izolasyonu + +AI fallback zinciri daha önce ortak `localStorage` anahtarında tutuluyordu. Ayar artık normalize edilmiş hesap e-postasına bağlı anahtarda saklanıyor; oturum değişiminde yeni hesabın zinciri yeniden yükleniyor ve önceki hesabın değeri yazılmıyor. Depolama erişimi olmayan ortamlarda varsayılan zincir kullanılıyor. + +Doğrulama: 293 otomatik test ve üretim derlemesi başarılı. Gerçek iki hesaplı AI ayarı tarayıcı kabulü ve canlı sağlayıcı testi ayrıca yapılmalı. diff --git a/src/context/AppContext.jsx b/src/context/AppContext.jsx index 5b36e24..563bd6f 100644 --- a/src/context/AppContext.jsx +++ b/src/context/AppContext.jsx @@ -458,8 +458,8 @@ export const AppProvider = ({ children }) => { const VARSAYILAN_ZINCIR = ['glmflash', 'gemini', 'deepseek']; const ZINCIR_SURUM = 3; // artırıldığında kayıtlı zincir yeni düzene taşınır - const [aiModelConfig, setAiModelConfig] = useState(() => { - const saved = localStorage.getItem('ihaleist_ai_model_config'); + const aiModelAnahtari = (email) => `ihaleist_ai_model_config:${encodeURIComponent(String(email || '').toLowerCase())}`; + const normalizeAiModelConfig = (saved) => { if (saved) { try { const cfg = JSON.parse(saved); @@ -478,11 +478,27 @@ export const AppProvider = ({ children }) => { } catch { /* yoksay */ } } return { chain: VARSAYILAN_ZINCIR, surum: ZINCIR_SURUM }; + }; + + const [aiModelConfig, setAiModelConfig] = useState(() => { + try { return normalizeAiModelConfig(localStorage.getItem(aiModelAnahtari(getStoredUser()?.email))); } + catch { return normalizeAiModelConfig(null); } }); + const [aiConfigLoadedKey, setAiConfigLoadedKey] = useState(null); useEffect(() => { - yerelKaydet('ihaleist_ai_model_config', aiModelConfig); - }, [aiModelConfig]); + const key = authUser?.email ? aiModelAnahtari(authUser.email) : null; + if (!key) { setAiModelConfig(normalizeAiModelConfig(null)); setAiConfigLoadedKey(null); return; } + let saved = null; + try { saved = localStorage.getItem(key); } catch { /* özel tarama */ } + setAiModelConfig(normalizeAiModelConfig(saved)); + setAiConfigLoadedKey(key); + }, [authUser?.email]); + + useEffect(() => { + if (!aiConfigLoadedKey) return; + yerelKaydet(aiConfigLoadedKey, aiModelConfig); + }, [aiModelConfig, aiConfigLoadedKey]); const getModelById = (id) => AI_MODEL_CATALOG.find(m => m.id === id); const activeAiModel = getModelById(aiModelConfig.chain[0]) || AI_MODEL_CATALOG[0];