feat: Integrate News Engine Console with Pipeline Monitor service
Backend Integration:
- Created PipelineClient for communicating with Pipeline Monitor (port 8100)
- Added proxy endpoints in monitoring.py:
* /api/v1/monitoring/pipeline/stats - Queue status and article counts
* /api/v1/monitoring/pipeline/health - Pipeline service health
* /api/v1/monitoring/pipeline/queues/{name} - Queue details
* /api/v1/monitoring/pipeline/workers - Worker status
Frontend Integration:
- Added Pipeline Monitor API functions to monitoring.ts
- Updated Monitoring page to display:
* Redis queue status (keyword, rss, search, summarize, assembly)
* Article statistics (today, total, active keywords)
* Pipeline health status
* Worker status for each pipeline type
Architecture:
- Console acts as API Gateway, proxying requests to Pipeline Monitor
- Pipeline Monitor (services/pipeline/monitor) manages:
* RSS Collector, Google Search, AI Summarizer, Article Assembly workers
* Redis queues for async job processing
* MongoDB for article and keyword storage
This integration allows the News Engine Console to monitor and display
real-time pipeline activity, queue status, and worker health.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
127
services/news-engine-console/backend/app/core/pipeline_client.py
Normal file
127
services/news-engine-console/backend/app/core/pipeline_client.py
Normal file
@ -0,0 +1,127 @@
|
||||
"""
|
||||
Pipeline Monitor API Client
|
||||
파이프라인 모니터 서비스와 통신하는 HTTP 클라이언트
|
||||
"""
|
||||
import os
|
||||
import httpx
|
||||
from typing import Dict, Any, Optional
|
||||
from fastapi import HTTPException
|
||||
|
||||
# Pipeline Monitor 서비스 URL
|
||||
PIPELINE_MONITOR_URL = os.getenv("PIPELINE_MONITOR_URL", "http://localhost:8100")
|
||||
|
||||
|
||||
class PipelineClient:
|
||||
"""Pipeline Monitor API와 통신하는 클라이언트"""
|
||||
|
||||
def __init__(self):
|
||||
self.base_url = PIPELINE_MONITOR_URL
|
||||
self.client = httpx.AsyncClient(base_url=self.base_url, timeout=30.0)
|
||||
|
||||
async def close(self):
|
||||
"""클라이언트 연결 종료"""
|
||||
await self.client.aclose()
|
||||
|
||||
async def _request(
|
||||
self,
|
||||
method: str,
|
||||
path: str,
|
||||
params: Optional[Dict[str, Any]] = None,
|
||||
json: Optional[Dict[str, Any]] = None
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Pipeline Monitor API에 HTTP 요청 전송
|
||||
|
||||
Args:
|
||||
method: HTTP 메소드 (GET, POST, DELETE 등)
|
||||
path: API 경로
|
||||
params: 쿼리 파라미터
|
||||
json: 요청 바디 (JSON)
|
||||
|
||||
Returns:
|
||||
API 응답 데이터
|
||||
|
||||
Raises:
|
||||
HTTPException: API 요청 실패 시
|
||||
"""
|
||||
try:
|
||||
response = await self.client.request(
|
||||
method=method,
|
||||
url=path,
|
||||
params=params,
|
||||
json=json
|
||||
)
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
except httpx.HTTPStatusError as e:
|
||||
raise HTTPException(
|
||||
status_code=e.response.status_code,
|
||||
detail=f"Pipeline Monitor API error: {e.response.text}"
|
||||
)
|
||||
except httpx.RequestError as e:
|
||||
raise HTTPException(
|
||||
status_code=503,
|
||||
detail=f"Failed to connect to Pipeline Monitor: {str(e)}"
|
||||
)
|
||||
|
||||
# Stats & Health
|
||||
async def get_stats(self) -> Dict[str, Any]:
|
||||
"""전체 파이프라인 통계 조회"""
|
||||
return await self._request("GET", "/api/stats")
|
||||
|
||||
async def get_health(self) -> Dict[str, Any]:
|
||||
"""헬스 체크"""
|
||||
return await self._request("GET", "/api/health")
|
||||
|
||||
# Queue Management
|
||||
async def get_queue_details(self, queue_name: str) -> Dict[str, Any]:
|
||||
"""특정 큐의 상세 정보 조회"""
|
||||
return await self._request("GET", f"/api/queues/{queue_name}")
|
||||
|
||||
# Worker Management
|
||||
async def get_workers(self) -> Dict[str, Any]:
|
||||
"""워커 상태 조회"""
|
||||
return await self._request("GET", "/api/workers")
|
||||
|
||||
# Keyword Management
|
||||
async def get_keywords(self) -> list:
|
||||
"""등록된 키워드 목록 조회"""
|
||||
return await self._request("GET", "/api/keywords")
|
||||
|
||||
async def add_keyword(self, keyword: str, schedule: str = "30min") -> Dict[str, Any]:
|
||||
"""새 키워드 등록"""
|
||||
return await self._request(
|
||||
"POST",
|
||||
"/api/keywords",
|
||||
params={"keyword": keyword, "schedule": schedule}
|
||||
)
|
||||
|
||||
async def delete_keyword(self, keyword_id: str) -> Dict[str, Any]:
|
||||
"""키워드 삭제"""
|
||||
return await self._request("DELETE", f"/api/keywords/{keyword_id}")
|
||||
|
||||
async def trigger_keyword(self, keyword: str) -> Dict[str, Any]:
|
||||
"""수동으로 키워드 처리 트리거"""
|
||||
return await self._request("POST", f"/api/trigger/{keyword}")
|
||||
|
||||
# Article Management
|
||||
async def get_articles(self, limit: int = 10, skip: int = 0) -> Dict[str, Any]:
|
||||
"""최근 생성된 기사 목록 조회"""
|
||||
return await self._request(
|
||||
"GET",
|
||||
"/api/articles",
|
||||
params={"limit": limit, "skip": skip}
|
||||
)
|
||||
|
||||
async def get_article(self, article_id: str) -> Dict[str, Any]:
|
||||
"""특정 기사 상세 정보 조회"""
|
||||
return await self._request("GET", f"/api/articles/{article_id}")
|
||||
|
||||
|
||||
# 전역 클라이언트 인스턴스
|
||||
pipeline_client = PipelineClient()
|
||||
|
||||
|
||||
async def get_pipeline_client() -> PipelineClient:
|
||||
"""의존성 주입용 Pipeline 클라이언트 가져오기"""
|
||||
return pipeline_client
|
||||
Reference in New Issue
Block a user