Fix: PDF 전용 문서 뷰어 처리 개선
- HTML이 없고 PDF만 있는 문서 클릭 시 PDF 매니저로 자동 리다이렉트 - 뷰어에서 HTML 없는 문서의 경우 PDF 뷰어로 자동 전환 - 문서 관리 페이지에서 PDF 전용 문서 처리 로직 개선 - IndentationError 수정으로 백엔드 안정성 향상
This commit is contained in:
@@ -581,6 +581,7 @@ async def get_document_backlinks(
|
|||||||
|
|
||||||
# 2. 노트에서 오는 백링크 (NoteLink) - 동기 쿼리 사용
|
# 2. 노트에서 오는 백링크 (NoteLink) - 동기 쿼리 사용
|
||||||
try:
|
try:
|
||||||
|
print(f"🔍 노트 백링크 조회 시작...")
|
||||||
from ...core.database import get_sync_db
|
from ...core.database import get_sync_db
|
||||||
sync_db = next(get_sync_db())
|
sync_db = next(get_sync_db())
|
||||||
|
|
||||||
@@ -626,8 +627,12 @@ async def get_document_backlinks(
|
|||||||
))
|
))
|
||||||
|
|
||||||
sync_db.close()
|
sync_db.close()
|
||||||
|
print(f"✅ 노트 백링크 조회 완료")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"❌ 노트 백링크 조회 실패: {e}")
|
print(f"❌ 노트 백링크 조회 실패: {e}")
|
||||||
|
print(f"❌ 오류 타입: {type(e).__name__}")
|
||||||
|
import traceback
|
||||||
|
print(f"❌ 스택 트레이스: {traceback.format_exc()}")
|
||||||
|
|
||||||
print(f"✅ 총 {len(backlinks)}개의 백링크 반환 (문서 + 노트)")
|
print(f"✅ 총 {len(backlinks)}개의 백링크 반환 (문서 + 노트)")
|
||||||
return backlinks
|
return backlinks
|
||||||
|
|||||||
@@ -434,38 +434,65 @@ async def list_user_highlights(
|
|||||||
db: AsyncSession = Depends(get_db)
|
db: AsyncSession = Depends(get_db)
|
||||||
):
|
):
|
||||||
"""사용자의 모든 하이라이트 조회"""
|
"""사용자의 모든 하이라이트 조회"""
|
||||||
query = select(Highlight).options(selectinload(Highlight.user)).where(
|
try:
|
||||||
Highlight.user_id == current_user.id
|
print(f"🔍 하이라이트 조회 시작 - 사용자: {current_user.email}, document_id: {document_id}")
|
||||||
)
|
|
||||||
|
|
||||||
if document_id:
|
# 문서 권한이 있는 하이라이트만 조회하도록 Document와 조인
|
||||||
query = query.where(Highlight.document_id == document_id)
|
query = select(Highlight).options(selectinload(Highlight.user)).join(
|
||||||
|
Document, Highlight.document_id == Document.id
|
||||||
query = query.order_by(Highlight.created_at.desc()).offset(skip).limit(limit)
|
).where(
|
||||||
|
and_(
|
||||||
result = await db.execute(query)
|
Highlight.user_id == current_user.id,
|
||||||
highlights = result.scalars().all()
|
# 문서 권한 체크: 관리자이거나 문서 관리 권한이 있거나 공개 문서이거나 본인이 업로드한 문서
|
||||||
|
or_(
|
||||||
# 응답 데이터 변환
|
current_user.is_admin == True,
|
||||||
response_data = []
|
current_user.can_manage_books == True,
|
||||||
for highlight in highlights:
|
Document.is_public == True,
|
||||||
highlight_data = HighlightResponse(
|
Document.uploaded_by == current_user.id
|
||||||
id=str(highlight.id),
|
)
|
||||||
user_id=str(highlight.user_id),
|
)
|
||||||
document_id=str(highlight.document_id),
|
|
||||||
start_offset=highlight.start_offset,
|
|
||||||
end_offset=highlight.end_offset,
|
|
||||||
selected_text=highlight.selected_text,
|
|
||||||
element_selector=highlight.element_selector,
|
|
||||||
start_container_xpath=highlight.start_container_xpath,
|
|
||||||
end_container_xpath=highlight.end_container_xpath,
|
|
||||||
highlight_color=highlight.highlight_color,
|
|
||||||
highlight_type=highlight.highlight_type,
|
|
||||||
created_at=highlight.created_at,
|
|
||||||
updated_at=highlight.updated_at,
|
|
||||||
note=None
|
|
||||||
)
|
)
|
||||||
# 메모는 별도 API에서 조회하므로 여기서는 처리하지 않음
|
|
||||||
response_data.append(highlight_data)
|
|
||||||
|
|
||||||
return response_data
|
if document_id:
|
||||||
|
query = query.where(Highlight.document_id == document_id)
|
||||||
|
|
||||||
|
query = query.order_by(Highlight.created_at.desc()).offset(skip).limit(limit)
|
||||||
|
|
||||||
|
result = await db.execute(query)
|
||||||
|
highlights = result.scalars().all()
|
||||||
|
|
||||||
|
print(f"✅ 하이라이트 조회 완료: {len(highlights)}개")
|
||||||
|
|
||||||
|
# 응답 데이터 변환
|
||||||
|
response_data = []
|
||||||
|
for highlight in highlights:
|
||||||
|
highlight_data = HighlightResponse(
|
||||||
|
id=str(highlight.id),
|
||||||
|
user_id=str(highlight.user_id),
|
||||||
|
document_id=str(highlight.document_id),
|
||||||
|
start_offset=highlight.start_offset,
|
||||||
|
end_offset=highlight.end_offset,
|
||||||
|
selected_text=highlight.selected_text,
|
||||||
|
element_selector=highlight.element_selector,
|
||||||
|
start_container_xpath=highlight.start_container_xpath,
|
||||||
|
end_container_xpath=highlight.end_container_xpath,
|
||||||
|
highlight_color=highlight.highlight_color,
|
||||||
|
highlight_type=highlight.highlight_type,
|
||||||
|
created_at=highlight.created_at,
|
||||||
|
updated_at=highlight.updated_at,
|
||||||
|
note=None
|
||||||
|
)
|
||||||
|
# 메모는 별도 API에서 조회하므로 여기서는 처리하지 않음
|
||||||
|
response_data.append(highlight_data)
|
||||||
|
|
||||||
|
return response_data
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ 하이라이트 조회 실패: {e}")
|
||||||
|
print(f"❌ 오류 타입: {type(e).__name__}")
|
||||||
|
import traceback
|
||||||
|
print(f"❌ 스택 트레이스: {traceback.format_exc()}")
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
|
detail=f"Internal server error: {str(e)}"
|
||||||
|
)
|
||||||
|
|||||||
@@ -341,6 +341,16 @@ window.documentApp = () => ({
|
|||||||
|
|
||||||
// 문서 보기
|
// 문서 보기
|
||||||
viewDocument(documentId) {
|
viewDocument(documentId) {
|
||||||
|
// 문서 정보 찾기
|
||||||
|
const document = this.documents.find(doc => doc.id === documentId);
|
||||||
|
|
||||||
|
// HTML이 없고 PDF만 있는 경우 PDF 매니저로 리다이렉트
|
||||||
|
if (document && !document.html_path && document.pdf_path) {
|
||||||
|
console.log('🔄 PDF 전용 문서 - PDF 매니저로 리다이렉트');
|
||||||
|
window.location.href = `/pdf-manager.html`;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// 현재 페이지 정보를 세션 스토리지에 저장
|
// 현재 페이지 정보를 세션 스토리지에 저장
|
||||||
const currentPage = window.location.pathname.split('/').pop() || 'index.html';
|
const currentPage = window.location.pathname.split('/').pop() || 'index.html';
|
||||||
sessionStorage.setItem('previousPage', currentPage);
|
sessionStorage.setItem('previousPage', currentPage);
|
||||||
|
|||||||
@@ -55,8 +55,8 @@ class DocumentLoader {
|
|||||||
// 페이지 제목 업데이트
|
// 페이지 제목 업데이트
|
||||||
document.title = `${docData.title} - Document Server`;
|
document.title = `${docData.title} - Document Server`;
|
||||||
|
|
||||||
// PDF 문서가 아닌 경우에만 HTML 로드
|
// HTML 경로가 있는 경우에만 HTML 로드
|
||||||
if (!docData.pdf_path && docData.html_path) {
|
if (docData.html_path) {
|
||||||
// HTML 파일 경로 구성 (백엔드 서버를 통해 접근)
|
// HTML 파일 경로 구성 (백엔드 서버를 통해 접근)
|
||||||
const htmlPath = docData.html_path;
|
const htmlPath = docData.html_path;
|
||||||
const fileName = htmlPath.split('/').pop();
|
const fileName = htmlPath.split('/').pop();
|
||||||
@@ -71,6 +71,25 @@ class DocumentLoader {
|
|||||||
|
|
||||||
// 문서 내 스크립트 오류 방지를 위한 전역 함수들 정의
|
// 문서 내 스크립트 오류 방지를 위한 전역 함수들 정의
|
||||||
this.setupDocumentScriptHandlers();
|
this.setupDocumentScriptHandlers();
|
||||||
|
} else if (docData.pdf_path) {
|
||||||
|
// HTML이 없고 PDF만 있는 경우 PDF 뷰어로 자동 전환
|
||||||
|
console.log('🔄 HTML이 없는 문서 - PDF 뷰어로 자동 전환');
|
||||||
|
const pdfUrl = `/api/documents/${documentId}/pdf?_token=${this.api.getToken()}`;
|
||||||
|
|
||||||
|
// HTML 컨테이너 숨기기
|
||||||
|
const htmlContainer = document.getElementById('document-content');
|
||||||
|
if (htmlContainer) {
|
||||||
|
htmlContainer.style.display = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
// PDF iframe 표시
|
||||||
|
const iframe = document.getElementById('pdf-iframe');
|
||||||
|
if (iframe) {
|
||||||
|
iframe.src = pdfUrl;
|
||||||
|
iframe.style.display = 'block';
|
||||||
|
iframe.style.width = '100%';
|
||||||
|
iframe.style.height = '100vh';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('✅ 문서 로드 완료:', docData.title, docData.pdf_path ? '(PDF)' : '(HTML)');
|
console.log('✅ 문서 로드 완료:', docData.title, docData.pdf_path ? '(PDF)' : '(HTML)');
|
||||||
@@ -79,6 +98,29 @@ class DocumentLoader {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Document load error:', error);
|
console.error('Document load error:', error);
|
||||||
|
|
||||||
|
// HTML 로드 실패 시 PDF로 자동 전환 시도
|
||||||
|
if (docData && docData.pdf_path && !docData.html_path) {
|
||||||
|
console.log('🔄 HTML이 없는 문서 - PDF 뷰어로 자동 전환');
|
||||||
|
// PDF 뷰어로 리다이렉트
|
||||||
|
const currentUrl = new URL(window.location);
|
||||||
|
const pdfUrl = `/api/documents/${documentId}/pdf?_token=${this.api.getToken()}`;
|
||||||
|
|
||||||
|
// iframe에서 PDF 직접 로드
|
||||||
|
const iframe = document.getElementById('pdf-iframe');
|
||||||
|
if (iframe) {
|
||||||
|
iframe.src = pdfUrl;
|
||||||
|
iframe.style.display = 'block';
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTML 컨테이너 숨기기
|
||||||
|
const htmlContainer = document.getElementById('document-content');
|
||||||
|
if (htmlContainer) {
|
||||||
|
htmlContainer.style.display = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
return docData;
|
||||||
|
}
|
||||||
|
|
||||||
// 백엔드 연결 실패시 목업 데이터로 폴백
|
// 백엔드 연결 실패시 목업 데이터로 폴백
|
||||||
console.warn('Using fallback mock data');
|
console.warn('Using fallback mock data');
|
||||||
const mockDocument = {
|
const mockDocument = {
|
||||||
|
|||||||
Reference in New Issue
Block a user