"use client"; import { useState, useEffect, useCallback } from "react"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui"; import { Button } from "@/components/ui"; import { Input } from "@/components/ui"; import { RAGEngine } from "@/rag"; export interface RSSFeedInfo { name: string; type: "naver" | "generic"; url?: string; blogId?: string; itemCount?: number; lastUpdated?: Date; } export interface RSSManagerProps { ragEngine: RAGEngine; onFeedAdded?: (feedInfo: RSSFeedInfo) => void; onFeedRemoved?: (feedName: string) => void; onFeedRefreshed?: (feedName: string, itemCount: number) => void; } export function RSSManager({ ragEngine, onFeedAdded, onFeedRemoved, onFeedRefreshed, }: RSSManagerProps) { const [feeds, setFeeds] = useState([]); const [newFeedUrl, setNewFeedUrl] = useState(""); const [customFeedName, setCustomFeedName] = useState(""); const [maxItems, setMaxItems] = useState("20"); const [isLoading, setIsLoading] = useState(false); const [refreshingFeeds, setRefreshingFeeds] = useState>( new Set() ); const [error, setError] = useState(null); const [success, setSuccess] = useState(null); // Load existing feeds on component mount useEffect(() => { loadExistingFeeds(); }, [ragEngine]); const loadExistingFeeds = useCallback(() => { try { const feedNames = ragEngine.getRSSFeedNames(); const feedInfos: RSSFeedInfo[] = feedNames.map((name) => { const info = ragEngine.getRSSFeedInfo(name); return { name, type: info.type || "generic", url: info.rssUrl, blogId: info.blogId, }; }); setFeeds(feedInfos); } catch (err) { console.error("Failed to load existing feeds:", err); } }, [ragEngine]); const clearMessages = () => { setError(null); setSuccess(null); }; const detectFeedType = ( url: string ): { type: "naver" | "generic"; blogId?: string } => { const blogId = RAGEngine.extractNaverBlogId(url); if (blogId) { return { type: "naver", blogId }; } return { type: "generic" }; }; const addFeed = async () => { if (!newFeedUrl.trim()) { setError("RSS URL을 입력해주세요."); return; } setIsLoading(true); clearMessages(); try { const { type, blogId } = detectFeedType(newFeedUrl); const feedName = customFeedName.trim() || (type === "naver" ? `네이버블로그_${blogId}` : `RSS피드_${Date.now()}`); const config = { maxItems: parseInt(maxItems) || 20, includeContent: true, }; let result; if (type === "naver" && blogId) { result = await ragEngine.addNaverBlogRSS(blogId, feedName, config); } else { result = await ragEngine.addRSSFeed(newFeedUrl, feedName, config); } const newFeedInfo: RSSFeedInfo = { name: result.feedName, type, url: type === "naver" ? `https://rss.blog.naver.com/${blogId}` : newFeedUrl, blogId: type === "naver" ? blogId : undefined, itemCount: result.itemCount, lastUpdated: new Date(), }; setFeeds((prev) => [...prev, newFeedInfo]); setNewFeedUrl(""); setCustomFeedName(""); setSuccess( `RSS 피드가 성공적으로 추가되었습니다! ${result.itemCount}개의 문서를 학습했습니다.` ); onFeedAdded?.(newFeedInfo); } catch (err) { setError( `RSS 피드 추가 실패: ${ err instanceof Error ? err.message : "알 수 없는 오류" }` ); } finally { setIsLoading(false); } }; const removeFeed = async (feedName: string) => { try { const success = ragEngine.removeRSSFeed(feedName); if (success) { setFeeds((prev) => prev.filter((feed) => feed.name !== feedName)); setSuccess(`RSS 피드 "${feedName}"가 제거되었습니다.`); onFeedRemoved?.(feedName); } else { setError(`RSS 피드 "${feedName}" 제거에 실패했습니다.`); } } catch (err) { setError( `RSS 피드 제거 실패: ${ err instanceof Error ? err.message : "알 수 없는 오류" }` ); } }; const refreshFeed = async (feedName: string) => { setRefreshingFeeds((prev) => new Set([...prev, feedName])); clearMessages(); try { const result = await ragEngine.refreshRSSFeed(feedName); setFeeds((prev) => prev.map((feed) => feed.name === feedName ? { ...feed, itemCount: result.itemCount, lastUpdated: new Date() } : feed ) ); setSuccess( `RSS 피드 "${feedName}"가 새로고침되었습니다! ${result.itemCount}개의 문서를 처리했습니다.` ); onFeedRefreshed?.(feedName, result.itemCount); } catch (err) { setError( `RSS 피드 새로고침 실패: ${ err instanceof Error ? err.message : "알 수 없는 오류" }` ); } finally { setRefreshingFeeds((prev) => { const next = new Set(prev); next.delete(feedName); return next; }); } }; return (
{/* Add New Feed */} RSS 피드 추가
setNewFeedUrl(e.target.value)} className="w-full" />

네이버 블로그: https://rss.blog.naver.com/블로그ID 또는 블로그ID만 입력

setCustomFeedName(e.target.value)} className="w-full" />
setMaxItems(e.target.value)} className="w-full h-10 px-3 rounded-md border border-gray-300 focus:outline-none focus:ring-2 focus:ring-blue-500" />
{/* Existing Feeds */} 등록된 RSS 피드 ({feeds.length}개) {feeds.length === 0 ? (

등록된 RSS 피드가 없습니다.

) : (
{feeds.map((feed) => (

{feed.name}

{feed.type === "naver" ? "네이버" : "RSS"}
{feed.url && ( )} {feed.itemCount !== undefined && (

문서 수: {feed.itemCount}개

)} {feed.lastUpdated && (

마지막 업데이트: {feed.lastUpdated.toLocaleString()}

)}
))}
)}
{/* Status Messages */} {error && (

{error}

)} {success && (

{success}

)}
); }