Phase 1.2: 참고 소스 관리 — 소스 CRUD + UI
백엔드 (crawler_platform/app/api/routes.py):
- POST /projects/{name}/sources: 프로젝트에 소스 추가/업데이트
- InlineSourceConfig 재사용, KnowledgeRepository.upsert_source 호출
- DELETE /projects/{name}/sources/{source_name}: 소스 삭제
- 404 처리 + 외래 키 정리
- GET /projects/{name} 응답에 base_url 필드 추가
프론트엔드 API 레이어 (src/lib/api/):
- sources.ts: sourcesApi.create/delete + Zod 스키마 (Source, deleteSourceResponseSchema)
TanStack Query 훅 (src/hooks/):
- useSources.ts: useCreateSource, useDeleteSource (mutation + 캐시 무효화)
- queryKeys에 sources.byProject 키 팩토리
ConfigureSourcesPage 완전 교체 (src/pages/):
- 2열 레이아웃: 등록된 소스 목록 + 소스 추가 폼
- react-hook-form + zod 검증 (name 정규식, type enum, trust 0~1, rate_limit 1~600)
- 소스 카드 표시: type 배지, 신뢰도, base_url 링크, 삭제 버튼
- 빈 상태/로딩 스켈레톤/에러+재시도 모두 처리
- "크롤 진행" 버튼은 소스 1개 이상일 때만 활성
- sonner 토스트 알림
i18n locale 키 sources.* 추가 (한/영)
다음 단계: Phase 1.3 — URL 시드 크롤 (CrawlPage 실제 구현)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -407,6 +407,7 @@ def register_routes(app, database_url: str) -> None:
|
||||
"id": source.id,
|
||||
"name": source.name,
|
||||
"type": source.type,
|
||||
"base_url": source.base_url,
|
||||
"trust_level": source.trust_level,
|
||||
"respect_robots_txt": source.respect_robots_txt,
|
||||
"rate_limit_per_minute": source.rate_limit_per_minute,
|
||||
@@ -415,6 +416,42 @@ def register_routes(app, database_url: str) -> None:
|
||||
],
|
||||
}
|
||||
|
||||
@app.post("/projects/{project_name}/sources")
|
||||
def add_project_source(project_name: str, request: InlineSourceConfig):
|
||||
"""Add or update a source on an existing project."""
|
||||
with session_scope(database_url) as session:
|
||||
repo = KnowledgeRepository(session)
|
||||
project = repo.get_project(project_name)
|
||||
source = repo.upsert_source(project, SourceConfig(**request.model_dump()))
|
||||
return {
|
||||
"id": source.id,
|
||||
"name": source.name,
|
||||
"type": source.type,
|
||||
"base_url": source.base_url,
|
||||
"trust_level": source.trust_level,
|
||||
"respect_robots_txt": source.respect_robots_txt,
|
||||
"rate_limit_per_minute": source.rate_limit_per_minute,
|
||||
}
|
||||
|
||||
@app.delete("/projects/{project_name}/sources/{source_name}")
|
||||
def delete_project_source(project_name: str, source_name: str):
|
||||
with session_scope(database_url) as session:
|
||||
repo = KnowledgeRepository(session)
|
||||
project = repo.get_project(project_name)
|
||||
source = session.scalar(
|
||||
select(models.Source).where(
|
||||
models.Source.project_id == project.id,
|
||||
models.Source.name == source_name,
|
||||
)
|
||||
)
|
||||
if source is None:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail=f"Source '{source_name}' not found in project '{project_name}'",
|
||||
)
|
||||
session.delete(source)
|
||||
return {"ok": True, "deleted": source_name}
|
||||
|
||||
@app.get("/ontology/{domain}")
|
||||
def ontology(domain: str):
|
||||
return ontology_to_dict(ontology_for_domain(domain))
|
||||
|
||||
Reference in New Issue
Block a user