Device
This commit is contained in:
293
backend/modules/device/repository.py
Normal file
293
backend/modules/device/repository.py
Normal file
@@ -0,0 +1,293 @@
|
||||
from config.database import get_connection, release_connection
|
||||
|
||||
def _row_to_dict(row):
|
||||
"""
|
||||
Chuyển đổi một dòng kết quả từ tuple (từ câu lệnh JOIN) thành dictionary.
|
||||
"""
|
||||
if not row:
|
||||
return None
|
||||
|
||||
device_dict = {
|
||||
"id": str(row[0]),
|
||||
"device_type_id": str(row[1]),
|
||||
"name": row[2],
|
||||
"description": row[3],
|
||||
"ip_address": row[4],
|
||||
"port": row[5],
|
||||
"latitude": row[6],
|
||||
"longitude": row[7],
|
||||
"color": row[8],
|
||||
"avatar_url": row[9],
|
||||
"is_active": row[10],
|
||||
"created": row[11].isoformat() if row[11] else None,
|
||||
"modified": row[12].isoformat() if row[12] else None
|
||||
}
|
||||
|
||||
# Nếu câu truy vấn có JOIN với device_type
|
||||
if len(row) > 13:
|
||||
device_dict["device_type_name"] = row[13]
|
||||
device_dict["device_type_icon"] = row[14]
|
||||
|
||||
return device_dict
|
||||
|
||||
|
||||
def find_all_devices():
|
||||
"""
|
||||
Lấy danh sách tất cả các thiết bị trong database, JOIN với bảng device_type
|
||||
để hiển thị tên loại thiết bị và icon đại diện.
|
||||
"""
|
||||
conn = get_connection()
|
||||
cur = None
|
||||
try:
|
||||
cur = conn.cursor()
|
||||
cur.execute("""
|
||||
SELECT
|
||||
d.id, d.device_type_id, d.name, d.description, d.ip_address, d.port,
|
||||
d.latitude, d.longitude, d.color, d.avatar_url, d.is_active, d.created, d.modified,
|
||||
dt.name as device_type_name, dt.icon_url as device_type_icon
|
||||
FROM device d
|
||||
JOIN device_type dt ON d.device_type_id = dt.id
|
||||
ORDER BY d.created DESC
|
||||
""")
|
||||
rows = cur.fetchall()
|
||||
|
||||
devices = []
|
||||
for row in rows:
|
||||
devices.append(_row_to_dict(row))
|
||||
|
||||
return devices
|
||||
finally:
|
||||
if cur:
|
||||
cur.close()
|
||||
release_connection(conn)
|
||||
|
||||
|
||||
def find_device_by_id(device_id):
|
||||
"""
|
||||
Tìm thiết bị theo ID, JOIN với device_type để lấy thông tin chi tiết.
|
||||
"""
|
||||
conn = get_connection()
|
||||
cur = None
|
||||
try:
|
||||
cur = conn.cursor()
|
||||
cur.execute("""
|
||||
SELECT
|
||||
d.id, d.device_type_id, d.name, d.description, d.ip_address, d.port,
|
||||
d.latitude, d.longitude, d.color, d.avatar_url, d.is_active, d.created, d.modified,
|
||||
dt.name as device_type_name, dt.icon_url as device_type_icon
|
||||
FROM device d
|
||||
JOIN device_type dt ON d.device_type_id = dt.id
|
||||
WHERE d.id = %s
|
||||
""", (device_id,))
|
||||
|
||||
row = cur.fetchone()
|
||||
return _row_to_dict(row) if row else None
|
||||
finally:
|
||||
if cur:
|
||||
cur.close()
|
||||
release_connection(conn)
|
||||
|
||||
|
||||
def find_device_by_name(name):
|
||||
"""
|
||||
Tìm thiết bị theo tên (không phân biệt hoa thường) để phục vụ validate trùng tên.
|
||||
"""
|
||||
conn = get_connection()
|
||||
cur = None
|
||||
try:
|
||||
cur = conn.cursor()
|
||||
cur.execute("""
|
||||
SELECT id, device_type_id, name, description, ip_address, port, latitude, longitude, color, avatar_url, is_active, created, modified
|
||||
FROM device
|
||||
WHERE LOWER(name) = LOWER(%s)
|
||||
""", (name,))
|
||||
|
||||
row = cur.fetchone()
|
||||
return _row_to_dict(row) if row else None
|
||||
finally:
|
||||
if cur:
|
||||
cur.close()
|
||||
release_connection(conn)
|
||||
|
||||
|
||||
def find_device_by_ip(ip_address):
|
||||
"""
|
||||
Tìm thiết bị theo địa chỉ IP để phục vụ validate tránh trùng lặp IP thiết bị.
|
||||
"""
|
||||
conn = get_connection()
|
||||
cur = None
|
||||
try:
|
||||
cur = conn.cursor()
|
||||
cur.execute("""
|
||||
SELECT id, device_type_id, name, description, ip_address, port, latitude, longitude, color, avatar_url, is_active, created, modified
|
||||
FROM device
|
||||
WHERE ip_address = %s
|
||||
""", (ip_address,))
|
||||
|
||||
row = cur.fetchone()
|
||||
return _row_to_dict(row) if row else None
|
||||
finally:
|
||||
if cur:
|
||||
cur.close()
|
||||
release_connection(conn)
|
||||
|
||||
|
||||
def insert_device(data):
|
||||
"""
|
||||
Tạo mới một thiết bị và cấu hình mặc định (MonitorConfig & AlertConfig)
|
||||
trong cùng một database transaction.
|
||||
"""
|
||||
conn = get_connection()
|
||||
cur = None
|
||||
try:
|
||||
cur = conn.cursor()
|
||||
|
||||
# 1. Thêm thiết bị vào bảng device
|
||||
cur.execute("""
|
||||
INSERT INTO device (device_type_id, name, description, ip_address, port, latitude, longitude, color, avatar_url, is_active)
|
||||
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
|
||||
RETURNING id, device_type_id, name, description, ip_address, port, latitude, longitude, color, avatar_url, is_active, created, modified
|
||||
""", (
|
||||
data["device_type_id"],
|
||||
data["name"],
|
||||
data.get("description"),
|
||||
data["ip_address"],
|
||||
data.get("port"),
|
||||
data["latitude"],
|
||||
data["longitude"],
|
||||
data["color"],
|
||||
data.get("avatar_url"),
|
||||
data.get("is_active", True)
|
||||
))
|
||||
|
||||
device_row = cur.fetchone()
|
||||
device_id = device_row[0]
|
||||
|
||||
# 2. Thêm cấu hình giám sát mặc định (MonitorConfig) cho thiết bị vừa tạo
|
||||
# enable_ping mặc định bật True để thực hiện Ping giám sát
|
||||
cur.execute("""
|
||||
INSERT INTO monitor_config (device_id, enable_ping, ping_count, ping_timeout, ping_interval, enable_snmp)
|
||||
VALUES (%s, %s, %s, %s, %s, %s)
|
||||
""", (
|
||||
device_id,
|
||||
True, # enable_ping
|
||||
3, # ping_count
|
||||
5, # ping_timeout (giây)
|
||||
60, # ping_interval (giây)
|
||||
False # enable_snmp
|
||||
))
|
||||
|
||||
# 3. Thêm cấu hình cảnh báo mặc định (AlertConfig) cho thiết bị vừa tạo
|
||||
cur.execute("""
|
||||
INSERT INTO alert_config (device_id, is_enabled, fail_threshold, cooldown_minutes, notify_web, notify_email)
|
||||
VALUES (%s, %s, %s, %s, %s, %s)
|
||||
""", (
|
||||
device_id,
|
||||
True, # is_enabled
|
||||
3, # fail_threshold (số lần fail liên tiếp trước khi alert)
|
||||
30, # cooldown_minutes (thời gian tránh spam alert)
|
||||
True, # notify_web (hiển thị thông báo trên web)
|
||||
False # notify_email (mặc định tắt gửi email)
|
||||
))
|
||||
|
||||
# 4. Lấy lại thiết bị kèm thông tin DeviceType đã JOIN để trả về đầy đủ dữ liệu
|
||||
cur.execute("""
|
||||
SELECT
|
||||
d.id, d.device_type_id, d.name, d.description, d.ip_address, d.port,
|
||||
d.latitude, d.longitude, d.color, d.avatar_url, d.is_active, d.created, d.modified,
|
||||
dt.name as device_type_name, dt.icon_url as device_type_icon
|
||||
FROM device d
|
||||
JOIN device_type dt ON d.device_type_id = dt.id
|
||||
WHERE d.id = %s
|
||||
""", (device_id,))
|
||||
full_row = cur.fetchone()
|
||||
|
||||
# Commit toàn bộ transaction
|
||||
conn.commit()
|
||||
|
||||
return _row_to_dict(full_row)
|
||||
|
||||
except Exception:
|
||||
conn.rollback()
|
||||
raise
|
||||
finally:
|
||||
if cur:
|
||||
cur.close()
|
||||
release_connection(conn)
|
||||
|
||||
|
||||
def update_device_db(device_id, data):
|
||||
"""
|
||||
Cập nhật thông tin chi tiết của một thiết bị.
|
||||
"""
|
||||
conn = get_connection()
|
||||
cur = None
|
||||
try:
|
||||
cur = conn.cursor()
|
||||
|
||||
update_fields = []
|
||||
params = []
|
||||
|
||||
fields_list = [
|
||||
"device_type_id", "name", "description",
|
||||
"ip_address", "port", "latitude",
|
||||
"longitude", "color", "avatar_url", "is_active"
|
||||
]
|
||||
|
||||
for key in fields_list:
|
||||
if key in data:
|
||||
update_fields.append(f"{key} = %s")
|
||||
params.append(data[key])
|
||||
|
||||
if not update_fields:
|
||||
return find_device_by_id(device_id)
|
||||
|
||||
update_fields.append("modified = CURRENT_TIMESTAMP")
|
||||
|
||||
sql = f"""
|
||||
UPDATE device
|
||||
SET {', '.join(update_fields)}
|
||||
WHERE id = %s
|
||||
RETURNING id, device_type_id, name, description, ip_address, port, latitude, longitude, color, avatar_url, is_active, created, modified
|
||||
"""
|
||||
params.append(device_id)
|
||||
|
||||
cur.execute(sql, tuple(params))
|
||||
row = cur.fetchone()
|
||||
conn.commit()
|
||||
|
||||
if row:
|
||||
# Lấy chi tiết có kèm JOIN device_type để trả về đầy đủ
|
||||
return find_device_by_id(device_id)
|
||||
return None
|
||||
|
||||
except Exception:
|
||||
conn.rollback()
|
||||
raise
|
||||
finally:
|
||||
if cur:
|
||||
cur.close()
|
||||
release_connection(conn)
|
||||
|
||||
|
||||
def delete_device_db(device_id):
|
||||
"""
|
||||
Xóa thiết bị khỏi database. Do có khóa ngoại với Cascade Delete,
|
||||
tất cả các dòng liên quan trong monitor_config, alert_config, device_status, alert_log sẽ tự động bị xóa.
|
||||
"""
|
||||
conn = get_connection()
|
||||
cur = None
|
||||
try:
|
||||
cur = conn.cursor()
|
||||
cur.execute("""
|
||||
DELETE FROM device
|
||||
WHERE id = %s
|
||||
""", (device_id,))
|
||||
conn.commit()
|
||||
except Exception:
|
||||
conn.rollback()
|
||||
raise
|
||||
finally:
|
||||
if cur:
|
||||
cur.close()
|
||||
release_connection(conn)
|
||||
Reference in New Issue
Block a user