5da7a0040b
fix(stock,docs): portfolio total_buy 수량 곱산 + insta-trends spec 변경 이력 (F4 + F6)
...
[F4] /api/portfolio 응답의 summary.total_buy가 종목별 단가 × 수량의 합이
되도록 fix. 기존 인라인 코드가 purchase_price를 수량 미곱산으로 단순
누적해 명세(qty 100 · avg 72000 → 7,200,000)와 어긋났음. API_SPEC.md에
purchase_price 필드 의미 + total_buy 계산식 명시. test 3건 (단가 곱산,
avg_price 폴백, 다종목 합산).
[F6] insta-trends spec/plan 상단에 "google_trends → youtube_trending"
변경 이력 추가. Google Trends endpoint 폐기로 source 교체된 이력이
본문 검색 시 혼란 주는 문제 차단. 사유 cross-ref:
feedback_external_data_sources.md
2026-05-17 14:06:19 +09:00
faffca0967
Merge pull request 'feat/security-hardening' ( #5 ) from feat/security-hardening into main
...
Reviewed-on: #5
2026-05-17 14:00:03 +09:00
49c5c57be5
docs(env): add ALLOW_UNAUTHENTICATED_ADMIN guidance for F2
2026-05-17 13:58:24 +09:00
6053e69afc
fix(stock): admin API auth hardening — ADMIN_API_KEY 빈 값 시 503 거부 (CODE_REVIEW F2)
...
운영 .env에 ADMIN_API_KEY가 누락되면 verify_admin이 무조건 통과해서
/api/trade/balance, /api/trade/order 인증이 무력화되던 문제 차단.
- ADMIN_API_KEY 설정 + 올바른 키 → 통과 (기존 동작)
- ADMIN_API_KEY 설정 + 잘못된 키 → 401 (기존 동작)
- ADMIN_API_KEY 미설정 + ALLOW_UNAUTHENTICATED_ADMIN=true → 통과 (dev mode)
- ADMIN_API_KEY 미설정 + dev flag 없음 → 503 (신규, 운영 보호)
.env.example에 신규 ALLOW_UNAUTHENTICATED_ADMIN=false 안내 추가.
stock/pytest.ini 신규 (pythonpath=. 설정으로 tests 모듈 import 가능).
test_admin_auth.py 4 케이스 (RED → GREEN 검증, regression 포함).
2026-05-17 13:53:50 +09:00
1e5e1bcdff
fix(packs-lab): sign-link path traversal — startswith → relative_to (CODE_REVIEW F1)
...
str(abs_path).startswith(str(PACK_HOST_DIR))는 trailing slash가 없어
sibling 경로(/foo/packs ↔ /foo/packs_evil)를 통과시켜 DSM API에 잘못된
호스트 경로를 전달할 수 있었음. Path.relative_to 기반으로 컴포넌트 단위
엄격 검증으로 교체. test_sign_link_rejects_sibling_path 회귀 테스트
추가 (RED → GREEN 검증).
2026-05-17 13:50:22 +09:00
64fbbb7958
fix(insta-lab): replace Google Trends with YouTube Data API (Google API 폐기 대응)
...
Google이 비공식 trends endpoint 두 가지(/trends/.../rss + /trends/api/dailytrends)
모두 404로 폐기 (NAS에서 직접 호출 시 확정). 대안으로 YouTube Data API v3
mostPopular(regionCode=KR, 50개)로 source 교체:
- source 이름: google_trends → youtube_trending
- 키워드: 영상 제목 정제 (대괄호·이모지 제거, 60자 limit)
- API 키: YOUTUBE_DATA_API_KEY (agent-office와 공유, .env 그대로 활용)
- 키 미설정 시 graceful skip
- docker-compose insta-lab에 환경변수 추가
- 테스트 9/9 pass (기존 6 + youtube 3 신규)
2026-05-17 11:54:31 +09:00
cfbb72051f
fix(insta-lab): Google Trends — RSS endpoint도 404 폐기, dailytrends JSON API로 교체
...
Google이 /trends/trendingsearches/daily/rss?geo=KR도 404로 폐기 (직전
fix에서 RSS로 교체했으나 NAS에서 실제 호출 시 404 확인). 대안으로 비공식
/trends/api/dailytrends?hl=ko&tz=-540&geo=KR&ns=15 JSON API로 교체.
응답 앞 `)]}'` XSSI 보호 prefix는 정규식으로 자르고 JSON 파싱.
중복 키워드 제거 + 등장 순서 보존.
2026-05-17 09:30:40 +09:00
bf5897fc85
fix(insta-lab): trend_collector — Google Trends RSS + seed placeholder filter
...
(1) pytrends 4.x가 Google API 변경으로 trending_searches(pn='south_korea')
가 404 반환 → daily trending searches RSS endpoint를 requests로 직접 호출
하도록 교체. pytrends 의존성 제거.
(2) category_seeds 프롬프트 템플릿에 placeholder ('...', 'TBD' 등) 또는
2자 미만 값이 들어가면 NAVER가 400 Bad Request 반환 → _seeds_for에
_is_valid_seed 가드 추가, 모두 invalid면 DEFAULT_CATEGORY_SEEDS 폴백.
테스트 8/8 PASS (기존 6 + placeholder/fallback 2 신규).
2026-05-17 09:21:38 +09:00
ad6c744f2c
fix(deploy): increase docker/buildkit/pip timeouts for NAS slow build
...
webhook 자동 배포가 pip install (pytrends 추가 후 75s+)에서 buildkit
context deadline exceeded로 실패하던 이슈 대응. scripts/deploy.sh
상단에 COMPOSE_HTTP_TIMEOUT/DOCKER_CLIENT_TIMEOUT/BUILDKIT_STEP_LOG_MAX_SIZE
10분 환경변수 설정 + insta-lab Dockerfile의 pip install에 --timeout 600
--retries 5 추가. NAS Celeron J4025 환경 영구 대응.
2026-05-17 09:03:20 +09:00
aad9bfbe8b
Merge pull request 'feat/insta-trends' ( #4 ) from feat/insta-trends into main
...
Reviewed-on: #4
2026-05-17 08:52:49 +09:00
42bd53ee7b
feat(insta): _bg_extract uses preferences + 09:00 trends_collect cron
2026-05-16 17:58:52 +09:00
86694ae4fe
feat(agent-office): InstaAgent collect_trends action + preferences-aware on_schedule
2026-05-16 17:57:44 +09:00
41225b3337
feat(insta-lab): main.py — trends + preferences endpoints
...
- POST /api/insta/trends/collect — background trend collection via trend_collector.collect_all
- GET /api/insta/trends — list external trends with source/category/days filters
- GET /api/insta/preferences — return category weights (defaults seeded on init_db)
- PUT /api/insta/preferences — upsert category weights
- Modified GET /api/insta/keywords to accept source= filter (source present → list_trends, else existing list_trending_keywords, backward compatible)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-05-16 17:54:09 +09:00
6bb5c2fb40
feat(insta-lab): keyword_extractor.extract_with_weights for category proportions
2026-05-16 17:51:16 +09:00
bd1773e29e
feat(insta-lab): trend_collector adds Google Trends + LLM category classification
2026-05-16 17:48:26 +09:00
685320f3cf
feat(insta-lab): trend_collector with NAVER popular fetcher
2026-05-16 17:47:17 +09:00
b3982c8f72
feat(insta-lab): db migration — trending_keywords.source + account_preferences + CRUD
...
- Idempotent ALTER TABLE adds source column (default 'manual') + idx_tk_source index
- New account_preferences table seeded with economy/psychology/celebrity at weight=1.0
- add_trending_keyword now accepts optional source param
- New helpers: add_external_trend, list_trends, get_preferences, upsert_preferences
- test_db updated: six→seven tables; test_preferences_crud.py (7 new tests, all pass)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-05-16 17:44:01 +09:00
002c0893f8
chore(insta-lab): add pytrends>=4.9 dependency
2026-05-16 17:41:30 +09:00
d6081ba2d3
docs(insta-trends): implementation plan (10 TDD-grouped tasks)
...
trend_collector NAVER+Google+LLM 분류, db migration + preferences CRUD,
extract_with_weights, 4 endpoints + keywords source 필터, InstaAgent
collect_trends action + preferences-aware schedule, web-ui 탭 + 3 패널,
스모크 매트릭스.
2026-05-16 17:39:19 +09:00
10cb3ae1df
docs(insta-trends): 셀프 리뷰 보강 — LLM 분류 캐시 위치, days 쿼리 의미 명시
2026-05-16 17:31:22 +09:00
e3348da642
docs(insta-trends): 외부 트렌드 + 카테고리 가중치 설계
...
NAVER 인기 + Google Trends 두 source 수집, account_preferences로 카테고리
가중치 모델, 가중치 기반 키워드 추출 알고리즘, Insta 페이지 Cards/Trends
탭 분리.
2026-05-16 17:30:45 +09:00
088bbaa097
fix(deploy): use docker inspect for healthcheck (호스트/컨테이너 둘 다 동작)
...
기존 curl http://lotto:8000/health은 deployer 컨테이너 내부에서만
Docker DNS가 'lotto'를 해석. 호스트 셸에서 sudo bash로 직접 실행 시
DNS 해석 실패해 모든 서비스가 HEALTH_FAIL로 오판정. docker inspect로
이미 정의된 compose healthcheck 결과를 직접 조회하도록 변경. starting
상태는 최대 60초 대기 후 최종 판정.
2026-05-16 02:11:38 +09:00
be322557ee
fix(insta-lab): pin to bookworm + manual Chromium deps (drop --with-deps)
...
python:3.12-slim이 trixie(Debian 13)로 옮겨가면서 Playwright 1.48의
--with-deps가 ttf-ubuntu-font-family / ttf-unifont 등 ubuntu20.04
fallback 패키지를 시도하다 apt 실패 → Docker build exit 100.
해결: python:3.12-slim-bookworm 명시(Debian 12, Playwright 공식 지원)
+ Chromium 런타임 라이브러리 직접 apt 설치 + --with-deps 제거.
2026-05-16 01:58:53 +09:00
70438caa1f
fix(scripts): blog-lab → insta-lab in deploy/healthcheck service lists
...
배포 스크립트 hardcoded 서비스 리스트가 blog-lab을 참조해 머지 후
첫 webhook 배포가 rsync(/repo/blog-lab 없음) + docker compose
(서비스 미정의) 양쪽에서 실패. SERVICES/BUILD_TARGETS/HEALTH_ENDPOINTS/
DATA_DIRS를 insta-lab 기준으로 갱신. CONTAINER_NAMES는 blog-lab 고아
정리용으로 유지(다음번 docker rm -f가 안전 실행).
2026-05-16 01:51:45 +09:00
e16029ebdb
Merge pull request 'feat/insta-agent' ( #3 ) from feat/insta-agent into main
...
Reviewed-on: #3
2026-05-16 01:43:21 +09:00
cefc3119c0
docs(claude-md): replace blog-lab references with insta-lab
2026-05-16 00:53:58 +09:00
5485d4858a
chore: remove blog-lab service and BlogAgent (replaced by insta-lab)
2026-05-16 00:52:05 +09:00
fbd963db86
feat(agent-office): telegram render_<id> callback dispatches to InstaAgent
2026-05-16 00:49:30 +09:00
9095423026
feat(agent-office): register InstaAgent + 09:30 cron job
2026-05-16 00:47:28 +09:00
6eb24090ed
feat(agent-office): InstaAgent — daily extract + keyword push + media group render
2026-05-16 00:47:24 +09:00
8cb5a01431
feat(agent-office): replace blog_* proxy with insta_* helpers
2026-05-16 00:47:16 +09:00
8a4a8790ca
chore(agent-office): swap BLOG_LAB_URL for INSTA_LAB_URL
2026-05-16 00:47:12 +09:00
2200748122
chore(nginx): replace /api/blog-marketing with /api/insta
2026-05-16 00:40:41 +09:00
7bc0a7cd77
chore(compose): replace blog-lab service with insta-lab
2026-05-16 00:40:26 +09:00
b84efd730b
feat(insta-lab): main.py FastAPI endpoints + BackgroundTasks
...
13 REST endpoints covering health, status, news, keywords, slates,
tasks, and prompt templates. All background functions are async def
so FastAPI awaits them without asyncio.run conflicts.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-05-16 00:38:34 +09:00
11bd223612
feat(insta-lab): card_renderer with Jinja + Playwright (1080x1350)
2026-05-16 00:35:55 +09:00
c3a5d7210f
feat(insta-lab): card_writer with Claude 10-page JSON generator
2026-05-16 00:31:34 +09:00
07c4459085
feat(insta-lab): keyword_extractor with frequency + Claude refinement
2026-05-16 00:30:38 +09:00
c057304981
feat(insta-lab): news_collector with NAVER news.json + dedupe
2026-05-16 00:27:13 +09:00
d1245d040c
feat(insta-lab): db.py with 6 tables + CRUD
2026-05-16 00:26:28 +09:00
34ca407ca2
feat(insta-lab): anchor templates/default/ directory with .gitkeep
2026-05-16 00:22:42 +09:00
b1ef778fc5
feat(insta-lab): project scaffold (Dockerfile, requirements, config)
...
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-05-16 00:20:49 +09:00
30706e2eb6
docs(insta-agent): add implementation plan (18 TDD tasks)
...
scaffold → db → news_collector → keyword_extractor → card_writer →
card_renderer → main.py FastAPI → docker-compose/nginx 교체 →
agent-office service_proxy/InstaAgent/registry/scheduler/webhook
콜백 → blog-lab 폐기 → CLAUDE.md → 스모크 테스트.
2026-05-15 08:58:15 +09:00
6062445c12
fix(stock-webai): final review notes — env default + 1-time auth error log
...
(1) docker-compose: ${WEBAI_API_KEY} → ${WEBAI_API_KEY:-} matches
project convention, avoids "variable not set" warning when NAS .env
lacks the key during initial deploy.
(2) auth.py: ERROR log when WEBAI_API_KEY env unset fires only on
first miss, then silent (module-level _WEBAI_AUTH_WARNED flag).
Flag resets when env becomes configured, so future regressions log
again. Eliminates log spam under web-ai polling (~3/min).
All 102 tests still pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-15 08:56:03 +09:00
13da2226c3
feat(nginx-webai): /api/webai/ location with rate limit + X-WebAI-Key forward
...
limit_req_zone webai:5m rate=60r/m, burst=20 nodelay, return 429 on
limit hit. Proxies to stock:8000 with X-Real-IP, X-Forwarded-For,
and X-WebAI-Key headers preserved.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-15 08:48:14 +09:00
1e377e1559
chore(stock-webai): pass WEBAI_API_KEY env to stock container
...
Required by /api/webai/* endpoints. Operator must set WEBAI_API_KEY
in NAS /volume1/docker/webpage/.env before deploy.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-15 08:46:52 +09:00
eb75d692f5
test(stock-webai): edge cases — 401 no leak, 503 env missing, unknown date
...
Verifies auth failure responses contain no portfolio/sentiment data,
503 when WEBAI_API_KEY env unset (existing endpoints unaffected),
news-sentiment unknown date returns empty result.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-15 08:44:17 +09:00
6c25866487
docs(insta-agent): 셀프 리뷰 수정 — 6테이블 표기 일치, auto_select 설정 위치 명확화
2026-05-15 08:42:38 +09:00
6ac7469f26
docs(insta-agent): blog-lab 폐기 및 insta-lab 설계 (1080x1350 카드 피드)
...
뉴스 수집 → 키워드 추출 → 10페이지 카드 카피·PNG 생성 → 텔레그램 푸시 →
사용자 수동 인스타 업로드 파이프라인. blog-lab 디렉토리·DB 폐기, 포트
18700 재활용, agents/blog.py → agents/insta.py, Playwright 기반 카드 렌더.
2026-05-15 08:42:03 +09:00
d1b2b6a4ba
feat(stock-webai): /api/webai/news-sentiment daily dump
...
JOINs news_sentiment with krx_master for name fallback. Sorted by
score DESC. Date param defaults to latest. Empty table returns
{date: null, count: 0, items: []}. 4 integration tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-15 08:40:49 +09:00