e4c8d971fc
- scripts/backup.sh: mongodump + bütünlük denetimi, son 14 yedek - scripts/restore.sh: ayrı veritabanına geri yükleme ve sayım karşılaştırma - scripts/backup-pull.sh + systemd zamanlayıcıları; kurulum docs/DEPLOY.md §6 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
24 KiB
24 KiB
🧠 Proje Hafızası
▶️ Sıradaki Adım
Faz 5 – Adım 5 (kullanıcıda): docs/DEPLOY.md §6.1–6.3: Atlas'a betik + systemd timer kurulumu, ilk yedek, restore.sh latest kakeibo_restore_test ile karşılaştırma, bu bilgisayarda pull timer. Kullanıcı çıktıları (journal satırları, "✅ kakeibo ile aynı") bildirince Faz 5 ✅ → Faz 6 (ay sonu değerlendirmesi + dönem kapatma/devir).
📌 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:resetsilerse API ile yeniden oluştur (ileridedb:seed). - Sürüm kontrolü: Gitea (
https://gitea.ta1sph.info/suphicakir/kakeibo.git), dalmain. - 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), tarihYYYY-MM-DDmetin, 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 alanlarmodels/common.rsyardı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_sessioncookie'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). AuthUserher 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_updateile 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ığı; ilerideusers.preferences.themeile 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çicomponents/logo.tsxaynı ç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ıştaqc.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) → önceme=null, sonraremoveQueries(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_onlyvarsayılan; sistemde hiç hane yokken ilk kayıt yeni hane kurar (sunucu sahibi), sonra yalnızca davetle.openisteğ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*+.fingerprintsilinir, gerçek kaynaktouchlanı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. - Canlı: domain
kakeibo.ta1sph.website(DNS → 159.146.61.151, ev dış IP'si olmalı), Gitea Dokploy'a bağlı. Canlı MongoDB = ayrı Dokploy servisikakeibo-kakeibomongo-fibzhc(rs0), kullanıcıkakeibo_app(readWrite/kakeibo). - Dönem: bitiş hariç, en fazla 62 gün, hane başına tek açık, çakışma yok (metin karşılaştırması), kapalı salt okunur, daraltmada dışarıda harcama kalırsa 409. Ad boşsa otomatik (
calendar::default_period_name). Tüm üyeler açabilir/düzenleyebilir. - ts-rs:
i64tutarlar#[ts(type = "number")]; isteklerdeOption<i64>için#[ts(type = "number", optional)](yoksa?kayboluyor). - Plan kalemleri: tarih isteğe bağlı (dönem içinde), tutar > 0, kopyalama göreli gün + dönem sonuna kırpma, aynı tür+ad (büyük/küçük harf duyarsız) atlanır. Notlar: başlık 1–120, metin ≤ 20.000. Kapalı dönemde hepsi 409 (
period::get_open). Silme = soft delete. - İzolasyon iki katmanlı: kayıt okuma
scope()+ dönemget_open(hid, period_id)+ yazma filtresiscope(). Tek katmanı bozmak açık oluşturmuyor (mutasyon denemesiyle görüldü). - Bütçe: aylık =
budgets{scope:"month"}; haftalık otomatik = (aylık − elle girilenler) gün oranında,money::split_proportional(en büyük kalan, toplam tam eşit). Elle hafta =scope:"week", week_start;null→ soft delete. Aylık değişince elle haftalar korunur; elle toplam > aylık →weekly_over. - Özet: spendable = gelir − sabit gider − birikim − borç + artı (
carryovers.to_period_id).unallocated= spendable − bütçe toplamı (negatif = fazla dağıtım). - Öneri: default = hane
settings.budget_split_pct× spendable (≤0 → available:false); previous/avg3 = önceki kapalı dönem(ler)inentriestoplamı / dönem sayısı (yuvarlayarak). - 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
- Geliştirme makinesi: CachyOS nodejs 26.13.0-2 ↔ simdjson .so.36 uyumsuzluğu → simdjson 2:5.0.5-1'e düşürüldü (2026-11-12). Düzeltilmiş nodejs gelene kadar
pacman -Syu --ignore simdjson.node: libsimdjson.so.34hatası görülürse sebep budur. - Canlı yedekleme kurulumu bekliyor (betikler hazır,
docs/DEPLOY.md§6) → gerçek veri girişinden önce. - Canlı MongoDB auth kapalı (Dokploy replica set betiği keyFile kullanmıyor, özel Command'ı yok sayıyor). İç ağda, port kapalı. Ayrıntı:
docs/DEPLOY.md§4. - Ü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.exampleve monorepo klasörleri oluşturuldu.git init -b main. - Adım 3:
CLAUDE.md,docs/MEMORY.md,docs/PHASES.mdoluşturuldu. Gitea remote eklendi, ilk commit push edildi. - Adım 4: Rust kuruldu.
docker-compose.dev.yml(mongo:7, rs0, healthcheck ile otomatikrs.initiate). Backend: Axum, yalnızcaGET /api/health. Frontend: Vite react-ts şablonu,/apiproxy → 8080, geçici durum ekranı. Kökpackage.json: concurrently ilenpm run dev. Uçtan uca test edildi (Mongo PRIMARY, proxy çalışıyor). - Adım 5:
docs/DATA_MODEL.mdhazı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ü:colsabitleri,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ıştadb::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 +AuthUserextractor),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:
AuthUserDB doğrulaması +require_owner,services/invite.rs(create/list_active/revoke/claim), kayıt davet koduylamemberolarak katılma (services/account.rsyeniden 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:resetartı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;
cnpaketi = shadcn'in clsx+tailwind-merge yerine geçeni, incelendi), Inter (@fontsource-variable, çevrimdışı).index.css: grup + durum token'ları, odak halkası,moneyutility, 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ı), ThemeTogglecompact. Tasarım önizlemesi →/dev/tasarim(yalnızca DEV). CDP e2e (10 kontrol) iki hata buldu, düzeltildi: giriş sonrasıdonusyarışı, çıkıştaqc.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). Metinlert.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.rsRegistrationMode,AppError::RegistrationClosed(403),account::new_household_allowed,GET /api/auth/registration(RegistrationInfoTS 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_DIRconfig, graceful shutdown (SIGTERM/Ctrl+C).Dockerfile,.dockerignore,docs/DEPLOY.mdbaş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. - Adım 2: Mevcut tek MongoDB (butcem-mongo) Bütçem'e ait → kullanıcı ayrı MongoDB seçti. Dokploy MongoDB (replica set) kuruldu,
kakeibo_appoluşturuldu, port yayını yok (iki servis denull). Sorunlar: kullanıcı şifreyi komuta düz yazıp paylaştı; Dokploy betiği root şifresini servis Args'ına düz yazıyor → iki şifre de mongosh içinde üretilerek yenilendi. passwordPrompt yapıştırma hatası (U_STRINGPREP). Auth açma denemesi: Dokploy özel Command'ı uygulamadı → auth kapalı kabul edildi (açık konu). - Adım 3–4: Dokploy Application
kakeibo-app(iç adkakeibo-kakeiboapp-nd5kek). Sorunlar: push yapılmamıştı (Dockerfile yok); Provider GitHub sekmesinde kalmıştı (Github Provider not found, kullanıcı çözdü); Traefik 404 (kullanıcı çözdü, büyük olasılıkla domain sonrası redeploy). Yayında: https://kakeibo.ta1sph.website — health ok, Let's Encrypt, ilk hesap açıldı. Faz 3 tamamlandı (yedekleme hariç).
2026-11-12 — Faz 4
- Adım 1:
services/calendar.rs(parse/format,weeksPazartesi başlangıçlı + kısmi haftalar, Türkçe ay adları; 4 test),models/period.rs,services/period.rs(2 test),routes/period.rs(list/current/create/get/patch; DTO'dalast_date,days,weeks).tests/periods.rs(5) — çakışma kontrolü bilerek bozulunca test yakaladı. Backend toplam 58 test. - Adım 2:
models/plan_item.rs,models/note.rs,services/plan.rs(list/create/update/delete/copy_previous),services/note.rs,routes/plan.rs,routes/note.rs,period::get_open+closed().tests/plan_notes.rs(5): CRUD + doğrulama, kopyalama (göreli gün, 28 Şubat'a kırpma, tekrar yok), kapalı dönem salt okunur, not filtresi, izolasyon. Backend 72 test. - Adım 3:
services/money.rs(split_proportional+ 3 test; beklenen değer hatası testteydi, elle hesapla düzeltildi),services/budget.rs(summary, view, set_monthly, set_week, suggest),routes/budget.rs(5 uç nokta, 9 TS tipi).tests/budget.rs(6) — özet formülü bilerek bozulunca yakaladı. Backend 88 test. - Adım 4: İlk dönem sihirbazı
/plan/donem-ac?adim=1..5(features/period-wizard/): Dönem → Gelirler → Giderler → Birikim → Bütçe. Dönem 1. adımda açılır, her adım hemen kaydedilir; adım adreste (yenileme/geri tuşu çalışır).lib/periods.ts(Query kancaları; değişincequeryKeys.period(id)öneki yenilenir),lib/period-dates.ts(son gün dahil ↔ backend bitiş hariç),lib/money-schema.ts(moneyText,toKurus),components/money-input.tsx(inputMode=decimal, blur'da1.250,50),PageLoading/PageError,SummaryTiles,NoPeriodCard(Panel + Plan). Frontend 60 test; CDP e2e 43 kontrol geçti, test verisi demo hanesinden silindi. - Not: üretim JS paketi ~620 kB (gzip 195 kB) → Faz 10'da sayfa bazlı bölme (lazy route).
- Adım 5: Plan ekranı (
features/planning/):plan-read.tsx(salt okunur: kalemler, 4 grup + haftalık döküm, "elle" etiketi),plan-edit.tsx(taslak formu, Kaydet/Vazgeç,useBlocker+ beforeunload ile kaydedilmemiş değişiklik onayı).lib/plan-draft.ts(toDraft/diffPlan: yalnızca değişenler gönderilir),lib/budget-preview.ts(backendsplit_proportional+viewile birebir; aynı test örnekleri),useSavePlan(sıralı istekler; hata olursa yeniden yükler, transaction değil). Ortak:DistributionBar,SuggestionPicker(sihirbaz da kullanıyor). Frontend 69 test. - E2E artık ayrı sunucuda:
REGISTRATION=open PORT=8091 STATIC_DIR=frontend/dist MONGODB_URI=…/kakeibo_test_e2e(kullanıcının dev verisine dokunulmaz; bitince DB silinir). 31 kontrol geçti; ekran görüntüsüyle 3 düzen sorunu bulundu/düzeltildi (dar satırlar, mobil tutar/tarih, hafta etiketi). - Adım 6: Notlar:
features/notes/(NotesSection— Plan'dacontext="planning", Değerlendirme'dereviewile yeniden kullanılacak;NoteEditorYaz/Önizle sekmeleri; zod şeması karakteri backend gibi sayar),lib/notes.ts(+useHouseholdyazar adı için),components/markdown.tsx(ham HTML yok, bağlantılar yeni sekme),components/ui/textarea.tsx,index.css.markdownstilleri. Yeni bağımlılık: react-markdown 10, remark-gfm 4. E2E 16 kontrol (XSS, 409 çakışma dahil).DistributionBar0/0 durumunda "tamamı dağıtıldı" demiyor. Faz 4 tamamlandı. - Not: JS paketi 820 kB (gzip 254 kB; markdown ~200 kB) → Faz 10'da route bazlı lazy + Markdown'u ayrı parçaya al.
npm audituyarıları shadcn CLI bağımlılıklarından (deepmerge, minimatch), uygulamaya girmiyor;--forceshadcn'i 1.0'a düşürür, yapılmadı.
2026-11-12 — Faz 5
- Adım 1: Harcamalar backend:
models/entry.rs,services/entry.rs(list +from/tofiltresi, sıradate↓_id↓; create/update/delete soft/restore),routes/entry.rs(5 uç nokta),catalog::find. Grup kategoriden türetilir (istemci göndermez). Pasif kategori/ödeme türü yeni seçimde reddedilir, kaydın mevcut değeri olarak kalabilir. Ödeme türü isteğe bağlı; düzenlemede""→ kaldır (projedeki "boş metin = kaldır" kuralı; ts-rsdeserialize_withdesteklemediği için çift-Option yerine).tests/entries.rs(6) —previousönerisi artık gerçek kayıtla test edildi. Mutasyon: grup türetme bozulunca yakalandı; restore'da hane filtresi tek başına kaldırılıncaget_openikinci katman olarak korudu, ikisi birden kaldırılınca 2 test yakaladı. Backend 97 test. - Adım 2: Gerçekleşen harcama (saklanmaz, her istekte aggregation):
GET /summary→spent_total,remaining(= harcanabilir − harcanan, negatifse aşılmış).GET /budget→ gruptaspent(dönem), haftadaspent.spent_by_group_and_date(grup×gün toplanır, Rust'ta haftalara dağıtılır; tarih metin karşılaştırması). Silinen kayıt sayılmaz, kategori değişince harcama yeni gruba geçer. Testactual_spending_in_summary_and_budget(hafta sınırları 6/7 Aralık, dönem son günü, önceki dönem hariç, geri al). Mutasyon: hafta sınırı<=→<ve silinenleri sayma → ikisi de yakalandı. Backend 98 test. - Adım 3: Defter ekranı (
features/ledger/):WeekPicker(◀ ▶, "Bu hafta", haftanın harcanan/bütçe toplamı),EntryForm(büyükMoneyInput, 4 renkli grup düğmesi, kategori/ödeme türü çipleri, tarih, açıklama; kayıttan sonra tutar+açıklama temizlenir, odak tutara döner),schema.ts(entrySchema(start,last),fieldForError: sunucu mesajı → alan). Hafta adreste:/defter?hafta=N(bugünün haftasında parametre yok); başka haftaya tarihli kayıt → o haftaya geçer; hafta değişince tarih o haftaya çekilir.lib/ledger.ts(weekIndexFor,defaultEntryDate,parseWeekParam, son seçim localStoragekakeibo.ledger.last: grup + her grubun son kategorisi + ödeme türü; pasifleşen seçim düşer).lib/catalog.ts(useCategories,usePaymentMethods, 5 dk staleTime),lib/entries.ts(useEntries,useCreateEntry),queryKeys.entries. Frontend 79 test; e2e 31 kontrol (ayrı 8091 sunucu, DB silindi). Mobilde "Beklenmeyen" taşması düzeltildi. - Adım 4: Defter listesi ve özet:
entry-list.tsx(gün güngroupByDay, gün adıformatWeekday= Intl tr-TR UTC; satır: kategori emojisi grup renginde, grup · ödeme · açıklama; düzenle/sil 44 px), sil → sonneraction"↩️ Geri al" (8 sn) →useRestoreEntry. Düzenleme aynıEntryForm(entry+key, Güncelle/Vazgeç;versiongönderilir, 409 Türkçe root hatası;''→ ödeme/açıklama kaldır). Katalog pasifler dahil okunur (eski kayıtların adı; seçenekte yalnız aktifler + kaydın mevcut değeri).week-summary.tsxgrup bazlı bütçe/harcanan/kalan + çubuk, aşımda "Aşıldı" kırmızı. Masaüstü iki sütun (form solda yapışkan); dar sütunda taşmaya karşı container query (@container,@lg:grid-cols-4). Frontend 81 test; e2e 29 kontrol (+ Adım 3'ün 31'i yeniden geçti). Not: e2e betikleri aynı DB'yi paylaşırsa birbirini bozar → her biri temiz DB ile. - Adım 5: Yedekleme betikleri:
scripts/backup.sh(Swarm etiketiyle konteyneri bulur,mongodump --archive --gzip→.partial→gzip -t+mongorestore --dryRun→ ad verilir; sonKEEP=14,BACKUP_GROUPile okuma izni),scripts/restore.sh(dosya adından kaynak DB,--nsFrom/--nsToile başka adla; mevcut hedefe yalnızca--force+--drop;COMPARE_DBile sayım karşılaştırma),scripts/backup-pull.sh(rsync SSH BatchMode,~/Yedekler/kakeibo, son 60, 48 saatten eski →notify-send).deploy/systemd/(sunucu 03:30, bu bilgisayar 09:00 + açılış,Persistent=true). Kullanıcı kararı: sunucu dışı kopya bu bilgisayara (S3 değil).npm run db:backup/db:restore(yerel,backups/gitignore). Yerel test: yedek → geri yükleme sayıları kakeibo_dev ile aynı, koruma, bozuk dosya hatası, saklama ✅.