# Kuruş — Telegram Mini App (SPEC)

> Bu dosya, `uploads/kurus-teknik-sartname.md` şartnamesinin bu platforma uyarlanmış
> sözleşmesidir. Şartname detayları referans alınır; platforma özgü farklar aşağıda.

## Goal

Telegram Mini App olarak çalışan kişisel harcama takibi: harcamayı 5 saniyede kaydet,
ay sonunda paranın nereye gittiğini gör, Excel olarak indir. Bot: `@kurus_hesapla_bot`.

## Platforma uyarlama (şartnameden FARKLAR)

- **Tek process, tek port (PORT env, default 3000).** Nginx/systemd yok — platform
  TLS + domain + statik servisi yönetir. Fastify hem API'yi hem webhook'u hem de
  React build'ini (statik) aynı portta sunar.
- **PostgreSQL 16**, konteyner İÇİNDE localhost:5432'de çalışır, dışarı açılmaz.
  Veri dizini: `/workspace/data/pg` (initdb ile kurulur). Başlatma: `scripts/dev.sh`.
- **Ortam değişkenleri** deploy sırasında sağlanır: `BOT_TOKEN`, `WEBHOOK_SECRET_PATH`,
  `WEBHOOK_SECRET_TOKEN`, `DATABASE_URL`, `WEBAPP_URL`, `PORT`. Geliştirmede `.env`
  (repo dışı) kullanılır; deploy'da `{{secret:NAME}}` placeholder.
- **/privacy** statik HTML, API aynı portta servis eder.
- Git/GitHub Actions/rsync bölümleri platform tarafından yapılır — uygulanmaz.

## Scope (v1)

- Kapsam: harcama ekle/düzenle/sil, kategori atama, aylık özet + kategori kırılımı,
  ay bazlı gezinme, Excel (.xlsx) dışa aktarma (bota gönderilir), TR+EN arayüz.
- Dışarıda (v2): gelir, bütçe limiti, tekrarlayan harcama, çoklu para birimi,
  banka entegrasyonu, ortak hesap.
- **Para birimi TRY, tek.** Tutarlar DB'de kuruş cinsinden tam sayı (`amount_minor`).
  Hiçbir yerde float yok. 12,50 TL → `1250`.

## Repo yapısı (pnpm workspaces)

```
kurus/
├── apps/
│   ├── web/          # React 19 + Vite + TS + Tailwind v4 (statik build)
│   └── api/          # Fastify + grammY bot + Excel (tek process, dist/server.js)
├── packages/
│   └── shared/       # Zod şemaları, tipler, para formatlama
├── scripts/dev.sh    # postgres başlat + API dev
└── package.json      # pnpm workspaces
```

## Pages & Endpoints (tek port, `/api` ve `/tg` altında)

| Adres | İçerik |
|---|---|
| `/` | Mini app (React build, SPA) |
| `/api/*` | Backend API |
| `/tg/webhook/<SECRET_PATH>` | Telegram webhook |
| `/privacy` | Gizlilik metni (statik HTML) |
| `/api/health` | Auth'suz sağlık |
| `/api/me` | Kullanıcı + kategoriler |
| `/api/expenses?from&to&categoryId&cursor&limit` | Cursor sayfalama, tarihe azalan |
| `POST/PATCH/DELETE /api/expenses[/:id]` | CRUD (soft delete) |
| `/api/summary?month=YYYY-MM` | Toplam + kategori kırılımı + günlük seri |
| `/api/summary/months` | Veri olan aylar |
| `POST /api/categories` | Özel kategori |
| `POST /api/export` | Excel üretir, bota sendDocument |

## Data Model (Drizzle + Postgres)

- `users`: id, tg_user_id (unique), first_name, username, language_code, currency='TRY',
  created_at, last_seen_at
- `categories`: id, user_id (NULL = sistem varsayılanı), slug, name_tr, name_en, emoji,
  color, sort_order, archived_at; unique (COALESCE(user_id,0), slug)
- `expenses`: id, user_id, category_id (SET NULL), amount_minor (>0, kuruş), currency='TRY',
  note (<=280), spent_at DATE, created_at, updated_at, deleted_at (soft delete);
  index (user_id, spent_at DESC, id DESC) WHERE deleted_at IS NULL
- `export_jobs`: id, user_id, range_from, range_to, status (pending|done|failed), error, created_at

