"""봇 감지 이력 기록 CRUD — '몇 번째 요청/어떤 포트에서 감지됐나'를 축적(패턴 분석용).""" from sqlalchemy import text from common.database.db_session_manager import DB_SESSION_MNG from common.enums import DBType, DBWRType class BotDetectionLog: DB = DBType.MAIN.value async def record(self, event: dict): """감지 이벤트 1건 저장. 로깅 실패가 검색을 막지 않도록 호출부에서 예외를 삼킨다.""" sql = text(""" INSERT INTO bot_detection (source, query, ip_request_no, proxy_port, elapsed_sec, marker, headless, html_len) VALUES (:source, :query, :ip_request_no, :proxy_port, :elapsed_sec, :marker, :headless, :html_len) """) params = {k: event.get(k) for k in ("source", "query", "ip_request_no", "proxy_port", "elapsed_sec", "marker", "headless", "html_len")} s = await DB_SESSION_MNG.start_session(self.DB, DBWRType.DB_WRITE.value) try: await s.execute(sql, params) await s.commit() except Exception: await s.rollback() raise finally: await DB_SESSION_MNG.end_session(self.DB, DBWRType.DB_WRITE.value) async def recent_count(self, minutes: int = 60) -> int: """최근 N분간 봇 감지(차단) 건수 — 차단율 급증 알림·모니터링용.""" sql = text("SELECT count(*) FROM bot_detection WHERE created_at > now() - make_interval(mins => :m)") s = await DB_SESSION_MNG.start_session(self.DB, DBWRType.DB_READ.value) try: return int((await s.execute(sql, {"m": minutes})).scalar() or 0) finally: await DB_SESSION_MNG.end_session(self.DB, DBWRType.DB_READ.value)