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 세션 수 임계