Добавление заметок к устройствам. Возможность добавить, удалить, изменить заметку. Синхронизируется с нынешней и старой картой
This commit is contained in:
parent
82a91c0e88
commit
090cbcd477
3 changed files with 92 additions and 25 deletions
|
|
@ -1,5 +1,6 @@
|
||||||
from fastapi import APIRouter, HTTPException
|
from fastapi import APIRouter, HTTPException
|
||||||
from typing import List
|
from typing import List
|
||||||
|
from pydantic import BaseModel
|
||||||
from models.subnet import SubnetScanResult
|
from models.subnet import SubnetScanResult
|
||||||
from models.wifi import WifiScanResult
|
from models.wifi import WifiScanResult
|
||||||
from services.subnet_service import SubnetService
|
from services.subnet_service import SubnetService
|
||||||
|
|
@ -7,6 +8,9 @@ from services.wifi_service import WifiService
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
|
class NoteRequest(BaseModel):
|
||||||
|
note: str
|
||||||
|
|
||||||
@router.get("/scan", response_model=SubnetScanResult)
|
@router.get("/scan", response_model=SubnetScanResult)
|
||||||
def scan_subnet(subnet: str = "192.168.1.0/24", ports: List[int] = None):
|
def scan_subnet(subnet: str = "192.168.1.0/24", ports: List[int] = None):
|
||||||
"""Сканирует локальную подсеть и возвращает данные об устройствах"""
|
"""Сканирует локальную подсеть и возвращает данные об устройствах"""
|
||||||
|
|
@ -26,3 +30,36 @@ def scan_wifi(timeout: int = 30):
|
||||||
return result
|
return result
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise HTTPException(status_code=500, detail=f"Ошибка сканирования Wi-Fi: {str(e)}")
|
raise HTTPException(status_code=500, detail=f"Ошибка сканирования Wi-Fi: {str(e)}")
|
||||||
|
|
||||||
|
@router.post("/devices/{mac}/note")
|
||||||
|
def add_note(mac: str, request: NoteRequest):
|
||||||
|
"""Добавляет заметку для устройства по MAC-адресу"""
|
||||||
|
try:
|
||||||
|
subnet_service = SubnetService()
|
||||||
|
if not subnet_service.add_note(mac, request.note):
|
||||||
|
raise HTTPException(status_code=404, detail=f"Устройство с MAC {mac} не найдено")
|
||||||
|
return {"message": f"Заметка '{request.note}' добавлена для устройства {mac}"}
|
||||||
|
except Exception as e:
|
||||||
|
raise HTTPException(status_code=500, detail=f"Ошибка добавления заметки: {str(e)}")
|
||||||
|
|
||||||
|
@router.put("/devices/{mac}/note")
|
||||||
|
def update_note(mac: str, request: NoteRequest):
|
||||||
|
"""Обновляет заметку для устройства по MAC-адресу"""
|
||||||
|
try:
|
||||||
|
subnet_service = SubnetService()
|
||||||
|
if not subnet_service.update_note(mac, request.note):
|
||||||
|
raise HTTPException(status_code=404, detail=f"Устройство с MAC {mac} не найдено")
|
||||||
|
return {"message": f"Заметка '{request.note}' обновлена для устройства {mac}"}
|
||||||
|
except Exception as e:
|
||||||
|
raise HTTPException(status_code=500, detail=f"Ошибка обновления заметки: {str(e)}")
|
||||||
|
|
||||||
|
@router.delete("/devices/{mac}/note")
|
||||||
|
def delete_note(mac: str):
|
||||||
|
"""Удаляет заметку для устройства по MAC-адресу"""
|
||||||
|
try:
|
||||||
|
subnet_service = SubnetService()
|
||||||
|
if not subnet_service.delete_note(mac):
|
||||||
|
raise HTTPException(status_code=404, detail=f"Устройство с MAC {mac} не найдено")
|
||||||
|
return {"message": f"Заметка удалена для устройства {mac}"}
|
||||||
|
except Exception as e:
|
||||||
|
raise HTTPException(status_code=500, detail=f"Ошибка удаления заметки: {str(e)}")
|
||||||
|
|
@ -6,9 +6,10 @@ class Device(BaseModel):
|
||||||
mac: Optional[str] = None
|
mac: Optional[str] = None
|
||||||
hostname: Optional[str] = None
|
hostname: Optional[str] = None
|
||||||
vendor: Optional[str] = None
|
vendor: Optional[str] = None
|
||||||
open_ports: Optional[List[str]] = None
|
open_ports: List[str] = []
|
||||||
status: str = "online"
|
status: str
|
||||||
last_seen: Optional[str] = None
|
last_seen: Optional[str] = None
|
||||||
|
note: Optional[str] = None
|
||||||
|
|
||||||
class SubnetScanResult(BaseModel):
|
class SubnetScanResult(BaseModel):
|
||||||
devices: List[Device]
|
devices: List[Device] = []
|
||||||
|
|
@ -22,10 +22,7 @@ from scapy.sendrecv import srp, sr1
|
||||||
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s")
|
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s")
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class NetworkScanner:
|
class NetworkScanner:
|
||||||
"""Класс для сканирования сети с помощью ARP-запросов."""
|
|
||||||
|
|
||||||
def __init__(self, interface: str):
|
def __init__(self, interface: str):
|
||||||
self.interface = interface
|
self.interface = interface
|
||||||
conf.iface = interface
|
conf.iface = interface
|
||||||
|
|
@ -41,10 +38,7 @@ class NetworkScanner:
|
||||||
logger.error(f"Ошибка ARP-сканирования подсети {subnet}: {str(e)}")
|
logger.error(f"Ошибка ARP-сканирования подсети {subnet}: {str(e)}")
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
||||||
class PortScanner:
|
class PortScanner:
|
||||||
"""Класс для сканирования портов на устройстве."""
|
|
||||||
|
|
||||||
def __init__(self, interface: str):
|
def __init__(self, interface: str):
|
||||||
self.interface = interface
|
self.interface = interface
|
||||||
|
|
||||||
|
|
@ -67,7 +61,7 @@ class PortScanner:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
max_workers = min(len(port_config.get_ports()), 15) # Снижено с 100
|
max_workers = min(len(port_config.get_ports()), 15)
|
||||||
with ThreadPoolExecutor(max_workers=max_workers) as executor:
|
with ThreadPoolExecutor(max_workers=max_workers) as executor:
|
||||||
results = executor.map(check_port, port_config.get_ports())
|
results = executor.map(check_port, port_config.get_ports())
|
||||||
open_ports = [port for port in results if port is not None]
|
open_ports = [port for port in results if port is not None]
|
||||||
|
|
@ -77,10 +71,7 @@ class PortScanner:
|
||||||
logger.warning(f"Ошибка при сканировании портов для IP {ip}: {str(e)}")
|
logger.warning(f"Ошибка при сканировании портов для IP {ip}: {str(e)}")
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
||||||
class DeviceInfoCollector:
|
class DeviceInfoCollector:
|
||||||
"""Класс для сбора информации об устройстве."""
|
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.mac_lookup = MacLookup()
|
self.mac_lookup = MacLookup()
|
||||||
|
|
||||||
|
|
@ -98,10 +89,7 @@ class DeviceInfoCollector:
|
||||||
except:
|
except:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
class DeviceStorage:
|
class DeviceStorage:
|
||||||
"""Класс для хранения и управления картой устройств в JSON."""
|
|
||||||
|
|
||||||
def __init__(self, storage_path: str = "storage/devices/devices.json"):
|
def __init__(self, storage_path: str = "storage/devices/devices.json"):
|
||||||
self.storage_file = Path(storage_path)
|
self.storage_file = Path(storage_path)
|
||||||
self.storage_file.parent.mkdir(parents=True, exist_ok=True)
|
self.storage_file.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
@ -149,10 +137,7 @@ class DeviceStorage:
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Ошибка при сохранении устройств: {str(e)}")
|
logger.error(f"Ошибка при сохранении устройств: {str(e)}")
|
||||||
|
|
||||||
|
|
||||||
class SubnetService:
|
class SubnetService:
|
||||||
"""Сервис для сканирования подсети и управления картой устройств."""
|
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.interface = self._get_default_interface()
|
self.interface = self._get_default_interface()
|
||||||
self.network_scanner = NetworkScanner(self.interface)
|
self.network_scanner = NetworkScanner(self.interface)
|
||||||
|
|
@ -206,7 +191,8 @@ class SubnetService:
|
||||||
vendor=self.device_info.get_vendor(mac),
|
vendor=self.device_info.get_vendor(mac),
|
||||||
open_ports=self.port_scanner.scan(ip) or existing_device.open_ports,
|
open_ports=self.port_scanner.scan(ip) or existing_device.open_ports,
|
||||||
status="online",
|
status="online",
|
||||||
last_seen=now
|
last_seen=now,
|
||||||
|
note=existing_device.note
|
||||||
)
|
)
|
||||||
vendor = self.device_info.get_vendor(mac)
|
vendor = self.device_info.get_vendor(mac)
|
||||||
hostname = self.device_info.get_hostname(ip)
|
hostname = self.device_info.get_hostname(ip)
|
||||||
|
|
@ -218,14 +204,14 @@ class SubnetService:
|
||||||
vendor=vendor,
|
vendor=vendor,
|
||||||
open_ports=open_ports,
|
open_ports=open_ports,
|
||||||
status="online",
|
status="online",
|
||||||
last_seen=now
|
last_seen=now,
|
||||||
|
note=None
|
||||||
)
|
)
|
||||||
|
|
||||||
with ThreadPoolExecutor(max_workers=10) as executor:
|
with ThreadPoolExecutor(max_workers=10) as executor:
|
||||||
device_results = executor.map(process_device, discovered_devices)
|
device_results = executor.map(process_device, discovered_devices)
|
||||||
devices.extend([d for d in device_results if d is not None])
|
devices.extend([d for d in device_results if d is not None])
|
||||||
|
|
||||||
# Добавляет устройства со старой карты, в нынешнюю при отсутствии со статусом offline
|
|
||||||
active_macs = {d.mac for d in devices if d.mac}
|
active_macs = {d.mac for d in devices if d.mac}
|
||||||
for mac, device in saved_devices.items():
|
for mac, device in saved_devices.items():
|
||||||
if mac not in active_macs:
|
if mac not in active_macs:
|
||||||
|
|
@ -273,10 +259,53 @@ class SubnetService:
|
||||||
vendor=vendor,
|
vendor=vendor,
|
||||||
open_ports=open_ports,
|
open_ports=open_ports,
|
||||||
status="online",
|
status="online",
|
||||||
last_seen=now
|
last_seen=now,
|
||||||
|
note=None
|
||||||
)
|
)
|
||||||
logger.warning(f"Локальное устройство не найдено в подсети {subnet}")
|
logger.warning(f"Локальное устройство не найдено в подсети {subnet}")
|
||||||
return None
|
return None
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"Не удалось получить локальное устройство: {str(e)}")
|
logger.warning(f"Не удалось получить локальное устройство: {str(e)}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def add_note(self, mac: str, note: str) -> bool:
|
||||||
|
try:
|
||||||
|
saved_devices = self.storage.load_devices()
|
||||||
|
if mac not in saved_devices:
|
||||||
|
logger.warning(f"Устройство с MAC {mac} не найдено")
|
||||||
|
return False
|
||||||
|
saved_devices[mac].note = note
|
||||||
|
self.storage.save_devices(list(saved_devices.values()))
|
||||||
|
logger.info(f"Добавлена заметка '{note}' для устройства с MAC {mac}")
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Ошибка при добавлении заметки для MAC {mac}: {str(e)}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def update_note(self, mac: str, note: str) -> bool:
|
||||||
|
try:
|
||||||
|
saved_devices = self.storage.load_devices()
|
||||||
|
if mac not in saved_devices:
|
||||||
|
logger.warning(f"Устройство с MAC {mac} не найдено")
|
||||||
|
return False
|
||||||
|
saved_devices[mac].note = note
|
||||||
|
self.storage.save_devices(list(saved_devices.values()))
|
||||||
|
logger.info(f"Обновлена заметка '{note}' для устройства с MAC {mac}")
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Ошибка при обновлении заметки для MAC {mac}: {str(e)}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def delete_note(self, mac: str) -> bool:
|
||||||
|
try:
|
||||||
|
saved_devices = self.storage.load_devices()
|
||||||
|
if mac not in saved_devices:
|
||||||
|
logger.warning(f"Устройство с MAC {mac} не найдено")
|
||||||
|
return False
|
||||||
|
saved_devices[mac].note = None
|
||||||
|
self.storage.save_devices(list(saved_devices.values()))
|
||||||
|
logger.info(f"Удалена заметка для устройства с MAC {mac}")
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Ошибка при удалении заметки для MAC {mac}: {str(e)}")
|
||||||
|
return False
|
||||||
Loading…
Add table
Add a link
Reference in a new issue