Files
Hyungi Ahn 0ca78640ee infra: migrate application from Mac mini to GPU server
- Integrate ollama + ai-gateway into root docker-compose.yml
  (NVIDIA GPU runtime, single compose for all services)
- Change NAS mount from SMB (NAS_SMB_PATH) to NFS (NAS_NFS_PATH)
  Default: /mnt/nas/Document_Server (fstab registered on GPU server)
- Update config.yaml AI endpoints:
  primary → Mac mini MLX via Tailscale (100.76.254.116:8800)
  fallback/embedding/vision/rerank → ollama (same Docker network)
  gateway → ai-gateway (same Docker network)
- Update credentials.env.example (remove GPU_SERVER_IP, add NFS path)
- Mark gpu-server/docker-compose.yml as deprecated
- Update CLAUDE.md network diagram and AI model config
- Update architecture.md, deploy.md, devlog.md for GPU server as main
- Caddyfile: auto_https off, HTTP only (TLS at upstream proxy)
- Caddy port: 127.0.0.1:8080:80 (localhost only)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 07:47:09 +09:00

9.5 KiB

개발 로그

2026-04-02 — v2 전환 설계 완료

결정 사항

  • DEVONthink 탈피 결정. v1의 구조적 한계(AppleScript 취약성, macOS GUI 의존, 13개 DB 복잡성)를 더 이상 감수하지 않기로 함
  • 자체 웹앱 방향 확정. 기술 스택: FastAPI + PostgreSQL/pgvector + SvelteKit + Docker
  • OmniFocus 탈피 → Synology Calendar (CalDAV VTODO)로 대체
  • Synology 서비스 활용 극대화: Office(문서 편집/미리보기), Drive(파일 관리), Calendar(태스크), MailPlus(이메일+알림)
  • Document_Server 전체를 Synology Drive가 관리. PKM 하위 폴더로 자동분류 영역 분리
  • 문서 "원본" 정의 확정: immutable(PDF, 수신 HWP 등) / editable(Synology Office 포맷) / note(Markdown)
  • .docx/.xlsx는 교환 형식으로 취급. 서버에 영구 보관하지 않음
  • 데이터 3계층: 원본(NAS) → 가공(PostgreSQL) → 파생(pgvector+캐시)
  • kordoc 통합 결정 (HWP/HWPX/PDF → Markdown 파싱, Node.js 마이크로서비스)
  • AI 전략: Qwen3.5-35B-A3B(MLX) 우선, Claude API는 종량제로 최후 수단. GPU 서버에 AI Gateway 배치
  • Anthropic 약관 확인: 구독 OAuth의 서드파티 사용은 약관상 금지(2026.01~). 자동화에는 API 키만 사용
  • NanoClaw는 선택적 확장(대화형 인터페이스)으로 위치, 핵심 파이프라인 비의존
  • 장기 로드맵: GPU 서버 확장 → 메인 서버 승격, Mac mini → Roon Core 전용, Synology 장기 유지

산출물

  • docs/architecture-v2.md — 17개 섹션 + 부록 2개 (전체 시스템 설계)
  • 마이그레이션 계획서 — Step 15 (프로젝트 리네임+정리) + Phase 05 (v2 개발)
  • 프로젝트 리네임: DEVONThink_my server → hyungi_Document_Server

배경 논의 (Cowork 세션)

  • v1에서 16개 커밋 중 절반 이상이 AppleScript 버그 수정이었던 점이 전환의 직접적 계기
  • Synology Office iframe 임베드로 DEVONthink 미리보기 대체 가능성 논의
  • HWP 대응으로 kordoc(광진구청 류승인 주무관 제작, MIT 라이선스) 조사 및 채택
  • 편집 가능 문서의 "원본이 뭐냐" 논의 → Synology Office 포맷이 원본, .docx/.xlsx는 교환용
  • 가공 데이터 보관 전략 논의 → 파일로 저장하지 않고 PostgreSQL에만 저장, 버전 추적으로 선택적 재가공

2026-04-02 — 프로젝트 리네임 + v2 전환 실행

Step 1: 사전 정리

  • architecture-v2.md 커밋 (852b7da)
  • v1-archive 브랜치 + v1-final 태그 생성 (v1 상태 완벽 보존)

