diff --git a/docs/MEMORY.md b/docs/MEMORY.md index 481bbfc..9953587 100644 --- a/docs/MEMORY.md +++ b/docs/MEMORY.md @@ -5,6 +5,7 @@ **Bekleyen (kullanıcıda):** Faz 5 – Adım 5 canlı yedek kurulumu (`docs/DEPLOY.md` §6.1–6.3) → çıktılar gelince Faz 5 ✅. ## 📌 Alınan Kararlar +- **Dikkat (2026-11-13):** Claude'un hızlı ardışık `backend/src` değişiklikleri (bilerek bozup geri alma, toplu düzenleme) kullanıcının `npm run dev` içindeki `cargo watch`'ını backend kapalı halde bırakabiliyor (giriş: "Beklenmeyen hata", 5173 → 502). Böyle denemelerden sonra `curl localhost:8080/api/health`; kapalıysa `touch backend/src/main.rs` (cargo watch yeniden başlatır). Arayüz artık 502/503/504'te "Sunucu şu an yanıt vermiyor" der. - **Faz 9 kararları (kullanıcı, 2026-11-13):** (1) Aylık rapor biçimini **Claude tasarlar**: müşteri gözüyle ne yaptım / ne yapılabilirdi / ne yapılmalıydı. (2) JSON yedek: Claude best practice analizi yapıp kullanıcıyı üzmeyen çözüm üretir (öneri: sunucuda otomatik geri dönüş noktası + "geri al", önizleme, sahip). (3) **Tüm kredi kartı ve nakit/banka hesabı ekstreleri** içe aktarılabilsin; amaç **yıl başından beri** veriyi toplayıp sonraki aylara referans; grup/kategori eşleştirmesi kullanıcıya bırakılabilir → ayrı adım (Faz 9 Adım 4), geçmiş dönemler gerekir. - **Yeniden kapatmada birikim (kullanıcı "a", 2026-11-13):** bağlı TL alımı yeni tutara güncellenir (0 → silinir). - **Faz 8 kararları (kullanıcı, 2026-11-13):** kur kaynakları TCMB (döviz) + xaus.com ons×TCMB USD (altın; çeyrek/yarım/tam has gram ile) + manuel; değerleme **alış kuru**; kapanış birikimi onayla TL varlığına alım (tekrar eklenmez), geçmiş kapanışlar "bekleyen birikimler". diff --git a/frontend/src/lib/api.test.ts b/frontend/src/lib/api.test.ts index e92dc76..1c751ca 100644 --- a/frontend/src/lib/api.test.ts +++ b/frontend/src/lib/api.test.ts @@ -50,10 +50,19 @@ describe('api', () => { expect(err.isUnauthorized).toBe(true) }) - it('JSON olmayan hata (ör. proxy 502) → genel Türkçe mesaj', async () => { + it('backend kapalı (vekil 502/503/504) → "sunucu yanıt vermiyor"', async () => { globalThis.fetch = vi.fn().mockResolvedValue(new Response('Bad Gateway', { status: 502 })) await expect(api.get('/health')).rejects.toMatchObject({ status: 502, + code: 'unavailable', + message: 'Sunucu şu an yanıt vermiyor. Birkaç saniye sonra tekrar deneyin.', + }) + }) + + it('JSON olmayan diğer hata → genel Türkçe mesaj', async () => { + globalThis.fetch = vi.fn().mockResolvedValue(new Response('oops', { status: 500 })) + await expect(api.get('/health')).rejects.toMatchObject({ + status: 500, message: 'Beklenmeyen bir hata oluştu, lütfen tekrar deneyin.', }) }) diff --git a/frontend/src/lib/api.ts b/frontend/src/lib/api.ts index 296f06d..a9cbd4f 100644 --- a/frontend/src/lib/api.ts +++ b/frontend/src/lib/api.ts @@ -26,6 +26,8 @@ export class ApiError extends Error { const NETWORK_MESSAGE = 'Sunucuya ulaşılamadı. İnternet bağlantınızı kontrol edip tekrar deneyin.' const UNKNOWN_MESSAGE = 'Beklenmeyen bir hata oluştu, lütfen tekrar deneyin.' +/** Vekil (Vite / Traefik) backend'e ulaşamadı: yeniden başlıyor, güncelleniyor ya da kapalı */ +const UNAVAILABLE_MESSAGE = 'Sunucu şu an yanıt vermiyor. Birkaç saniye sonra tekrar deneyin.' type Method = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' @@ -48,6 +50,9 @@ async function request(method: Method, path: string, body?: unknown): Promise if (!res.ok) { const detail = (data as Partial | null)?.error + if (!detail && [502, 503, 504].includes(res.status)) { + throw new ApiError(res.status, 'unavailable', UNAVAILABLE_MESSAGE) + } throw new ApiError(res.status, detail?.code ?? 'unknown', detail?.message ?? UNKNOWN_MESSAGE) } return data as T