"""사이드바 설정 모듈. API Key 인증 + 캠페인 선택 UI. """ import os import requests import streamlit as st from core.api_client import ChainShiftClient def render_sidebar() -> tuple[dict, int, str] | None: """사이드바 렌더링. Returns: (auth_info, campaign_id, campaign_display) 또는 None (인증 실패 시) auth_info = {"api_key": str} """ with st.sidebar: st.header("설정") auth_info = _render_api_key_section() if not auth_info: return None st.divider() # 캠페인 선택 result = _render_campaign_selector(auth_info) if not result: return None campaign_id, campaign_display = result return auth_info, campaign_id, campaign_display def _render_api_key_section() -> dict | None: """API Key 인증 (env var 자동 인증 또는 직접 입력).""" env_api_key = os.environ.get("CHAINSHIFT_API_KEY", "") api_key = st.text_input( "API Key", type="password", placeholder="sk_live_xxx...", value=env_api_key, help="API Key 직접 입력 (환경변수 CHAINSHIFT_API_KEY 지원)", key="direct_api_key", ) if not api_key: st.info("API Key를 입력하세요") return None return { "api_key": api_key, "access_token": None, "email": None, } # ============================================================================= # Campaign selector # ============================================================================= def _render_campaign_selector(auth_info: dict) -> tuple[int, str] | None: """캠페인 선택 드롭다운.""" try: from core.data_fetchers import get_campaigns api_key = auth_info.get("api_key") or "" access_token = auth_info.get("access_token") or "" campaigns_response = get_campaigns( api_key=api_key, access_token=access_token ) campaigns = campaigns_response.get("data", {}).get("items", []) if not campaigns: st.error("캠페인을 찾을 수 없습니다") return None # Sort campaigns by ID descending (latest first) campaigns_sorted = sorted(campaigns, key=lambda c: c.get("id", 0), reverse=True) def format_campaign(c): return f"{c['name']} (#{c['id']})" st.markdown("**캠페인**") selected = st.selectbox( "캠페인 선택", options=campaigns_sorted, format_func=format_campaign, label_visibility="collapsed", ) selected_campaign_id = selected["id"] selected_campaign_display = format_campaign(selected) st.caption(f"Campaign #{selected_campaign_id}") return selected_campaign_id, selected_campaign_display except requests.exceptions.ConnectionError: st.error("서버 연결에 실패했습니다. 잠시 후 다시 시도해주세요.") return None except requests.exceptions.Timeout: st.error("서버 응답 시간이 초과되었습니다. 잠시 후 다시 시도해주세요.") return None except Exception as e: st.error(f"API 오류: {str(e)}") return None