Files
kakeibo/docs/MEMORY.md
T
suphicakir ad90551c6d feat(dağıtım): Docker imajı ve Axum'un arayüzü sunması
- Çok aşamalı Dockerfile: Node (arayüz) → Rust (release) → debian-slim, root olmayan kullanıcı
- Axum, STATIC_DIR tanımlıysa derlenmiş arayüzü sunar (SPA yönlendirmesi)
- Önbellek başlıkları (HTML no-cache, /assets 1 yıl), gzip/brotli sıkıştırma, istek logları
- Bilinmeyen /api/... → JSON 404; SIGTERM'de düzgün kapanış
- HEALTHCHECK (/api/health), .dockerignore, docs/DEPLOY.md

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 01:26:34 +03:00

76 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 🧠 Proje Hafızası
## ▶️ Sıradaki Adım
**Faz 3 – Adım 2:** Kullanıcıdan bilgi bekleniyor: alan adı, Dokploy sürümü/erişimi, Gitea'nın Dokploy'a bağlı olup olmadığı, MongoDB kurulum şekli (konteyner mi, doğrudan mı; Dokploy içinde mi), replica set durumu. Sonra §12.3 komutları (kullanıcı sunucuda çalıştırır).
## 📌 Alınan Kararlar
- **Yerel demo hesapları** (`kakeibo_dev`): `demo@ornek.com` / `demo12345` (owner, "Demo Hanesi") · `es@ornek.com` / `demo12345` (member, aynı hane). `npm run db:reset` silerse API ile yeniden oluştur (ileride `db:seed`).
- Sürüm kontrolü: Gitea (`https://gitea.ta1sph.info/suphicakir/kakeibo.git`), dal `main`.
- Her adım sonunda Claude commit atar ve haber verir; push'u kullanıcı kendi uygulamasıyla yapar (Claude `git push` çalıştırmaz).
- Node v26 (LTS) sistemde mevcut; ayrı kurulum gerekmedi.
- Yerel MongoDB host portu 27020 (27017 sistem mongod, 27018/27019 başka projeler).
- Frontend lint aracı: oxlint (Vite şablonu varsayılanı), ESLint yerine.
- Veri modeli v1 onaylandı (2026-11-11): ölçekli tam sayı (`_e6`), tarih `YYYY-MM-DD` metin, dönem bitişi hariç, kullanıcı = tek hane, ayarlar haneye gömülü, haftalık bütçe hesaplanır.
- Model yaklaşımı: `#[serde(flatten)]` yerine ortak alanlar `models/common.rs` yardımcılarıyla (`new_meta`, `scope`, `touch`). Modeller ilgili adımda eklenir.
- `households.household_id = _id` (scope filtresi her yerde aynı).
- Auth: JWT (HS256, jsonwebtoken `rust_crypto`) `kakeibo_session` cookie'de; HttpOnly, SameSite=Lax, Secure yalnızca APP_URL https ise. Beni hatırla → 30 gün Max-Age; değilse 12 saat JWT + oturum cookie'si. Kayıttan sonra 30 gün.
- Giriş sınırı: e-posta başına 15 dk'da 5 hatalı deneme (bellek içi, `auth/limiter.rs`). Olmayan kullanıcıda da sahte argon2 doğrulaması (zamanlama sızıntısı yok).
- Hata kodları: `{error:{code,message}}`; `invalid_credentials`, `unauthorized`, `bad_request`, `conflict`, `too_many_requests`.
- Hane izolasyonu kuralı: hane kimliği **her zaman** `AuthUser.household_id`'den alınır, asla URL/gövdeden. Başka hanenin kaydı → 404 (403 değil; varlığı sızdırılmaz).
- `AuthUser` her istekte DB'den kullanıcıyı doğrular (silinen kullanıcı → 401, rol DB'den). Owner-only işlemler: `auth.require_owner()?`.
- Davet: 8 karakter (31 harflik okunaklı alfabe), 7 gün, tek kullanım; `find_one_and_update` ile atomik claim, kayıt transaction'ı içinde.
- Kategoriler/ödeme türleri: tüm üyeler yönetir; silme yok (pasife alma); grup sonradan değişmez; ad grup içinde benzersiz (büyük/küçük harf duyarsız, pasifler dahil); PATCH'te `version` → iyimser kilit (409); sıralama tam liste ister.
- Tasarım: ana renk indigo (`oklch(0.42 0.13 268)`), grup renkleri Okabe-Ito (needs #0072b2, wants #cc79a7, culture #009e73, unexpected #d55e00), *-fg tonları AA (açık ≥5.0, koyu ≥9) hesaplanarak doğrulandı. Gelir #15803d, gider #c62828, uyarı #b45309.
- Tema seçimi localStorage (`kakeibo-theme`), yalnızca cihaz kolaylığı; ileride `users.preferences.theme` ile senkron olabilir.
- `parseMoney`: virgül varsa ondalık; virgül yoksa son noktadan sonra 1–2 rakam → ondalık ("1.25" = 1,25 ₺; "1.250" = 1250 ₺).
- Logo: Taslak A (indigo zemin, spiral krem defter, ¥) seçildi. Kaynak `frontend/brand/logo.svg` + `logo-fullbleed.svg` (maskable/Apple, içerik %82). Uygulama içi `components/logo.tsx` aynı çizim (elle senkron tutulmalı). Manifest Faz 10'da (vite-plugin-pwa).
- ts-rs 12: `backend/.cargo/config.toml` → `TS_RS_EXPORT_DIR=../frontend/src/types`. TS adları: User, HouseholdSummary, Household, Member, Invite, CatalogItem, Catalog{Create,Update,Order}Request, RegisterRequest, LoginRequest, MeResponse, HealthResponse, ApiErrorBody, Group, Role.
- QueryClient: 4xx tekrar denenmez; herhangi bir sorgu 401 alırsa `me` = null. Çıkışta `qc.clear()`.
- Adresler Türkçe: / (Panel), /defter, /plan, /birikim, /diger (mobil), /degerlendirme, /raporlar, /ayarlar, /giris, /kayit. Mobil alt menü: Panel, Defter, Plan, Birikim, Diğer.
- Çıkış: `qc.clear()` KULLANMA (açık ekranlar silinen sorguya bağlı kalıyor) → önce `me=null`, sonra `removeQueries` (me hariç).
- Canlıda SPA yönlendirmesi (Axum: bilinmeyen yol → index.html) Faz 3'te yapılmalı.
- Kayıt ekranı: "Yeni hane kur" / "Davetle katıl"; davet bağlantısı `/kayit?davet=KOD`. Sunucu 409 → e-posta alanı, "Davet…" 400 → kod alanı.
- Yönlendirme sonrası toast için `mutateAsync` + await kullanıldı (mutate() callback'leri bileşen kalkınca çalışmayabilir).
- **Kayıt politikası (kullanıcı kararı, 2026-11-12): Seçenek 1** → `REGISTRATION=invite_only` varsayılan; sistemde hiç hane yokken ilk kayıt yeni hane kurar (sunucu sahibi), sonra yalnızca davetle. `open` isteğe bağlı. Canlıda ilk hesabı kullanıcı `/kayit`'tan açar; demo hesaplar canlıya taşınmaz.
- Prettier ayarı eklendi (önceden yoktu → VS Code kaydedince çift tırnağa çevirebilirdi).
- Canlı imaj: node:26-alpine → rust:1.99-slim-trixie → debian:trixie-slim (ca-certificates + curl, uid 10001 `kakeibo`), 174 MB. Docker katman önbelleği için sahte lib/main ile bağımlılık derlemesi; kalıntılar `*kakeibo*` + `.fingerprint` silinir, gerçek kaynak `touch`lanır (yoksa sahte lib kullanılıyordu).
- Önbellek: HTML `no-cache`, `/assets/*` 1 yıl immutable, diğer statik 1 saat. Bilinmeyen `/api/*` → JSON 404.
- Sürümler: Rust 1.99.1 (edition 2024), Axum 0.8, React 19, Vite 8, TypeScript 7, Tailwind 4.3, shadcn 4.23, Vitest 5, react-router 8, TanStack Query 5.
## ❓ Açık Konular
- Üye çıkarma / rol değiştirme / sahipliği devretme henüz yok (Ayarlar ekranıyla birlikte, Faz 2+).
- Kapalı dönem yeniden açılırken başka açık dönem varsa davranış (Faz 6).
- Altın kuru için veri kaynağı seçimi (Faz 8).
- Aylık değerlendirme çıktı formatı kullanıcıdan gelecek (Faz 9).
- Canlı MongoDB kurulum şekli (konteyner mi, doğrudan mı) Faz 3'te birlikte kontrol edilecek.
## 📝 İlerleme Günlüğü
### 2026-11-11 — Faz 0
- **Adım 1:** Ortam kontrol edildi. CachyOS, fish. Node 26, npm 12, Docker 29 + Compose 5, Git, VS Code, gcc mevcut. Rust ve cargo-watch eksik; kurulum komutları verildi.
- **Adım 2:** `.vscode/` (extensions, settings, tasks, launch), `.gitignore`, `.env.example` ve monorepo klasörleri oluşturuldu. `git init -b main`.
- **Adım 3:** `CLAUDE.md`, `docs/MEMORY.md`, `docs/PHASES.md` oluşturuldu. Gitea remote eklendi, ilk commit push edildi.
- **Adım 4:** Rust kuruldu. `docker-compose.dev.yml` (mongo:7, rs0, healthcheck ile otomatik `rs.initiate`). Backend: Axum, yalnızca `GET /api/health`. Frontend: Vite react-ts şablonu, `/api` proxy → 8080, geçici durum ekranı. Kök `package.json`: concurrently ile `npm run dev`. Uçtan uca test edildi (Mongo PRIMARY, proxy çalışıyor).
- **Adım 5:** `docs/DATA_MODEL.md` hazırlandı ve onaylandı. **Faz 0 tamamlandı.**
### 2026-11-11 — Faz 1
- **Adım 1:** `config.rs` (env + doğrulama, JWT_SECRET ≥ 32), `db.rs` (5 sn timeout + ping), `error.rs` (`AppError` → `{error:{code,message}}` Türkçe; 5xx detayı yalnızca logda), `state.rs`, `routes/` (health: DB yoksa 503). Açılışta DB/ayar hatasında anlaşılır log + çıkış kodu 1. Elle test: 4 senaryo geçti.
- **Adım 2:** `db/` modülü: `col` sabitleri, `migrations.rs` (`schema_migrations`, v1 = koleksiyonları oluştur), `indexes.rs` (DATA_MODEL §4 + tüm hane koleksiyonlarına `(household_id, updated_at)`). `models/common.rs`: UUID v7, `new_meta`, `scope`, `touch`. Açılışta `db::prepare`. 4 birim testi; idempotency ve tek açık dönem indeksi elle doğrulandı. `uuid` (v7) eklendi.
- **Adım 3:** Kimlik doğrulama: `auth/` (password argon2id + spawn_blocking, jwt, limiter, session cookie + `AuthUser` extractor), `extract.rs` (`ValidJson` = JSON + validator, Türkçe 400), `services/account.rs` (kayıt tek transaction: hane + owner + 13 kategori + 4 ödeme türü), `services/seed.rs`, `routes/auth.rs` (register/login/logout/me). Yeni crate'ler: argon2, jsonwebtoken, axum-extra(cookie), validator, time. 11 birim testi; 13 senaryo curl ile, rollback DB'de doğrulandı.
- **Adım 4:** `AuthUser` DB doğrulaması + `require_owner`, `services/invite.rs` (create/list_active/revoke/claim), kayıt davet koduyla `member` olarak katılma (`services/account.rs` yeniden düzenlendi), `routes/household.rs` (GET /household, invites GET/POST/DELETE). 13 birim testi; 15 senaryo e2e (izolasyon 404, member 403, kod tekrar kullanımı, rollback) geçti.
- **Adım 5:** `models/group.rs`, `services/catalog.rs` (kategori + ödeme türü ortak: list/create/update/reorder), `routes/catalog.rs`. Backend lib+bin'e ayrıldı (`AppState::new`). Entegrasyon testleri: `tests/isolation.rs` (3), `tests/catalog.rs` (4) — toplam 22 test. İzolasyon testi, kasıtlı açık eklenerek doğrulandı (yakaladı). `db:reset` artık test DB'lerini de siler. Dev-deps: tower, http-body-util. **Faz 1 tamamlandı.**
### 2026-11-12 — Faz 2
- **Adım 1:** Tailwind v4 (@tailwindcss/vite), shadcn init (radix, preset nova; `cn` paketi = shadcn'in clsx+tailwind-merge yerine geçeni, incelendi), Inter (@fontsource-variable, çevrimdışı). `index.css`: grup + durum token'ları, odak halkası, `money` utility, reduced-motion. `lib/format.ts` (+22 vitest), `lib/theme.tsx`, `lib/groups.ts`, `components/theme-toggle.tsx`. Butonlar 44/48 px'e büyütüldü. Geçici önizleme ekranı (App.tsx), 375 px headless ekran görüntüsüyle iki temada kontrol edildi.
- **Adım 2:** 3 logo taslağı (A indigo defter, B açık defter + para, C hanko) 512/64/32/16 px önizlemeyle sunuldu; kullanıcı A'yı seçti. `scripts/icons.sh` (`npm run icons`): favicon.svg/.ico, icon-192/512, icon-maskable-512, apple-touch-icon (180), brand/icon-1024 (Tauri). Maskable güvenli alan görsel olarak doğrulandı. `index.html`: ikon linkleri, theme-color, description.
- **Adım 3:** ts-rs eklendi (17 tip), health ve hata gövdesi struct'a çevrildi (`ApiErrorBody`). Frontend: @tanstack/react-query, `lib/api.ts` (+7 vitest), `lib/query.ts`, `lib/auth.ts`, main.tsx'te QueryClientProvider. Geçici oturum paneli. Tarayıcı e2e (CDP betiği): yanlış şifre mesajı, giriş, yenilemede kalıcılık, httpOnly (document.cookie'de yok), çıkış — hepsi geçti.
- **Adım 4:** react-router 8 (createBrowserRouter), `RequireAuth`/`RequireGuest`/`Splash`, `AppLayout` (masaüstü kenar çubuğu + mobil üst çubuk + 5'li alt menü, safe-area), Diğer sayfası, 7 yer tutucu sayfa, 404, `lib/strings.ts`, `useDocumentTitle`, `lib/return-path.ts` (+10 test, açık yönlendirme koruması), ThemeToggle `compact`. Tasarım önizlemesi → `/dev/tasarim` (yalnızca DEV). CDP e2e (10 kontrol) iki hata buldu, düzeltildi: giriş sonrası `donus` yarışı, çıkışta `qc.clear()`. Masaüstü içerik boşluğu düzeltildi.
- **Adım 5:** react-hook-form, zod 4, @hookform/resolvers, sonner; shadcn input/label/checkbox/card (44 px / 20 px'e büyütüldü). `components/form-field.tsx`, `components/toaster.tsx`, `features/auth/` (auth-shell, login-page, register-page, schemas + 8 vitest). Metinler `t.auth`. CDP e2e 11 kontrol geçti (boş form, yanlış şifre, giriş + toast, davet linki, kayıtlı e-posta → alan hatası, geçersiz davet → alan hatası, yeni hane). Frontend 47 test. **Faz 2 tamamlandı.**
### 2026-11-12 — Faz 3 öncesi
- Kullanıcı `api.http`'deki kayıt örneğini (yeni@ornek.com) hesap sandı → dosya başlığına hazır hesaplar kutusu ve "bu bir KAYIT isteğidir" uyarısı eklendi.
- Kayıt politikası: `config.rs` `RegistrationMode`, `AppError::RegistrationClosed` (403), `account::new_household_allowed`, `GET /api/auth/registration` (`RegistrationInfo` TS tipi). `tests/registration.rs` (2). Kayıt ekranı kapalıyken yalnızca davet modu + açıklama. `.env`/`.env.example`: `REGISTRATION=invite_only`. Prettier (`.prettierrc.json`, `.prettierignore`, `format` / `format:check`), tüm src biçimlendirildi (yalnızca boşluk).
### 2026-11-12 — Faz 3
- **Adım 1:** tower-http (fs, compression gzip/br, trace). `routes::app()` + `routes/spa.rs` (ServeDir + index.html fallback + cache middleware), API fallback JSON 404, `STATIC_DIR` config, graceful shutdown (SIGTERM/Ctrl+C). `Dockerfile`, `.dockerignore`, `docs/DEPLOY.md` başlatıldı. İmaj yerelde çalıştırıldı (host network, 8090): 10 kontrol + tarayıcı ekran görüntüsü geçti. İlk derlemede Docker önbellek hilesi hatası (sahte lib.rs) bulundu, düzeltildi.