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 client_url: str = "" nego_chat_url: str = "http://localhost:3300" agent_base_url: str = "http://localhost:9500" # 협상 agent(9500). 공용 카탈로그 변경 알림용. lps_base_url: str = "http://localhost:9600" # 인터넷 최저가 검색 LPS(9600). 검색요청 enqueue 용. lps_api_key: str = "" # LPS API guard 키 — LPS 쪽 [WebServerConfig].api_keys 와 동일 값. 빈값=헤더 미첨부(개발) 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 워커수. # PostgreSQL max_connections 를 넘지 않도록 설정해야 한다. (예: 10+20=30 x 2 x 5워커 = 300) pool_size: int = 10 max_overflow: int = 20 # SSL/TLS 모드: ""/"disable"=미사용(로컬), "require"/"verify-ca"/"verify-full"=관리형 DB(RDS/Aurora/Azure). sslmode: str = "" # 인터넷 최저가 검색(LPS)의 결과 DB(lps_db) — **읽기전용** 접속. # LPS 는 별도 서비스(자체 스키마 소유)이고, negodata 는 price_history 를 주기 배치로 읽어만 온다. # 그래서 read_* 만 둔다(write 엔진 미등록 = 앱 차원 읽기전용 강제). name 이 비면 미사용(등록 스킵). class LpsDBConfig(ConfigModel): db_type: str = "postgresql" name: str = "" # 비우면 LPS 연동 비활성(엔진 미등록, 배치 스킵) read_host: str = "" read_port: int = 5432 read_id: str = "" read_pw: str = "" show_log: bool = False # 배치 전용이라 작은 풀이면 충분 (동시 사용처 = 스케줄러 잡 1개) pool_size: int = 2 max_overflow: int = 2 sslmode: str = "" class JwtToken(ConfigModel): access_key: str = "" refresh_key: str = "" access_expire_min: int = 30 refresh_expire_day: int = 7 # 정적 파일(상품 이미지 등) 저장소 = Azure Blob Storage. # infinith 와 같은 계정/컨테이너 SAS 를 그대로 쓰고, blob_root 로 디렉터리만 분리한다. # SDK 없이 SAS 토큰을 URL 에 붙여 httpx 로 PUT 한다(services/azure_blob_client.py). class StorageConfig(ConfigModel): azure_blob_base_url: str = "" # https://.blob.core.windows.net/ azure_blob_sas_token: str = "" # SAS 토큰(쿼리스트링). 만료 있음 — 만료되면 업로드 실패 blob_root: str = "negodata" # 컨테이너 내 최상위 디렉터리(infinith 파일과 분리) max_image_mb: int = 4 # 업로드 허용 최대 크기(MB). 프론트 ImageDropzone 와 일치 # 협상 초청 메일 발송 설정. services/email.py 가 ACS → SMTP 순으로 시도한다. # 1순위: Azure Communication Services(ACS) Email — endpoint + accesskey. # azure_acs_sender 는 검증된 MailFrom 주소(예: donotreply@negodata.o2o.kr). Blob 과 별개 리소스다. # 2순위(폴백): SMTP — ACS 미설정 시 사용. 둘 다 비우면 발송 시 EmailUnavailable. class MailConfig(ConfigModel): azure_acs_endpoint: str = "" azure_acs_accesskey: str = "" azure_acs_sender: str = "" smtp_host: str = "" smtp_port: int = 587 smtp_user: str = "" smtp_password: str = "" smtp_from: str = "Negodata " smtp_starttls: bool = True @property def acs_configured(self) -> bool: return bool(self.azure_acs_endpoint and self.azure_acs_accesskey and self.azure_acs_sender)