From c7f55ac50df34ddc9e248e47853f08612e1ec184 Mon Sep 17 00:00:00 2001 From: Hyungi Ahn Date: Sat, 23 Aug 2025 15:00:01 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=ED=94=8C=EB=A1=9C=ED=8C=85=20=EB=A9=94?= =?UTF-8?q?=EB=AA=A8=EC=B0=BD=20=EA=B5=AC=ED=98=84=20=EB=B0=8F=20=EB=AC=B8?= =?UTF-8?q?=EC=84=9C=20=EC=82=AD=EC=A0=9C=20=EA=B8=B0=EB=8A=A5=20=EC=95=88?= =?UTF-8?q?=EC=A0=95=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ✨ 새로운 기능: - 플로팅 메모창: 드래그 가능한 독립적인 메모/하이라이트 창 - 크기 조절: 마우스로 창 크기 자유롭게 조정 - 위치 이동: 헤더 드래그로 원하는 위치에 배치 - 메모 검색: 실시간 메모 내용 검색 및 필터링 - 하이라이트 이동: 메모창에서 문서 내 하이라이트 위치로 스크롤 🛠️ 개선사항: - 문서 삭제 안정화: 외래키 제약조건 해결 (Note->Highlight->Document 순서) - 레이아웃 개선: 문서 뷰어가 전체 화면 사용 가능 - 사용자 경험: 사이드바 대신 플로팅 윈도우로 더 유연한 UI - 캐시 버스팅: JavaScript 파일 버전 관리 개선 🐛 버그 수정: - 중복 스크립트 로드 문제 해결 - 하이라이트 메모 업데이트 API 오류 수정 - 문서 삭제 시 500 에러 해결 --- backend/src/api/routes/documents.py | 60 +++++++--- backend/src/api/routes/highlights.py | 27 ++++- frontend/hierarchy.html | 162 ++++++++++++++++++++++++++- frontend/index.html | 11 +- frontend/static/js/hierarchy.js | 128 +++++++++++++++++++++ frontend/viewer.html | 4 +- 6 files changed, 362 insertions(+), 30 deletions(-) diff --git a/backend/src/api/routes/documents.py b/backend/src/api/routes/documents.py index 5ac9386..f193a5e 100644 --- a/backend/src/api/routes/documents.py +++ b/backend/src/api/routes/documents.py @@ -506,25 +506,55 @@ async def delete_document( if document.thumbnail_path and os.path.exists(document.thumbnail_path): os.remove(document.thumbnail_path) - # 관련 데이터 먼저 삭제 (외래키 제약 조건 해결) + # 관련 데이터 안전하게 삭제 (외래키 제약 조건 해결) from ...models.highlight import Highlight from ...models.note import Note from ...models.bookmark import Bookmark - # 메모 먼저 삭제 (하이라이트를 참조하므로) - await db.execute(delete(Note).where(Note.document_id == document_id)) - - # 북마크 삭제 - await db.execute(delete(Bookmark).where(Bookmark.document_id == document_id)) - - # 마지막으로 하이라이트 삭제 - await db.execute(delete(Highlight).where(Highlight.document_id == document_id)) - - # 문서-태그 관계 삭제 (Document.tags 관계를 통해 자동 처리됨) - - # 마지막으로 문서 삭제 - await db.execute(delete(Document).where(Document.id == document_id)) - await db.commit() + try: + print(f"DEBUG: Starting deletion of document {document_id}") + + # 1. 먼저 해당 문서의 모든 하이라이트 ID 조회 + highlight_ids_result = await db.execute(select(Highlight.id).where(Highlight.document_id == document_id)) + highlight_ids = [row[0] for row in highlight_ids_result.fetchall()] + print(f"DEBUG: Found {len(highlight_ids)} highlights to delete") + + # 2. 하이라이트에 연결된 모든 메모 삭제 + total_notes_deleted = 0 + for highlight_id in highlight_ids: + note_result = await db.execute(delete(Note).where(Note.highlight_id == highlight_id)) + total_notes_deleted += note_result.rowcount + print(f"DEBUG: Deleted {total_notes_deleted} notes by highlight_id") + + # 3. document_id로 직접 연결된 메모도 삭제 (혹시 있다면) + direct_note_result = await db.execute(delete(Note).where(Note.document_id == document_id)) + print(f"DEBUG: Deleted {direct_note_result.rowcount} notes by document_id") + + # 4. 북마크 삭제 + bookmark_result = await db.execute(delete(Bookmark).where(Bookmark.document_id == document_id)) + print(f"DEBUG: Deleted {bookmark_result.rowcount} bookmarks") + + # 5. 하이라이트 삭제 (이제 메모가 모두 삭제되었으므로 안전) + highlight_result = await db.execute(delete(Highlight).where(Highlight.document_id == document_id)) + print(f"DEBUG: Deleted {highlight_result.rowcount} highlights") + + # 6. 문서-태그 관계는 SQLAlchemy가 자동으로 처리 + + # 7. 마지막으로 문서 삭제 + doc_result = await db.execute(delete(Document).where(Document.id == document_id)) + print(f"DEBUG: Deleted {doc_result.rowcount} documents") + + # 8. 커밋 + await db.commit() + print(f"DEBUG: Successfully deleted document {document_id}") + + except Exception as e: + print(f"ERROR: Failed to delete document {document_id}: {e}") + await db.rollback() + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=f"Failed to delete document: {str(e)}" + ) return {"message": "Document deleted successfully"} diff --git a/backend/src/api/routes/highlights.py b/backend/src/api/routes/highlights.py index 68d4ca0..4386983 100644 --- a/backend/src/api/routes/highlights.py +++ b/backend/src/api/routes/highlights.py @@ -381,9 +381,30 @@ async def delete_highlight( detail="Not enough permissions" ) - # 하이라이트 삭제 (CASCADE로 메모도 함께 삭제됨) - await db.execute(delete(Highlight).where(Highlight.id == highlight_id)) - await db.commit() + # 안전한 하이라이트 삭제 (연결된 메모 먼저 삭제) + try: + print(f"DEBUG: Starting deletion of highlight {highlight_id}") + + # 1. 먼저 연결된 메모 삭제 + from ...models.note import Note + note_result = await db.execute(delete(Note).where(Note.highlight_id == highlight_id)) + print(f"DEBUG: Deleted {note_result.rowcount} notes for highlight {highlight_id}") + + # 2. 하이라이트 삭제 + highlight_result = await db.execute(delete(Highlight).where(Highlight.id == highlight_id)) + print(f"DEBUG: Deleted {highlight_result.rowcount} highlights") + + # 3. 커밋 + await db.commit() + print(f"DEBUG: Successfully deleted highlight {highlight_id}") + + except Exception as e: + print(f"ERROR: Failed to delete highlight {highlight_id}: {e}") + await db.rollback() + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=f"Failed to delete highlight: {str(e)}" + ) return {"message": "Highlight deleted successfully"} diff --git a/frontend/hierarchy.html b/frontend/hierarchy.html index af7c5b4..4b738c7 100644 --- a/frontend/hierarchy.html +++ b/frontend/hierarchy.html @@ -144,7 +144,7 @@
- +