docs: README 전면 업데이트 — music/blog/realestate/agent-office 추가
- 누락된 4개 서비스 (music-lab, blog-lab, realestate-lab, agent-office) 섹션 추가 - agent-office FSM + WebSocket + 텔레그램 양방향 구조 설명 - LLM provider 추상화 (claude/ollama 전환) 반영 - 환경변수 섹션에 Anthropic/Suno/Naver/Telegram/공공데이터 키 추가 - Windows AI 서버 스펙 정정 (RTX 3070 Ti → RTX 5070 Ti 16GB) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
355
README.md
355
README.md
@@ -1,31 +1,40 @@
|
||||
# web-backend
|
||||
|
||||
Synology NAS 기반 개인 웹 플랫폼 백엔드 모노레포.
|
||||
로또 분석, 주식 포트폴리오, 여행 앨범, 블로그, 투두리스트를 하나의 서비스로 운영한다.
|
||||
로또 분석, 주식 포트폴리오, AI 음악 생성, 블로그 마케팅, 부동산 청약, AI 에이전트 오피스, 여행 앨범을 하나의 Docker Compose 스택으로 운영한다.
|
||||
|
||||
---
|
||||
|
||||
## 서비스 구성
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ lotto-frontend (Nginx:8080) │
|
||||
│ ├── 정적 SPA 서빙 (React + Vite) │
|
||||
│ └── API 리버스 프록시 │
|
||||
│ ├── /api/ → lotto-backend:8000 │
|
||||
│ ├── /api/stock/ → stock-lab:8000 │
|
||||
│ ├── /api/trade/ → stock-lab:8000 │
|
||||
│ ├── /api/portfolio → stock-lab:8000 │
|
||||
│ ├── /api/travel/ → travel-proxy:8000 │
|
||||
│ └── /webhook → deployer:9000 │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
┌──────────────────────────────────────────────────────────────────────┐
|
||||
│ lotto-frontend (Nginx:8080) │
|
||||
│ ├── 정적 SPA 서빙 (React + Vite) │
|
||||
│ └── API 리버스 프록시 │
|
||||
│ ├── /api/ → lotto-backend:8000 (로또·블로그·투두)│
|
||||
│ ├── /api/stock/, /trade/ → stock-lab:8000 │
|
||||
│ ├── /api/portfolio → stock-lab:8000 │
|
||||
│ ├── /api/music/ → music-lab:8000 │
|
||||
│ ├── /api/blog-marketing/ → blog-lab:8000 │
|
||||
│ ├── /api/realestate/ → realestate-lab:8000 │
|
||||
│ ├── /api/agent-office/ → agent-office:8000 (+ WebSocket) │
|
||||
│ ├── /api/travel/ → travel-proxy:8000 │
|
||||
│ ├── /media/music/… (nginx 직접 서빙, 생성 오디오) │
|
||||
│ ├── /media/travel/… (nginx 직접 서빙, 사진/썸네일) │
|
||||
│ └── /webhook → deployer:9000 │
|
||||
└──────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
| 컨테이너 | 포트 | 역할 |
|
||||
|---------|------|------|
|
||||
| `lotto-backend` | 18000 | 로또·블로그·투두 API |
|
||||
| `stock-lab` | 18500 | 주식 뉴스·포트폴리오·자산 추적 |
|
||||
| `travel-proxy` | 19000 | 여행 사진 API + 썸네일 생성 |
|
||||
| `lotto-backend` | 18000 | 로또 데이터 수집·분석·추천 + 블로그·투두 API |
|
||||
| `stock-lab` | 18500 | 주식 뉴스·AI 요약·KIS 실계좌·포트폴리오·자산 추적 |
|
||||
| `music-lab` | 18600 | AI 음악 생성 (Suno + 로컬 MusicGen 듀얼 프로바이더) |
|
||||
| `blog-lab` | 18700 | 블로그 마케팅 수익화 (키워드→글 생성→리뷰→발행) |
|
||||
| `realestate-lab` | 18800 | 청약 공고 자동 수집·프로필 매칭 |
|
||||
| `agent-office` | 18900 | AI 에이전트 가상 오피스 (WebSocket + 텔레그램 봇) |
|
||||
| `travel-proxy` | 19000 | 여행 사진 API + 온디맨드 썸네일 |
|
||||
| `lotto-frontend` | 8080 | SPA 서빙 + 리버스 프록시 |
|
||||
| `webpage-deployer` | 19010 | Gitea Webhook → 자동 배포 |
|
||||
|
||||
@@ -35,47 +44,19 @@ Synology NAS 기반 개인 웹 플랫폼 백엔드 모노레포.
|
||||
|
||||
```
|
||||
web-backend/
|
||||
├── backend/ # lotto-backend 서비스 (Python/FastAPI)
|
||||
│ ├── app/
|
||||
│ │ ├── main.py # 라우터, 스케줄러
|
||||
│ │ ├── db.py # SQLite CRUD (7개 테이블)
|
||||
│ │ ├── generator.py # 몬테카를로 시뮬레이션 엔진
|
||||
│ │ ├── analyzer.py # 5가지 통계 분석
|
||||
│ │ ├── checker.py # 당첨 결과 채점
|
||||
│ │ ├── collector.py # 로또 데이터 수집
|
||||
│ │ ├── recommender.py # 추천 알고리즘
|
||||
│ │ └── utils.py # 메트릭 계산
|
||||
│ └── Dockerfile
|
||||
│
|
||||
├── stock-lab/ # stock-lab 서비스 (Python/FastAPI)
|
||||
│ ├── app/
|
||||
│ │ ├── main.py # 라우터, 스케줄러
|
||||
│ │ ├── db.py # SQLite CRUD (4개 테이블)
|
||||
│ │ ├── scraper.py # 네이버 금융 뉴스 크롤링
|
||||
│ │ ├── price_fetcher.py # 현재가 조회 (3분 캐시)
|
||||
│ │ └── holidays.json # 한국 주식시장 휴장일
|
||||
│ └── Dockerfile
|
||||
│
|
||||
├── travel-proxy/ # travel-proxy 서비스 (Python/FastAPI)
|
||||
│ ├── app/
|
||||
│ │ └── main.py # 사진 API, 썸네일 생성 (Pillow)
|
||||
│ └── Dockerfile
|
||||
│
|
||||
├── deployer/ # Gitea Webhook 수신 → 자동 배포
|
||||
│ ├── app.py # HMAC SHA256 검증 + 배포 트리거
|
||||
│ └── Dockerfile
|
||||
│
|
||||
├── nginx/
|
||||
│ └── default.conf # 리버스 프록시 + SPA + 캐시
|
||||
│
|
||||
├── scripts/
|
||||
│ ├── deploy.sh # 운영 배포 (git pull → rsync → compose up)
|
||||
│ ├── deploy-nas.sh # rsync 전용 스크립트
|
||||
│ └── healthcheck.sh # 전체 서비스 헬스 체크
|
||||
│
|
||||
├── backend/ # lotto-backend (로또·블로그·투두)
|
||||
├── stock-lab/ # 주식·포트폴리오
|
||||
├── music-lab/ # AI 음악 생성
|
||||
├── blog-lab/ # 블로그 마케팅 파이프라인
|
||||
├── realestate-lab/ # 청약 자동 수집·매칭
|
||||
├── agent-office/ # AI 에이전트 오피스 (WS + 텔레그램)
|
||||
├── travel-proxy/ # 여행 사진 + 썸네일
|
||||
├── deployer/ # Gitea Webhook 수신 → 자동 배포
|
||||
├── nginx/default.conf # 리버스 프록시 + SPA + 캐시
|
||||
├── scripts/ # deploy.sh, deploy-nas.sh, healthcheck.sh
|
||||
├── docker-compose.yml
|
||||
├── .env.example
|
||||
└── CLAUDE.md
|
||||
└── CLAUDE.md # Claude Code 작업용 상세 컨텍스트
|
||||
```
|
||||
|
||||
---
|
||||
@@ -83,13 +64,9 @@ web-backend/
|
||||
## 빠른 시작 (로컬 개발)
|
||||
|
||||
```bash
|
||||
# 1. 환경변수 설정
|
||||
cp .env.example .env
|
||||
|
||||
# 2. 컨테이너 실행 (.env 기본값으로 즉시 실행 가능)
|
||||
docker compose up -d
|
||||
|
||||
# 3. 확인
|
||||
curl http://localhost:18000/health
|
||||
curl http://localhost:18500/health
|
||||
```
|
||||
@@ -99,106 +76,111 @@ curl http://localhost:18500/health
|
||||
| Frontend + API | http://localhost:8080 |
|
||||
| lotto-backend | http://localhost:18000 |
|
||||
| stock-lab | http://localhost:18500 |
|
||||
| music-lab | http://localhost:18600 |
|
||||
| blog-lab | http://localhost:18700 |
|
||||
| realestate-lab | http://localhost:18800 |
|
||||
| agent-office | http://localhost:18900 |
|
||||
| travel-proxy | http://localhost:19000 |
|
||||
|
||||
---
|
||||
|
||||
## API 목록
|
||||
## 서비스별 기능
|
||||
|
||||
### lotto-backend (`/api/`)
|
||||
### 1. lotto-backend (`/api/`)
|
||||
|
||||
#### 로또
|
||||
로또 당첨번호 수집·통계 분석·몬테카를로 시뮬레이션 기반 추천 + 투두·블로그 CRUD.
|
||||
|
||||
| 메서드 | 경로 | 설명 |
|
||||
|--------|------|------|
|
||||
| GET | `/api/lotto/latest` | 최신 당첨번호 |
|
||||
| GET | `/api/lotto/{drw_no}` | 특정 회차 |
|
||||
| GET | `/api/lotto/stats` | 번호 빈도 통계 |
|
||||
| GET | `/api/lotto/analysis` | 5가지 통계 분석 리포트 |
|
||||
| GET | `/api/lotto/best` | 시뮬레이션 최적 번호 (기본 20쌍) |
|
||||
| GET | `/api/lotto/simulation` | 시뮬레이션 상세 결과 |
|
||||
| GET | `/api/lotto/recommend` | 통계 기반 추천 |
|
||||
| GET | `/api/lotto/recommend/heatmap` | 히트맵 기반 추천 |
|
||||
| GET | `/api/lotto/recommend/batch` | 배치 추천 |
|
||||
| POST | `/api/admin/simulate` | 시뮬레이션 수동 실행 |
|
||||
| POST | `/api/admin/sync_latest` | 당첨번호 수동 동기화 |
|
||||
- **로또**: 당첨번호 조회, 5종 통계 분석, 시뮬레이션 최적 번호(`best_picks` 20쌍), 통계/히트맵/스마트/배치 추천, 전략 가중치(EMA+Softmax), 구매 이력 관리
|
||||
- **추천 이력**: 즐겨찾기·태그·메모 관리
|
||||
- **투두리스트**: UUID PK, 상태(todo/in_progress/done)
|
||||
- **블로그**: 일기형 포스트 (tags JSON 배열, date DESC)
|
||||
|
||||
#### 추천 이력
|
||||
**스케줄러**
|
||||
- 09:10 / 21:10 — 당첨번호 동기화 + 추천 채점
|
||||
- 00:05, 04:05, 08:05, 12:05, 16:05, 20:05 — 몬테카를로 시뮬레이션 (후보 20,000 → 상위 100 → best_picks 20쌍 교체)
|
||||
|
||||
| 메서드 | 경로 | 설명 |
|
||||
|--------|------|------|
|
||||
| GET | `/api/history` | 목록 (limit, offset, favorite, tag, sort) |
|
||||
| PATCH | `/api/history/{id}` | 즐겨찾기·메모·태그 수정 |
|
||||
| DELETE | `/api/history/{id}` | 삭제 |
|
||||
### 2. stock-lab (`/api/stock/`, `/api/trade/`, `/api/portfolio`)
|
||||
|
||||
#### 투두리스트
|
||||
주식 뉴스 스크래핑 + LLM 요약 + KIS 실계좌 연동 + 포트폴리오·자산 스냅샷.
|
||||
|
||||
| 메서드 | 경로 | 설명 |
|
||||
|--------|------|------|
|
||||
| GET | `/api/todos` | 전체 목록 |
|
||||
| POST | `/api/todos` | 생성 (status: todo\|in_progress\|done) |
|
||||
| PUT | `/api/todos/{id}` | 수정 |
|
||||
| DELETE | `/api/todos/done` | 완료 항목 일괄 삭제 |
|
||||
| DELETE | `/api/todos/{id}` | 개별 삭제 |
|
||||
- **뉴스**: 네이버 증권 + 해외 사이트 크롤링, LLM 기반 한국어 요약
|
||||
- **실계좌**: Windows AI 서버(192.168.45.59:8000) 프록시 → KIS Open API (잔고/주문)
|
||||
- **포트폴리오**: 종목·예수금·매도 히스토리 관리, 현재가 자동 조회
|
||||
- **자산 스냅샷**: 평일 15:40 자동 저장 (KRX 공휴일 판별, `holidays.json` 매년 갱신)
|
||||
|
||||
> ⚠️ `/done` 라우트는 반드시 `/{id}` 보다 먼저 등록해야 함
|
||||
**LLM provider 전환** — `LLM_PROVIDER` 환경변수
|
||||
- `claude` (기본): Anthropic Messages API (`claude-haiku-4-5`)
|
||||
- `ollama`: Windows AI 서버 Ollama (`qwen3:14b`)
|
||||
|
||||
#### 블로그
|
||||
**현재가 조회**: 네이버 모바일 API → HTML 파싱 폴백, 3분 TTL 메모리 캐시
|
||||
|
||||
| 메서드 | 경로 | 설명 |
|
||||
|--------|------|------|
|
||||
| GET | `/api/blog/posts` | 글 목록 (`{"posts": [...]}`, date DESC) |
|
||||
| POST | `/api/blog/posts` | 글 생성 (date 미입력 시 오늘 날짜) |
|
||||
| PUT | `/api/blog/posts/{id}` | 글 수정 |
|
||||
| DELETE | `/api/blog/posts/{id}` | 글 삭제 |
|
||||
### 3. music-lab (`/api/music/`)
|
||||
|
||||
블로그 포스트 구조: `{ id, title, tags[], body, date, excerpt, created_at, updated_at }`
|
||||
듀얼 프로바이더 AI 음악 생성.
|
||||
|
||||
---
|
||||
- **Suno** (`suno`): REST API 연동, 보컬·가사·인스트루멘탈. 1회 요청 시 2개 variation 생성, 곡 연장, 보컬 분리, WAV 변환, 12스템 분리, 뮤직비디오, AI Cover 등 풀 스위트 지원
|
||||
- **로컬 MusicGen** (`local`): Windows AI PC(RTX 5070 Ti, 16GB VRAM) 인스트루멘탈 전용
|
||||
- **라이브러리**: 생성 파일은 `/app/data/music/`에 저장되고 Nginx가 `/media/music/`으로 직접 서빙
|
||||
- **가사 도구**: 저장·편집·타임스탬프 기반 가라오케 동기
|
||||
|
||||
### stock-lab (`/api/stock/`, `/api/trade/`, `/api/portfolio`)
|
||||
### 4. blog-lab (`/api/blog-marketing/`)
|
||||
|
||||
#### 뉴스 & 지표
|
||||
블로그 마케팅 수익화 4단계 파이프라인 (`draft → marketed → reviewed → published`).
|
||||
|
||||
| 메서드 | 경로 | 설명 |
|
||||
|--------|------|------|
|
||||
| GET | `/api/stock/news` | 뉴스 목록 (limit, category) |
|
||||
| GET | `/api/stock/indices` | 주요 지표 (KOSPI 등) |
|
||||
| POST | `/api/stock/scrap` | 뉴스 수동 스크랩 |
|
||||
```
|
||||
리서치(Naver Search + 상위 블로그 본문 크롤링)
|
||||
→ 작가(AI 초안 생성)
|
||||
→ 마케터(전환율 강화 + 브랜드 링크 삽입)
|
||||
→ 평가자(6기준×10점, 42/60 통과 시 published)
|
||||
```
|
||||
|
||||
#### 실계좌 (Windows AI 서버 프록시)
|
||||
- **AI 엔진**: Claude API (`claude-sonnet-4-20250514`)
|
||||
- **키워드 분석**: 네이버 검색(블로그+쇼핑) API + 경쟁도/기회 점수
|
||||
- **수익 추적**: 포스트별 월간 클릭/구매/수익 기록
|
||||
- **프롬프트 템플릿**: DB에 저장 → 코드 배포 없이 수정 가능
|
||||
|
||||
| 메서드 | 경로 | 설명 |
|
||||
|--------|------|------|
|
||||
| GET | `/api/trade/balance` | 실계좌 잔고 조회 |
|
||||
| POST | `/api/trade/order` | 주문 (BUY\|SELL, price=0이면 시장가) |
|
||||
### 5. realestate-lab (`/api/realestate/`)
|
||||
|
||||
#### 포트폴리오
|
||||
공공데이터포털 청약홈 API 연동 + 프로필 기반 자동 매칭.
|
||||
|
||||
| 메서드 | 경로 | 설명 |
|
||||
|--------|------|------|
|
||||
| GET | `/api/portfolio` | 전체 조회 (현재가·손익·예수금 포함) |
|
||||
| POST | `/api/portfolio` | 종목 추가 |
|
||||
| PUT | `/api/portfolio/{id}` | 종목 수정 |
|
||||
| DELETE | `/api/portfolio/{id}` | 종목 삭제 |
|
||||
| GET | `/api/portfolio/cash` | 예수금 전체 조회 |
|
||||
| PUT | `/api/portfolio/cash` | 예수금 upsert |
|
||||
| DELETE | `/api/portfolio/cash/{broker}` | 예수금 삭제 |
|
||||
| POST | `/api/portfolio/snapshot` | 총 자산 스냅샷 수동 저장 |
|
||||
| GET | `/api/portfolio/snapshot/history` | 자산 변화 이력 (days=0: 전체) |
|
||||
- **공고 수집**: 09:00 매일 자동 (`DATA_GO_KR_API_KEY` 필요)
|
||||
- **상태 갱신 + 재매칭**: 00:00 매일 자동
|
||||
- **프로필 매칭**: 지역·주택형·소득·부양가족 등으로 점수화, 신규 매칭 알림
|
||||
- **대시보드**: 진행 중 공고수, 신규 매칭수, 다가오는 일정 요약
|
||||
|
||||
---
|
||||
### 6. agent-office (`/api/agent-office/`)
|
||||
|
||||
### travel-proxy (`/api/travel/`)
|
||||
AI 에이전트 가상 오피스 — 2D 픽셀아트 사무실에서 에이전트가 실제 작업을 수행한다.
|
||||
|
||||
| 메서드 | 경로 | 설명 |
|
||||
|--------|------|------|
|
||||
| GET | `/api/travel/regions` | 지역 GeoJSON |
|
||||
| GET | `/api/travel/photos` | 사진 목록 (region, page, size) |
|
||||
| POST | `/api/travel/reload` | 캐시 초기화 |
|
||||
- **아키텍처**: stock-lab / music-lab 기존 API를 서비스 프록시로 호출 (직접 DB 접근 없음)
|
||||
- **FSM 상태**: `idle → working → waiting(승인 대기) → reporting → break`
|
||||
- **실시간 동기화**: WebSocket `/api/agent-office/ws` (init, agent_state, task_complete, command_result)
|
||||
- **텔레그램 연동**: 양방향 알림 + 인라인 키보드 승인
|
||||
- 봇이 작업 결과를 텔레그램으로 푸시, 명령은 텔레그램에서 바로 에이전트에 전달
|
||||
- Webhook 검증 후 `chat.id` 기준 라우팅
|
||||
- **에이전트**: Stock Agent (뉴스 요약), Music Agent (음악 생성) 등 확장 가능 구조
|
||||
|
||||
- 썸네일: `/media/travel/.thumb/{album}/{file}` (nginx 직접 서빙, 30일 캐시)
|
||||
- 원본: `/media/travel/{album}/{file}` (nginx 직접 서빙, 7일 캐시)
|
||||
**스케줄러**
|
||||
- 08:00 매일 — 주식 뉴스 요약 (`stock_news_job`)
|
||||
- 60초 — 유휴 에이전트 휴식 체크 (`idle_check_job`)
|
||||
|
||||
### 7. travel-proxy (`/api/travel/`)
|
||||
|
||||
여행 사진 API + 온디맨드 썸네일 생성.
|
||||
|
||||
- 원본: `/data/travel/` (RO 마운트)
|
||||
- 썸네일: 480×480 Pillow 리사이징, `/data/thumbs/` 영구 캐시 (tmp → rename 원자성 보장)
|
||||
- 메모리 캐시: 앨범 스캔 결과 TTL 300초
|
||||
- 메타: `region_map.json`, `regions.geojson`
|
||||
|
||||
### 8. deployer (`/webhook`)
|
||||
|
||||
Gitea Webhook 수신 → NAS 자동 배포.
|
||||
|
||||
- HMAC SHA256 서명 검증 (`compare_digest`, `WEBHOOK_SECRET`)
|
||||
- 수신 즉시 200 응답 후 BackgroundTask로 배포
|
||||
- 배포 스크립트: `git pull` → `.releases/` 백업 → `rsync` → `docker compose up -d --build` → `chown PUID:PGID`
|
||||
- 타임아웃 10분
|
||||
|
||||
---
|
||||
|
||||
@@ -213,8 +195,6 @@ curl http://localhost:18500/health
|
||||
→ 상위 100개 DB 저장 → best_picks 20개 교체
|
||||
```
|
||||
|
||||
**5가지 채점 기법:**
|
||||
|
||||
| 기법 | 가중치 | 내용 |
|
||||
|------|--------|------|
|
||||
| 빈도 Z-score | 25% | 번호 출현 빈도의 표준편차 |
|
||||
@@ -223,28 +203,21 @@ curl http://localhost:18500/health
|
||||
| 공동 출현 | 15% | 번호 쌍 동시 출현 빈도 |
|
||||
| 다양성 | 10% | 연속번호·범위·구간 커버리지 |
|
||||
|
||||
**스케줄:** 매일 0, 4, 8, 12, 16, 20시 (하루 6회, 각 5분)
|
||||
### LLM 요약 provider 추상화 (stock-lab)
|
||||
|
||||
`ai_summarizer.py`는 provider 분리 구조. `summarize_news(articles)` 시그니처는 provider와 무관하게 고정.
|
||||
|
||||
- `_summarize_with_claude`: Anthropic Messages API 직접 호출 (httpx, SDK 의존성 없음)
|
||||
- `_summarize_with_ollama`: Ollama `/api/generate` (타임아웃 180s, qwen3:14b 첫 로드 대응)
|
||||
- 실패 시 `LLMError` (구 `OllamaError` alias 유지)
|
||||
|
||||
### 총 자산 스냅샷 (stock-lab)
|
||||
|
||||
```
|
||||
평일 15:40 자동 실행 → holidays.json으로 공휴일 스킵
|
||||
→ 포트폴리오 현재가 조회 → total_eval
|
||||
→ 예수금 합계 → total_cash
|
||||
→ asset_snapshots upsert (date UNIQUE, 같은 날 중복 시 덮어씀)
|
||||
```
|
||||
평일 15:40 자동 실행 → `holidays.json`으로 공휴일 스킵 → 포트폴리오 현재가 조회 + 예수금 합계 → `asset_snapshots` upsert (date UNIQUE).
|
||||
|
||||
### 현재가 조회 (stock-lab)
|
||||
### 에이전트 FSM + WS 동기화 (agent-office)
|
||||
|
||||
- 네이버 모바일 API 우선 (`m.stock.naver.com/api/stock/{ticker}/basic`)
|
||||
- 실패 시 네이버 금융 HTML 파싱 폴백
|
||||
- 3분 TTL 메모리 캐시
|
||||
|
||||
### 여행 사진 썸네일 (travel-proxy)
|
||||
|
||||
- 480×480 리사이징 (Pillow), 확장자 유지 (JPEG/PNG/WEBP)
|
||||
- 온디맨드 생성 후 `/data/thumbs/` 영구 캐시
|
||||
- 원자성 보장: tmp 파일 작성 후 rename
|
||||
DB에 저장된 에이전트 상태가 바뀔 때마다 `websocket_manager`가 전체 클라이언트에 브로드캐스트. 텔레그램 봇은 `waiting` 상태 작업에 인라인 키보드를 붙여 승인 요청. 승인/거부 결과가 DB → WS → 프론트로 전파.
|
||||
|
||||
---
|
||||
|
||||
@@ -252,7 +225,7 @@ curl http://localhost:18500/health
|
||||
|
||||
```
|
||||
git push → Gitea → X-Gitea-Signature (HMAC SHA256)
|
||||
→ deployer:9000/webhook (서명 검증, compare_digest 사용)
|
||||
→ deployer:9000/webhook (서명 검증, compare_digest)
|
||||
→ BackgroundTask: scripts/deploy.sh (10분 타임아웃)
|
||||
1. git pull
|
||||
2. .releases/{timestamp}/ 백업
|
||||
@@ -261,39 +234,29 @@ git push → Gitea → X-Gitea-Signature (HMAC SHA256)
|
||||
5. chown PUID:PGID
|
||||
```
|
||||
|
||||
> 프론트엔드는 **자동 배포 안 됨** — 로컬 빌드 후 NAS에 수동 업로드
|
||||
> 프론트엔드는 **자동 배포 안 됨** — 로컬 빌드 후 NAS에 수동 업로드 (`scripts/deploy.bat --frontend`)
|
||||
|
||||
---
|
||||
|
||||
## 데이터베이스
|
||||
|
||||
### lotto.db (`/app/data/lotto.db`)
|
||||
각 서비스는 독립 SQLite DB를 `/app/data/` 볼륨에 저장.
|
||||
|
||||
| 테이블 | 설명 |
|
||||
|--------|------|
|
||||
| `draws` | 로또 당첨번호 |
|
||||
| `recommendations` | 추천 이력 (즐겨찾기·태그·채점 포함) |
|
||||
| `simulation_runs` | 시뮬레이션 실행 기록 |
|
||||
| `simulation_candidates` | 시뮬레이션 후보 (점수 5종) |
|
||||
| `best_picks` | 현재 활성 최적 번호 20개 (is_active 플래그) |
|
||||
| `todos` | 투두리스트 (UUID PK) |
|
||||
| `blog_posts` | 블로그 글 (tags: JSON 배열) |
|
||||
|
||||
### stock.db (`/app/data/stock.db`)
|
||||
|
||||
| 테이블 | 설명 |
|
||||
|--------|------|
|
||||
| `articles` | 뉴스 기사 (hash UNIQUE, category: domestic\|overseas) |
|
||||
| `portfolio` | 보유 종목 (broker, ticker, quantity, avg_price) |
|
||||
| `broker_cash` | 증권사별 예수금 (broker UNIQUE) |
|
||||
| `asset_snapshots` | 일별 총 자산 스냅샷 (date UNIQUE) |
|
||||
| DB | 소유 서비스 | 주요 테이블 |
|
||||
|----|------------|-----------|
|
||||
| `lotto.db` | lotto-backend | draws, recommendations, simulation_runs/candidates, best_picks, purchase_history, strategy_performance/weights, weekly_reports, todos, blog_posts |
|
||||
| `stock.db` | stock-lab | articles, portfolio, broker_cash, asset_snapshots, sell_history |
|
||||
| `music.db` | music-lab | music_tasks, music_library (provider, lyrics, image_url, suno_id, file_hash, cover_images, wav_url, video_url, stem_urls) |
|
||||
| `blog_marketing.db` | blog-lab | keyword_analyses, blog_posts, brand_links, commissions, generation_tasks, prompt_templates |
|
||||
| `realestate.db` | realestate-lab | announcements, announcement_models, user_profile, match_results, collect_log |
|
||||
| `agent_office.db` | agent-office | agent_config, agent_tasks, agent_logs, telegram_state |
|
||||
|
||||
---
|
||||
|
||||
## 환경변수
|
||||
|
||||
```env
|
||||
# 경로 설정
|
||||
# 경로
|
||||
RUNTIME_PATH=.
|
||||
REPO_PATH=.
|
||||
FRONTEND_PATH=./frontend/dist
|
||||
@@ -306,6 +269,32 @@ PGID=1000
|
||||
# 외부 서비스
|
||||
WINDOWS_AI_SERVER_URL=http://192.168.45.59:8000
|
||||
WEBHOOK_SECRET=your_secret_here
|
||||
|
||||
# LLM (stock-lab, blog-lab, agent-office 공통)
|
||||
ANTHROPIC_API_KEY=sk-ant-...
|
||||
ANTHROPIC_MODEL=claude-haiku-4-5-20251001
|
||||
LLM_PROVIDER=claude # claude | ollama
|
||||
OLLAMA_URL=http://192.168.45.59:11435
|
||||
OLLAMA_MODEL=qwen3:14b
|
||||
|
||||
# music-lab
|
||||
SUNO_API_KEY=
|
||||
MUSIC_AI_SERVER_URL=
|
||||
MUSIC_MEDIA_BASE=/media/music
|
||||
|
||||
# blog-lab
|
||||
NAVER_CLIENT_ID=
|
||||
NAVER_CLIENT_SECRET=
|
||||
|
||||
# realestate-lab
|
||||
DATA_GO_KR_API_KEY=
|
||||
|
||||
# agent-office
|
||||
TELEGRAM_BOT_TOKEN=
|
||||
TELEGRAM_CHAT_ID=
|
||||
TELEGRAM_WEBHOOK_URL=
|
||||
STOCK_LAB_URL=http://stock-lab:8000
|
||||
MUSIC_LAB_URL=http://music-lab:8000
|
||||
```
|
||||
|
||||
---
|
||||
@@ -316,9 +305,9 @@ WEBHOOK_SECRET=your_secret_here
|
||||
|------|----|
|
||||
| 장비 | Synology NAS (Intel Celeron J4025, 18GB RAM) |
|
||||
| Docker | Synology Container Manager |
|
||||
| Git 서버 | Gitea (NAS 내부 self-hosted) |
|
||||
| AI 서버 | Windows PC (192.168.45.59:8000) — RTX 3070 Ti + Ollama |
|
||||
| Python | 3.12 (`slim` / `alpine` 기반 이미지) |
|
||||
| Git 서버 | Gitea (NAS 내부 self-hosted, `gahusb.synology.me`) |
|
||||
| AI 서버 | Windows PC (192.168.45.59) — RTX 5070 Ti (16GB VRAM) + Ollama + MusicGen |
|
||||
| Python | 3.12 (`slim` 기반 이미지) |
|
||||
| DB | SQLite (볼륨 마운트로 영속 저장) |
|
||||
|
||||
---
|
||||
@@ -327,8 +316,18 @@ WEBHOOK_SECRET=your_secret_here
|
||||
|
||||
- **`.env` 파일** — 절대 커밋 금지. `.env.example`만 레포에 포함
|
||||
- **Nginx trailing slash** — `/api/portfolio`는 두 location 블록으로 처리 (trailing slash 유무 모두 매칭)
|
||||
- **라우트 순서** — `/api/todos/done`은 `/api/todos/{id}` 보다 먼저 등록 필수
|
||||
- **라우트 순서** — `DELETE /api/todos/done`은 `/api/todos/{id}` 보다 먼저 등록 필수 (FastAPI prefix 매칭)
|
||||
- **캐시 전략** — `index.html`: no-store / `assets/`: 1년 immutable
|
||||
- **PUID/PGID** — travel-proxy는 NAS 파일 권한을 위해 환경변수 주입 필수
|
||||
- **공휴일 목록** — `stock-lab/app/holidays.json` 매년 수동 갱신 필요 (KRX 기준)
|
||||
- **Windows AI 서버** — IP 192.168.45.59 (공유기 DHCP 고정 예약)
|
||||
- **공휴일 목록** — `stock-lab/app/holidays.json` 매년 수동 갱신 (KRX 기준)
|
||||
- **Windows AI 서버 IP** — `192.168.45.59` 공유기 DHCP 고정 예약. Synology Tailscale은 userspace 모드라 TCP 불가 → 로컬 IP 사용
|
||||
- **Suno CDN** — `cdn1.suno.ai` URL은 임시 만료 → 생성 즉시 로컬 다운로드 필수
|
||||
- **LLM provider 롤백** — Claude API 장애 시 `.env`의 `LLM_PROVIDER=ollama`로 전환 후 `docker compose up -d`
|
||||
- **시뮬레이션 교체 방식** — `best_picks`는 교체형 (`is_active=0` 비활성화 후 신규 입력)
|
||||
|
||||
---
|
||||
|
||||
## 참고 문서
|
||||
|
||||
- `CLAUDE.md` — Claude Code 작업용 상세 컨텍스트 (API 전체 목록, 테이블 스키마 등)
|
||||
- `docs/` — 서비스별 기획·설계 문서
|
||||
|
||||
Reference in New Issue
Block a user