feat: 완전한 문서 관리 시스템 구현

 주요 기능:
- 문서 사라짐 문제 해결: API limit 제한으로 인한 문서 누락 해결
- 서적별 문서 관리: HTML과 PDF 통합 관리 시스템
- PDF 뷰어 개선: 인증, 네비게이션, 에러 처리 강화
- 서적 편집/삭제: 완전한 서적 관리 기능

🔧 기술적 개선:
- /api/documents/all 엔드포인트 추가 (모든 문서 조회)
- HTML/PDF 문서 타입별 아이콘 및 필터링
- 서적별 뷰에서 편집/삭제 버튼 추가
- PDF Manager와 서적 편집 페이지 연동

🎨 UI/UX 개선:
- Devonthink 스타일 서적 그룹화
- HTML 문서 순서 관리와 PDF 관리 섹션 분리
- 문서 타입별 시각적 구분 (HTML: 파란색, PDF: 빨간색)
- 2단계 확인을 통한 안전한 서적 삭제

�� 버그 수정:
- PDF 삭제 시 undefined ID 전달 문제 해결
- 서적 편집 페이지 422 오류 해결 (URL 파라미터 문제)
- PDF.js 워커 설정 및 인증 토큰 처리 개선
This commit is contained in:
hyungi
2025-09-05 11:00:17 +09:00
parent cfb9485d4f
commit 6a537008db
85 changed files with 375 additions and 28 deletions

View File

@@ -83,12 +83,13 @@ router = APIRouter()
@router.get("/", response_model=List[DocumentResponse])
async def list_documents(
skip: int = 0,
limit: int = 50,
limit: int = 50, # 기본값 복원
tag: Optional[str] = None,
search: Optional[str] = None,
current_user: User = Depends(get_current_active_user),
db: AsyncSession = Depends(get_db)
):
"""페이지네이션이 있는 문서 목록 조회"""
"""문서 목록 조회"""
query = select(Document).options(
selectinload(Document.uploader),
@@ -160,6 +161,70 @@ async def list_documents(
return response_data
@router.get("/all", response_model=List[DocumentResponse])
async def list_all_documents(
current_user: User = Depends(get_current_active_user),
db: AsyncSession = Depends(get_db)
):
"""모든 문서 조회 (페이지네이션 없음) - 프론트엔드 전용"""
query = select(Document).options(
selectinload(Document.uploader),
selectinload(Document.tags),
selectinload(Document.book), # 서적 정보 추가
selectinload(Document.category) # 소분류 정보 추가
)
# 권한 필터링 (관리자가 아니면 공개 문서 + 자신이 업로드한 문서만)
if not current_user.is_admin:
query = query.where(
or_(
Document.is_public == True,
Document.uploaded_by == current_user.id
)
)
query = query.order_by(Document.created_at.desc())
result = await db.execute(query)
documents = result.scalars().all()
# 응답 데이터 변환
response_data = []
for doc in documents:
doc_data = DocumentResponse(
id=str(doc.id),
title=doc.title,
description=doc.description,
html_path=doc.html_path, # None 가능 (PDF만 업로드한 경우)
pdf_path=doc.pdf_path,
thumbnail_path=doc.thumbnail_path,
file_size=doc.file_size,
page_count=doc.page_count,
language=doc.language,
is_public=doc.is_public,
is_processed=doc.is_processed,
created_at=doc.created_at,
updated_at=doc.updated_at,
document_date=doc.document_date,
uploader_name=doc.uploader.full_name or doc.uploader.email,
tags=[tag.name for tag in doc.tags],
# 서적 정보 추가
book_id=str(doc.book.id) if doc.book else None,
book_title=doc.book.title if doc.book else None,
book_author=doc.book.author if doc.book else None,
# 소분류 정보 추가
category_id=str(doc.category.id) if doc.category else None,
category_name=doc.category.name if doc.category else None,
sort_order=doc.sort_order,
# PDF 매칭 정보 추가
matched_pdf_id=str(doc.matched_pdf_id) if doc.matched_pdf_id else None
)
response_data.append(doc_data)
return response_data
@router.get("/hierarchy/structured", response_model=dict)
async def get_documents_by_hierarchy(
current_user: User = Depends(get_current_active_user),
@@ -375,10 +440,14 @@ async def upload_document(
await db.commit()
# 문서 정보를 다시 로드 (태그 포함)
# 문서 정보를 다시 로드 (태그, 서적, 카테고리 포함)
result = await db.execute(
select(Document)
.options(selectinload(Document.tags))
.options(
selectinload(Document.tags),
selectinload(Document.book),
selectinload(Document.category)
)
.where(Document.id == document.id)
)
document_with_tags = result.scalar_one()
@@ -401,6 +470,14 @@ async def upload_document(
document_date=document_with_tags.document_date,
uploader_name=current_user.full_name or current_user.email,
tags=[tag.name for tag in document_with_tags.tags],
# 서적 정보 추가
book_id=str(document_with_tags.book.id) if document_with_tags.book else None,
book_title=document_with_tags.book.title if document_with_tags.book else None,
book_author=document_with_tags.book.author if document_with_tags.book else None,
# 소분류 정보 추가
category_id=str(document_with_tags.category.id) if document_with_tags.category else None,
category_name=document_with_tags.category.name if document_with_tags.category else None,
sort_order=document_with_tags.sort_order,
matched_pdf_id=str(document_with_tags.matched_pdf_id) if document_with_tags.matched_pdf_id else None
)