o2o-negosium-original/lps/config/config_models.py
민헌 c81df5bd88 refactor(lps): 설정을 TOML 단일 소스로 통합 — env/.env 이중 관리 제거
설정이 .env(compose 주입)·config.toml·코드 곳곳의 os.environ 직독 3계층에
흩어져 관리가 어려웠다. TOML 하나로 통합한다(협의 결정).

- 신설 [WorkerConfig](동시성·폴백·프로필·데드라인·유예·Chrome·하트비트),
  [AlertConfig](웹훅·쿨다운·임계 10종). [WebServerConfig].api_keys(guard),
  [DecodoConfig].ip_request_budget/port_cooldown_sec 추가 — 흩어져 있던
  LPS_* env 20여 개를 섹션으로 흡수.
- server_configs 의 env override 계층(DB_*·시크릿·NAVER_KEYS 등) 삭제.
  남는 env 는 APP_ENV(부트스트랩)·PROCESS_COUNT/WORKER_CONCURRENCY(실행
  스크립트 대화형 입력 전용)·LPS_LIVE(테스트 옵트인)뿐.
- Docker: env 주입 → config.docker.toml 마운트 + APP_ENV=docker.
  이미지 무시크릿 유지, 마운트 누락 시 FileNotFoundError 즉시 실패.
  .env.example 삭제, config.docker.toml.example 신설.
- negodata 호출부: guard 키를 env 직독에서 [WebServerConfig].lps_api_key
  (+기존 관례대로 env override)로 이동.
- 실행 스크립트: 프로필·폴백·예산 프롬프트 제거(toml 소스 안내),
  동시성/프로세스 수만 임시 override 로 유지.
- docs 7종·example toml 의 env 표기를 toml 키로 일괄 갱신.
- 전체 145 passed + APP_ENV=docker 로딩·API 기동 스모크 확인.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 21:11:31 +09:00

