# JoshqunAPI

| Ortam | Base URL |
|-------|----------|
| **Dev** | `http://srvr:1618` |

---

## Auth

| Method | Endpoint | Aciklama |
|--------|----------|----------|
| `POST` | `/auth/register` | Yeni kullanici kaydi |
| `POST` | `/auth/login` | Giris yap, token al |
| `POST` | `/auth/refresh` | Refresh token ile yeni access token al |
| `POST` | `/auth/logout` | Refresh token'i gecersiz kil |
| `POST` | `/auth/change-password` | Sifre degistir (tum refresh token'lar iptal) |
| `GET` | `/auth/me` | Token sahibi bilgisi |
| `PATCH` | `/auth/me` | Profil bilgilerini guncelle (full_name, display_name, phone_number, bio, location) |
| `POST` | `/auth/me/profile-picture` | Profil resmi yukle (JPEG/PNG/WebP, max 5MB) |

### Token Sistemi

| Token | Sure | Kullanim |
|-------|------|----------|
| `token` | 24 saat | `Authorization: Bearer` header ile tum API istekleri |
| `refresh_token` | 30 gun | Sadece `POST /auth/refresh` ile yeni token almak icin |

Refresh token DB'de SHA256 hash olarak saklanir. Her refresh'te **rotate** edilir (eskisi revoke edilir, yenisi dondurulur).
Sifre degisikliginde kullaniciya ait tum refresh token'lar DB'den silinir.

### Response (login/register/change-password)

```json
{
  "token": "eyJ...",
  "refresh_token": "eyJ...",
  "user": {"id": "...", "email": "...", "fullName": "...", "displayName": "..."}
}
```

### Request Body

**Register:**
```json
{"email": "...", "password": "...", "full_name": "...", "display_name": "...", "phone_number": "..."}
```

**Login:**
```json
{"email": "...", "password": "..."}
```

**Refresh:**
```json
{"refresh_token": "eyJ..."}
```

**Logout:**
```json
{"refresh_token": "eyJ..."}
```

**Change Password:**
```json
{"current_password": "...", "new_password": "..."}
```

**GET /auth/me** response:
```json
{
  "id": "...", "email": "...", "fullName": "...", "displayName": "...",
  "phoneNumber": "...", "profilePictureUrl": "/f/...jpg?prefix=profiles",
  "bio": "...", "location": "...", "isAdmin": false, "userRole": "seeker",
  "createdAt": "..."
}
```

**PATCH /auth/me** body (tum alanlar optional):
```json
{"full_name": "...", "display_name": "...", "phone_number": "...", "bio": "...", "location": "..."}
```

Token `Authorization: Bearer <token>` header ile gonderilir.

Tum endpoint'ler (`/table/*`, `/tables/*`, `/f` POST/GET/DELETE, `/auth/me/*`)
Bearer token gerektirir.
`GET /f/{filename}` public dosyalar icin auth gerektirmez, private dosyalar token gerektirir.
Local/private IP'lerden yapilan isteklerde gecerli token varsa gercek kullanici kullanilir,
token yoksa mock admin (private IP'ler icin).

### Guvenlik

| Kural | Limit |
|-------|-------|
| Password policy | En az 8 karakter, en fazla 128 karakter, 1 buyuk, 1 kucuk, 1 rakam |
| Email validation | Format kontrolu, lowercase, trim, max 255 karakter |
| Rate limit (register) | IP basina saatte max **5** kayit |
| Rate limit (login) | IP basina 15dk'da max **5** giris denemesi |
| Rate limit (login/account) | Email basina saatte max **10** basarisiz deneme |
| Rate limit (upload) | IP basina dakikada max **30** yukleme |
| Row-level access | Tablolarda `user_id` varsa, kullanici sadece kendi kayitlarini degistirebilir (admin hariç) |
| Batch insert | Tek seferde max **500** kayit |
| Docs access | `/docs` ve `/redoc` `DOCS_ENABLED` ile acilip kapatilabilir |
| Debug endpoint | `/debug-ip` admin yetkisi gerektirir |

Asan limit asimi `429 Too Many Requests` doner.

### Ornek curl kullanimi

```bash
# Token al
LOGIN=$(curl -s http://srvr:1618/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email":"mehmetacoskun@me.com","password":"J0shqun."}')
TOKEN=$(echo "$LOGIN" | python3 -c "import sys,json;print(json.load(sys.stdin)['token'])")

# Tablo sorgula
curl -s http://srvr:1618/table/misc \
  -H "Authorization: Bearer $TOKEN"

# Kayit ekle
curl -s -X POST http://srvr:1618/table/misc \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"column1":"deger"}'
```

---

## Table CRUD

| Method | Endpoint | Aciklama |
|--------|----------|----------|
| `GET` | `/table/{table}/schema` | Sutun bilgileri, tipler, PK |
| `GET` | `/table/{table}` | Kayitlari listeler |
| `GET` | `/table/{table}/{id}` | Tek kayit |
| `POST` | `/table/{table}` | Yeni kayit olusturur |
| `PUT` | `/table/{table}/{id}` | Kaydi tamamen gunceller |
| `PATCH` | `/table/{table}/{id}` | Kaydi kismen gunceller |
| `DELETE` | `/table/{table}/{id}` | Kaydi siler |
| `POST` | `/table/{table}/batch` | Toplu kayit ekleme (max 500) |

### Liste Parametreleri

| Param | Varsayilan | Aciklama |
|-------|-----------|----------|
| `page` | 1 | Sayfa numarasi |
| `page_size` | 20 | Sayfa basina kayit (max 100) |
| `sort` | PK | Siralama kolonu |
| `order` | `asc` | `asc` veya `desc` |
| `filter_col` | - | Filtre kolonu |
| `filter_op` | - | `eq`, `contains`, `startswith`, `gt`, `lt` |
| `filter_val` | - | Filtre degeri |

### Filtreleme Ornekleri

```bash
# contains - "Numeroloji" iceren kayitlar
curl -s 'http://srvr:1618/table/misc?limit=10&filter_col=description&filter_op=contains&filter_val=num' \
  -H "Authorization: Bearer $TOKEN"

# eq - birebir eslesme
curl -s 'http://srvr:1618/table/misc?filter_col=description&filter_op=eq&filter_val=Enneagram' \
  -H "Authorization: Bearer $TOKEN"

# gt - ID'si 200'den buyuk olan kayitlar
curl -s 'http://srvr:1618/table/misc?filter_col=id&filter_op=gt&filter_val=200' \
  -H "Authorization: Bearer $TOKEN"

# startswith - "Nu" ile baslayan description
curl -s 'http://srvr:1618/table/misc?filter_col=description&filter_op=startswith&filter_val=Nu' \
  -H "Authorization: Bearer $TOKEN"

# lt - ID'si 10'dan kucuk olan kayitlar
curl -s 'http://srvr:1618/table/misc?filter_col=id&filter_op=lt&filter_val=10' \
  -H "Authorization: Bearer $TOKEN"
```

### Filtreleme + Siralama

Filtre ve siralama birlikte kullanilabilir:

```bash
curl -s 'http://srvr:1618/table/misc?limit=20&sort=title&order=asc&filter_col=description&filter_op=contains&filter_val=num' \
  -H "Authorization: Bearer $TOKEN"
```

Auth: Bearer token required.

### Row-Level Access Control

Tablolarda `user_id` kolonu varsa, kullanici sadece kendi kayitlarini olusturabilir, guncelleyebilir ve silebilir. Admin kullanicilar bu sinirlamadan muafdir. `GET` isteklerinde sinirlama yoktur — tum kayitlar okunabilir.

---

## Upload

| Method | Endpoint | Aciklama |
|--------|----------|----------|
| `POST` | `/f` | Dosya yukler (`prefix` ile klasore, `is_public` ile public erisim) |
| `GET` | `/f` | Dosyalari listeler |
| `GET` | `/f/{filename}` | Dosyayi indirir (public dosyalar auth gerektirmez) |
| `DELETE` | `/f/{filename}` | Dosyayi siler |
| `POST` | `/f/sync` | DB ve fiziksel dosyalari senkronize eder (admin) |

Image dosyalari (JPEG, PNG, WebP) -> resize 1920px, compress, 300x300 thumbnail.
Non-image dosyalar oldugu gibi kaydedilir.

Limits:
- Max file size: **50MB**
- Allowed extensions: `jpg, jpeg, png, webp, gif, bmp, pdf, doc, docx, xls, xlsx, csv, json, xml, txt`
- Prefix: yalnizca harf, rakam, `.`, `-`, `_`, `/` karakterlerine izin verilir, `..` ile directory traversal engellenir
- Max image pixels: 50 megapiksel (decompression bomb korumasi)
- Filename: timestamp tabanli, guvenli formatta otomatik olusturulur

### Liste Parametreleri

| Param | Tip | Aciklama |
|-------|-----|----------|
| `page` | int | Sayfa (default 1) |
| `page_size` | int | Sayfa basi kayit (default 20, max 100) |
| `sort` | string | `created_at`, `filename`, `size_bytes` |
| `order` | string | `asc` veya `desc` |
| `search` | string | Dosya adinda ara |
| `ext` | string | Uzantiya gore filtre (ornek: `png`) |
| `prefix` | string | Prefix'e gore filtre |
| `user_id` | UUID | Kullaniciya gore filtre |

### Upload Parametreleri

| Param | Tip | Varsayilan | Aciklama |
|-------|-----|-----------|----------|
| `file` | File | (zorunlu) | Yuklenecek dosya |
| `prefix` | Form | `""` | Klasor (ornek: `synctest`) |
| `is_public` | Form | `true` | `false` olursa dosyaya sadece token ile erisilir |

Response'da `userId` alani dosyayi kimin yukledigini gosterir (`null` olabilir).
Liste endpoint'inde `user_id` query parametresi ile kullaniciya gore filtreleme yapilabilir.

Auth: `POST /f`, `GET /f`, `DELETE /f`, `POST /f/sync` -> Bearer token required.
`POST /f/sync` -> admin yetkisi gerekir.
`GET /f/{filename}` -> public dosyalar icin auth gerekmez, private dosyalar icin token gerekir.

### Ornek curl kullanimi

```bash
# Token al
LOGIN=$(curl -s http://srvr:1618/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email":"mehmetacoskun@me.com","password":"J0shqun."}')
TOKEN=$(echo "$LOGIN" | python3 -c "import sys,json;print(json.load(sys.stdin)['token'])")

# Public dosya yukle
curl -s -X POST http://srvr:1618/f \
  -H "Authorization: Bearer $TOKEN" \
  -F "file=@resim.png" \
  -F "prefix=synctest" \
  -F "is_public=true"
# Response: {"filename":"20260617160130_cd137b.png","isPublic":true,"url":"/f/20260617160130_cd137b.png?prefix=synctest",...}

# Public dosyaya auth'siz eris
curl -s http://srvr:1618/f/20260617160130_cd137b.png?prefix=synctest -o resim.png

# Private dosya yukle (is_public=false)
curl -s -X POST http://srvr:1618/f \
  -H "Authorization: Bearer $TOKEN" \
  -F "file=@gizli.pdf" \
  -F "prefix=admin"
# Response: {"filename":"...","isPublic":false,...}

# Private dosyaya erisim icin token gerekir
curl -s -H "Authorization: Bearer $TOKEN" \
  "http://srvr:1618/f/gizli.pdf?prefix=admin" -o gizli.pdf

# Dosyalari listele (auth gerekir)
curl -s http://srvr:1618/f?prefix=synctest \
  -H "Authorization: Bearer $TOKEN"
# Response: {"files":[{"filename":"...","isPublic":true,"userId":"..."}],"total":1,...}

# Kullaniciya gore filtrele
curl -s "http://srvr:1618/f?user_id=00000000-0000-0000-0000-000000000000" \
  -H "Authorization: Bearer $TOKEN"

# DB ve fiziksel dosyalari senkronize et
# Private IP'lerde (srvr) token gerekmez, public IP'lerde admin token gerekir
curl -s -X POST http://srvr:1618/f/sync
# Response: {"deleted_db":["orphan_record.txt"],"deleted_fs":["synctest/orphan_file.png"]}

---

## Table HTML (CRUD)

| Method | Endpoint | Aciklama |
|--------|----------|----------|
| `GET` | `/tables` | Tablo listesi (HTML/JSON) |
| `GET` | `/tables/{table}` | Tablo icerigi (HTML) |
| `GET` | `/tables/{table}/new` | Yeni kayit formu |
| `POST` | `/tables/{table}/new` | Kayit olustur |
| `GET` | `/tables/{table}/{row_id}` | Kayit detayi (HTML) |
| `GET` | `/tables/{table}/{row_id}/edit` | Duzenleme formu |
| `POST` | `/tables/{table}/{row_id}/edit` | Kaydi guncelle |
| `POST` | `/tables/{table}/delete` | Toplu/tekli silme |

Ozelikler: checkbox ile satir secimi, kolon basliklarina tiklayarak sirala (asc/desc toggle), filter (column/operator/value), pagination, batch delete.

Auth: Bearer token required.

---

## Error Format

Tum hatalar ayni formatta doner:

```json
{
  "error": {
    "code": 422,
    "type": "validation_error",
    "message": "Request validation failed",
    "details": [{"field": "email", "message": "field required"}]
  }
}
```

| HTTP | Type | Aciklama |
|------|------|----------|
| 400 | `validation_error` | Gecersiz istek (dosya tipi, boyut, vs.) |
| 401 | `http_error` | Gecersiz/eksik token |
| 403 | `http_error` | Yetki yok (admin) |
| 404 | `http_error` | Kayit bulunamadi |
| 409 | `http_error` | Email zaten kayitli |
| 413 | `validation_error` | Dosya boyutu asildi |
| 422 | `validation_error` | Request body validasyon hatasi |
| 429 | `http_error` | Rate limit asildi (register/login) |
| 500 | `internal_error` | Beklenmeyen hata (detay gizlenir) |

---

## CORS

Ayarlar `.env`'deki `CORS_ORIGINS` ile yapilir, birden fazla origin virgulle ayrilir.

```bash
CORS_ORIGINS=http://localhost:3000,https://example.com
```

---

## Database Tables

| Tablo | Aciklama | Yonetim |
|-------|----------|---------|
| `users` | Kullanicilar (email, password_hash, role) | `/table/users` |
| `ads` | Ilanlar (title, description, price, status) | `/table/ads` |
| `ad_media` | Ilan medyalari (image/video URL'leri) | `/table/ad_media` |
| `fastupload` | Dosya yukleme kayitlari (API uzerinden) | Sadece `/f` API |
| `categories` | Kategoriler | `/table/categories` |

Protected: `fastupload` tablosuna `/table/fastupload` ile dogrudan erisilemez.

---

## Health

| Endpoint | Aciklama |
|----------|----------|
| `GET /` | INFO.md sayfasi |
| `GET /health` | JSON health check (DB, uptime) |

```
GET http://srvr:1618/              # Dev - INFO.md
GET http://srvr:1618/health        # Dev - JSON health
```
