mirror of
https://github.com/sotam0316/brain_dogfood.git
synced 2026-04-25 03:48:38 +09:00
377 lines
12 KiB
Markdown
377 lines
12 KiB
Markdown
# 뇌사료 ↔ 옵시디언 플러그인 연동 구현 계획
|
|
|
|
> 작성일: 2026-04-16
|
|
> 작업 경로: `c:\project\my_util\memo_server`
|
|
> 서버 주소: `your-server-ip:5093`
|
|
> 원격 경로: `/home/your-username/Script/memo_server`
|
|
|
|
---
|
|
|
|
## 1. 제품 컨셉 (확정)
|
|
|
|
```
|
|
뇌사료 단독: 웹 기반 빠른 캡처 메모 + AI 태깅/요약
|
|
뇌사료 + 옵시디언: 뇌사료(INPUT/캡처) → 옵시디언(PROCESS/정리/아카이브/그래프)
|
|
```
|
|
|
|
**포지셔닝 핵심:**
|
|
- 뇌사료는 옵시디언의 "웹 프론트엔드 + AI 레이어" 역할
|
|
- 옵시디언 사용자의 고질적 불편(로컬 파일 기반 → 모바일/웹 접근 어려움)을 해결
|
|
- 뇌사료 → 옵시디언은 **단방향 동기화** (뇌사료가 Single Source of Truth)
|
|
- 옵시디언은 읽기/정리 전용 뷰로 사용
|
|
|
|
---
|
|
|
|
## 2. 현재 아키텍처 현황
|
|
|
|
### 2-1. 서버 구조 (Flask + Blueprint)
|
|
```
|
|
app/
|
|
├── __init__.py # create_app(), Blueprint 등록, 보안 미들웨어
|
|
├── constants.py # GROUP_DEFAULT="default", GROUP_FILES="files", GROUP_DONE="done"
|
|
├── database.py # SQLite, DB_PATH=data/memos.db
|
|
├── auth.py # @login_required 데코레이터 (세션 기반)
|
|
├── security.py # Fernet 암호화/복호화 (PBKDF2 + ENCRYPTION_SEED)
|
|
├── ai.py # Gemini AI 태깅/요약
|
|
└── routes/
|
|
├── __init__.py # register_blueprints(app) ← 여기에 새 Blueprint 추가
|
|
├── memo.py # /api/memos CRUD
|
|
├── file.py # /api/files 업로드/다운로드
|
|
├── ai.py # /api/ai
|
|
├── auth.py # /login /logout
|
|
├── settings.py # /api/settings
|
|
└── main.py # / 메인 페이지
|
|
```
|
|
|
|
### 2-2. DB 스키마 (SQLite: data/memos.db)
|
|
```sql
|
|
memos(
|
|
id, title, content, summary, color,
|
|
is_pinned, status, -- status: 'active' | 'done'
|
|
group_name, -- 'default' | 'files' | 'done' | 사용자 정의
|
|
is_encrypted, -- 0 | 1
|
|
created_at, updated_at
|
|
)
|
|
tags(id, memo_id, name, source) -- source: 'user' | 'ai'
|
|
attachments(id, memo_id, filename, original_name, file_type, size, created_at)
|
|
memo_links(id, source_id, target_id) -- 백링크/전방링크
|
|
```
|
|
|
|
### 2-3. 현재 인증 방식
|
|
- **세션 기반** (`session['logged_in']`) — 브라우저 전용
|
|
- API Token 인증 **없음** → Phase 1에서 추가 필요
|
|
|
|
### 2-4. 암호화 방식
|
|
```python
|
|
# app/security.py
|
|
# PBKDF2(password + ENCRYPTION_SEED) → Fernet 키 → 본문 암호화
|
|
# .env의 ENCRYPTION_SEED 필수
|
|
encrypt_content(content, password)
|
|
decrypt_content(encrypted_data, password) # 실패 시 None 반환
|
|
```
|
|
암호화 메모는 `is_encrypted=1`, 복호화는 `/api/memos/{id}/decrypt` POST로 처리.
|
|
|
|
---
|
|
|
|
## 3. 구현 계획 (3단계)
|
|
|
|
---
|
|
|
|
### Phase 1. API Token 인증 추가 (뇌사료 서버)
|
|
|
|
**목적:** 세션 없이 외부에서 API를 호출할 수 있도록 Token 인증 추가
|
|
**원칙:** 기존 `@login_required` 데코레이터를 건드리지 않고 새 데코레이터 추가
|
|
|
|
#### 1-1. `.env`에 토큰 추가
|
|
```env
|
|
# .env (기존 항목 유지, 아래 추가)
|
|
OBSIDIAN_API_TOKEN=your_secret_token_here
|
|
```
|
|
|
|
#### 1-2. `app/auth.py`에 토큰 인증 데코레이터 추가
|
|
```python
|
|
def token_required(view):
|
|
"""API Token 기반 인증 데코레이터 (Obsidian 플러그인 전용)"""
|
|
@functools.wraps(view)
|
|
def wrapped_view(**kwargs):
|
|
token = request.headers.get('X-API-Token') or request.args.get('token')
|
|
expected = os.getenv('OBSIDIAN_API_TOKEN', '')
|
|
if not expected or token != expected:
|
|
return jsonify({'error': 'Unauthorized'}), 401
|
|
return view(**kwargs)
|
|
return wrapped_view
|
|
```
|
|
|
|
#### 1-3. 새 Blueprint 파일 생성: `app/routes/sync.py`
|
|
```python
|
|
# app/routes/sync.py
|
|
# 옵시디언 동기화 전용 Blueprint
|
|
|
|
sync_bp = Blueprint('sync', __name__)
|
|
|
|
# GET /api/sync/export
|
|
# 전체 메모 목록 반환 (암호화 메모는 플레이스홀더 처리)
|
|
# 쿼리파라미터: since=2024-01-01T00:00:00 (증분 동기화용)
|
|
|
|
# POST /api/sync/decrypt/<id>
|
|
# 단일 암호화 메모 복호화 반환
|
|
# 헤더: X-Memo-Password: 비밀번호
|
|
|
|
# GET /api/sync/groups
|
|
# 그룹 목록 반환
|
|
```
|
|
|
|
#### 1-4. `app/routes/__init__.py`에 Blueprint 등록
|
|
```python
|
|
from .sync import sync_bp
|
|
app.register_blueprint(sync_bp)
|
|
```
|
|
|
|
**완료 기준:** `curl -H "X-API-Token: xxx" http://서버/api/sync/export` 가 JSON 반환
|
|
|
|
---
|
|
|
|
### Phase 2. Python 동기화 스크립트 (로컬 실행)
|
|
|
|
**목적:** 뇌사료 API를 호출해서 옵시디언 Vault에 .md 파일로 저장
|
|
**실행 방식:** Windows Task Scheduler 또는 cron으로 주기 실행 (5~10분)
|
|
**위치:** 프로젝트 루트의 `obsidian_sync/` 폴더
|
|
|
|
#### 2-1. 파일 구조
|
|
```
|
|
obsidian_sync/
|
|
├── obsidian_sync.py # 메인 동기화 스크립트
|
|
├── config.json # 설정 파일
|
|
└── last_sync.txt # 마지막 동기화 시각 저장 (증분 동기화용)
|
|
```
|
|
|
|
#### 2-2. `obsidian_sync/config.json`
|
|
```json
|
|
{
|
|
"server_url": "http://your-server-ip:5093",
|
|
"api_token": "your_secret_token_here",
|
|
"vault_path": "C:/Users/your-username/Documents/ObsidianVault/뇌사료",
|
|
"sync_interval_minutes": 10,
|
|
"encrypted_memo_handling": "placeholder",
|
|
"frontmatter": true,
|
|
"group_to_folder": {
|
|
"default": "inbox",
|
|
"files": "files",
|
|
"done": "archive"
|
|
}
|
|
}
|
|
```
|
|
|
|
#### 2-3. 변환 규칙 (뇌사료 → .md)
|
|
|
|
| 뇌사료 필드 | 옵시디언 변환 |
|
|
|---|---|
|
|
| `title` | 파일명 + H1 헤더 |
|
|
| `content` | 본문 (HTML → Markdown 변환) |
|
|
| `tags` | frontmatter `tags:` + 본문 `#태그` |
|
|
| `group_name` | 하위 폴더 분류 |
|
|
| `backlinks` | 본문 하단 `[[링크제목]]` |
|
|
| `is_encrypted=1` | `[🔒 암호화된 메모 — 뇌사료에서 확인]` |
|
|
| `created_at` | frontmatter `date:` |
|
|
| `updated_at` | frontmatter `updated:` |
|
|
| `summary` | frontmatter `summary:` |
|
|
|
|
#### 2-4. 생성될 .md 예시
|
|
```markdown
|
|
---
|
|
id: 42
|
|
date: 2026-04-15
|
|
updated: 2026-04-16
|
|
tags: [python, flask, ai]
|
|
source: 뇌사료
|
|
group: default
|
|
---
|
|
|
|
# 메모 제목
|
|
|
|
본문 내용...
|
|
|
|
---
|
|
**Tags:** #python #flask #ai
|
|
**Links:** [[관련 메모 제목]]
|
|
**Source:** [뇌사료에서 열기](http://your-server-ip:5093)
|
|
```
|
|
|
|
**완료 기준:** 스크립트 실행 후 Vault 폴더에 .md 파일 생성 확인
|
|
|
|
---
|
|
|
|
### Phase 3. TypeScript 옵시디언 플러그인 (선택적 고도화)
|
|
|
|
**목적:** Phase 2가 안정화된 후, 옵시디언 내에서 직접 UI 제공
|
|
**전제조건:** Phase 1, 2 완료 후 진행
|
|
**개발 언어:** TypeScript (옵시디언 플러그인 필수)
|
|
|
|
#### 3-1. 플러그인 저장소 구조
|
|
```
|
|
obsidian-brainsryo-plugin/ # 별도 Git 저장소 권장
|
|
├── src/
|
|
│ ├── main.ts # 플러그인 진입점
|
|
│ ├── api.ts # 뇌사료 API 클라이언트
|
|
│ ├── converter.ts # 뇌사료 JSON → Obsidian .md 변환
|
|
│ ├── settings.ts # 플러그인 설정 UI
|
|
│ └── modal.ts # 암호화 메모 비밀번호 입력 모달
|
|
├── manifest.json
|
|
├── package.json
|
|
└── tsconfig.json
|
|
```
|
|
|
|
#### 3-2. 플러그인 설정 항목 (UI)
|
|
```
|
|
뇌사료 서버 URL: [http://your-server-ip:5093]
|
|
API Token: [***************]
|
|
저장 폴더: [뇌사료/]
|
|
동기화 주기: [10분]
|
|
암호화 메모 처리: [플레이스홀더 ▼] ← 또는 "비밀번호 입력"
|
|
자동 동기화: [ON/OFF]
|
|
```
|
|
|
|
#### 3-3. 암호화 메모 처리 흐름 (플러그인 버전)
|
|
```
|
|
옵시디언에서 암호화 메모 클릭
|
|
→ 비밀번호 입력 모달 표시 (Obsidian Modal API)
|
|
→ POST /api/sync/decrypt/{id} (헤더: X-Memo-Password)
|
|
→ 복호화 성공 시 임시 .md 생성 후 표시
|
|
→ 닫으면 임시 파일 삭제 (디스크에 평문 저장 안 함)
|
|
```
|
|
|
|
#### 3-4. 개발 우선순위
|
|
1. 단방향 동기화 (뇌사료 → 옵시디언) 기본 버전
|
|
2. 설정 UI
|
|
3. 증분 동기화 (마지막 동기화 이후 변경분만)
|
|
4. 암호화 메모 지원 (비밀번호 모달)
|
|
5. (미래) 옵시디언 → 뇌사료 import 기능
|
|
|
|
---
|
|
|
|
## 4. 구현 순서 체크리스트
|
|
|
|
### Phase 1 — API Token (뇌사료 서버 수정)
|
|
- [ ] `.env`에 `OBSIDIAN_API_TOKEN` 추가
|
|
- [ ] `.env.example`에도 항목 추가 (값 없이)
|
|
- [ ] `app/auth.py`에 `token_required` 데코레이터 추가
|
|
- [ ] `app/routes/sync.py` 파일 생성
|
|
- [ ] `GET /api/sync/export` 엔드포인트 (`since` 파라미터 지원)
|
|
- [ ] `POST /api/sync/decrypt/<id>` 엔드포인트
|
|
- [ ] `GET /api/sync/groups` 엔드포인트
|
|
- [ ] `app/routes/__init__.py`에 `sync_bp` 등록
|
|
- [ ] 서버 배포 (`python deploy.py` — 사용자 승인 필수)
|
|
|
|
### Phase 2 — Python 동기화 스크립트
|
|
- [ ] `obsidian_sync/` 폴더 생성
|
|
- [ ] `obsidian_sync/config.json` 작성
|
|
- [ ] `obsidian_sync/obsidian_sync.py` 작성
|
|
- [ ] API 호출 (Token 인증)
|
|
- [ ] HTML → Markdown 변환 (`markdownify` 라이브러리)
|
|
- [ ] frontmatter 생성
|
|
- [ ] 태그/백링크 변환
|
|
- [ ] 그룹 → 폴더 분류
|
|
- [ ] 증분 동기화 (`last_sync.txt`)
|
|
- [ ] 암호화 메모 플레이스홀더 처리
|
|
- [ ] 스크립트 테스트 (실제 Vault에 파일 생성 확인)
|
|
- [ ] Windows Task Scheduler 등록
|
|
|
|
### Phase 3 — TypeScript 옵시디언 플러그인 (나중에)
|
|
- [ ] Node.js 개발 환경 세팅
|
|
- [ ] obsidian-sample-plugin 템플릿 클론
|
|
- [ ] API 클라이언트 구현
|
|
- [ ] 설정 UI 구현
|
|
- [ ] 동기화 로직 구현
|
|
- [ ] 암호화 메모 모달 구현
|
|
- [ ] 로컬 설치 테스트 (`.obsidian/plugins/` 복사)
|
|
|
|
---
|
|
|
|
## 5. API 스펙 (Phase 1에서 구현할 엔드포인트)
|
|
|
|
### `GET /api/sync/export`
|
|
```
|
|
Headers: X-API-Token: {OBSIDIAN_API_TOKEN}
|
|
Params:
|
|
- since (optional): ISO 8601 datetime, 이 시각 이후 수정된 메모만 반환
|
|
- limit (optional): 기본 1000
|
|
Response:
|
|
[
|
|
{
|
|
"id": 42,
|
|
"title": "메모 제목",
|
|
"content": "<p>HTML 본문</p>",
|
|
"summary": "AI 요약",
|
|
"tags": [{"name": "python", "source": "ai"}, ...],
|
|
"group_name": "default",
|
|
"is_encrypted": false,
|
|
"is_pinned": false,
|
|
"backlinks": [{"source_id": 10, "title": "다른 메모"}],
|
|
"links": [{"target_id": 20, "title": "링크된 메모"}],
|
|
"created_at": "2026-04-15T10:00:00",
|
|
"updated_at": "2026-04-16T08:00:00"
|
|
},
|
|
// is_encrypted=true인 경우:
|
|
{
|
|
"id": 99,
|
|
"title": "암호화된 메모",
|
|
"content": null,
|
|
"is_encrypted": true,
|
|
...
|
|
}
|
|
]
|
|
```
|
|
|
|
### `POST /api/sync/decrypt/<id>`
|
|
```
|
|
Headers:
|
|
X-API-Token: {OBSIDIAN_API_TOKEN}
|
|
X-Memo-Password: {메모_비밀번호}
|
|
Response (성공): {"content": "복호화된 본문"}
|
|
Response (실패): {"error": "Invalid password"}, 403
|
|
```
|
|
|
|
### `GET /api/sync/groups`
|
|
```
|
|
Headers: X-API-Token: {OBSIDIAN_API_TOKEN}
|
|
Response: {"groups": ["default", "files", "done", "custom_group1", ...]}
|
|
```
|
|
|
|
---
|
|
|
|
## 6. 주의사항 및 설계 원칙
|
|
|
|
> **단방향 원칙:** 뇌사료 → 옵시디언 방향만 동기화.
|
|
> 옵시디언에서 .md를 수정해도 뇌사료 DB에는 반영되지 않음.
|
|
> 양방향 동기화는 충돌 처리 복잡도가 높아 Phase 3 이후 별도 검토.
|
|
|
|
> **암호화 메모 보안:**
|
|
> 복호화된 본문은 절대 디스크에 저장하지 않음.
|
|
> Phase 2 스크립트는 기본적으로 `encrypted_memo_handling: "placeholder"` 유지.
|
|
|
|
> **파일명 규칙:** 옵시디언 파일명 특수문자 금지 (`/ \ : * ? " < > |`)
|
|
> 뇌사료 제목의 해당 문자는 `_`로 치환. 중복 시 `제목_id42.md` 형식.
|
|
|
|
---
|
|
|
|
## 7. 작업 이어받기 안내 (다른 AI 인스턴스용)
|
|
|
|
1. **먼저 읽을 파일들:**
|
|
- `app/auth.py` — 현재 인증 구조 확인
|
|
- `app/routes/__init__.py` — Blueprint 등록 방식 확인
|
|
- `app/routes/memo.py` — 기존 API 패턴 참고
|
|
- `app/security.py` — 암호화/복호화 함수 확인
|
|
- `.env` — `OBSIDIAN_API_TOKEN` 추가 여부 확인
|
|
|
|
2. **시작점:** Phase 1 체크리스트 항목 순서대로 진행
|
|
|
|
3. **배포 방법:** 수정 완료 후 `python deploy.py` 실행
|
|
(반드시 사용자 승인 후 실행 — 사용자 규칙)
|
|
|
|
4. **테스트:**
|
|
```bash
|
|
curl -H "X-API-Token: {토큰}" http://your-server-ip:5093/api/sync/export
|
|
```
|
|
|
|
5. **추가 의존성:** `pip install markdownify` (Phase 2 스크립트에서 필요)
|