116 lines
5.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from pydantic import BaseModel
from config.config_loader import ConfigModel
class WebServerConfig(ConfigModel):
server_name: str = ""
port: int = 0
process_count: int = 1
is_ssl: bool = False
is_test: bool = False
# CORS 허용 오리진(프론트). 비우면 CORS 미적용. 예: ["http://localhost:5173"]
cors_origins: list[str] = []
# API guard 키. 비우면 개방 모드(개발). 채우면 /v1 전체에 X-API-Key 검증(prod).
# 여러 개 등록 가능 — 무중단 키 교체(새 키 추가 → 호출자 전환 → 옛 키 제거).
api_keys: list[str] = []
class LogConfig(ConfigModel):
print_console: bool = True
log_level: str = "debug"
# DB Read/Write 분리 설정.
# 하나의 논리 DB 에 대해 write(주) / read(복제) 접속 정보를 각각 가진다.
class MainDBConfig(ConfigModel):
db_type: str = "postgresql"
name: str = ""
write_host: str = ""
write_port: int = 5432
write_id: str = ""
write_pw: str = ""
read_host: str = ""
read_port: int = 5432
read_id: str = ""
read_pw: str = ""
show_log: bool = False
# 커넥션 풀 사이징. 실제 동시 커넥션 = (pool_size + max_overflow) x 엔진수(R/W=2) x process_count.
pool_size: int = 10
max_overflow: int = 20
# 커넥션 예산(자동 산정). >0 이면 process_count 에 맞춰 pool_size/max_overflow 를 자동 계산한다:
# (pool+overflow)x2xprocess_count ≤ connection_budget 가 되도록. (이때 위 pool_size/max_overflow 는 무시)
# PG max_connections·공유 DB 동거 서비스(worker·negosium 등)를 고려한 'lps API 가 쓸 총 커넥션 상한'.
# 0 이면 자동 산정 끔(위 pool_size/max_overflow 그대로 사용). env DB_CONNECTION_BUDGET 로 override.
connection_budget: int = 40
# SSL/TLS 모드: ""/"disable"=미사용(로컬), "require"/"verify-ca"/"verify-full"=관리형 DB(RDS/Aurora/Azure).
sslmode: str = ""
# ── 시크릿(API 키 등)도 TOML 로 통합 관리. config.local.toml 은 미커밋(*.toml). ──
# 배포는 이 파일을 마운트하거나(권장), 환경별로 바뀌는 값만 env override 한다(DB_HOST 등).
class NaverKey(BaseModel):
id: str = ""
secret: str = ""
class NaverConfig(ConfigModel):
"""네이버 쇼핑 오픈API 키. 여러 개면 429/403 로테이션에 자동 포함."""
keys: list[NaverKey] = []
class OpenAIConfig(ConfigModel):
"""AI 유사도 판정/검색어 생성용 OpenAI."""
api_key: str = ""
model: str = "gpt-4o-mini"
class DecodoConfig(ConfigModel):
"""DECODO residential 프록시(쿠팡 전용, 포트기반 sticky). 값이 다 차야 활성."""
host: str = ""
username: str = ""
password: str = ""
port_start: int = 0
port_end: int = 0
session_minutes: int = 10
cost_per_gb: float = 0.0 # DECODO residential 요금($/GB) — 검색 원가의 대역폭 비용 산정용(플랜에 맞게 설정)
# IP(포트 세션)당 요청 예산 — 도달 시 차단당하기 전에 선제 회전(평판 보존). 0=비활성.
# 실측상 5회 부근 차단 이력 → 보수적 3. 튜닝은 ip_session 분석(docs/database.md).
ip_request_budget: int = 3
# 차단 감지된 포트 격리 시간(초). 0=자동(max(sticky, 30분)) — sticky 만료 후 복귀라 사실상 새 IP.
port_cooldown_sec: int = 0
class WorkerConfig(ConfigModel):
"""워커 런타임 설정. (동시성만 실행 시 WORKER_CONCURRENCY env 로 임시 override 가능 — 대화형 스크립트용)"""
concurrency: int = 1 # 상품 동시 검색 수(워커별 브라우저 세트, Chrome 최대 4×N). 로컬 권장 2~3
fallbacks: list[str] = [] # 오픈마켓 폴백(기본 비활성). 예: ["gmarket", "auction", "st11"] — 켜기 전 라이브 스모크
profile_dir: str = "/tmp" # Chrome 프로필 베이스 경로. 영속 볼륨이면 재시작에도 cf_clearance 유지(재웜업 회피)
job_deadline_sec: float = 300 # 잡 1건 처리 상한(크롤 행 방어). 0=무제한(테스트용)
shutdown_grace_sec: float = 60 # graceful 종료 유예 — docker stop_grace_period 를 이보다 길게
chrome_channel: str = "chrome" # 로컬: 실제 Chrome 채널
chrome_executable: str = "" # 컨테이너: 시스템 chromium 경로(설정 시 channel 무시, --no-sandbox 적용)
heartbeat_file: str = "/tmp/lps_worker_heartbeat" # 하트비트 파일(Docker HEALTHCHECK 신선도 확인)
class AlertConfig(ConfigModel):
"""임계 알림(AlertManager) 설정 — 룰 의미는 docs/operations.md 표 참고."""
webhook: str = "" # Slack 호환 웹훅 URL. 비우면 로그로만 알림
cooldown_min: int = 30 # 같은 룰 재발송 억제 시간(분). 해소 알림은 즉시
dead_1h: int = 20 # 최근 1h DEAD 잡 수 임계
blocks_1h: int = 80 # 최근 1h 봇 감지 수 임계
queue_lag_sec: int = 300 # 가장 오래된 PENDING 대기 초 임계
pool_pct: int = 90 # DB 커넥션 풀 포화율(%) 임계
source_fail_30m: int = 5 # 소스별 30분 내 시도 N회 이상 & 성공 0건
deadline_1h: int = 5 # 최근 1h 잡 데드라인 강제종료 수 임계
cost_1h_usd: float = 1.0 # 최근 1h 검색원가 합($) 임계
ports_low_pct: int = 30 # 가용 프록시 포트 비율(%) 임계
block_sessions_6h: int = 1 # 최근 6h '예산 회전에도 차단된' IP 세션 수 임계