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 yaptik:
- Enrichment kosulunu 'not any socialProofs' yerine 'not any winnerVariant' yaptik
- Top Rankings API'den winnerVariant, merchantListings ve socialProofs'u birlestiriyoruz
- Search API urunlerinde winnerVariant bos gelir, bu kosul Search API orijinli urunleri dogru tespit eder
Neden yaptik:
- Search API winnerVariant ve merchantListings dondurmuyor (bos dict/liste)
- data_consolidator.py barkodu winnerVariant.barcode'dan, menseiyi merchantListings[].customValues'dan aliyor
- Search API'de socialProofs dolu geldiginden eski kosul ('not any socialProofs') hic tetiklenmiyordu
- Sonuc: Mensei ve Barkod tablari sifir gosteriyordu (kpek urunleri analizi #118 bug'i)
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:
- data_consolidator.py'de "Satıcı bilgileri" bloğunu sildik
- Return dict'ten seller_count, has_buybox, is_trendyol_product alanlarını çıkardık
Neden yaptık:
- merchantListings Trendyol'un public API'sinden gelmediği için
seller_count her zaman 0, has_buybox her zaman false dönüyordu.
- is_trendyol_product barcode-prefix tabanlıydı, güvenilirliği düşüktü.
- SellerX (CiroNet) tarafında frontend bu sütunları zaten kaldırdı —
scraper response'unda da gereksiz yer kaplamasın diye temizliyoruz.
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 yaptık:
- build_consolidated_report(): normalized_products boş olunca
None yerine empty=true içeren boş dashboard döndürüyor
- normalize_product(): seller_count, has_buybox, is_trendyol_product alanları eklendi
- Social proof: metrik bazında en doğru kaynak seçimi (inline vs enrichment API)
Neden yaptık:
- Boş kategorilerde dashboard-data endpoint 500 fırlatıyordu,
kullanıcı loading skeleton'dan çıkamıyordu
- Satıcı sayısı ve buybox bilgisi Ürünler tablosu için gerekli
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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:
- /app/../logs yazılamadığında /tmp/logs'a fallback eklendi
Neden yaptık:
- Docker container'da /logs dizinine yazma izni olmadığı için backend başlatılamıyordu
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>
The chown was running before cp seeded categories from initial-categories,
so the copied files remained owned by root. Now permissions are fixed
after all file operations complete.
Co-Authored-By: Claude Opus 4.6 <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>
- Run container as root initially to fix volume mount permissions
- Use gosu to drop to appuser before starting uvicorn
- chown /data directories at startup (Coolify mounts volumes as root)
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>
Coolify remaps volume mounts to its own managed directories, so
./categories was mapped to an empty dir instead of the repo data.
Changes:
- Backend build context changed to repo root (.) so categories/ is accessible
- Dockerfile copies categories into /data/initial-categories/
- startup.sh seeds /data/categories from bundled data if empty
- Removed categories volume mount (reports still persisted via volume)
- Added root .dockerignore (categories NOT excluded)
- Updated CI workflow to match new build context
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>