Ne yaptik:
- scraper.py fetch_page: "sst": "BEST_SELLER" parametresi eklendi
- scraper.py fetch_page: "countryCode": "TR" parametresi eklendi
- scraper.py _normalize_search_product: socialProof (TEKIL) alanindan okunuyor,
eski cogul socialProofs fallback olarak birakildi
- main.py _find_leaves: pathModel artik "<slug>-x-c<categoryId>" formatinda
uretiliyor (queue_worker.py ile ayni mantik)
Neden yaptik:
- Trendyol Search API siralama parametresi verilmezse sayfalar arasi tutarsiz
sonuc donuyordu: 240 urunluk cekimde sadece 134 benzersiz urun geliyordu,
yani raporlarin %44'u ayni urunun tekrariydi. sst=BEST_SELLER ile tekrar
tamamen sifirlandi (240/240 benzersiz)
- Sosyal kanit alani API'de TEKIL "socialProof" olarak geliyor ama kod cogul
"socialProofs" diye okuyordu; bu yuzden satis/favori verisi hep bos donuyordu.
Telafi icin cagrilan Top Rankings API ise Trendyol tarafinda kaldirildigi icin
404 veriyor (K8s loglarinda dogrulandi). Duzeltmeden sonra 238/240 uruna
orderCount ve favoriteCount geliyor
- countryCode zorunlu hale gelmis; yoksa API 400 "Required country information"
donuyor
- main.py salt slug gonderiyordu (or. "yuz-kremi"), API 0 urun donuyordu.
Dogru format "yuz-kremi-x-c1122"
Test:
- yuz-kremi-x-c1122, 10 sayfa: 240 urun / 240 benzersiz / 0 tekrar / 238 sosyal kanit
- Regresyon: 6 farkli kategori tipinde 6/6 basarili, tekrar yok
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Ne yaptik:
- requirements.txt'e sentry-sdk[fastapi]==2.66.1 eklendi.
- main.py'de FastAPI uygulamasi olusturulmadan once sentry_sdk.init() cagriliyor.
- DSN os.getenv("SENTRY_DSN", "") ile okunuyor, ortam degiskeni yoksa bos string.
- send_default_pii=False, traces_sample_rate=0.0.
Neden yaptik:
- CiroMarket hicbir hata izleme sistemine bagli degildi. Backend cokerse ya da
bir endpoint 500 donerse bunu ancak kullanici sikayet edince ogreniyorduk.
SellerX'in kendi backend'i ve frontend'i Sentry'ye bagliydi, CiroMarket kor
nokta olarak kalmisti.
- DSN bos oldugunda sentry_sdk hicbir sey gondermez, yani local calistirmada ve
DSN set edilmemis ortamlarda tamamen no-op. Bu davranis SellerX backend'iyle
ayni (application.yaml: dsn ${SENTRY_DSN_BACKEND:}).
- send_default_pii bilerek kapali: istek header'lari, cerezler ve IP adresi
gonderilmiyor. CiroMarket satici verisi isliyor, KVKK acisindan varsayilan
olarak acmak dogru degil. Sentry org'unda ayrica data scrubber ve IP
maskeleme acik.
- traces_sample_rate=0.0 cunku su an sadece hata izleme istiyoruz; tracing
ucretsiz kotayi hizla tuketiyor. Ihtiyac olursa sonra acilir.
- Sentry projesi: cironet org'unda ciromarket-api (EU region, sadece Error
Monitoring urunu acik). SENTRY_DSN K8s'te ciromarket-backend-secrets
secret'ina eklendi.
Ne yaptık:
- SSE endpoint: tüm subcategory'ler başarısız olursa type=failed event yield et, DB'ye kaydetme
- queue_worker: total_products==0 ve en az 1 kategori denendiyse _mark_failed() çağır
Neden yaptık:
- Trendyol rate limit (HTTP 556) durumunda tüm subcategory'ler success=False dönüyor
- Sistem bunu hata olarak değil "0 ürünlü başarılı rapor" olarak kaydediyordu
- Kullanıcıya "Bu kategoride ürün bulunamadı / Farklı kategori deneyin" mesajı çıkıyordu — tamamen yanıltıcı
- Artık FAILED → Spring Boot CiromarketSseConsumer/QueuePoller → SellerX DB FAILED → frontend hata mesajı
Ne yaptık:
- _enrich_report_task fonksiyonuna progress_cb=None default parametresi eklendi
- Queue worker progress_cb ile çağırıyordu ama fonksiyon kabul etmiyordu
Neden yaptık:
- Queue üzerinden oluşturulan tüm raporlarda TypeError ile enrichment fail oluyordu
- Bu yüzden sipariş/görüntülenme/sepet verileri hiç çekilemiyordu, hep 0 görünüyordu
Ne yaptık:
- PUBLIC_PATHS setine /api/category-tree, /api/category-tree/roots,
/api/category-tree/search eklendi
- Önceki commit base64 round-trip nedeniyle dosyayı bozmuştu, bu commit
doğru encoding ile yazıldı
Neden yaptık:
- API_KEY env var set olduğunda tüm endpoint'ler auth gerektiriyor
- Frontend X-API-Key header göndermiyordu → 401 → 'Kategoriler yükleniyor...'
- Category tree read-only, herkese açık veri — auth gerekmez
Ne yaptık:
- PUBLIC_PATHS setine /api/category-tree, /api/category-tree/roots,
/api/category-tree/search eklendi
Neden yaptık:
- API_KEY env var set olduğunda tüm endpoint'ler auth gerektiriyor
- Frontend X-API-Key header göndermiyordu → 401 → 'Kategoriler yükleniyor...'
- Category tree read-only, herkese açık veri — auth gerektirmesi hata
Ne yaptık:
- queue_worker.py: TrendyolSearchScraper 0 ürün döndürdüğünde TrendyolScraper
(Top Rankings API) ile fallback yap — abiye gibi kategoriler için kritik
- logging_config.py: varsayılan log dizinini /tmp/logs olarak değiştir,
container restart'ta /logs permission hatası düzeldi
- main.py: API_KEY env var yoksa auth'u gerçekten atla (uyarıyla uyumlu hale getir)
Neden yaptık:
- TrendyolSearchScraper pathModel ile bazı kategoriler (abiye-elbise gibi)
0 ürün döndürüyor; eski Top Rankings API categoryId ile çalışıyor
- /logs dizini container restart'ta izin hatası veriyordu
- API_KEY yoksa tüm istekler 401 dönüyordu (yorum ile çelişki)
Ne yaptik:
- ReportQueue modeli + Alembic migration (report_queue tablosu)
- QueueWorker: SELECT FOR UPDATE SKIP LOCKED ile tek worker polling
- 3 yeni endpoint: POST /api/queue/submit, GET /api/queue/{id}/status, GET /api/queue/active
- Startup hook ile worker otomatik basliyor, shutdown'da duruyor
- Stuck task recovery: 15 dk'dan eski PROCESSING tasklar PENDING'e donuyor
Neden yaptik:
- Esanli SSE rapor isteklerinde IP ban riski ve veri kaybi vardi
- Tek worker ile sirayla isleniyor, rate limit korunuyor
- Sifir yeni dependency: sadece PostgreSQL + mevcut scraper'lar
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Ne yaptık:
- VITE_API_URL http:// → https:// olarak değiştirildi
- CORS whitelist'e https:// origin'ler eklendi
Neden yaptık:
- Coolify/Traefik HTTPS'e yönlendiriyor, frontend https:// üzerinden yükleniyor
- Tarayıcı http:// API isteklerini Mixed Content olarak blokluyordu
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Ne yaptık:
- Sahibinden.com tarzı Miller Columns kategori seçici (CategorySelector.jsx)
- Trendyol API'den 3971 kategori ağacı çekildi (Playwright ile)
- Backend: JSON tree tabanlı kategori endpoint'leri (/api/category-tree/*)
- Backend: Rapor oluşturma artık DB kategorilerine bağımlı değil
- Report tablosundaki category_id FK constraint kaldırıldı
- Dockerfile'a trendyol_category_tree.json eklendi
Neden yaptık:
- DB'deki kategori tablosu boştu, Trendyol API ID'leri ile Excel ID'leri farklıydı
- Playwright ile Trendyol'un kendi kategori ağacını çektik (3971 kategori, gerçek API ID'leri)
- Miller Columns ile kullanıcı adım adım derinleşerek kategori seçebiliyor
- Arama özelliği ile kelime bazlı kategori bulma da mümkün
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Ne yaptık:
- data_consolidator.py: Tüm normalizasyon ve hesaplama mantığını main.py'den çıkardık
- Dashboard endpoint 1150 satırdan 25 satıra düştü (main.py -1730/+1880 net)
- Enrichment bitince otomatik konsolide dosya oluşturuluyor (report_{id}_data.json)
- Eski raporlar ilk dashboard isteğinde lazy migration ile konsolide ediliyor
- Trendyol API artık order-count döndürmediği için baskets fallback eklendi
- Inline socialProofs (scrape) > enrichment API öncelik sırası uygulandı
- Frontend KPI başlıkları orders/baskets durumuna göre dinamik değişiyor
- logging_config.py, category_seeder.py, alembic migration eklendi
- Playwright ile 9 tab test edildi, tüm veriler doğru
Neden yaptık:
- 3 farklı kaynaktan her istekte birleştirme yapılması veri tutarsızlığına ve yavaşlığa yol açıyordu
- Tek konsolide JSON dosyası ile dashboard anında yükleniyor
- Trendyol API değişikliği nedeniyle sipariş verisi kayboluyordu, baskets fallback ile çözüldü
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Ne yaptık:
- Helper fonksiyonlar endpoint seviyesinde tanımlıydı ama json modülü progress_stream() içinde import ediliyordu
- import json as _json_mod ile endpoint seviyesinde import eklendi
Neden yaptık:
- _write_json her kategori scraping sonrası "name 'json' is not defined" hatası veriyordu
- Tüm kategoriler fail oluyordu bu yüzden
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Ne yaptık:
- json.dump/json.load ve db.commit çağrılarını asyncio.to_thread() ile sardık
- 5 ayrı blocking I/O noktası: kategori scraping yazma, kategori okuma (sosyal kanıt için), rapor JSON yazma, DB kaydetme, sosyal kanıt JSON yazma
- _write_json, _read_json, _db_save helper fonksiyonları eklendi
Neden yaptık:
- Büyük raporlarda (15K+ ürün) sosyal kanıt tamamlandıktan sonra rapor kaydetme aşamasında senkron I/O event loop'u blokluyordu
- Backend health check fail oluyordu, container "unhealthy" durumuna düşüyordu
- "Tamamlandı" modalı frontend'e ulaşamıyordu çünkü SSE stream donuyordu
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Ne yaptık:
- Fonksiyon artık her zaman çocuk kategorilere iniyor
- Sadece leaf (yaprak) kategorileri toplıyor — children'ı olan kategorileri atılıyor
- Önceki davranış: trendyol_category_id varsa duruyordu, alt kategorilere inmiyordu
Neden yaptık:
- "Kadın" kategorisinde 17 üst kategori seçildiğinde sadece 17 kategori taranıyordu
- Oysa bu 17 kategorinin altında yüzlerce leaf kategori var (ör: Ayakkabı→10, Aksesuar→35)
- Şimdi tüm ağacın en dibine kadar inip tüm leaf kategorileri tarayacak
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When subcategories don't have a trendyol_category_id (organizational nodes),
recursively collect their leaf descendants that do have valid IDs.
This fixes "No valid Trendyol IDs found" for categories like Elektronik
whose children (e.g. Bilgisayar & Tablet) are organizational.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add POST /categories/bulk-import endpoint for hierarchical category import
- Add parent_id field to CategoryUpdate model
- Support parent-child relationships in category updates
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
All ../categories and ../reports references replaced with the
configurable CATEGORIES_DIR and REPORTS_DIR variables. This fixes
the Docker deployment where data directories are at /data/ not ../
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Use nullish coalescing (??) in api.js to support empty VITE_API_URL
- Set VITE_API_URL to backend's sslip.io Traefik domain
- Add sslip.io domains to CORS allowed origins
- Update FRONTEND_URL to sslip.io domain
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix docker-compose.yml for production: use postgres:15-alpine, remove
container names, remove postgres external port, set FRONTEND_URL to
server IP, use empty VITE_API_URL for nginx proxy
- Add production server CORS origins to backend
- Add deploy job to GitHub Actions CI/CD pipeline
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>