Step 2: v1 파일 정리

  • v1 전용 파일 git rm 완료 (e48b6a2)
  • 삭제: applescript/, launchd/, v1 scripts, v1 docs, tests/test_classify.py, requirements.txt
  • 유지: scripts/prompts/classify_document.txt, credentials.env.example (v2 필드로 갱신)

Step 3: Gitea 리포 리네임 + 로컬 폴더 리네임

  • Gitea: devonthink_home → hyungi_document_server
  • 로컬 폴더: DEVONThink_my server → hyungi_Document_Server
  • git remote set-url + git ls-remote 검증 + push 완료

Step 4: 문서 전면 재작성

  • CLAUDE.md — v2 기준으로 전면 재작성
  • README.md — 프로젝트명, 기술 스택, 디렉토리 구조 갱신
  • docs/deploy.md — Docker Compose 기반 배포 가이드로 교체
  • docs/claude-code-commands.md → docs/development-stages.md 변환
  • docs/architecture-v2.md → docs/architecture.md 승격

Step 5: v2 프로젝트 스캐폴딩

  • 전체 디렉토리 구조 생성 (app/, services/kordoc/, gpu-server/, frontend/, migrations/, tests/)
  • 동작하는 최소 코드 수준: FastAPI main.py, PostgreSQL 스키마, kordoc server.js, config.yaml 등
  • docker-compose.yml, Caddyfile, credentials.env.example 생성
  • tests/init.py + conftest.py 포함

Step 1~5 전체 완료.


2026-04-02 — Phase 0: 기반 구축 시작

users 테이블 + ORM 모델 추가

  • migrations/001_initial_schema.sql에 users 테이블 포함 (username, password_hash, totp_secret, is_active)
  • app/models/user.py — SQLAlchemy 2.0 Mapped 스타일 ORM 모델
  • architecture.md 섹션 6 스키마와 일치

Auth API 엔드포인트 구현

  • app/api/auth.py — 4개 엔드포인트: POST /login (JWT발급+TOTP), POST /refresh, GET /me, POST /change-password
  • app/core/auth.py — bcrypt 비밀번호 해싱, JWT 발급/검증, TOTP 검증, get_current_user 의존성
  • Pydantic 스키마: LoginRequest, TokenResponse, RefreshRequest, ChangePasswordRequest, UserResponse

main.py 라우터 등록 + health 강화

  • auth 라우터 등록: /api/auth prefix
  • health 엔드포인트에 DB 연결 상태 포함 (connected/disconnected)
  • lifespan 핸들러로 DB 초기화/정리

Docker 설정 수정

초기 admin 유저 시드 스크립트

셋업 위자드 구현 (a601991)

  • app/api/setup.py — 6개 엔드포인트: GET /status, POST /admin, POST /totp/init, POST /totp/verify, POST /verify-nas, GET / (HTML)
  • app/templates/setup.html — Jinja2 단일 HTML, Vanilla JS + qrcode.js CDN, 3단계 위자드
  • app/main.py — setup 라우터 등록 + 셋업 미들웨어 (유저 0명 시 /setup 리다이렉트, /health /docs 등 바이패스)
  • Rate Limiting: IP당 5분 내 5회 실패 시 차단
  • TOTP 흐름: init에서 secret 반환(DB 미저장) → verify에서 코드 검증 후 DB 저장
  • scripts/seed_admin.py CLI 백업 수단 유지
  • requirements.txt에 jinja2 추가

Phase 0 완료 기준 달성 상태

  • docker compose up → FastAPI 구동
  • DB 스키마 (users, documents, tasks, processing_queue)
  • JWT + TOTP 인증 (로그인, 토큰 갱신, 비밀번호 변경)
  • 셋업 위자드 (관리자 생성 + TOTP + NAS 확인)
  • /health — DB 연결 상태 포함
  • /docs — OpenAPI 문서
  • NAS SMB 마운트 실제 검증 (Mac mini 배포 시)
  • config.yaml 로딩 검증 (Mac mini 배포 시)

2026-04-02 — Phase 1: 데이터 마이그레이션 파이프라인 구현 완료

Step 1: kordoc /parse 실제 구현

  • services/kordoc/server.js — stub → 실제 파싱 구현 (kordoc ^1.7.0 + pdfjs-dist ^4.0.0)
  • HWP/HWPX/PDF → Markdown 변환, .md/.txt 직접 읽기, 이미지는 requires_ocr 플래그 반환
  • 타임아웃 30초, 파일 미존재 404, 파싱 실패 422

