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 yaptik:
- .gitea/workflows/deploy-frontend.yaml silindi
Neden yaptik:
- CiroMarket frontend artık deploy edilmiyor
- Sadece backend (FastAPI) + db ayakta
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:
- topSellingProducts useMemo'da ürün adına göre dedup eklendi
- Aynı isimli ürünlerden sadece en yüksek satışlı olanı tutulur
Neden yaptık:
- Trendyol'da aynı ürünün farklı varyantları (renk/beden) ayrı product_id
ile scrape ediliyor ama hepsinde aynı aggregate satış sayısı var
- Bu yüzden 'Buharlı Temizleyici 26 Parça 2000w ultra güçlü Gri' gibi
ürünler listede 3 kez aynı sayı ile görünüyordu (2, 3, 4. sıra)
Ne yaptık:
- deploy job: Coolify response'undan deployment_uuid parse edip
GITHUB_OUTPUT'a yazıyor.
- verify job: artık uygulama UUID'si yerine spesifik deployment UUID'sini
polluyor (queued → in_progress → finished/failed geçişleri).
- Polling süresi 5 dk → 10 dk (max 60 × 10s).
Neden yaptık:
- Eski verify uygulama UUID'sini polling ediyordu. Yeni deploy başlamadan
önceki eski "running:healthy" status'u yakalayıp 4 saniyede early-exit
yapıyordu. Workflow yeşil görünür ama gerçek deploy'u doğrulamamış olur.
- Yeni davranış: workflow gerçekten Coolify deploy'unun bitmesini bekliyor.
Fail olursa workflow da fail eder, notify-failure job tetiklenir.
Ne yaptık:
- .github/workflows/deploy.yml: validate (backend pytest + frontend lint/build)
→ build-push GHCR (backend + frontend image) → Coolify webhook
→ verify (Coolify polling + public URL health check) → notify-failure
(GitHub issue auto-create) zinciri eklendi.
- .github/workflows/ci.yml: eski "Deploy to Coolify" job'u kaldırıldı.
CI artık sadece test + build doğrulaması yapıyor; deploy ayrı workflow'a alındı.
- Trigger: main branch'e push (workflow_dispatch ile manuel de tetiklenebilir).
Neden yaptık:
- Şu ana kadar Coolify deploy manuel tetikleniyordu (CLAUDE.md "Coolify deploy
otomatik tetiklenmez" notu). Her push sonrası Coolify panel'inden el ile
redeploy gerekiyordu.
- SellerX'in deploy-frontend.yml mimarisi — validate → build-push → deploy
→ verify zinciri — battle-tested. Aynı pattern'i trendyol-analiz'e port
ediyoruz.
- GHCR'a image push: ileride Coolify build yerine "image: ghcr.io/..." pull
yapabilsin diye. Şu an compose 'build:' kullansa bile cache + rollback için
GHCR'da hazır image bulunuyor.
- verify polling + public URL health check: deploy başarısız olursa GitHub
Actions otomatik fail eder ve notify-failure GitHub Issue açar.
Gerekli GitHub Secrets (Settings → Secrets and variables → Actions):
- COOLIFY_BASE_URL
- COOLIFY_API_TOKEN
- COOLIFY_TRENDYOL_UUID = x4c08gc84kcw4oow0ggg44cg
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:
- CLAUDE.md'ye "URL Mimarisi" ve "Container/Traefik Sorunları" bölümleri eklendi
Neden yaptık:
- "Failed to fetch" hatasının kök nedeni (VITE_API_URL Coolify build ARG vs .env çakışması)
ve container'ı Traefik label'sız başlatınca "Bad Gateway" olması belgelendi
- Bir dahaki seferde aynı hata dizisine girmeyelim diye
Co-Authored-By: Claude Sonnet 4.6 <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>
Ne yaptık:
- Service label adlarını Coolify'ın oluşturduğu router adlarıyla eşleştirdik
- frontend: frontend → http-0-x4c08gc84kcw4oow0ggg44cg-frontend
- backend: backend → http-0-x4c08gc84kcw4oow0ggg44cg-backend
Neden yaptık:
- Traefik router adı ile service adı eşleşmediğinde 502 Bad Gateway dönüyordu
- Coolify router'ları kendi UUID formatında oluşturuyor, service adları da aynı formatta olmalı
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Ne yaptık:
- Frontend service'ine traefik.http.services.frontend.loadbalancer.server.port=80 label'ı eklendi
Neden yaptık:
- Traefik frontend container'ın hangi portta çalıştığını bilmiyordu (502 Bad Gateway)
- Backend'de bu label vardı ama frontend'de eksikti
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Ne yaptık:
- "Tümünü Seç" artık arama filtresi aktifken bile tüm alt kategorileri seçiyor
- Tüm alt kategoriler seçiliyse subcategory_ids gönderilmiyor (backend zaten tümünü tarıyor)
- URL uzunluk limitine takılma riski ortadan kalktı
- Label'a seçili/toplam sayacı eklendi (ör: 45/105)
Neden yaptık:
- Kozmetik gibi 105 alt kategorili ana kategoride "Tümünü Seç" sadece
filtrelenmiş kategorileri seçiyordu, bu yüzden raporda 1 kategori (~100 ürün)
taranıyordu. Şimdi 105 kategori (~10.500 ürün) taranacak.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Traefik defaults to port 80 when no loadbalancer port is specified.
Backend listens on 8001, causing the trendyol-api domain to timeout.
Co-Authored-By: Claude Opus 4.6 <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>