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