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:
jungwoo choi
2025-11-04 22:06:46 +09:00
parent d6ae03f42b
commit 7d29b7ca85
4 changed files with 331 additions and 1 deletions

View 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