From a8e24ab114c122ec50e6f3ecc06ad1ea824afd4f Mon Sep 17 00:00:00 2001 From: Hyungi Ahn Date: Fri, 24 Apr 2026 07:20:38 +0900 Subject: [PATCH] =?UTF-8?q?fix(documents):=20accept-suggestion=20=ED=95=AD?= =?UTF-8?q?=EC=83=81=20409=20=EB=B2=84=EA=B7=B8=20+=20compose=20127.0.0.1?= =?UTF-8?q?=20=EB=B0=94=EC=9D=B8=EB=94=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - accept-suggestion: documents.updated_at != expected stale 검사 제거. classify_worker 가 source_updated_at 을 pre-commit 값으로 저장하는데 SQLAlchemy onupdate 가 commit 에서 updated_at 을 bump → 항상 불일치 → 승인 영구 불가. payload 교체 검사 하나만으로 core race 는 막힘. 사용자 직접 편집 감지는 별도 user_updated_at 컬럼 도입 시 재논의. - docker-compose.yml: postgres/kordoc/fastapi/frontend 포트 127.0.0.1 바인딩. GPU 서버 로컬에만 있던 drift 를 main 으로 승격. UFW-Docker 우회 컨텍스트에서 불필요한 LAN 노출 축소. Co-Authored-By: Claude Opus 4.7 (1M context) --- app/api/documents.py | 20 +++++++++----------- docker-compose.yml | 8 ++++---- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/app/api/documents.py b/app/api/documents.py index b484c8b..eed8b6f 100644 --- a/app/api/documents.py +++ b/app/api/documents.py @@ -770,9 +770,14 @@ async def accept_suggestion( - 200 (no-op): ai_suggestion 이 이미 NULL — 이전 승인/반려 후 중복 호출로 간주 - 200 (applied): payload 적용 + ai_suggestion 을 NULL 로 clear - - 409 Conflict: 두 가지 벡터로 stale 감지 - · ai_suggestion.source_updated_at != expected → 새 classify 가 payload 덮어씀 - · documents.updated_at != expected → 사용자가 doc 을 다른 경로로 수정함 + - 409 Conflict: ai_suggestion.source_updated_at != expected + → 새 classify 결과가 payload 를 덮어쓴 race 감지 (payload 교체) + + Note: 원 plan 은 documents.updated_at != expected 로 "문서 전체 수정" 도 감지하려 + 했으나, SQLAlchemy onupdate 가 모든 워커 commit(classify/embed/...)에서 updated_at + 을 bump 하므로 이 검사는 항상 fail → accept 영구 불가. payload 교체 검사 하나만으로 + core race 는 막히고, 사용자 직접 편집 감지는 별도 user_updated_at 컬럼이 들어와야 + 의미 있다. 일단 payload key 검사만 유지. """ from sqlalchemy import select as sa_select @@ -792,7 +797,7 @@ async def accept_suggestion( expected = body.expected_source_updated_at - # Stale 검사 1: payload 교체 감지 (새 classify 결과가 덮어쓴 경우) + # Stale 검사: payload 교체 감지 (새 classify 결과가 덮어쓴 경우) raw_src = doc.ai_suggestion.get("source_updated_at") suggestion_src = None if isinstance(raw_src, str): @@ -806,13 +811,6 @@ async def accept_suggestion( detail="제안 payload 가 교체되었습니다. 목록을 새로고침하세요.", ) - # Stale 검사 2: 문서 전체 수정 감지 (사용자가 title/태그를 다른 경로로 편집) - if doc.updated_at != expected: - raise HTTPException( - status_code=409, - detail="문서가 다른 곳에서 수정되었습니다. 목록을 새로고침하세요.", - ) - # payload 적용 proposed_category = doc.ai_suggestion.get("proposed_category") proposed_path = doc.ai_suggestion.get("proposed_path") diff --git a/docker-compose.yml b/docker-compose.yml index d78b833..e18ca3b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -9,7 +9,7 @@ services: POSTGRES_USER: pkm POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} ports: - - "15432:5432" + - "127.0.0.1:15432:5432" healthcheck: test: ["CMD-SHELL", "pg_isready -U pkm"] interval: 5s @@ -20,7 +20,7 @@ services: kordoc-service: build: ./services/kordoc ports: - - "3100:3100" + - "127.0.0.1:3100:3100" volumes: - ${NAS_NFS_PATH:-/mnt/nas/Document_Server}:/documents:ro mem_limit: 4g @@ -135,7 +135,7 @@ services: fastapi: build: ./app ports: - - "8000:8000" + - "127.0.0.1:8000:8000" volumes: - ${NAS_NFS_PATH:-/mnt/nas/Document_Server}:/documents - ./config.yaml:/app/config.yaml:ro @@ -159,7 +159,7 @@ services: frontend: build: ./frontend ports: - - "3000:3000" + - "127.0.0.1:3000:3000" depends_on: - fastapi restart: unless-stopped