feat(güvenlik): giriş ve kayıtta Cloudflare Turnstile, IP başına 3 hatalı denemede 15 dk kilit

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-14 00:29:00 +03:00
parent 5cbf97d884
commit a2eff1a984
27 changed files with 1247 additions and 164 deletions
+1
View File
@@ -87,6 +87,7 @@ npm run db:restore -- <dosya|latest> <hedef_db> [--force]
- Ayarlar: `/ayarlar` menü (+ tema) → alt sayfalar `SettingsSubpage` ("‹ Ayarlar"): `/ayarlar/kategoriler`, `/odeme-turleri`, `/butce` (varsayılan yüzdeler + öneri yöntemi), `/hane` (ad, üyeler, davet bağlantısı `/kayit?davet=`), `/hesap` (ad, şifre), `/veri` (yedek, CSV, ekstre). Kancalar `lib/household.ts`. Şifre değişince `users.sessions_valid_from` → eski jetonlar 401 (`AuthUser`). Katalog yönetimi `features/settings/catalog-editor.tsx` + `lib/catalog.ts` (`useCreateCatalog`/`useUpdateCatalog`/`useReorderCatalog`, `moveId`); sıralama isteği grubun **tüm** kimlikleri (pasifler dahil); grup sonradan değişmez.
- Erişilebilirlik kuralları: `--muted-foreground` açık temada 0.52 (gri zeminde AA); kart bileşenleri (`Section`, rapor bölümü) `min-w-0` — ızgara öğesi içeriğine uzayıp telefonda taşmasın; `Button` `sm`/`icon-sm` dokunmatikte (`pointer-coarse`) 44 px; tutarlar `formatMoney` ile **bölünmez boşluklu** (`1.250,00\u00a0₺`; testlerde `\u00a0`); bütçe çubuğu `budgetLevel` (≥ %80 turuncu + "Bütçe kullanımı %N", aşım kırmızı); `Markdown shiftHeadings` (sayfada tek h1). Cümlelerde sayıdan sonra ek yok.
- PWA: `vite.config.ts` `VitePWA` (registerType `prompt`, `/api` asla önbelleğe alınmaz, `navigateFallbackDenylist /api`, xlsx + yabancı font alt kümeleri ön önbellekte yok); `lib/pwa.tsx` (`PwaUpdater` "Yeni sürüm hazır → Yenile", saatlik yoklama; `OfflineBanner`), `lib/online.ts`. `main.tsx` `onlineManager.setOnline(navigator.onLine)`. Oturum sorgusu ağ hatası/duraklama → `RequireAuth` giriş sayfasına **atmaz**, "Tekrar dene" gösterir. Geliştirmede SW yok. DEPLOY §7.
- Giriş güvenliği: `auth/guard.rs` (IP başına üst üste 3 hata → giriş+kayıt 15 dk kilit, bellek içi; e-postaya göre kilit YOK), `auth/captcha.rs` (Cloudflare Turnstile; anahtar yoksa kapalı; testte `Captcha::Fixed`), `auth/client_ip.rs` (`TRUST_PROXY` → en sağdaki X-Forwarded-For). Hata kodları `locked` (429 + Retry-After), `captcha` (400), `invalid_credentials` (kalan hak mesajda). `GET /auth/guard`. Arayüz `features/auth/guard.tsx` (`useAuthSecurity`), `lock-notice.tsx`, `components/turnstile.tsx`. Testler `TestApp::with_captcha`, `send_with` (X-Forwarded-For). DEPLOY §8.
- Hane izolasyonu: hane kimliği yalnızca `AuthUser.household_id`'den; sorgular `scope(hid)` ile başlar; başka hane → 404.
## Ortam