mirror of
https://github.com/nethunterzist/trendyol-analiz
synced 2026-07-01 01:17:04 +00:00
fix(api): category-tree endpoint'lerini public yap
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
This commit is contained in:
@@ -52,7 +52,7 @@ if not API_KEY:
|
||||
API_KEY_HEADER = APIKeyHeader(name="X-API-Key", auto_error=False)
|
||||
|
||||
# Paths that do not require API key authentication
|
||||
PUBLIC_PATHS = {"/health"}
|
||||
PUBLIC_PATHS = {"/health", "/api/category-tree", "/api/category-tree/roots", "/api/category-tree/search"}
|
||||
|
||||
async def verify_api_key(request: Request, api_key: Optional[str] = Security(API_KEY_HEADER)):
|
||||
"""
|
||||
@@ -1667,13 +1667,19 @@ async def create_report(
|
||||
log_sse.info(f"SSE stream started: task={task_id}, category={main_cat_name}")
|
||||
try:
|
||||
# Send initial info
|
||||
yield f"data: {json_module.dumps({'type': 'info', 'message': f'📂 {main_cat_name} kategorisi seçildi', 'progress': 0})}\n\n"
|
||||
yield f"data: {json_module.dumps({'type': 'info', 'message': f'📂 {main_cat_name} kategorisi seçildi', 'progress': 0})}
|
||||
|
||||
"
|
||||
await asyncio.sleep(0.1)
|
||||
|
||||
yield f"data: {json_module.dumps({'type': 'info', 'message': f'📊 {len(categories_to_scrape)} alt kategori bulundu', 'progress': 0})}\n\n"
|
||||
yield f"data: {json_module.dumps({'type': 'info', 'message': f'📊 {len(categories_to_scrape)} alt kategori bulundu', 'progress': 0})}
|
||||
|
||||
"
|
||||
await asyncio.sleep(0.1)
|
||||
|
||||
yield f"data: {json_module.dumps({'type': 'info', 'message': '🚀 Veri çekimi başlatılıyor...', 'progress': 5})}\n\n"
|
||||
yield f"data: {json_module.dumps({'type': 'info', 'message': '🚀 Veri çekimi başlatılıyor...', 'progress': 5})}
|
||||
|
||||
"
|
||||
await asyncio.sleep(0.5)
|
||||
|
||||
# Start synchronous scraping with progress updates
|
||||
@@ -1694,13 +1700,17 @@ async def create_report(
|
||||
for idx, (path_model, cat_name, cat_id) in enumerate(categories_to_scrape, 1):
|
||||
progress = int((idx / len(categories_to_scrape)) * 80) + 10
|
||||
|
||||
yield f"data: {json_module.dumps({'type': 'processing', 'message': f'🔍 [{idx}/{len(categories_to_scrape)}] {cat_name} çekiliyor...', 'progress': progress, 'current': idx, 'total': len(categories_to_scrape)})}\n\n"
|
||||
yield f"data: {json_module.dumps({'type': 'processing', 'message': f'🔍 [{idx}/{len(categories_to_scrape)}] {cat_name} çekiliyor...', 'progress': progress, 'current': idx, 'total': len(categories_to_scrape)})}
|
||||
|
||||
"
|
||||
await asyncio.sleep(0.1)
|
||||
|
||||
try:
|
||||
if path_model:
|
||||
# New Search API — works for both -c and -s categories
|
||||
yield f"data: {json_module.dumps({'type': 'api', 'message': f'🌐 API: Trendyol Search - {path_model}', 'progress': progress})}\n\n"
|
||||
yield f"data: {json_module.dumps({'type': 'api', 'message': f'🌐 API: Trendyol Search - {path_model}', 'progress': progress})}
|
||||
|
||||
"
|
||||
await asyncio.sleep(0.1)
|
||||
|
||||
scraper = TrendyolSearchScraper(path_model)
|
||||
@@ -1732,7 +1742,9 @@ async def create_report(
|
||||
|
||||
elif cat_id:
|
||||
# Legacy fallback — old top-rankings API
|
||||
yield f"data: {json_module.dumps({'type': 'api', 'message': f'🌐 API: Trendyol Best Seller - Kategori ID: {cat_id}', 'progress': progress})}\n\n"
|
||||
yield f"data: {json_module.dumps({'type': 'api', 'message': f'🌐 API: Trendyol Best Seller - Kategori ID: {cat_id}', 'progress': progress})}
|
||||
|
||||
"
|
||||
await asyncio.sleep(0.1)
|
||||
|
||||
scraper = TrendyolScraper(cat_id)
|
||||
@@ -1768,7 +1780,9 @@ async def create_report(
|
||||
"file_path": filename
|
||||
})
|
||||
|
||||
yield f"data: {json_module.dumps({'type': 'success', 'message': f'✅ {cat_name} tamamlandı - {len(products)} ürün bulundu', 'progress': progress})}\n\n"
|
||||
yield f"data: {json_module.dumps({'type': 'success', 'message': f'✅ {cat_name} tamamlandı - {len(products)} ürün bulundu', 'progress': progress})}
|
||||
|
||||
"
|
||||
await asyncio.sleep(0.1)
|
||||
else:
|
||||
results["failed"] += 1
|
||||
@@ -1780,7 +1794,9 @@ async def create_report(
|
||||
"total_products": 0,
|
||||
"file_path": None
|
||||
})
|
||||
yield f"data: {json_module.dumps({'type': 'warning', 'message': f'⚠️ {cat_name} - Ürün bulunamadı', 'progress': progress})}\n\n"
|
||||
yield f"data: {json_module.dumps({'type': 'warning', 'message': f'⚠️ {cat_name} - Ürün bulunamadı', 'progress': progress})}
|
||||
|
||||
"
|
||||
await asyncio.sleep(0.1)
|
||||
|
||||
except Exception as e:
|
||||
@@ -1793,14 +1809,18 @@ async def create_report(
|
||||
"total_products": 0,
|
||||
"file_path": None
|
||||
})
|
||||
yield f"data: {json_module.dumps({'type': 'error', 'message': f'❌ {cat_name} - Hata: {str(e)}', 'progress': progress})}\n\n"
|
||||
yield f"data: {json_module.dumps({'type': 'error', 'message': f'❌ {cat_name} - Hata: {str(e)}', 'progress': progress})}
|
||||
|
||||
"
|
||||
await asyncio.sleep(0.1)
|
||||
|
||||
# Rate limiting (non-blocking)
|
||||
await asyncio.sleep(1.5)
|
||||
|
||||
# Generate report file
|
||||
yield f"data: {json_module.dumps({'type': 'info', 'message': '📝 Rapor dosyası oluşturuluyor...', 'progress': 88})}\n\n"
|
||||
yield f"data: {json_module.dumps({'type': 'info', 'message': '📝 Rapor dosyası oluşturuluyor...', 'progress': 88})}
|
||||
|
||||
"
|
||||
await asyncio.sleep(0.5)
|
||||
|
||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
@@ -1822,7 +1842,9 @@ async def create_report(
|
||||
await asyncio.to_thread(_write_json, json_filename, combined_data)
|
||||
|
||||
# Save to database
|
||||
yield f"data: {json_module.dumps({'type': 'info', 'message': '💾 Veritabanına kaydediliyor...', 'progress': 93})}\n\n"
|
||||
yield f"data: {json_module.dumps({'type': 'info', 'message': '💾 Veritabanına kaydediliyor...', 'progress': 93})}
|
||||
|
||||
"
|
||||
await asyncio.sleep(0.5)
|
||||
|
||||
new_report = Report(
|
||||
@@ -1856,7 +1878,9 @@ async def create_report(
|
||||
log_sse.info(f"Background enrichment started for report {report_id_for_enrich}")
|
||||
|
||||
# Wait for enrichment to complete, sending progress updates via SSE
|
||||
yield f"data: {json_module.dumps({'type': 'info', 'message': '📊 Sosyal kanıt verileri toplanıyor...', 'progress': 90})}\n\n"
|
||||
yield f"data: {json_module.dumps({'type': 'info', 'message': '📊 Sosyal kanıt verileri toplanıyor...', 'progress': 90})}
|
||||
|
||||
"
|
||||
await asyncio.sleep(0.5)
|
||||
|
||||
progress_key = f"social_{report_id_for_enrich}"
|
||||
@@ -1874,26 +1898,38 @@ async def create_report(
|
||||
|
||||
if sp_total > 0:
|
||||
overall_pct = 90 + int(sp_pct * 0.09)
|
||||
yield f"data: {json_module.dumps({'type': 'info', 'message': f'📊 Sosyal kanıt: {sp_processed}/{sp_total} ürün (%{sp_pct})', 'progress': overall_pct})}\n\n"
|
||||
yield f"data: {json_module.dumps({'type': 'info', 'message': f'📊 Sosyal kanıt: {sp_processed}/{sp_total} ürün (%{sp_pct})', 'progress': overall_pct})}
|
||||
|
||||
"
|
||||
|
||||
await asyncio.sleep(3)
|
||||
waited += 3
|
||||
|
||||
enrich_status = enrichment_progress.get(report_id_for_enrich) or {}
|
||||
if enrich_status.get("status") == "completed":
|
||||
yield f"data: {json_module.dumps({'type': 'info', 'message': '✅ Sosyal kanıt tamamlandı!', 'progress': 99})}\n\n"
|
||||
yield f"data: {json_module.dumps({'type': 'info', 'message': '✅ Sosyal kanıt tamamlandı!', 'progress': 99})}
|
||||
|
||||
"
|
||||
elif enrich_status.get("status") == "error":
|
||||
err_msg = str(enrich_status.get("error", ""))[:100]
|
||||
yield f"data: {json_module.dumps({'type': 'warning', 'message': f'⚠️ Sosyal kanıt hatası: {err_msg}', 'progress': 99})}\n\n"
|
||||
yield f"data: {json_module.dumps({'type': 'warning', 'message': f'⚠️ Sosyal kanıt hatası: {err_msg}', 'progress': 99})}
|
||||
|
||||
"
|
||||
else:
|
||||
yield f"data: {json_module.dumps({'type': 'warning', 'message': '⚠️ Sosyal kanıt zaman aşımı, arka planda devam ediyor...', 'progress': 99})}\n\n"
|
||||
yield f"data: {json_module.dumps({'type': 'warning', 'message': '⚠️ Sosyal kanıt zaman aşımı, arka planda devam ediyor...', 'progress': 99})}
|
||||
|
||||
"
|
||||
else:
|
||||
yield f"data: {json_module.dumps({'type': 'info', 'message': '📊 Rapor JSON olarak kaydedildi (DB atlandı)', 'progress': 99})}\n\n"
|
||||
yield f"data: {json_module.dumps({'type': 'info', 'message': '📊 Rapor JSON olarak kaydedildi (DB atlandı)', 'progress': 99})}
|
||||
|
||||
"
|
||||
|
||||
await asyncio.sleep(0.1)
|
||||
|
||||
# Final success message
|
||||
yield f"data: {json_module.dumps({'type': 'complete', 'message': '✅ Rapor başarıyla oluşturuldu!', 'progress': 100, 'report_id': report_id_for_enrich, 'total_products': results['total_products'], 'successful': results['successful'], 'enrichment_status': enrich_status.get('status', 'unknown')})}\n\n"
|
||||
yield f"data: {json_module.dumps({'type': 'complete', 'message': '✅ Rapor başarıyla oluşturuldu!', 'progress': 100, 'report_id': report_id_for_enrich, 'total_products': results['total_products'], 'successful': results['successful'], 'enrichment_status': enrich_status.get('status', 'unknown')})}
|
||||
|
||||
"
|
||||
await asyncio.sleep(0.1)
|
||||
|
||||
except asyncio.CancelledError:
|
||||
@@ -1901,7 +1937,9 @@ async def create_report(
|
||||
return
|
||||
except Exception as e:
|
||||
log_sse.error(f"SSE stream error: task={task_id}, error={e}", exc_info=True)
|
||||
yield f"data: {json_module.dumps({'type': 'error', 'message': f'❌ Kritik hata: {str(e)}', 'progress': -1})}\n\n"
|
||||
yield f"data: {json_module.dumps({'type': 'error', 'message': f'❌ Kritik hata: {str(e)}', 'progress': -1})}
|
||||
|
||||
"
|
||||
|
||||
return StreamingResponse(progress_stream(), media_type="text/event-stream")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user