import json from config.database import get_connection, release_connection # ============================================ # REPOSITORY LAYER: Tương tác trực tiếp với bảng monitor_config trong PostgreSQL # ============================================ # Luồng đi tổng thể: # Controller → Service → Repository → Database # Repository CHỈ chịu trách nhiệm thực thi SQL, KHÔNG chứa logic nghiệp vụ. # # Bảng monitor_config có quan hệ 1-1 với bảng device: # - Mỗi device có đúng 1 bản ghi monitor_config (tạo tự động khi tạo device) # - Khóa ngoại: device_id → device(id) ON DELETE CASCADE # - Ràng buộc UNIQUE trên device_id → đảm bảo không trùng # ============================================ def _row_to_dict(row): """ Chuyển đổi một dòng kết quả (tuple) từ câu query thành dictionary. Thứ tự các cột phải khớp với SELECT trong các hàm bên dưới. """ if not row: return None return { "id": str(row[0]), "device_id": str(row[1]), "enable_ping": row[2], "ping_count": row[3], "ping_timeout": row[4], "ping_interval": row[5], "enable_snmp": row[6], "snmp_version": row[7], "snmp_community": row[8], "snmp_port": row[9], "snmp_interval": row[10], "snmp_timeout": row[11], "snmp_custom_oids": row[12], # PostgreSQL JSONB → Python dict (tự động) "created": row[13].isoformat() if row[13] else None, "modified": row[14].isoformat() if row[14] else None } # ============================================ # FIND BY DEVICE ID: Lấy cấu hình giám sát của một thiết bị # ============================================ # Luồng đi: # GET /api/devices/{device_id}/monitor-config # → Controller → Service gọi hàm này # → Trả về dict nếu tìm thấy, None nếu không # ============================================ def find_monitor_config_by_device_id(device_id): conn = get_connection() cur = None try: cur = conn.cursor() cur.execute(""" SELECT id, device_id, enable_ping, ping_count, ping_timeout, ping_interval, enable_snmp, snmp_version, snmp_community, snmp_port, snmp_interval, snmp_timeout, snmp_custom_oids, created, modified FROM monitor_config WHERE device_id = %s """, (device_id,)) row = cur.fetchone() return _row_to_dict(row) if row else None finally: if cur: cur.close() release_connection(conn) # ============================================ # UPDATE: Cập nhật cấu hình giám sát của thiết bị # ============================================ # Luồng đi: # PUT /api/devices/{device_id}/monitor-config # → Controller validate body → Service kiểm tra tồn tại → Repository cập nhật DB # # Cách hoạt động: # - Dùng Dynamic UPDATE: chỉ cập nhật các trường có trong data (không ghi đè toàn bộ) # - Trường snmp_custom_oids cần chuyển sang JSON string trước khi lưu vào JSONB # - Tự động cập nhật cột modified = CURRENT_TIMESTAMP # - RETURNING: trả về toàn bộ row sau khi update (tránh phải SELECT lại) # ============================================ def update_monitor_config_db(device_id, data): conn = get_connection() cur = None try: cur = conn.cursor() # Xây dựng câu lệnh UPDATE động — chỉ SET các trường có trong data update_fields = [] params = [] # Danh sách các trường được phép cập nhật allowed_fields = [ "enable_ping", "ping_count", "ping_timeout", "ping_interval", "enable_snmp", "snmp_version", "snmp_community", "snmp_port", "snmp_interval", "snmp_timeout", "snmp_custom_oids" ] for key in allowed_fields: if key in data: value = data[key] # ⚠️ XỬ LÝ ĐẶC BIỆT: snmp_custom_oids là JSONB trong PostgreSQL # Python dict cần chuyển sang JSON string trước khi INSERT/UPDATE # Ví dụ: {"sysName": "1.3.6.1.2.1.1.5.0"} → '{"sysName": "1.3.6.1.2.1.1.5.0"}' if key == "snmp_custom_oids" and isinstance(value, dict): value = json.dumps(value) update_fields.append(f"{key} = %s") params.append(value) if not update_fields: # Không có trường nào cần cập nhật → trả về config hiện tại return find_monitor_config_by_device_id(device_id) # Thêm cập nhật thời gian modified update_fields.append("modified = CURRENT_TIMESTAMP") sql = f""" UPDATE monitor_config SET {', '.join(update_fields)} WHERE device_id = %s RETURNING id, device_id, enable_ping, ping_count, ping_timeout, ping_interval, enable_snmp, snmp_version, snmp_community, snmp_port, snmp_interval, snmp_timeout, snmp_custom_oids, created, modified """ params.append(device_id) cur.execute(sql, tuple(params)) row = cur.fetchone() conn.commit() return _row_to_dict(row) if row else None except Exception: conn.rollback() raise finally: if cur: cur.close() release_connection(conn)