- **Seed:** 9 sistem kategorisi (market, yeme-icme, ulasim, fatura, saglik, giyim, eglence, ev, diger)
  — TR/EN ad, emoji, renk. `user_id IS NULL` paylaşımlı.

## Kimlik Doğrulama

- Her `/api/*` isteği `X-Telegram-Init-Data` başlığı taşır. `verifyInitData`:
  HMAC(sha256, key=HMAC("WebAppData", botToken), data=dataCheckString) karşılaştır,
  `hash`'i string'e dahil etme, `auth_date` 24 saat tazelik kontrolü, timing-safe equal.
- Her sorguda `WHERE user_id = req.user.id` ZORUNLU. İstemciden user_id alınmaz.
- Rate limit: `@fastify/rate-limit` — genel 60/dk, yazma 20/dk.

## Bot (grammY)

- Webhook: `WEBAPP_URL/tg/webhook/WEBHOOK_SECRET_PATH`, secret_token başlık kontrolü.
- Komutlar (TR+EN): start, ekle/add, ozet/summary, excel. Menu button → web_app.
- Sohbetten hızlı ekleme: `120 market` / `45,50 kahve` parse → kayıt + onay + Düzenle/Sil butonları.
- /start: karşılama + "Uygulamayı aç" web_app butonu.
- /sil: hesap silme onay akışı.

## Excel (ExcelJS)

- Sayfa 1 "Harcamalar": Tarih/Kategori/Tutar/Not, frozen header, autofilter, TOPLAM formülü.
- Sayfa 2 "Özet": Kategori/Toplam/Adet/Pay.
- `sendDocument` ile bot sohbetine gönderilir, tarayıcıya indirilmez.
- >5000 satır → export_jobs + 202 + arka plan. v1'de senkron yeterli (küçük ölçek).

## Frontend

- `index.html`'de telegram-web-app.js CDN, bundle öncesi.
- `tg.ready()`, `expand()`, `disableVerticalSwipes()`, `setHeaderColor('secondary_bg_color')`.
- Tema: themeParams → CSS değişkenleri, `data-scheme` light/dark, themeChanged dinlenir.
- Marka: --kurus-brass #C8963E (tek vurgu), koyu: #9A6F26. Dekorasyon yok (gradyan/gölge/cam yok).
- Fontlar: **Bricolage Grotesque** (tutar/başlık) + **Inter** (gövde), Türkçe set, woff2 self-host.
- **Amount** bileşeni tek tutar gösterimi: lira büyük + kuruş küçük/paslı.
- **Aylık şerit**: yatay yığılmış bar, <3% kategoriler "Diğer"e toplanır. Pasta grafik yok.
- Ekranlar: `/` ana (ay gezgini + büyük tutar + şerit + günlere göre liste), ekleme (bottom sheet),
  düzenleme, `/ozet` (kategori listesi + günlük çubuk + top3 + excel), boş durumlar.
- Telegram bileşenleri: MainButton/BackButton/showConfirm/Haptic. offClick temizliği zorunlu.
- Optimistic ekleme (TanStack Query), staleTime 30s.

## Acceptance Criteria

- `curl /api/health` → `{"ok":true}` (auth'suz)
- Auth yok → 401; bozuk hash → 401; 25 saat eski auth_date → 401
- Başkasının expense.id ile PATCH/DELETE → 404
- `POST /api/expenses {amountMinor:12450, categoryId, spentAt}` → kayıt, kuruş tam sayı
- `GET /api/summary?month=YYYY-MM` → totalMinor, byCategory (share), byDay
- Bot webhook gizli yol + secret_token doğrulaması
- Excel .xlsx açılır, TOPLAM formülü çalışır
- Frontend: Telegram'da iframe açılır, tema uyumlu, TR karakterler doğru
- Bundle < 200KB gzip, /api/summary 5000 kayıtta <200ms

## Tech Choices

- React 19 + Vite + TS + Tailwind v4 + TanStack Query (frontend)
- Node 24 + Fastify + TypeScript + Drizzle ORM + PostgreSQL 16 (backend)
- grammY (bot), ExcelJS (Excel), Zod (doğrulama, packages/shared)
- Tek repo, pnpm workspaces, üç paket

## Status Log

- 2026-08-08: Şartname okundu; ortam kuruldu (Node 24, pnpm, PostgreSQL 16 @ /workspace/data/pg,
  dev DB `kurus` + kullanıcı `kurus`). Build başladı.
