"""Reusable audit-tab renderer with data loaders supplied by the application.""" from __future__ import annotations from typing import Any, Callable AuditInventoryLoader = Callable[[], list[dict[str, Any]]] AuditEventsLoader = Callable[[int, int, str, str], list[dict[str, Any]]] def render_hmm_audit_tab( st: Any, inventory_loader: AuditInventoryLoader, events_loader: AuditEventsLoader, error_type: type[Exception], ) -> None: """Render HMM access audit data without owning DB connection details.""" st.markdown( '
' '감사로그 ( HMM 접근 관리 )' '
', unsafe_allow_html=True, ) st.markdown( '
' 'HMM 백오피스의 사용자·그룹·역할·토큰·접근 정책 변경 이력을 시간순으로 확인합니다. ' '이벤트 유형과 처리 상태로 필터링해 운영 변경의 성공·실패를 추적할 수 있습니다.' '
', unsafe_allow_html=True, ) try: inventory = inventory_loader() except error_type as exc: st.error(str(exc)) return event_types = tuple( str(item.get("event_type") or "").strip() for item in inventory if str(item.get("event_type") or "").strip() ) st.markdown('
조회 조건
', unsafe_allow_html=True) with st.container(key="poc4_hmm_audit_filters"): event_column, status_column = st.columns(2) with event_column: selected_event_type = st.selectbox( "이벤트 유형", options=("", *event_types), format_func=lambda value: "전체 이벤트" if not value else value, key="poc4_hmm_audit_event_filter", ) with status_column: selected_status = st.selectbox( "처리 상태", options=("", "SUCCESS", "FAILURE", "DENIED"), format_func=lambda value: "전체 상태" if not value else value, key="poc4_hmm_audit_status_filter", ) days_column, limit_column, refresh_column = st.columns([1.5, 1, 0.8]) with days_column: days = st.slider( "조회 기간", min_value=1, max_value=90, value=7, format="%d일", key="poc4_hmm_audit_days", ) with limit_column: row_limit = st.number_input( "최대 건수", min_value=10, max_value=500, value=100, step=10, key="poc4_hmm_audit_row_limit", ) with refresh_column: st.markdown('
', unsafe_allow_html=True) if st.button( "새로고침", icon=":material/refresh:", width="stretch", key="poc4_hmm_audit_refresh", ): inventory_loader.clear() events_loader.clear() st.rerun() try: events = events_loader( int(days), int(row_limit), selected_event_type, selected_status ) except error_type as exc: st.error(str(exc)) return success_count = sum( 1 for item in events if str(item.get("status") or "").upper() == "SUCCESS" ) with st.container(key="poc4_hmm_audit_metrics"): type_metric, event_metric, success_metric, failure_metric = st.columns(4) type_metric.metric("이벤트 유형", len(inventory)) event_metric.metric("조회 이벤트", len(events)) success_metric.metric("성공", success_count) failure_metric.metric("실패·거부", len(events) - success_count) st.markdown( '
이벤트 유형 현황
' f'
현재 기록된 이벤트 유형 {len(inventory)}개 · ' '유형별 누적 건수와 최근 발생 시각을 확인합니다.
', unsafe_allow_html=True, ) with st.container(key="poc4_hmm_audit_inventory_panel"): with st.expander("감사 이벤트 유형", expanded=True): if inventory: st.dataframe( [ { "이벤트 유형": str(item.get("event_type") or ""), "누적 건수": int(item.get("event_count") or 0), "최근 발생(KST)": str(item.get("latest_event_time") or ""), } for item in inventory ], column_config={ "이벤트 유형": st.column_config.TextColumn(width="large"), "누적 건수": st.column_config.NumberColumn(width="small"), "최근 발생(KST)": st.column_config.TextColumn(width="medium"), }, hide_index=True, width="stretch", height=min(360, 72 + 36 * len(inventory)), ) else: st.caption("아직 기록된 HMM 접근 관리 이벤트가 없습니다.") st.markdown( '
감사 이벤트
' '
최신 이벤트부터 표시합니다. ' '처리 상태와 대상 식별자, 오류 메시지를 먼저 확인하세요.
', unsafe_allow_html=True, ) show_details = st.toggle( "상세 메시지 표시", value=True, key="poc4_hmm_audit_show_details", ) if not events: st.info("선택한 조건에 해당하는 HMM 접근 관리 이벤트가 없습니다.") return display_rows: list[dict[str, Any]] = [] for event in events: row: dict[str, Any] = { "감사 ID": int(event.get("audit_id") or 0), "발생시각(KST)": str(event.get("event_time") or ""), "이벤트 유형": str(event.get("event_type") or ""), "상태": str(event.get("status") or ""), "토큰 Key ID": event.get("key_id"), "대상 Object ID": event.get("object_id"), "처리 행": event.get("row_count"), "오류 코드": str(event.get("error_code") or ""), } if show_details: row["메시지"] = str(event.get("message") or "") display_rows.append(row) column_config: dict[str, Any] = { "감사 ID": st.column_config.NumberColumn(width="small"), "발생시각(KST)": st.column_config.TextColumn(width="medium"), "이벤트 유형": st.column_config.TextColumn(width="large"), "상태": st.column_config.TextColumn(width="small"), "토큰 Key ID": st.column_config.NumberColumn(width="small"), "대상 Object ID": st.column_config.NumberColumn(width="small"), "처리 행": st.column_config.NumberColumn(width="small"), "오류 코드": st.column_config.TextColumn(width="medium"), } if show_details: column_config["메시지"] = st.column_config.TextColumn(width="large") with st.container(key="poc4_hmm_audit_event_panel"): st.dataframe( display_rows, column_config=column_config, hide_index=True, width="stretch", height=min(640, 104 + 38 * len(display_rows)), )