Step 2: 큐 소비자 인프라

  • app/workers/queue_consumer.py — APScheduler AsyncIOScheduler 1분 간격 실행
  • 배치 처리: extract=5, classify=3, embed=1
  • stage 체이닝: extract → classify → embed 자동 enqueue
  • stale 항목 자동 복구 (processing 상태 10분 초과)
  • app/main.py — lifespan에 APScheduler 연결, yield 후 shutdown 보장

Step 3: 텍스트 추출 워커

  • app/workers/extract_worker.py — 포맷별 분기 처리
  • KORDOC_FORMATS (hwp, hwpx, pdf) → kordoc HTTP POST
  • TEXT_FORMATS (md, txt, csv, json, xml, html) → 직접 파일 읽기
  • IMAGE_FORMATS → Phase 2 OCR로 연기

Step 4: AI 분류 워커

  • app/workers/classify_worker.py — extracted_text 8000자 → AIClient.classify() 호출
  • app/ai/client.py — strip_thinking(), parse_json_response() 추가 (v1 pkm_utils.py에서 포팅)
  • Qwen3.5의 태그 제거 + 비정형 JSON 파싱 로직

Step 5: 벡터 임베딩 워커

  • app/workers/embed_worker.py — nomic-embed-text-v1.5 (GPU 서버), 6000자 제한
  • GPU 서버 불가 시 graceful fail → 재시도

Step 6: DEVONthink 마이그레이션 스크립트

  • scripts/migrate_from_devonthink.py — --dry-run, --source-dir, --target-dir, --database-url 지원
  • DEVONthink 내보내기 → NAS PKM 구조 복사 + documents/processing_queue DB 등록

Phase 1 완료 기준 달성 상태

  • kordoc 파싱 (HWP/HWPX/PDF → Markdown)
  • 큐 소비자 + APScheduler 연동
  • extract → classify → embed 워커 3개
  • AI 클라이언트 think 태그 / JSON 파싱 보강
  • 마이그레이션 스크립트
  • Step 7: 통합 테스트 + 배치 실행 (Mac mini 배포 후)

2026-04-02 — Phase 2: 핵심 기능 구현 완료 (4b69533)

문서 CRUD API

  • app/api/documents.py — 5개 엔드포인트
  • POST /api/documents/ — 파일 업로드 (Inbox 저장 + extract 큐 등록)
  • GET /api/documents/ — 목록 조회 (페이징 + domain/source/format 필터)
  • GET /api/documents/{id} — 단건 조회
  • PATCH /api/documents/{id} — 메타데이터 수동 수정
  • DELETE /api/documents/{id} — DB 삭제 (기본), ?delete_file=true로 파일도 삭제

하이브리드 검색 API

  • app/api/search.py — GET /api/search/?q={query}&mode={mode}
  • 4가지 모드: fts, trgm, vector, hybrid (기본)
  • hybrid 가중치: FTS 0.4 + Trigram 0.2 + Vector 0.4
  • 벡터 불가 시 FTS 0.6 + Trigram 0.4 폴백
  • 결과에 snippet(200자) 포함

파일 워처

  • app/workers/file_watcher.py — Inbox 디렉토리 5분 간격 스캔
  • 신규 파일: Document 생성 + extract 큐 등록
  • 변경 파일: 해시 비교 → 재추출 큐 등록
  • .DS_Store, .tmp, .part 등 무시 파일 처리

벡터 인덱스 마이그레이션

  • migrations/002_vector_index.sql — IVFFlat 인덱스 (cosine, lists=50)
  • 문서 수 증가 시 lists 값 조정 필요

Phase 2 완료 기준 달성 상태

  • 문서 CRUD API (업로드, 목록, 조회, 수정, 삭제)
  • 하이브리드 검색 (FTS + Trigram + Vector)
  • Inbox 파일 워처 (신규/변경 자동 감지 → 파이프라인 등록)
  • 처리 파이프라인 전체 동작 (upload/watch → extract → classify → embed → search)
  • 문서 뷰어 UI (Phase 4로 이관)
  • SvelteKit 프론트엔드 (Phase 4로 이관)