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:
@@ -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 ✅.
|
**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
|
## 📌 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.
|
- **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).
|
- **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".
|
- **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".
|
||||||
|
|||||||
@@ -50,10 +50,19 @@ describe('api', () => {
|
|||||||
expect(err.isUnauthorized).toBe(true)
|
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 }))
|
globalThis.fetch = vi.fn().mockResolvedValue(new Response('Bad Gateway', { status: 502 }))
|
||||||
await expect(api.get('/health')).rejects.toMatchObject({
|
await expect(api.get('/health')).rejects.toMatchObject({
|
||||||
status: 502,
|
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.',
|
message: 'Beklenmeyen bir hata oluştu, lütfen tekrar deneyin.',
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -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 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.'
|
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'
|
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) {
|
if (!res.ok) {
|
||||||
const detail = (data as Partial<ApiErrorBody> | null)?.error
|
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)
|
throw new ApiError(res.status, detail?.code ?? 'unknown', detail?.message ?? UNKNOWN_MESSAGE)
|
||||||
}
|
}
|
||||||
return data as T
|
return data as T
|
||||||
|
|||||||
Reference in New Issue
Block a user