fix(arayüz): sunucuya ulaşılamadığında açıklayıcı hata mesajı

- Vekil 502/503/504 döndüğünde "Beklenmeyen hata" yerine "Sunucu şu an yanıt vermiyor"
- Yerel geliştirmede backend'in kapalı kalması durumu MEMORY'ye not edildi

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-13 02:05:31 +03:00
parent feb2e1d16c
commit 33f64a0459
3 changed files with 16 additions and 1 deletions
+1
View File
@@ -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".
+10 -1
View File
@@ -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.',
})
})
+5
View File
@@ -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<T>(method: Method, path: string, body?: unknown): Promise
if (!res.ok) {
const detail = (data as Partial<ApiErrorBody> | 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