Форматирование всего серверного кода по стандартам (black)
This commit is contained in:
parent
e61ffd793c
commit
06a5a758a3
28 changed files with 611 additions and 305 deletions
|
|
@ -26,7 +26,9 @@ async def register(user: UserCreate):
|
||||||
raise e
|
raise e
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Ошибка в register: {type(e).__name__} - {str(e)}")
|
logger.error(f"Ошибка в register: {type(e).__name__} - {str(e)}")
|
||||||
raise HTTPException(status_code=500, detail="Не удалось зарегистрировать пользователя")
|
raise HTTPException(
|
||||||
|
status_code=500, detail="Не удалось зарегистрировать пользователя"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.post("/login", response_model=Token)
|
@router.post("/login", response_model=Token)
|
||||||
|
|
@ -36,13 +38,17 @@ async def login(user: UserLogin, request: Request):
|
||||||
client_id = request.client.host
|
client_id = request.client.host
|
||||||
logger.debug(f"Попытка авторизации для client_id {client_id}")
|
logger.debug(f"Попытка авторизации для client_id {client_id}")
|
||||||
result = auth_service.login(user, client_id)
|
result = auth_service.login(user, client_id)
|
||||||
logger.info(f"Успешная авторизация для client_id {client_id}, session_id: {result['session_id']}")
|
logger.info(
|
||||||
|
f"Успешная авторизация для client_id {client_id}, session_id: {result['session_id']}"
|
||||||
|
)
|
||||||
return result
|
return result
|
||||||
except HTTPException as e:
|
except HTTPException as e:
|
||||||
raise e
|
raise e
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Ошибка в login: {type(e).__name__} - {str(e)}")
|
logger.error(f"Ошибка в login: {type(e).__name__} - {str(e)}")
|
||||||
raise HTTPException(status_code=500, detail="Не удалось авторизовать пользователя")
|
raise HTTPException(
|
||||||
|
status_code=500, detail="Не удалось авторизовать пользователя"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/me")
|
@router.get("/me")
|
||||||
|
|
@ -55,13 +61,15 @@ async def get_current_user(request: Request):
|
||||||
return {
|
return {
|
||||||
"user": result["user"],
|
"user": result["user"],
|
||||||
"access_token": result["access_token"],
|
"access_token": result["access_token"],
|
||||||
"token_type": "bearer"
|
"token_type": "bearer",
|
||||||
}
|
}
|
||||||
except HTTPException as e:
|
except HTTPException as e:
|
||||||
raise e
|
raise e
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Ошибка в get_current_user: {type(e).__name__} - {str(e)}")
|
logger.error(f"Ошибка в get_current_user: {type(e).__name__} - {str(e)}")
|
||||||
raise HTTPException(status_code=500, detail="Не удалось получить данные пользователя")
|
raise HTTPException(
|
||||||
|
status_code=500, detail="Не удалось получить данные пользователя"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/logout")
|
@router.get("/logout")
|
||||||
|
|
@ -90,7 +98,7 @@ async def check_users_file():
|
||||||
return {
|
return {
|
||||||
"exists": False,
|
"exists": False,
|
||||||
"has_users": False,
|
"has_users": False,
|
||||||
"message": "Файл users.json не существует"
|
"message": "Файл users.json не существует",
|
||||||
}
|
}
|
||||||
|
|
||||||
data = storage._read_data()
|
data = storage._read_data()
|
||||||
|
|
@ -100,15 +108,17 @@ async def check_users_file():
|
||||||
return {
|
return {
|
||||||
"exists": True,
|
"exists": True,
|
||||||
"has_users": True,
|
"has_users": True,
|
||||||
"message": "Файл users.json существует и содержит пользователей"
|
"message": "Файл users.json существует и содержит пользователей",
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
logger.info(f"Файл {file_path} существует, но не содержит пользователей")
|
logger.info(f"Файл {file_path} существует, но не содержит пользователей")
|
||||||
return {
|
return {
|
||||||
"exists": True,
|
"exists": True,
|
||||||
"has_users": False,
|
"has_users": False,
|
||||||
"message": "Файл users.json существует, но не содержит пользователей"
|
"message": "Файл users.json существует, но не содержит пользователей",
|
||||||
}
|
}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Ошибка при проверке файла users.json: {type(e).__name__} - {str(e)}")
|
logger.error(
|
||||||
|
f"Ошибка при проверке файла users.json: {type(e).__name__} - {str(e)}"
|
||||||
|
)
|
||||||
raise HTTPException(status_code=500, detail="Ошибка при проверке файла")
|
raise HTTPException(status_code=500, detail="Ошибка при проверке файла")
|
||||||
|
|
@ -123,10 +123,7 @@ async def get_zone(zone: str = Path(..., description="Zone name")) -> FirewallZo
|
||||||
try:
|
try:
|
||||||
zone_info = firewalld.get_zone_settings(zone)
|
zone_info = firewalld.get_zone_settings(zone)
|
||||||
return FirewallZone(
|
return FirewallZone(
|
||||||
name=zone,
|
name=zone, target=FirewallTarget.DEFAULT, interfaces=[], **zone_info
|
||||||
target=FirewallTarget.DEFAULT,
|
|
||||||
interfaces=[],
|
|
||||||
**zone_info
|
|
||||||
)
|
)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ from services.ip_service import IPService
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
ip_service = IPService()
|
ip_service = IPService()
|
||||||
|
|
||||||
|
|
||||||
@router.post("/check_ip")
|
@router.post("/check_ip")
|
||||||
async def check_ip_config(data: Dict[str, Any] = Body(...)):
|
async def check_ip_config(data: Dict[str, Any] = Body(...)):
|
||||||
"""Проверяет доступность IP-адреса"""
|
"""Проверяет доступность IP-адреса"""
|
||||||
|
|
@ -12,26 +13,40 @@ async def check_ip_config(data: Dict[str, Any] = Body(...)):
|
||||||
result = await ip_service.check_ip_config(data)
|
result = await ip_service.check_ip_config(data)
|
||||||
return result
|
return result
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise HTTPException(status_code=500, detail=f"Ошибка проверки IP конфигурации: {str(e)}")
|
raise HTTPException(
|
||||||
|
status_code=500, detail=f"Ошибка проверки IP конфигурации: {str(e)}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.post("/apply_ip")
|
@router.post("/apply_ip")
|
||||||
async def apply_ip_config(data: Dict[str, Any] = Body(...)):
|
async def apply_ip_config(data: Dict[str, Any] = Body(...)):
|
||||||
"""Применяет проверенные IP-настройки"""
|
"""Применяет проверенные IP-настройки"""
|
||||||
try:
|
try:
|
||||||
await ip_service.apply_ip_config(data)
|
await ip_service.apply_ip_config(data)
|
||||||
return {"success": True, "message": f"Настройки сетевого профиля для {data['connection_id']} применены"}
|
return {
|
||||||
|
"success": True,
|
||||||
|
"message": f"Настройки сетевого профиля для {data['connection_id']} применены",
|
||||||
|
}
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
raise HTTPException(status_code=404, detail=str(e))
|
raise HTTPException(status_code=404, detail=str(e))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise HTTPException(status_code=500, detail=f"Ошибка проверки IP конфигурации: {str(e)}")
|
raise HTTPException(
|
||||||
|
status_code=500, detail=f"Ошибка проверки IP конфигурации: {str(e)}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.post("/save_ip")
|
@router.post("/save_ip")
|
||||||
async def save_ip_config(data: Dict[str, Any] = Body(...)):
|
async def save_ip_config(data: Dict[str, Any] = Body(...)):
|
||||||
"""Применяет проверенные IP-настройки (Нужен для того, чтобы откатить настройки сетевого профиля обратно)"""
|
"""Применяет проверенные IP-настройки (Нужен для того, чтобы откатить настройки сетевого профиля обратно)"""
|
||||||
try:
|
try:
|
||||||
await ip_service.apply_ip_config(data)
|
await ip_service.apply_ip_config(data)
|
||||||
return {"success": True, "message": f"Настройки сетевого профиля для {data['connection_id']} применены"}
|
return {
|
||||||
|
"success": True,
|
||||||
|
"message": f"Настройки сетевого профиля для {data['connection_id']} применены",
|
||||||
|
}
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
raise HTTPException(status_code=404, detail=str(e))
|
raise HTTPException(status_code=404, detail=str(e))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise HTTPException(status_code=500, detail=f"Ошибка проверки IP конфигурации: {str(e)}")
|
raise HTTPException(
|
||||||
|
status_code=500, detail=f"Ошибка проверки IP конфигурации: {str(e)}"
|
||||||
|
)
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,13 @@ import logging
|
||||||
from typing import Dict, Tuple, List, Union
|
from typing import Dict, Tuple, List, Union
|
||||||
|
|
||||||
from services.network_service import NetworkService
|
from services.network_service import NetworkService
|
||||||
from models.network import NetworkInterfacesContainer, Activation, Deactivation, NetworkInterface, WiFiDevice
|
from models.network import (
|
||||||
|
NetworkInterfacesContainer,
|
||||||
|
Activation,
|
||||||
|
Deactivation,
|
||||||
|
NetworkInterface,
|
||||||
|
WiFiDevice,
|
||||||
|
)
|
||||||
|
|
||||||
logging.basicConfig(level=logging.INFO)
|
logging.basicConfig(level=logging.INFO)
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
@ -19,19 +25,19 @@ class ContainerPositionManager:
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.positions: Dict[str, Dict[str, Union[int, bool]]] = {
|
self.positions: Dict[str, Dict[str, Union[int, bool]]] = {
|
||||||
'activation': {'x': 0, 'y': 0, 'isOpen': False},
|
"activation": {"x": 0, "y": 0, "isOpen": False},
|
||||||
'deactivation': {'x': 0, 'y': 0, 'isOpen': False}
|
"deactivation": {"x": 0, "y": 0, "isOpen": False},
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_position_and_state(self, container_name: str) -> Tuple[int, int, bool]:
|
def get_position_and_state(self, container_name: str) -> Tuple[int, int, bool]:
|
||||||
container = self.positions.get(container_name)
|
container = self.positions.get(container_name)
|
||||||
if not container:
|
if not container:
|
||||||
raise ValueError(f"Неверное имя контейнера: {container_name}")
|
raise ValueError(f"Неверное имя контейнера: {container_name}")
|
||||||
return container['x'], container['y'], container['isOpen']
|
return container["x"], container["y"], container["isOpen"]
|
||||||
|
|
||||||
def update_position(self, container_id: int, x: int, y: int, isOpen: bool) -> None:
|
def update_position(self, container_id: int, x: int, y: int, isOpen: bool) -> None:
|
||||||
container_name = 'activation' if container_id == 1 else 'deactivation'
|
container_name = "activation" if container_id == 1 else "deactivation"
|
||||||
self.positions[container_name] = {'x': x, 'y': y, 'isOpen': isOpen}
|
self.positions[container_name] = {"x": x, "y": y, "isOpen": isOpen}
|
||||||
|
|
||||||
|
|
||||||
container_manager = ContainerPositionManager()
|
container_manager = ContainerPositionManager()
|
||||||
|
|
@ -45,44 +51,55 @@ async def get_network_data() -> Dict[str, Dict]:
|
||||||
try:
|
try:
|
||||||
network_data = await network_service.get_network_interfaces_container()
|
network_data = await network_service.get_network_interfaces_container()
|
||||||
|
|
||||||
activation_interfaces, deactivation_interfaces = _split_interfaces(network_data.interfaces)
|
activation_interfaces, deactivation_interfaces = _split_interfaces(
|
||||||
|
network_data.interfaces
|
||||||
|
)
|
||||||
|
|
||||||
activation_x, activation_y, activation_is_open = container_manager.get_position_and_state('activation')
|
activation_x, activation_y, activation_is_open = (
|
||||||
deactivation_x, deactivation_y, deactivation_is_open = container_manager.get_position_and_state('deactivation')
|
container_manager.get_position_and_state("activation")
|
||||||
|
)
|
||||||
|
deactivation_x, deactivation_y, deactivation_is_open = (
|
||||||
|
container_manager.get_position_and_state("deactivation")
|
||||||
|
)
|
||||||
|
|
||||||
activation = Activation(
|
activation = Activation(
|
||||||
id=1,
|
id=1,
|
||||||
interfaces=activation_interfaces,
|
interfaces=activation_interfaces,
|
||||||
container_x=activation_x,
|
container_x=activation_x,
|
||||||
container_y=activation_y,
|
container_y=activation_y,
|
||||||
isOpen=activation_is_open
|
isOpen=activation_is_open,
|
||||||
)
|
)
|
||||||
deactivation = Deactivation(
|
deactivation = Deactivation(
|
||||||
id=2,
|
id=2,
|
||||||
interfaces=deactivation_interfaces,
|
interfaces=deactivation_interfaces,
|
||||||
container_x=deactivation_x,
|
container_x=deactivation_x,
|
||||||
container_y=deactivation_y,
|
container_y=deactivation_y,
|
||||||
isOpen=deactivation_is_open
|
isOpen=deactivation_is_open,
|
||||||
)
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"activation": activation.model_dump(),
|
"activation": activation.model_dump(),
|
||||||
"deactivation": deactivation.model_dump()
|
"deactivation": deactivation.model_dump(),
|
||||||
}
|
}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Ошибка в get_network_data: {e}")
|
logger.error(f"Ошибка в get_network_data: {e}")
|
||||||
raise HTTPException(status_code=500, detail="Не удалось получить сетевые данные")
|
raise HTTPException(
|
||||||
|
status_code=500, detail="Не удалось получить сетевые данные"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _split_interfaces(interfaces: List[Union[NetworkInterface, WiFiDevice]]) -> Tuple[
|
def _split_interfaces(
|
||||||
List[Union[NetworkInterface, WiFiDevice]], List[Union[NetworkInterface, WiFiDevice]]]:
|
interfaces: List[Union[NetworkInterface, WiFiDevice]],
|
||||||
|
) -> Tuple[
|
||||||
|
List[Union[NetworkInterface, WiFiDevice]], List[Union[NetworkInterface, WiFiDevice]]
|
||||||
|
]:
|
||||||
"""
|
"""
|
||||||
Разделяет интерфейсы на активные и неактивные
|
Разделяет интерфейсы на активные и неактивные
|
||||||
"""
|
"""
|
||||||
activation_interfaces = []
|
activation_interfaces = []
|
||||||
deactivation_interfaces = []
|
deactivation_interfaces = []
|
||||||
for interface in interfaces:
|
for interface in interfaces:
|
||||||
if interface.device_state == '100':
|
if interface.device_state == "100":
|
||||||
activation_interfaces.append(interface)
|
activation_interfaces.append(interface)
|
||||||
else:
|
else:
|
||||||
deactivation_interfaces.append(interface)
|
deactivation_interfaces.append(interface)
|
||||||
|
|
@ -98,7 +115,9 @@ async def update_container_position(data: Dict[str, Union[int, bool]] = Body(...
|
||||||
|
|
||||||
required_keys = ["container_id", "container_x", "container_y", "isOpen"]
|
required_keys = ["container_id", "container_x", "container_y", "isOpen"]
|
||||||
if not all(key in data for key in required_keys):
|
if not all(key in data for key in required_keys):
|
||||||
raise ValueError("Отсутствуют обязательные поля: container_id, container_x, container_y, isOpen")
|
raise ValueError(
|
||||||
|
"Отсутствуют обязательные поля: container_id, container_x, container_y, isOpen"
|
||||||
|
)
|
||||||
|
|
||||||
container_id = data["container_id"]
|
container_id = data["container_id"]
|
||||||
container_x = data["container_x"]
|
container_x = data["container_x"]
|
||||||
|
|
@ -106,16 +125,24 @@ async def update_container_position(data: Dict[str, Union[int, bool]] = Body(...
|
||||||
is_open = data["isOpen"]
|
is_open = data["isOpen"]
|
||||||
|
|
||||||
# Проверка типов
|
# Проверка типов
|
||||||
if not isinstance(container_id, int) or not isinstance(container_x, int) or not isinstance(container_y,
|
if (
|
||||||
int) or not isinstance(
|
not isinstance(container_id, int)
|
||||||
is_open, bool):
|
or not isinstance(container_x, int)
|
||||||
|
or not isinstance(container_y, int)
|
||||||
|
or not isinstance(is_open, bool)
|
||||||
|
):
|
||||||
raise ValueError("Неверный тип данных в запросе")
|
raise ValueError("Неверный тип данных в запросе")
|
||||||
|
|
||||||
container_manager.update_position(container_id, container_x, container_y, is_open)
|
container_manager.update_position(
|
||||||
|
container_id, container_x, container_y, is_open
|
||||||
|
)
|
||||||
return {"success": True, "message": "Позиция и состояние контейнера обновлены"}
|
return {"success": True, "message": "Позиция и состояние контейнера обновлены"}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Ошибка обновления позиции контейнера: {e}")
|
logger.error(f"Ошибка обновления позиции контейнера: {e}")
|
||||||
raise HTTPException(status_code=400, detail=f"Не удалось обновить положение контейнера: {str(e)}")
|
raise HTTPException(
|
||||||
|
status_code=400,
|
||||||
|
detail=f"Не удалось обновить положение контейнера: {str(e)}",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/get_hostname")
|
@router.get("/get_hostname")
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ from services.wifi_service import WifiService
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
@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):
|
||||||
"""Сканирует локальную подсеть и возвращает данные об устройствах"""
|
"""Сканирует локальную подсеть и возвращает данные об устройствах"""
|
||||||
|
|
@ -18,6 +19,7 @@ def scan_subnet(subnet: str = "192.168.1.0/24", ports: List[int] = None):
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise HTTPException(status_code=500, detail=f"Ошибка сканирования: {str(e)}")
|
raise HTTPException(status_code=500, detail=f"Ошибка сканирования: {str(e)}")
|
||||||
|
|
||||||
|
|
||||||
@router.get("/wifi", response_model=WifiScanResult)
|
@router.get("/wifi", response_model=WifiScanResult)
|
||||||
def scan_wifi(timeout: int = 30):
|
def scan_wifi(timeout: int = 30):
|
||||||
"""Сканирует Wi-Fi точки доступа и возвращает данные о них"""
|
"""Сканирует Wi-Fi точки доступа и возвращает данные о них"""
|
||||||
|
|
@ -26,7 +28,10 @@ def scan_wifi(timeout: int = 30):
|
||||||
result = wifi_service.scan_wifi(timeout=timeout)
|
result = wifi_service.scan_wifi(timeout=timeout)
|
||||||
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")
|
@router.post("/devices/{mac}/note")
|
||||||
def add_note(mac: str, request: NoteRequest):
|
def add_note(mac: str, request: NoteRequest):
|
||||||
|
|
@ -43,7 +48,10 @@ def add_note(mac: str, request: NoteRequest):
|
||||||
except HTTPException as e:
|
except HTTPException as e:
|
||||||
raise e
|
raise e
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise HTTPException(status_code=500, detail=f"Ошибка добавления заметки: {str(e)}")
|
raise HTTPException(
|
||||||
|
status_code=500, detail=f"Ошибка добавления заметки: {str(e)}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.put("/devices/{mac}/note")
|
@router.put("/devices/{mac}/note")
|
||||||
def update_note(mac: str, request: NoteRequest):
|
def update_note(mac: str, request: NoteRequest):
|
||||||
|
|
@ -60,7 +68,10 @@ def update_note(mac: str, request: NoteRequest):
|
||||||
except HTTPException as e:
|
except HTTPException as e:
|
||||||
raise e
|
raise e
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise HTTPException(status_code=500, detail=f"Ошибка обновления заметки: {str(e)}")
|
raise HTTPException(
|
||||||
|
status_code=500, detail=f"Ошибка обновления заметки: {str(e)}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.delete("/devices/{mac}/note")
|
@router.delete("/devices/{mac}/note")
|
||||||
def delete_note(mac: str):
|
def delete_note(mac: str):
|
||||||
|
|
@ -77,4 +88,6 @@ def delete_note(mac: str):
|
||||||
except HTTPException as e:
|
except HTTPException as e:
|
||||||
raise e
|
raise e
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise HTTPException(status_code=500, detail=f"Ошибка удаления заметки: {str(e)}")
|
raise HTTPException(
|
||||||
|
status_code=500, detail=f"Ошибка удаления заметки: {str(e)}"
|
||||||
|
)
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,11 @@ from services.ufw_service import UFWService
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
class RuleNumbers(BaseModel):
|
class RuleNumbers(BaseModel):
|
||||||
numbers: List[int] = Field(..., description="Список номеров правил для удаления")
|
numbers: List[int] = Field(..., description="Список номеров правил для удаления")
|
||||||
|
|
||||||
|
|
||||||
@router.get("/rules", response_model=UFWRulesResponse)
|
@router.get("/rules", response_model=UFWRulesResponse)
|
||||||
async def get_ufw_rules():
|
async def get_ufw_rules():
|
||||||
"""
|
"""
|
||||||
|
|
@ -21,6 +23,7 @@ async def get_ufw_rules():
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise HTTPException(status_code=500, detail=f"Error: {str(e)}")
|
raise HTTPException(status_code=500, detail=f"Error: {str(e)}")
|
||||||
|
|
||||||
|
|
||||||
@router.get("/status", response_model=UFWStatus)
|
@router.get("/status", response_model=UFWStatus)
|
||||||
async def get_ufw_status():
|
async def get_ufw_status():
|
||||||
"""
|
"""
|
||||||
|
|
@ -32,6 +35,7 @@ async def get_ufw_status():
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise HTTPException(status_code=500, detail=f"Error: {str(e)}")
|
raise HTTPException(status_code=500, detail=f"Error: {str(e)}")
|
||||||
|
|
||||||
|
|
||||||
@router.post("/enable")
|
@router.post("/enable")
|
||||||
async def enable_ufw():
|
async def enable_ufw():
|
||||||
"""
|
"""
|
||||||
|
|
@ -47,6 +51,7 @@ async def enable_ufw():
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise HTTPException(status_code=500, detail=f"Error: {str(e)}")
|
raise HTTPException(status_code=500, detail=f"Error: {str(e)}")
|
||||||
|
|
||||||
|
|
||||||
@router.post("/disable")
|
@router.post("/disable")
|
||||||
async def disable_ufw():
|
async def disable_ufw():
|
||||||
"""
|
"""
|
||||||
|
|
@ -62,6 +67,7 @@ async def disable_ufw():
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise HTTPException(status_code=500, detail=f"Error: {str(e)}")
|
raise HTTPException(status_code=500, detail=f"Error: {str(e)}")
|
||||||
|
|
||||||
|
|
||||||
@router.post("/rules/in")
|
@router.post("/rules/in")
|
||||||
async def add_ufw_in_rules(rules: List[UFWRule]):
|
async def add_ufw_in_rules(rules: List[UFWRule]):
|
||||||
"""
|
"""
|
||||||
|
|
@ -74,7 +80,7 @@ async def add_ufw_in_rules(rules: List[UFWRule]):
|
||||||
if not status.enabled:
|
if not status.enabled:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=400,
|
status_code=400,
|
||||||
detail="UFW отключен. Включите UFW перед добавлением правил."
|
detail="UFW отключен. Включите UFW перед добавлением правил.",
|
||||||
)
|
)
|
||||||
|
|
||||||
result = ufw_service.add_rules(rules)
|
result = ufw_service.add_rules(rules)
|
||||||
|
|
@ -84,13 +90,17 @@ async def add_ufw_in_rules(rules: List[UFWRule]):
|
||||||
return {
|
return {
|
||||||
"status": "success",
|
"status": "success",
|
||||||
"message": f"Добавлено {successful_rules} из {total_rules} правил",
|
"message": f"Добавлено {successful_rules} из {total_rules} правил",
|
||||||
"details": result
|
"details": result,
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
raise HTTPException(status_code=500, detail=result.get("error", "Не удалось добавить правила"))
|
raise HTTPException(
|
||||||
|
status_code=500,
|
||||||
|
detail=result.get("error", "Не удалось добавить правила"),
|
||||||
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise HTTPException(status_code=500, detail=f"Error: {str(e)}")
|
raise HTTPException(status_code=500, detail=f"Error: {str(e)}")
|
||||||
|
|
||||||
|
|
||||||
@router.post("/rules/out")
|
@router.post("/rules/out")
|
||||||
async def add_ufw_out_rules(rules: List[UFWRule]):
|
async def add_ufw_out_rules(rules: List[UFWRule]):
|
||||||
"""
|
"""
|
||||||
|
|
@ -103,17 +113,23 @@ async def add_ufw_out_rules(rules: List[UFWRule]):
|
||||||
if not status.enabled:
|
if not status.enabled:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=400,
|
status_code=400,
|
||||||
detail="UFW отключен. Включите UFW перед добавлением правил."
|
detail="UFW отключен. Включите UFW перед добавлением правил.",
|
||||||
)
|
)
|
||||||
|
|
||||||
success = ufw_service.add_ufw_out_rules(rules)
|
success = ufw_service.add_ufw_out_rules(rules)
|
||||||
if success:
|
if success:
|
||||||
return {"status": "success", "message": "Исходящие правила UFW успешно добавлены"}
|
return {
|
||||||
|
"status": "success",
|
||||||
|
"message": "Исходящие правила UFW успешно добавлены",
|
||||||
|
}
|
||||||
else:
|
else:
|
||||||
raise HTTPException(status_code=500, detail="Не удалось добавить исходящие правила UFW")
|
raise HTTPException(
|
||||||
|
status_code=500, detail="Не удалось добавить исходящие правила UFW"
|
||||||
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise HTTPException(status_code=500, detail=f"Error: {str(e)}")
|
raise HTTPException(status_code=500, detail=f"Error: {str(e)}")
|
||||||
|
|
||||||
|
|
||||||
@router.delete("/rules")
|
@router.delete("/rules")
|
||||||
async def delete_ufw_rules(rule_numbers: RuleNumbers):
|
async def delete_ufw_rules(rule_numbers: RuleNumbers):
|
||||||
"""
|
"""
|
||||||
|
|
@ -126,7 +142,7 @@ async def delete_ufw_rules(rule_numbers: RuleNumbers):
|
||||||
return {
|
return {
|
||||||
"status": "success",
|
"status": "success",
|
||||||
"message": f"Удалено {result['successful']} из {result['total']} правил",
|
"message": f"Удалено {result['successful']} из {result['total']} правил",
|
||||||
"details": result
|
"details": result,
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
raise HTTPException(status_code=500, detail=result["error"])
|
raise HTTPException(status_code=500, detail=result["error"])
|
||||||
|
|
|
||||||
|
|
@ -12,10 +12,12 @@ router = APIRouter()
|
||||||
logging.basicConfig(level=logging.INFO)
|
logging.basicConfig(level=logging.INFO)
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class WebSocketManager:
|
class WebSocketManager:
|
||||||
"""
|
"""
|
||||||
Класс для управления подключениями к WebSocket-серверу
|
Класс для управления подключениями к WebSocket-серверу
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
"""Инициализация менеджера подключений к WebSocket-серверу"""
|
"""Инициализация менеджера подключений к WebSocket-серверу"""
|
||||||
self.active_connections: Set[WebSocket] = set()
|
self.active_connections: Set[WebSocket] = set()
|
||||||
|
|
@ -41,8 +43,10 @@ class WebSocketManager:
|
||||||
logger.error(f"Сообщение об ошибке трансляции: {e}")
|
logger.error(f"Сообщение об ошибке трансляции: {e}")
|
||||||
self.disconnect(connection)
|
self.disconnect(connection)
|
||||||
|
|
||||||
|
|
||||||
manager = WebSocketManager()
|
manager = WebSocketManager()
|
||||||
|
|
||||||
|
|
||||||
@router.websocket("/api/v1")
|
@router.websocket("/api/v1")
|
||||||
async def websocket_endpoint(websocket: WebSocket):
|
async def websocket_endpoint(websocket: WebSocket):
|
||||||
"""Обработчик WebSocket-соединения"""
|
"""Обработчик WebSocket-соединения"""
|
||||||
|
|
@ -52,7 +56,9 @@ async def websocket_endpoint(websocket: WebSocket):
|
||||||
data = await websocket.receive_json()
|
data = await websocket.receive_json()
|
||||||
logger.info(f"Полученные данные: {data}")
|
logger.info(f"Полученные данные: {data}")
|
||||||
|
|
||||||
if all(key in data for key in ["container_id", "container_x", "container_y"]):
|
if all(
|
||||||
|
key in data for key in ["container_id", "container_x", "container_y"]
|
||||||
|
):
|
||||||
await update_container_position(data)
|
await update_container_position(data)
|
||||||
|
|
||||||
network_data = await get_network_data()
|
network_data = await get_network_data()
|
||||||
|
|
@ -69,18 +75,22 @@ async def websocket_endpoint(websocket: WebSocket):
|
||||||
if websocket.client_state != WebSocketState.DISCONNECTED:
|
if websocket.client_state != WebSocketState.DISCONNECTED:
|
||||||
await websocket.close()
|
await websocket.close()
|
||||||
|
|
||||||
|
|
||||||
@router.on_event("startup")
|
@router.on_event("startup")
|
||||||
async def startup_event():
|
async def startup_event():
|
||||||
"""Запуск таска для трансляции сетевых данных"""
|
"""Запуск таска для трансляции сетевых данных"""
|
||||||
asyncio.create_task(broadcast_data())
|
asyncio.create_task(broadcast_data())
|
||||||
|
|
||||||
|
|
||||||
async def broadcast_data():
|
async def broadcast_data():
|
||||||
"""Таск для трансляции сетевых данных"""
|
"""Таск для трансляции сетевых данных"""
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
network_data = await get_network_data()
|
network_data = await get_network_data()
|
||||||
if "activation" not in network_data:
|
if "activation" not in network_data:
|
||||||
logger.error("Ошибка: Ключ 'activation' отсутствует в полученных данных сети")
|
logger.error(
|
||||||
|
"Ошибка: Ключ 'activation' отсутствует в полученных данных сети"
|
||||||
|
)
|
||||||
await asyncio.sleep(2)
|
await asyncio.sleep(2)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ class CORSMiddlewareConfig:
|
||||||
"""
|
"""
|
||||||
Класс для настройки CORS политики в FastAPI
|
Класс для настройки CORS политики в FastAPI
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, app: FastAPI) -> None:
|
def __init__(self, app: FastAPI) -> None:
|
||||||
"""Инициализируем приложение FastAPI и список допустимых CORS-оригинов"""
|
"""Инициализируем приложение FastAPI и список допустимых CORS-оригинов"""
|
||||||
self.app = app
|
self.app = app
|
||||||
|
|
|
||||||
|
|
@ -7,29 +7,33 @@ import logging
|
||||||
logging.basicConfig(level=logging.INFO)
|
logging.basicConfig(level=logging.INFO)
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def init_config():
|
def init_config():
|
||||||
"""Создаёт конфигурационный файл с SECRET_KEY"""
|
"""Создаёт конфигурационный файл с SECRET_KEY"""
|
||||||
config_path = "/etc/gonets/config.ini"
|
config_path = "/etc/gonets/config.ini"
|
||||||
config_dir = os.path.dirname(config_path)
|
config_dir = os.path.dirname(config_path)
|
||||||
|
|
||||||
if os.path.exists(config_path):
|
if os.path.exists(config_path):
|
||||||
logger.info(f"Конфигурационный файл {config_path} уже существует, пропускаем инициализацию")
|
logger.info(
|
||||||
|
f"Конфигурационный файл {config_path} уже существует, пропускаем инициализацию"
|
||||||
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
secret_key = secrets.token_urlsafe(32)
|
secret_key = secrets.token_urlsafe(32)
|
||||||
os.makedirs(config_dir, exist_ok=True)
|
os.makedirs(config_dir, exist_ok=True)
|
||||||
config = configparser.ConfigParser()
|
config = configparser.ConfigParser()
|
||||||
config["JWT"] = {
|
config["JWT"] = {"secret_key": secret_key}
|
||||||
"secret_key": secret_key
|
|
||||||
}
|
|
||||||
with open(config_path, "w") as config_file:
|
with open(config_path, "w") as config_file:
|
||||||
config.write(config_file)
|
config.write(config_file)
|
||||||
os.chmod(config_path, 0o600)
|
os.chmod(config_path, 0o600)
|
||||||
logger.info(f"Создан конфигурационный файл {config_path}")
|
logger.info(f"Создан конфигурационный файл {config_path}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Ошибка при создании конфигурационного файла: {type(e).__name__} - {str(e)}")
|
logger.error(
|
||||||
|
f"Ошибка при создании конфигурационного файла: {type(e).__name__} - {str(e)}"
|
||||||
|
)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
init_config()
|
init_config()
|
||||||
|
|
@ -33,7 +33,6 @@ class PortConfig:
|
||||||
631: "IPP (Printing)",
|
631: "IPP (Printing)",
|
||||||
993: "IMAPS",
|
993: "IMAPS",
|
||||||
995: "POP3S",
|
995: "POP3S",
|
||||||
|
|
||||||
# --- Базы данных ---
|
# --- Базы данных ---
|
||||||
1433: "Microsoft SQL Server",
|
1433: "Microsoft SQL Server",
|
||||||
1521: "Oracle DB",
|
1521: "Oracle DB",
|
||||||
|
|
@ -41,7 +40,6 @@ class PortConfig:
|
||||||
5432: "PostgreSQL",
|
5432: "PostgreSQL",
|
||||||
6379: "Redis",
|
6379: "Redis",
|
||||||
27017: "MongoDB",
|
27017: "MongoDB",
|
||||||
|
|
||||||
# --- VPN и удалённый доступ ---
|
# --- VPN и удалённый доступ ---
|
||||||
1723: "PPTP VPN",
|
1723: "PPTP VPN",
|
||||||
1701: "L2TP",
|
1701: "L2TP",
|
||||||
|
|
@ -50,20 +48,17 @@ class PortConfig:
|
||||||
1194: "OpenVPN",
|
1194: "OpenVPN",
|
||||||
3389: "RDP (Remote Desktop Protocol)",
|
3389: "RDP (Remote Desktop Protocol)",
|
||||||
5900: "VNC",
|
5900: "VNC",
|
||||||
|
|
||||||
# --- Прокси / Веб ---
|
# --- Прокси / Веб ---
|
||||||
1080: "SOCKS Proxy",
|
1080: "SOCKS Proxy",
|
||||||
8000: "HTTP-Alt",
|
8000: "HTTP-Alt",
|
||||||
8080: "HTTP Proxy",
|
8080: "HTTP Proxy",
|
||||||
8443: "HTTPS-Alt",
|
8443: "HTTPS-Alt",
|
||||||
|
|
||||||
# --- DevOps / Monitoring / Tools ---
|
# --- DevOps / Monitoring / Tools ---
|
||||||
9000: "SonarQube / PHP-FPM",
|
9000: "SonarQube / PHP-FPM",
|
||||||
9100: "Prometheus Node Exporter",
|
9100: "Prometheus Node Exporter",
|
||||||
9200: "Elasticsearch HTTP",
|
9200: "Elasticsearch HTTP",
|
||||||
9300: "Elasticsearch Cluster",
|
9300: "Elasticsearch Cluster",
|
||||||
11211: "Memcached",
|
11211: "Memcached",
|
||||||
|
|
||||||
# --- Прочее ---
|
# --- Прочее ---
|
||||||
6667: "IRC",
|
6667: "IRC",
|
||||||
4000: "ICQ / Custom apps",
|
4000: "ICQ / Custom apps",
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
import subprocess
|
import subprocess
|
||||||
from typing import Tuple
|
from typing import Tuple
|
||||||
|
|
||||||
|
|
||||||
class CLIExecutor:
|
class CLIExecutor:
|
||||||
"""
|
"""
|
||||||
Класс для выполнения CLI-команд
|
Класс для выполнения CLI-команд
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def run(command: list) -> Tuple[int, str]:
|
def run(command: list) -> Tuple[int, str]:
|
||||||
"""
|
"""
|
||||||
|
|
@ -20,6 +22,6 @@ class CLIExecutor:
|
||||||
command,
|
command,
|
||||||
stdout=subprocess.PIPE,
|
stdout=subprocess.PIPE,
|
||||||
stderr=subprocess.PIPE,
|
stderr=subprocess.PIPE,
|
||||||
check=False # Не вызываем исключение при ошибке
|
check=False, # Не вызываем исключение при ошибке
|
||||||
)
|
)
|
||||||
return result.returncode, result.stdout.decode('utf-8')
|
return result.returncode, result.stdout.decode("utf-8")
|
||||||
|
|
|
||||||
|
|
@ -19,4 +19,4 @@ network_service = NetworkService()
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
# uvicorn.run("main:app", host='0.0.0.0', port=8001, reload=True, ssl_keyfile="/etc/gonets/certs/gonets.key", ssl_certfile="/etc/gonets/certs/gonets.crt")
|
# uvicorn.run("main:app", host='0.0.0.0', port=8001, reload=True, ssl_keyfile="/etc/gonets/certs/gonets.key", ssl_certfile="/etc/gonets/certs/gonets.crt")
|
||||||
uvicorn.run("main:app", host='0.0.0.0', port=8001, reload=True)
|
uvicorn.run("main:app", host="0.0.0.0", port=8001, reload=True)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
|
|
||||||
class UserCreate(BaseModel):
|
class UserCreate(BaseModel):
|
||||||
"""
|
"""
|
||||||
Модель созданного пользователя
|
Модель созданного пользователя
|
||||||
|
|
@ -10,10 +11,12 @@ class UserCreate(BaseModel):
|
||||||
password (str): Пароль пользователя
|
password (str): Пароль пользователя
|
||||||
is_admin (bool): Административный доступ
|
is_admin (bool): Административный доступ
|
||||||
"""
|
"""
|
||||||
|
|
||||||
username: str
|
username: str
|
||||||
password: str
|
password: str
|
||||||
is_admin: bool = False
|
is_admin: bool = False
|
||||||
|
|
||||||
|
|
||||||
class UserLogin(BaseModel):
|
class UserLogin(BaseModel):
|
||||||
"""
|
"""
|
||||||
Модель авторизации пользователя
|
Модель авторизации пользователя
|
||||||
|
|
@ -22,9 +25,11 @@ class UserLogin(BaseModel):
|
||||||
username (str): Имя пользователя
|
username (str): Имя пользователя
|
||||||
password (str): Пароль пользователя
|
password (str): Пароль пользователя
|
||||||
"""
|
"""
|
||||||
|
|
||||||
username: str
|
username: str
|
||||||
password: str
|
password: str
|
||||||
|
|
||||||
|
|
||||||
class UserResponse(BaseModel):
|
class UserResponse(BaseModel):
|
||||||
"""
|
"""
|
||||||
Модель проверки наличия пользователя
|
Модель проверки наличия пользователя
|
||||||
|
|
@ -34,10 +39,12 @@ class UserResponse(BaseModel):
|
||||||
username (str): Имя пользователя
|
username (str): Имя пользователя
|
||||||
is_admin (bool): Административный доступ
|
is_admin (bool): Административный доступ
|
||||||
"""
|
"""
|
||||||
|
|
||||||
id: int
|
id: int
|
||||||
username: str
|
username: str
|
||||||
is_admin: bool
|
is_admin: bool
|
||||||
|
|
||||||
|
|
||||||
class Token(BaseModel):
|
class Token(BaseModel):
|
||||||
"""
|
"""
|
||||||
Токен пользователя
|
Токен пользователя
|
||||||
|
|
@ -48,6 +55,7 @@ class Token(BaseModel):
|
||||||
session_id (Optional[str]): Идентификатор сессии
|
session_id (Optional[str]): Идентификатор сессии
|
||||||
user (Optional[UserResponse]): Пользователь на основе проверки наличия пользователя
|
user (Optional[UserResponse]): Пользователь на основе проверки наличия пользователя
|
||||||
"""
|
"""
|
||||||
|
|
||||||
access_token: str
|
access_token: str
|
||||||
token_type: str
|
token_type: str
|
||||||
session_id: Optional[str] = None
|
session_id: Optional[str] = None
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ class IPv4Route(BaseModel):
|
||||||
next_hop (str): Адрес следующего узла (маршрутизатор)
|
next_hop (str): Адрес следующего узла (маршрутизатор)
|
||||||
metric (int): Метрика маршрута
|
metric (int): Метрика маршрута
|
||||||
"""
|
"""
|
||||||
|
|
||||||
destination: str
|
destination: str
|
||||||
prefix: int
|
prefix: int
|
||||||
next_hop: str
|
next_hop: str
|
||||||
|
|
@ -28,6 +29,7 @@ class IPv6Route(BaseModel):
|
||||||
next_hop (str): Адрес следующего узла (маршрутизатор)
|
next_hop (str): Адрес следующего узла (маршрутизатор)
|
||||||
metric (int): Метрика маршрута
|
metric (int): Метрика маршрута
|
||||||
"""
|
"""
|
||||||
|
|
||||||
destination: str
|
destination: str
|
||||||
prefix: int
|
prefix: int
|
||||||
next_hop: str
|
next_hop: str
|
||||||
|
|
@ -45,6 +47,7 @@ class IndividualIPConfig(BaseModel):
|
||||||
gateway (str): Основной шлюз по умолчанию
|
gateway (str): Основной шлюз по умолчанию
|
||||||
dns (List[str]): Список DNS-серверов
|
dns (List[str]): Список DNS-серверов
|
||||||
"""
|
"""
|
||||||
|
|
||||||
address: str
|
address: str
|
||||||
routes: List[Union[IPv4Route, IPv6Route]]
|
routes: List[Union[IPv4Route, IPv6Route]]
|
||||||
method: Optional[str]
|
method: Optional[str]
|
||||||
|
|
@ -59,6 +62,7 @@ class IPConfig(BaseModel):
|
||||||
Атрибуты:
|
Атрибуты:
|
||||||
configurations (List[IndividualIPConfig]): Список индивидуальных IP-конфигураций
|
configurations (List[IndividualIPConfig]): Список индивидуальных IP-конфигураций
|
||||||
"""
|
"""
|
||||||
|
|
||||||
configurations: List[IndividualIPConfig]
|
configurations: List[IndividualIPConfig]
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -78,6 +82,7 @@ class NetworkInterface(BaseModel):
|
||||||
container_x (int): Положение по X на холсте
|
container_x (int): Положение по X на холсте
|
||||||
container_y (int): Положение по Y на холсте
|
container_y (int): Положение по Y на холсте
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name: str
|
name: str
|
||||||
type: str
|
type: str
|
||||||
mac_address: str
|
mac_address: str
|
||||||
|
|
@ -102,6 +107,7 @@ class WiFiDevice(NetworkInterface):
|
||||||
frequency (Optional[float]): Частота
|
frequency (Optional[float]): Частота
|
||||||
mode (Union[str, int]): Режим работы
|
mode (Union[str, int]): Режим работы
|
||||||
"""
|
"""
|
||||||
|
|
||||||
ssid: Optional[str] = None
|
ssid: Optional[str] = None
|
||||||
bssid: Optional[str] = None
|
bssid: Optional[str] = None
|
||||||
bitrate: Optional[int] = None
|
bitrate: Optional[int] = None
|
||||||
|
|
@ -119,6 +125,7 @@ class NetworkInterfacesContainer(BaseModel):
|
||||||
container_x (int): Положение по оси X
|
container_x (int): Положение по оси X
|
||||||
container_y (int): Положение по оси Y
|
container_y (int): Положение по оси Y
|
||||||
"""
|
"""
|
||||||
|
|
||||||
interfaces: List[Union[NetworkInterface, WiFiDevice]]
|
interfaces: List[Union[NetworkInterface, WiFiDevice]]
|
||||||
container_x: int
|
container_x: int
|
||||||
container_y: int
|
container_y: int
|
||||||
|
|
@ -135,6 +142,7 @@ class Activation(BaseModel):
|
||||||
container_y (int): Положение по Y на холсте
|
container_y (int): Положение по Y на холсте
|
||||||
isOpen (bool): Флаг скрытия ноды на холсте
|
isOpen (bool): Флаг скрытия ноды на холсте
|
||||||
"""
|
"""
|
||||||
|
|
||||||
id: int
|
id: int
|
||||||
interfaces: List[Union[NetworkInterface, WiFiDevice]]
|
interfaces: List[Union[NetworkInterface, WiFiDevice]]
|
||||||
container_x: int
|
container_x: int
|
||||||
|
|
@ -153,6 +161,7 @@ class Deactivation(BaseModel):
|
||||||
container_y (int): Положение по Y на холсте
|
container_y (int): Положение по Y на холсте
|
||||||
isOpen (bool): Флаг скрытия ноды на холсте
|
isOpen (bool): Флаг скрытия ноды на холсте
|
||||||
"""
|
"""
|
||||||
|
|
||||||
id: int
|
id: int
|
||||||
interfaces: List[Union[NetworkInterface, WiFiDevice]]
|
interfaces: List[Union[NetworkInterface, WiFiDevice]]
|
||||||
container_x: int
|
container_x: int
|
||||||
|
|
|
||||||
|
|
@ -8,4 +8,5 @@ class NoteRequest(BaseModel):
|
||||||
Атрибуты:
|
Атрибуты:
|
||||||
note (str): Заметка устройства
|
note (str): Заметка устройства
|
||||||
"""
|
"""
|
||||||
|
|
||||||
note: str
|
note: str
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from typing import Optional, List
|
from typing import Optional, List
|
||||||
|
|
||||||
|
|
||||||
class Device(BaseModel):
|
class Device(BaseModel):
|
||||||
"""
|
"""
|
||||||
Сканируемое устройство в подсети
|
Сканируемое устройство в подсети
|
||||||
|
|
@ -15,6 +16,7 @@ class Device(BaseModel):
|
||||||
last_seen (Optional[str]): Последняя активность устройства
|
last_seen (Optional[str]): Последняя активность устройства
|
||||||
note (Optional[str]): Заметка устройства
|
note (Optional[str]): Заметка устройства
|
||||||
"""
|
"""
|
||||||
|
|
||||||
ip: str
|
ip: str
|
||||||
mac: Optional[str] = None
|
mac: Optional[str] = None
|
||||||
hostname: Optional[str] = None
|
hostname: Optional[str] = None
|
||||||
|
|
@ -24,6 +26,7 @@ class Device(BaseModel):
|
||||||
last_seen: Optional[str] = None
|
last_seen: Optional[str] = None
|
||||||
note: Optional[str] = None
|
note: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
class SubnetScanResult(BaseModel):
|
class SubnetScanResult(BaseModel):
|
||||||
"""
|
"""
|
||||||
Результат сканирования устройств в подсети
|
Результат сканирования устройств в подсети
|
||||||
|
|
@ -31,4 +34,5 @@ class SubnetScanResult(BaseModel):
|
||||||
Атрибуты:
|
Атрибуты:
|
||||||
devices (List[Device]): Список просканированных устройств
|
devices (List[Device]): Список просканированных устройств
|
||||||
"""
|
"""
|
||||||
|
|
||||||
devices: List[Device] = []
|
devices: List[Device] = []
|
||||||
|
|
@ -8,6 +8,7 @@ class Protocol(str, Enum):
|
||||||
"""
|
"""
|
||||||
Enum для сетевых протоколов UFW.
|
Enum для сетевых протоколов UFW.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
TCP = "tcp"
|
TCP = "tcp"
|
||||||
UDP = "udp"
|
UDP = "udp"
|
||||||
BOTH = "both"
|
BOTH = "both"
|
||||||
|
|
@ -27,6 +28,7 @@ class Action(str, Enum):
|
||||||
DENY: Запретить трафик без уведомления
|
DENY: Запретить трафик без уведомления
|
||||||
REJECT: Запретить трафик с уведомлением отправителю
|
REJECT: Запретить трафик с уведомлением отправителю
|
||||||
"""
|
"""
|
||||||
|
|
||||||
ALLOW = "allow"
|
ALLOW = "allow"
|
||||||
DENY = "deny"
|
DENY = "deny"
|
||||||
REJECT = "reject"
|
REJECT = "reject"
|
||||||
|
|
@ -40,12 +42,14 @@ class Direction(str, Enum):
|
||||||
IN: Входящий трафик
|
IN: Входящий трафик
|
||||||
OUT: Исходящий трафик
|
OUT: Исходящий трафик
|
||||||
"""
|
"""
|
||||||
|
|
||||||
IN = "in"
|
IN = "in"
|
||||||
OUT = "out"
|
OUT = "out"
|
||||||
|
|
||||||
|
|
||||||
class UFWRule(BaseModel):
|
class UFWRule(BaseModel):
|
||||||
"""Модель правила UFW"""
|
"""Модель правила UFW"""
|
||||||
|
|
||||||
action: Action
|
action: Action
|
||||||
direction: Direction
|
direction: Direction
|
||||||
from_ip: str
|
from_ip: str
|
||||||
|
|
@ -74,14 +78,18 @@ class UFWRule(BaseModel):
|
||||||
return v
|
return v
|
||||||
elif isinstance(v, str):
|
elif isinstance(v, str):
|
||||||
try:
|
try:
|
||||||
start, end = map(int, v.split(':'))
|
start, end = map(int, v.split(":"))
|
||||||
if not (1 <= start <= end <= 65535):
|
if not (1 <= start <= end <= 65535):
|
||||||
raise ValueError
|
raise ValueError
|
||||||
return v
|
return v
|
||||||
except (ValueError, TypeError):
|
except (ValueError, TypeError):
|
||||||
raise ValueError(f"Некорректный диапазон портов: {v}. Формат: start:end, где 1 <= start <= end <= 65535")
|
raise ValueError(
|
||||||
|
f"Некорректный диапазон портов: {v}. Формат: start:end, где 1 <= start <= end <= 65535"
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"Порт должен быть числом или строкой с диапазоном, получено: {type(v)}")
|
raise ValueError(
|
||||||
|
f"Порт должен быть числом или строкой с диапазоном, получено: {type(v)}"
|
||||||
|
)
|
||||||
|
|
||||||
@validator("from_ip", "to_ip")
|
@validator("from_ip", "to_ip")
|
||||||
def validate_ip(cls, v):
|
def validate_ip(cls, v):
|
||||||
|
|
@ -103,8 +111,12 @@ class UFWRule(BaseModel):
|
||||||
from_ip = ipaddress.ip_network(values["from_ip"], strict=False)
|
from_ip = ipaddress.ip_network(values["from_ip"], strict=False)
|
||||||
to_ip = ipaddress.ip_network(v, strict=False)
|
to_ip = ipaddress.ip_network(v, strict=False)
|
||||||
|
|
||||||
if isinstance(from_ip, ipaddress.IPv6Network) != isinstance(to_ip, ipaddress.IPv6Network):
|
if isinstance(from_ip, ipaddress.IPv6Network) != isinstance(
|
||||||
raise ValueError("Несовместимые версии IP: нельзя смешивать IPv4 и IPv6 в одном правиле")
|
to_ip, ipaddress.IPv6Network
|
||||||
|
):
|
||||||
|
raise ValueError(
|
||||||
|
"Несовместимые версии IP: нельзя смешивать IPv4 и IPv6 в одном правиле"
|
||||||
|
)
|
||||||
|
|
||||||
return v
|
return v
|
||||||
|
|
||||||
|
|
@ -188,21 +200,27 @@ class UFWRule(BaseModel):
|
||||||
groups = match.groupdict()
|
groups = match.groupdict()
|
||||||
|
|
||||||
# Преобразуем протокол в enum
|
# Преобразуем протокол в enum
|
||||||
proto_str = groups['protocol'].lower()
|
proto_str = groups["protocol"].lower()
|
||||||
protocol = Protocol.BOTH if proto_str == "tcp/udp" else \
|
protocol = (
|
||||||
Protocol.UDP if proto_str == "udp" else Protocol.TCP
|
Protocol.BOTH
|
||||||
|
if proto_str == "tcp/udp"
|
||||||
|
else Protocol.UDP if proto_str == "udp" else Protocol.TCP
|
||||||
|
)
|
||||||
|
|
||||||
# Преобразуем действие в enum (игнорируем регистр)
|
# Преобразуем действие в enum (игнорируем регистр)
|
||||||
action_str = groups['action'].lower()
|
action_str = groups["action"].lower()
|
||||||
action = Action.ALLOW if action_str == "allow" else \
|
action = (
|
||||||
Action.DENY if action_str == "deny" else Action.REJECT
|
Action.ALLOW
|
||||||
|
if action_str == "allow"
|
||||||
|
else Action.DENY if action_str == "deny" else Action.REJECT
|
||||||
|
)
|
||||||
|
|
||||||
# Преобразуем направление в enum
|
# Преобразуем направление в enum
|
||||||
direction_str = groups['direction'].lower()
|
direction_str = groups["direction"].lower()
|
||||||
direction = Direction.IN if direction_str == "in" else Direction.OUT
|
direction = Direction.IN if direction_str == "in" else Direction.OUT
|
||||||
|
|
||||||
# Обрабатываем IP адреса
|
# Обрабатываем IP адреса
|
||||||
from_ip = groups['from_ip'].strip()
|
from_ip = groups["from_ip"].strip()
|
||||||
if from_ip.lower() == "anywhere":
|
if from_ip.lower() == "anywhere":
|
||||||
from_ip = "0.0.0.0/0"
|
from_ip = "0.0.0.0/0"
|
||||||
|
|
||||||
|
|
@ -210,8 +228,8 @@ class UFWRule(BaseModel):
|
||||||
to_ip = "0.0.0.0/0" # По умолчанию anywhere
|
to_ip = "0.0.0.0/0" # По умолчанию anywhere
|
||||||
|
|
||||||
# Обрабатываем порт
|
# Обрабатываем порт
|
||||||
port = groups['port']
|
port = groups["port"]
|
||||||
if ':' in port:
|
if ":" in port:
|
||||||
# Оставляем как строку для диапазона
|
# Оставляем как строку для диапазона
|
||||||
port = port.strip()
|
port = port.strip()
|
||||||
else:
|
else:
|
||||||
|
|
@ -219,7 +237,7 @@ class UFWRule(BaseModel):
|
||||||
port = int(port)
|
port = int(port)
|
||||||
|
|
||||||
# Обрабатываем комментарий
|
# Обрабатываем комментарий
|
||||||
comment = groups.get('comment', '').strip() if groups.get('comment') else None
|
comment = groups.get("comment", "").strip() if groups.get("comment") else None
|
||||||
|
|
||||||
return cls(
|
return cls(
|
||||||
action=action,
|
action=action,
|
||||||
|
|
@ -228,7 +246,7 @@ class UFWRule(BaseModel):
|
||||||
to_ip=to_ip,
|
to_ip=to_ip,
|
||||||
port=port,
|
port=port,
|
||||||
protocol=protocol,
|
protocol=protocol,
|
||||||
comment=comment
|
comment=comment,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -241,6 +259,7 @@ class UFWRulesResponse(BaseModel):
|
||||||
rules: Список правил UFW
|
rules: Список правил UFW
|
||||||
total_rules: Общее количество правил
|
total_rules: Общее количество правил
|
||||||
"""
|
"""
|
||||||
|
|
||||||
enabled: bool = Field(..., description="Включен ли UFW")
|
enabled: bool = Field(..., description="Включен ли UFW")
|
||||||
rules: List[UFWRule] = Field(default_factory=list, description="Список правил UFW")
|
rules: List[UFWRule] = Field(default_factory=list, description="Список правил UFW")
|
||||||
|
|
||||||
|
|
@ -259,5 +278,6 @@ class UFWStatus(BaseModel):
|
||||||
message: Сообщение о статусе
|
message: Сообщение о статусе
|
||||||
last_checked: Время последней проверки статуса
|
last_checked: Время последней проверки статуса
|
||||||
"""
|
"""
|
||||||
|
|
||||||
enabled: bool = Field(..., description="Включен ли UFW")
|
enabled: bool = Field(..., description="Включен ли UFW")
|
||||||
message: str = Field(..., description="Сообщение о статусе")
|
message: str = Field(..., description="Сообщение о статусе")
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ class WifiAccessPoint(BaseModel):
|
||||||
vendor (Optional[str]): Производитель точки доступа (определяется по MAC-адресу)
|
vendor (Optional[str]): Производитель точки доступа (определяется по MAC-адресу)
|
||||||
hidden (bool): Флаг скрытия названия точки доступа (ssid)
|
hidden (bool): Флаг скрытия названия точки доступа (ssid)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
ssid: Optional[str] = None
|
ssid: Optional[str] = None
|
||||||
bssid: str
|
bssid: str
|
||||||
channel: Optional[int] = None
|
channel: Optional[int] = None
|
||||||
|
|
@ -33,4 +34,5 @@ class WifiScanResult(BaseModel):
|
||||||
Атрибуты:
|
Атрибуты:
|
||||||
access_points (List[WifiAccessPoint]): Список найденных Wi-Fi точек доступа
|
access_points (List[WifiAccessPoint]): Список найденных Wi-Fi точек доступа
|
||||||
"""
|
"""
|
||||||
|
|
||||||
access_points: List[WifiAccessPoint]
|
access_points: List[WifiAccessPoint]
|
||||||
|
|
|
||||||
|
|
@ -3,14 +3,22 @@ from scapy.sendrecv import srp
|
||||||
import netifaces
|
import netifaces
|
||||||
from netaddr import IPNetwork
|
from netaddr import IPNetwork
|
||||||
|
|
||||||
|
|
||||||
class NetworkScanner:
|
class NetworkScanner:
|
||||||
"""Иницилазицая сетевого сканера"""
|
"""Иницилазицая сетевого сканера"""
|
||||||
|
|
||||||
def __init__(self, interface=None):
|
def __init__(self, interface=None):
|
||||||
"""Инициализация параметров интерфейсов"""
|
"""Инициализация параметров интерфейсов"""
|
||||||
self.interface = interface or netifaces.gateways()['default'][netifaces.AF_INET][1]
|
self.interface = (
|
||||||
|
interface or netifaces.gateways()["default"][netifaces.AF_INET][1]
|
||||||
|
)
|
||||||
self.ip_info = netifaces.ifaddresses(self.interface).get(netifaces.AF_INET, [])
|
self.ip_info = netifaces.ifaddresses(self.interface).get(netifaces.AF_INET, [])
|
||||||
self.networks = [IPNetwork(f"{ip['addr']}/{ip['netmask']}") for ip in self.ip_info]
|
self.networks = [
|
||||||
self.local_mac = netifaces.ifaddresses(self.interface)[netifaces.AF_LINK][0]['addr']
|
IPNetwork(f"{ip['addr']}/{ip['netmask']}") for ip in self.ip_info
|
||||||
|
]
|
||||||
|
self.local_mac = netifaces.ifaddresses(self.interface)[netifaces.AF_LINK][0][
|
||||||
|
"addr"
|
||||||
|
]
|
||||||
|
|
||||||
def _create_arp_packet(self, target_ip):
|
def _create_arp_packet(self, target_ip):
|
||||||
"""Создание пакета ARP для отправки"""
|
"""Создание пакета ARP для отправки"""
|
||||||
|
|
@ -28,9 +36,11 @@ class NetworkScanner:
|
||||||
clients = []
|
clients = []
|
||||||
|
|
||||||
for _ in range(attempts):
|
for _ in range(attempts):
|
||||||
result = srp(packet, timeout=timeout, iface=self.interface, verbose=0)[0]
|
result = srp(packet, timeout=timeout, iface=self.interface, verbose=0)[
|
||||||
|
0
|
||||||
|
]
|
||||||
for _, received in result:
|
for _, received in result:
|
||||||
client = {'ip': received.psrc, 'mac': received.hwsrc}
|
client = {"ip": received.psrc, "mac": received.hwsrc}
|
||||||
if client not in clients: # Избегаем дублирования клиентов
|
if client not in clients: # Избегаем дублирования клиентов
|
||||||
clients.append(client)
|
clients.append(client)
|
||||||
|
|
||||||
|
|
@ -38,7 +48,7 @@ class NetworkScanner:
|
||||||
|
|
||||||
# Добавляет все локальные IP-адреса интерфейса как клиенты
|
# Добавляет все локальные IP-адреса интерфейса как клиенты
|
||||||
for ip_info in self.ip_info:
|
for ip_info in self.ip_info:
|
||||||
local_client = {'ip': ip_info['addr'], 'mac': self.local_mac}
|
local_client = {"ip": ip_info["addr"], "mac": self.local_mac}
|
||||||
if local_client not in all_clients:
|
if local_client not in all_clients:
|
||||||
all_clients.append(local_client)
|
all_clients.append(local_client)
|
||||||
|
|
||||||
|
|
@ -50,9 +60,10 @@ class NetworkScanner:
|
||||||
"""Лист адресов клиентов"""
|
"""Лист адресов клиентов"""
|
||||||
print("Доступные устройства в сети:")
|
print("Доступные устройства в сети:")
|
||||||
print("IP" + " " * 18 + "MAC")
|
print("IP" + " " * 18 + "MAC")
|
||||||
for client in sorted(clients, key=lambda x: x['ip']):
|
for client in sorted(clients, key=lambda x: x["ip"]):
|
||||||
print(f"{client['ip']:<16} {client['mac']}")
|
print(f"{client['ip']:<16} {client['mac']}")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
scanner = NetworkScanner()
|
scanner = NetworkScanner()
|
||||||
clients = scanner.scan_network()
|
clients = scanner.scan_network()
|
||||||
|
|
|
||||||
|
|
@ -26,8 +26,10 @@ ACCESS_TOKEN_EXPIRE_MINUTES = 30
|
||||||
|
|
||||||
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/auth/login")
|
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/auth/login")
|
||||||
|
|
||||||
|
|
||||||
class AuthService:
|
class AuthService:
|
||||||
"""Сервис для регистрации, авторизации и управления сессиями"""
|
"""Сервис для регистрации, авторизации и управления сессиями"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.storage = JsonStorage()
|
self.storage = JsonStorage()
|
||||||
self.session_storage = SessionStorage()
|
self.session_storage = SessionStorage()
|
||||||
|
|
@ -57,10 +59,14 @@ class AuthService:
|
||||||
db_user = self.storage.create_user(user)
|
db_user = self.storage.create_user(user)
|
||||||
return db_user
|
return db_user
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
logger.error(f"Ошибка при регистрации пользователя {user.username}: {str(e)}")
|
logger.error(
|
||||||
|
f"Ошибка при регистрации пользователя {user.username}: {str(e)}"
|
||||||
|
)
|
||||||
raise HTTPException(status_code=400, detail=str(e))
|
raise HTTPException(status_code=400, detail=str(e))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Ошибка при регистрации пользователя {user.username}: {type(e).__name__} - {str(e)}")
|
logger.error(
|
||||||
|
f"Ошибка при регистрации пользователя {user.username}: {type(e).__name__} - {str(e)}"
|
||||||
|
)
|
||||||
raise HTTPException(status_code=500, detail="Ошибка регистрации")
|
raise HTTPException(status_code=500, detail="Ошибка регистрации")
|
||||||
|
|
||||||
def login(self, user: UserLogin, client_id: str) -> dict:
|
def login(self, user: UserLogin, client_id: str) -> dict:
|
||||||
|
|
@ -70,20 +76,30 @@ class AuthService:
|
||||||
if not db_user:
|
if not db_user:
|
||||||
logger.error(f"Пользователь {user.username} не найден")
|
logger.error(f"Пользователь {user.username} не найден")
|
||||||
raise HTTPException(status_code=401, detail="Неверный логин или пароль")
|
raise HTTPException(status_code=401, detail="Неверный логин или пароль")
|
||||||
if not self.storage.verify_password(user.password, db_user["password_hash"]):
|
if not self.storage.verify_password(
|
||||||
|
user.password, db_user["password_hash"]
|
||||||
|
):
|
||||||
logger.error(f"Неверный пароль для пользователя {user.username}")
|
logger.error(f"Неверный пароль для пользователя {user.username}")
|
||||||
raise HTTPException(status_code=401, detail="Неверный логин или пароль")
|
raise HTTPException(status_code=401, detail="Неверный логин или пароль")
|
||||||
access_token = self.create_access_token(data={"sub": user.username})
|
access_token = self.create_access_token(data={"sub": user.username})
|
||||||
session_id = self.session_storage.create_session(user.username, access_token, client_id)
|
session_id = self.session_storage.create_session(
|
||||||
|
user.username, access_token, client_id
|
||||||
|
)
|
||||||
logger.info(f"Пользователь {user.username} успешно авторизован")
|
logger.info(f"Пользователь {user.username} успешно авторизован")
|
||||||
return {
|
return {
|
||||||
"access_token": access_token,
|
"access_token": access_token,
|
||||||
"token_type": "bearer",
|
"token_type": "bearer",
|
||||||
"session_id": session_id,
|
"session_id": session_id,
|
||||||
"user": UserResponse(id=db_user["id"], username=db_user["username"], is_admin=db_user["is_admin"])
|
"user": UserResponse(
|
||||||
|
id=db_user["id"],
|
||||||
|
username=db_user["username"],
|
||||||
|
is_admin=db_user["is_admin"],
|
||||||
|
),
|
||||||
}
|
}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Ошибка при авторизации пользователя {user.username}: {type(e).__name__} - {str(e)}")
|
logger.error(
|
||||||
|
f"Ошибка при авторизации пользователя {user.username}: {type(e).__name__} - {str(e)}"
|
||||||
|
)
|
||||||
raise HTTPException(status_code=500, detail="Ошибка авторизации")
|
raise HTTPException(status_code=500, detail="Ошибка авторизации")
|
||||||
|
|
||||||
def get_current_user(self, token: str = Depends(oauth2_scheme)) -> UserResponse:
|
def get_current_user(self, token: str = Depends(oauth2_scheme)) -> UserResponse:
|
||||||
|
|
@ -97,7 +113,9 @@ class AuthService:
|
||||||
if not db_user:
|
if not db_user:
|
||||||
logger.error(f"Пользователь {username} не найден")
|
logger.error(f"Пользователь {username} не найден")
|
||||||
raise HTTPException(status_code=401, detail="Пользователь не найден")
|
raise HTTPException(status_code=401, detail="Пользователь не найден")
|
||||||
return UserResponse(id=db_user["id"], username=db_user["username"], is_admin=db_user["is_admin"])
|
return UserResponse(
|
||||||
|
id=db_user["id"], username=db_user["username"], is_admin=db_user["is_admin"]
|
||||||
|
)
|
||||||
|
|
||||||
def get_user_by_session(self, client_id: str) -> dict:
|
def get_user_by_session(self, client_id: str) -> dict:
|
||||||
"""Получает пользователя и токен по client_id"""
|
"""Получает пользователя и токен по client_id"""
|
||||||
|
|
@ -112,8 +130,12 @@ class AuthService:
|
||||||
logger.error(f"Пользователь {username} не найден")
|
logger.error(f"Пользователь {username} не найден")
|
||||||
raise HTTPException(status_code=401, detail="Пользователь не найден")
|
raise HTTPException(status_code=401, detail="Пользователь не найден")
|
||||||
return {
|
return {
|
||||||
"user": UserResponse(id=db_user["id"], username=db_user["username"], is_admin=db_user["is_admin"]),
|
"user": UserResponse(
|
||||||
"access_token": token
|
id=db_user["id"],
|
||||||
|
username=db_user["username"],
|
||||||
|
is_admin=db_user["is_admin"],
|
||||||
|
),
|
||||||
|
"access_token": token,
|
||||||
}
|
}
|
||||||
|
|
||||||
def logout(self, client_id: str):
|
def logout(self, client_id: str):
|
||||||
|
|
@ -122,7 +144,9 @@ class AuthService:
|
||||||
session = self.session_storage.get_latest_session(client_id)
|
session = self.session_storage.get_latest_session(client_id)
|
||||||
if session:
|
if session:
|
||||||
self.session_storage.delete_session(session["session_id"])
|
self.session_storage.delete_session(session["session_id"])
|
||||||
logger.info(f"Пользователь успешно вышел из системы для client_id {client_id}")
|
logger.info(
|
||||||
|
f"Пользователь успешно вышел из системы для client_id {client_id}"
|
||||||
|
)
|
||||||
return {"message": "Успешный выход из системы"}
|
return {"message": "Успешный выход из системы"}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Ошибка при выходе из системы: {type(e).__name__} - {str(e)}")
|
logger.error(f"Ошибка при выходе из системы: {type(e).__name__} - {str(e)}")
|
||||||
|
|
|
||||||
|
|
@ -67,10 +67,9 @@ class FirewalldService:
|
||||||
print(f"Timeout: {timeout}")
|
print(f"Timeout: {timeout}")
|
||||||
# Get the zone interface
|
# Get the zone interface
|
||||||
zone_proxy = self.bus.get(
|
zone_proxy = self.bus.get(
|
||||||
"org.fedoraproject.FirewallD1",
|
"org.fedoraproject.FirewallD1", "/org/fedoraproject/FirewallD1"
|
||||||
"/org/fedoraproject/FirewallD1"
|
|
||||||
)
|
)
|
||||||
zone_iface = zone_proxy['org.fedoraproject.FirewallD1.zone']
|
zone_iface = zone_proxy["org.fedoraproject.FirewallD1.zone"]
|
||||||
# Convert port to string
|
# Convert port to string
|
||||||
port_str = str(port)
|
port_str = str(port)
|
||||||
# Convert timeout to integer
|
# Convert timeout to integer
|
||||||
|
|
|
||||||
|
|
@ -10,10 +10,12 @@ import sdbus
|
||||||
from sdbus_async.networkmanager.settings import ConnectionProfile
|
from sdbus_async.networkmanager.settings import ConnectionProfile
|
||||||
from services.arp_service import NetworkScanner
|
from services.arp_service import NetworkScanner
|
||||||
|
|
||||||
|
|
||||||
class IPService:
|
class IPService:
|
||||||
"""
|
"""
|
||||||
Сервис проверки доступности IP-адреса и применения настроек сети
|
Сервис проверки доступности IP-адреса и применения настроек сети
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.arp_scanner = NetworkScanner()
|
self.arp_scanner = NetworkScanner()
|
||||||
self.bus = sdbus.sd_bus_open_system()
|
self.bus = sdbus.sd_bus_open_system()
|
||||||
|
|
@ -23,7 +25,7 @@ class IPService:
|
||||||
"""Перезапускает соединение с системной шиной D-Bus"""
|
"""Перезапускает соединение с системной шиной D-Bus"""
|
||||||
new_bus = sdbus.sd_bus_open_system()
|
new_bus = sdbus.sd_bus_open_system()
|
||||||
sdbus.set_default_bus(new_bus)
|
sdbus.set_default_bus(new_bus)
|
||||||
if hasattr(self, 'bus') and self.bus is not None:
|
if hasattr(self, "bus") and self.bus is not None:
|
||||||
self.bus = None
|
self.bus = None
|
||||||
self.bus = new_bus
|
self.bus = new_bus
|
||||||
|
|
||||||
|
|
@ -32,9 +34,12 @@ class IPService:
|
||||||
self._reset_bus()
|
self._reset_bus()
|
||||||
clients = self.arp_scanner.scan_network()
|
clients = self.arp_scanner.scan_network()
|
||||||
new_ip = args["ipv4_updates"]["ipv4.address-data"][0]["address"]
|
new_ip = args["ipv4_updates"]["ipv4.address-data"][0]["address"]
|
||||||
if any(client['ip'] == new_ip for client in clients):
|
if any(client["ip"] == new_ip for client in clients):
|
||||||
return {"available": False, "message": f"IP address {new_ip} уже занят."}
|
return {"available": False, "message": f"IP address {new_ip} уже занят."}
|
||||||
return {"available": True, "message": "Network profile settings are available for use."}
|
return {
|
||||||
|
"available": True,
|
||||||
|
"message": "Network profile settings are available for use.",
|
||||||
|
}
|
||||||
|
|
||||||
async def apply_ip_config(self, args: Dict[str, Any]) -> None:
|
async def apply_ip_config(self, args: Dict[str, Any]) -> None:
|
||||||
"""Применяет проверенные настройки IP"""
|
"""Применяет проверенные настройки IP"""
|
||||||
|
|
@ -51,7 +56,7 @@ class IPService:
|
||||||
settings_dict = profile.to_settings_dict()
|
settings_dict = profile.to_settings_dict()
|
||||||
|
|
||||||
for key_path, value in args["ipv4_updates"].items():
|
for key_path, value in args["ipv4_updates"].items():
|
||||||
section, *props = key_path.split('.')
|
section, *props = key_path.split(".")
|
||||||
current = settings_dict[section]
|
current = settings_dict[section]
|
||||||
for prop in props[:-1]:
|
for prop in props[:-1]:
|
||||||
current = current.setdefault(prop, {})
|
current = current.setdefault(prop, {})
|
||||||
|
|
@ -61,14 +66,16 @@ class IPService:
|
||||||
await connection.update(new_profile.to_dbus())
|
await connection.update(new_profile.to_dbus())
|
||||||
print(f"Обновлены настройки для {args['connection_id']}:")
|
print(f"Обновлены настройки для {args['connection_id']}:")
|
||||||
|
|
||||||
interface_name = settings_dict['connection']['interface-name']
|
interface_name = settings_dict["connection"]["interface-name"]
|
||||||
devices = await nm.get_devices()
|
devices = await nm.get_devices()
|
||||||
for device_path in devices:
|
for device_path in devices:
|
||||||
device = NetworkDeviceGeneric(device_path)
|
device = NetworkDeviceGeneric(device_path)
|
||||||
if await device.interface == interface_name:
|
if await device.interface == interface_name:
|
||||||
await device.disconnect()
|
await device.disconnect()
|
||||||
print(f"Отключён от {interface_name}")
|
print(f"Отключён от {interface_name}")
|
||||||
await nm.activate_connection(connection=connection_paths[0], device=device_path)
|
await nm.activate_connection(
|
||||||
|
connection=connection_paths[0], device=device_path
|
||||||
|
)
|
||||||
print(f"Переподключён к {args['connection_id']}")
|
print(f"Переподключён к {args['connection_id']}")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ from sdbus_async.networkmanager import (
|
||||||
NetworkManagerSettings,
|
NetworkManagerSettings,
|
||||||
NetworkDeviceWireless,
|
NetworkDeviceWireless,
|
||||||
AccessPoint,
|
AccessPoint,
|
||||||
DeviceType
|
DeviceType,
|
||||||
)
|
)
|
||||||
from sdbus import sd_bus_open_system
|
from sdbus import sd_bus_open_system
|
||||||
from models.network import (
|
from models.network import (
|
||||||
|
|
@ -17,10 +17,11 @@ from models.network import (
|
||||||
IPConfig,
|
IPConfig,
|
||||||
IndividualIPConfig,
|
IndividualIPConfig,
|
||||||
NetworkInterfacesContainer,
|
NetworkInterfacesContainer,
|
||||||
WiFiDevice
|
WiFiDevice,
|
||||||
)
|
)
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
|
|
||||||
class NetworkService:
|
class NetworkService:
|
||||||
"""Сервис для взаимодействия с NetworkManager через D-Bus"""
|
"""Сервис для взаимодействия с NetworkManager через D-Bus"""
|
||||||
|
|
||||||
|
|
@ -34,71 +35,91 @@ class NetworkService:
|
||||||
try:
|
try:
|
||||||
config = await device.get_applied_connection()
|
config = await device.get_applied_connection()
|
||||||
settings = config[0].get(ip_type, {})
|
settings = config[0].get(ip_type, {})
|
||||||
return str(settings.get("method", ('s', 'unknown'))[1])
|
return str(settings.get("method", ("s", "unknown"))[1])
|
||||||
except Exception:
|
except Exception:
|
||||||
return 'unknown'
|
return "unknown"
|
||||||
|
|
||||||
async def get_connection_name(self, device: NetworkDeviceGeneric) -> str:
|
async def get_connection_name(self, device: NetworkDeviceGeneric) -> str:
|
||||||
"""Извлекает имя соединения (GENERAL.CONNECTION) для устройства"""
|
"""Извлекает имя соединения (GENERAL.CONNECTION) для устройства"""
|
||||||
try:
|
try:
|
||||||
applied_connection = await device.get_applied_connection()
|
applied_connection = await device.get_applied_connection()
|
||||||
connection_settings = applied_connection[0]
|
connection_settings = applied_connection[0]
|
||||||
connection_info = connection_settings.get('connection', {})
|
connection_info = connection_settings.get("connection", {})
|
||||||
return str(connection_info.get('id', ('s', 'unknown'))[1])
|
return str(connection_info.get("id", ("s", "unknown"))[1])
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# print(f"Ошибка при получении соединения для устройства {await device.interface}: {e}")
|
# print(f"Ошибка при получении соединения для устройства {await device.interface}: {e}")
|
||||||
# срёт ошибки дл отключенных интерфейсов
|
# срёт ошибки дл отключенных интерфейсов
|
||||||
# добавить потом условия для отключенных интерфейсов
|
# добавить потом условия для отключенных интерфейсов
|
||||||
return 'unknown'
|
return "unknown"
|
||||||
|
|
||||||
async def get_ip_config(self, device: NetworkDeviceGeneric, ip_version: str) -> List[IndividualIPConfig]:
|
async def get_ip_config(
|
||||||
|
self, device: NetworkDeviceGeneric, ip_version: str
|
||||||
|
) -> List[IndividualIPConfig]:
|
||||||
"""Получает конфигурацию IP для устройства"""
|
"""Получает конфигурацию IP для устройства"""
|
||||||
configs = []
|
configs = []
|
||||||
device_ip = await getattr(device, f'ip{ip_version}_config')
|
device_ip = await getattr(device, f"ip{ip_version}_config")
|
||||||
if device_ip != '/':
|
if device_ip != "/":
|
||||||
ip_config = globals()[f'IPv{ip_version}Config'](device_ip, self.system_bus)
|
ip_config = globals()[f"IPv{ip_version}Config"](device_ip, self.system_bus)
|
||||||
address_data = await ip_config.address_data
|
address_data = await ip_config.address_data
|
||||||
for address in address_data:
|
for address in address_data:
|
||||||
config = IndividualIPConfig(
|
config = IndividualIPConfig(
|
||||||
address=str(address['address'][1]),
|
address=str(address["address"][1]),
|
||||||
routes=[
|
routes=[
|
||||||
globals()[f'IPv{ip_version}Route'](
|
globals()[f"IPv{ip_version}Route"](
|
||||||
destination=route.get('dest', ('s', '0.0.0.0' if ip_version == '4' else '::'))[1],
|
destination=route.get(
|
||||||
prefix=route.get('prefix', ('u', 0))[1],
|
"dest", ("s", "0.0.0.0" if ip_version == "4" else "::")
|
||||||
next_hop=route.get('next-hop', ('s', '0.0.0.0' if ip_version == '4' else '::'))[1],
|
)[1],
|
||||||
metric=route.get('metric', ('u', 0))[1]
|
prefix=route.get("prefix", ("u", 0))[1],
|
||||||
) for route in await ip_config.route_data
|
next_hop=route.get(
|
||||||
|
"next-hop",
|
||||||
|
("s", "0.0.0.0" if ip_version == "4" else "::"),
|
||||||
|
)[1],
|
||||||
|
metric=route.get("metric", ("u", 0))[1],
|
||||||
|
)
|
||||||
|
for route in await ip_config.route_data
|
||||||
],
|
],
|
||||||
method=await self.get_ip_method(device, f'ipv{ip_version}'),
|
method=await self.get_ip_method(device, f"ipv{ip_version}"),
|
||||||
gateway=str(await ip_config.gateway),
|
gateway=str(await ip_config.gateway),
|
||||||
dns=[str(ns['address'][1]) if ip_version == '4' else str(ns) for ns in await getattr(ip_config, 'nameserver_data' if ip_version == '4' else 'nameservers')]
|
dns=[
|
||||||
|
str(ns["address"][1]) if ip_version == "4" else str(ns)
|
||||||
|
for ns in await getattr(
|
||||||
|
ip_config,
|
||||||
|
"nameserver_data" if ip_version == "4" else "nameservers",
|
||||||
|
)
|
||||||
|
],
|
||||||
)
|
)
|
||||||
configs.append(config)
|
configs.append(config)
|
||||||
return configs
|
return configs
|
||||||
|
|
||||||
async def get_wifi_info(self, wifi_device: NetworkDeviceWireless) -> Dict[str, Optional[Union[str, int]]]:
|
async def get_wifi_info(
|
||||||
|
self, wifi_device: NetworkDeviceWireless
|
||||||
|
) -> Dict[str, Optional[Union[str, int]]]:
|
||||||
"""Получает информацию о Wi-Fi устройстве"""
|
"""Получает информацию о Wi-Fi устройстве"""
|
||||||
active_ap = await wifi_device.active_access_point
|
active_ap = await wifi_device.active_access_point
|
||||||
wifi_info = {
|
wifi_info = {
|
||||||
'ssid': None,
|
"ssid": None,
|
||||||
'bssid': None,
|
"bssid": None,
|
||||||
'bitrate': await wifi_device.bitrate,
|
"bitrate": await wifi_device.bitrate,
|
||||||
'strength': None,
|
"strength": None,
|
||||||
'frequency': None,
|
"frequency": None,
|
||||||
'mode': None
|
"mode": None,
|
||||||
}
|
}
|
||||||
if active_ap != '/':
|
if active_ap != "/":
|
||||||
ap = AccessPoint(active_ap, self.system_bus)
|
ap = AccessPoint(active_ap, self.system_bus)
|
||||||
wifi_info.update({
|
wifi_info.update(
|
||||||
'ssid': await ap.ssid,
|
{
|
||||||
'bssid': await ap.hw_address,
|
"ssid": await ap.ssid,
|
||||||
'strength': await ap.strength,
|
"bssid": await ap.hw_address,
|
||||||
'frequency': await ap.frequency,
|
"strength": await ap.strength,
|
||||||
'mode': await ap.mode
|
"frequency": await ap.frequency,
|
||||||
})
|
"mode": await ap.mode,
|
||||||
|
}
|
||||||
|
)
|
||||||
return wifi_info
|
return wifi_info
|
||||||
|
|
||||||
async def get_network_interfaces(self, initial_positions: Dict[str, Tuple[int, int]] = None) -> List[Union[NetworkInterface, WiFiDevice]]:
|
async def get_network_interfaces(
|
||||||
|
self, initial_positions: Dict[str, Tuple[int, int]] = None
|
||||||
|
) -> List[Union[NetworkInterface, WiFiDevice]]:
|
||||||
"""Получает список сетевых интерфейсов"""
|
"""Получает список сетевых интерфейсов"""
|
||||||
devices_paths = await self.nm.get_devices()
|
devices_paths = await self.nm.get_devices()
|
||||||
network_interfaces = []
|
network_interfaces = []
|
||||||
|
|
@ -109,24 +130,28 @@ class NetworkService:
|
||||||
interface_name = str(await generic_device.interface)
|
interface_name = str(await generic_device.interface)
|
||||||
device_type = DeviceType(await generic_device.device_type).name
|
device_type = DeviceType(await generic_device.device_type).name
|
||||||
|
|
||||||
if device_type == 'LOOPBACK':
|
if device_type == "LOOPBACK":
|
||||||
continue
|
continue
|
||||||
|
|
||||||
device_state = str(await generic_device.state)
|
device_state = str(await generic_device.state)
|
||||||
if device_state == '10':
|
if device_state == "10":
|
||||||
continue
|
continue
|
||||||
|
|
||||||
common_data = {
|
common_data = {
|
||||||
'name': interface_name,
|
"name": interface_name,
|
||||||
'type': device_type,
|
"type": device_type,
|
||||||
'device_state': device_state,
|
"device_state": device_state,
|
||||||
'active_connection': await self.get_connection_name(generic_device),
|
"active_connection": await self.get_connection_name(generic_device),
|
||||||
'mac_address': str(await generic_device.hw_address),
|
"mac_address": str(await generic_device.hw_address),
|
||||||
'device_mtu': str(await generic_device.mtu),
|
"device_mtu": str(await generic_device.mtu),
|
||||||
'ipv4': IPConfig(configurations=await self.get_ip_config(generic_device, '4')),
|
"ipv4": IPConfig(
|
||||||
'ipv6': IPConfig(configurations=await self.get_ip_config(generic_device, '6')),
|
configurations=await self.get_ip_config(generic_device, "4")
|
||||||
'x': initial_positions.get(interface_name, (100, 100))[0],
|
),
|
||||||
'y': initial_positions.get(interface_name, (100, 100))[1],
|
"ipv6": IPConfig(
|
||||||
|
configurations=await self.get_ip_config(generic_device, "6")
|
||||||
|
),
|
||||||
|
"x": initial_positions.get(interface_name, (100, 100))[0],
|
||||||
|
"y": initial_positions.get(interface_name, (100, 100))[1],
|
||||||
}
|
}
|
||||||
|
|
||||||
if device_type == "WIFI":
|
if device_type == "WIFI":
|
||||||
|
|
@ -141,7 +166,9 @@ class NetworkService:
|
||||||
async def get_hostname(self) -> Dict[str, str]:
|
async def get_hostname(self) -> Dict[str, str]:
|
||||||
"""Возвращает имя хоста из команды hostname в формате JSON"""
|
"""Возвращает имя хоста из команды hostname в формате JSON"""
|
||||||
try:
|
try:
|
||||||
result = subprocess.run(['hostname'], capture_output=True, text=True, check=True)
|
result = subprocess.run(
|
||||||
|
["hostname"], capture_output=True, text=True, check=True
|
||||||
|
)
|
||||||
hostname = result.stdout.strip()
|
hostname = result.stdout.strip()
|
||||||
return {"hostname": hostname}
|
return {"hostname": hostname}
|
||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError as e:
|
||||||
|
|
@ -149,52 +176,77 @@ class NetworkService:
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise RuntimeError(f"Error processing hostname output: {e}")
|
raise RuntimeError(f"Error processing hostname output: {e}")
|
||||||
|
|
||||||
async def get_interfaces_by_state(self, state: str, initial_positions: Dict[str, Tuple[int, int]] = None) -> List[NetworkInterface]:
|
async def get_interfaces_by_state(
|
||||||
|
self, state: str, initial_positions: Dict[str, Tuple[int, int]] = None
|
||||||
|
) -> List[NetworkInterface]:
|
||||||
"""
|
"""
|
||||||
Получает список интерфейсов по состоянию
|
Получает список интерфейсов по состоянию
|
||||||
"""
|
"""
|
||||||
all_interfaces = await self.get_network_interfaces(initial_positions)
|
all_interfaces = await self.get_network_interfaces(initial_positions)
|
||||||
return [interface for interface in all_interfaces if interface.device_state == state]
|
return [
|
||||||
|
interface for interface in all_interfaces if interface.device_state == state
|
||||||
|
]
|
||||||
|
|
||||||
async def get_interfaces_container(self, state: Optional[str] = None, initial_positions: Dict[str, Tuple[int, int]] = None, container_x: int = 0, container_y: int = 0) -> NetworkInterfacesContainer:
|
async def get_interfaces_container(
|
||||||
|
self,
|
||||||
|
state: Optional[str] = None,
|
||||||
|
initial_positions: Dict[str, Tuple[int, int]] = None,
|
||||||
|
container_x: int = 0,
|
||||||
|
container_y: int = 0,
|
||||||
|
) -> NetworkInterfacesContainer:
|
||||||
"""Получает объект, содержащий список интерфейсов и координаты контейнера"""
|
"""Получает объект, содержащий список интерфейсов и координаты контейнера"""
|
||||||
if state:
|
if state:
|
||||||
interfaces = await self.get_interfaces_by_state(state, initial_positions)
|
interfaces = await self.get_interfaces_by_state(state, initial_positions)
|
||||||
else:
|
else:
|
||||||
interfaces = await self.get_network_interfaces(initial_positions)
|
interfaces = await self.get_network_interfaces(initial_positions)
|
||||||
return NetworkInterfacesContainer(
|
return NetworkInterfacesContainer(
|
||||||
interfaces=interfaces,
|
interfaces=interfaces, container_x=container_x, container_y=container_y
|
||||||
container_x=container_x,
|
|
||||||
container_y=container_y
|
|
||||||
)
|
)
|
||||||
|
|
||||||
async def get_network_interfaces_container(self, initial_positions: Optional[Dict[str, Tuple[int, int]]] = None, ) -> NetworkInterfacesContainer:
|
async def get_network_interfaces_container(
|
||||||
|
self,
|
||||||
|
initial_positions: Optional[Dict[str, Tuple[int, int]]] = None,
|
||||||
|
) -> NetworkInterfacesContainer:
|
||||||
"""Получает контейнер с сетевыми интерфейсами и координатами контейнера"""
|
"""Получает контейнер с сетевыми интерфейсами и координатами контейнера"""
|
||||||
interfaces = await self.get_network_interfaces(initial_positions)
|
interfaces = await self.get_network_interfaces(initial_positions)
|
||||||
return NetworkInterfacesContainer(
|
return NetworkInterfacesContainer(
|
||||||
interfaces=interfaces,
|
interfaces=interfaces, container_x=0, container_y=0
|
||||||
container_x=0,
|
|
||||||
container_y=0
|
|
||||||
)
|
)
|
||||||
|
|
||||||
async def get_activated_interfaces(self, initial_positions: Dict[str, Tuple[int, int]] = None) -> List[NetworkInterface]:
|
async def get_activated_interfaces(
|
||||||
|
self, initial_positions: Dict[str, Tuple[int, int]] = None
|
||||||
|
) -> List[NetworkInterface]:
|
||||||
"""Получает список активных сетевых интерфейсов"""
|
"""Получает список активных сетевых интерфейсов"""
|
||||||
return await self.get_interfaces_by_state('100', initial_positions)
|
return await self.get_interfaces_by_state("100", initial_positions)
|
||||||
|
|
||||||
async def get_deactivated_interfaces(self, initial_positions: Dict[str, Tuple[int, int]] = None) -> List[NetworkInterface]:
|
async def get_deactivated_interfaces(
|
||||||
|
self, initial_positions: Dict[str, Tuple[int, int]] = None
|
||||||
|
) -> List[NetworkInterface]:
|
||||||
"""Получает список неактивных сетевых интерфейсов"""
|
"""Получает список неактивных сетевых интерфейсов"""
|
||||||
all_interfaces = await self.get_network_interfaces(initial_positions)
|
all_interfaces = await self.get_network_interfaces(initial_positions)
|
||||||
return [interface for interface in all_interfaces if interface.device_state != '100']
|
return [
|
||||||
|
interface for interface in all_interfaces if interface.device_state != "100"
|
||||||
|
]
|
||||||
|
|
||||||
async def get_activated_interfaces_container(self, initial_positions: Dict[str, Tuple[int, int]] = None, container_x: int = 0, container_y: int = 0) -> NetworkInterfacesContainer:
|
async def get_activated_interfaces_container(
|
||||||
|
self,
|
||||||
|
initial_positions: Dict[str, Tuple[int, int]] = None,
|
||||||
|
container_x: int = 0,
|
||||||
|
container_y: int = 0,
|
||||||
|
) -> NetworkInterfacesContainer:
|
||||||
"""Получает объект, содержащий список активных интерфейсов и координаты контейнера"""
|
"""Получает объект, содержащий список активных интерфейсов и координаты контейнера"""
|
||||||
return await self.get_interfaces_container('100', initial_positions, container_x, container_y)
|
return await self.get_interfaces_container(
|
||||||
|
"100", initial_positions, container_x, container_y
|
||||||
|
)
|
||||||
|
|
||||||
async def get_deactivated_interfaces_container(self, initial_positions: Dict[str, Tuple[int, int]] = None, container_x: int = 0, container_y: int = 0) -> NetworkInterfacesContainer:
|
async def get_deactivated_interfaces_container(
|
||||||
|
self,
|
||||||
|
initial_positions: Dict[str, Tuple[int, int]] = None,
|
||||||
|
container_x: int = 0,
|
||||||
|
container_y: int = 0,
|
||||||
|
) -> NetworkInterfacesContainer:
|
||||||
"""Получает объект, содержащий список неактивных интерфейсов и координаты контейнера"""
|
"""Получает объект, содержащий список неактивных интерфейсов и координаты контейнера"""
|
||||||
interfaces = await self.get_deactivated_interfaces(initial_positions)
|
interfaces = await self.get_deactivated_interfaces(initial_positions)
|
||||||
return NetworkInterfacesContainer(
|
return NetworkInterfacesContainer(
|
||||||
interfaces=interfaces,
|
interfaces=interfaces, container_x=container_x, container_y=container_y
|
||||||
container_x=container_x,
|
|
||||||
container_y=container_y
|
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -19,9 +19,12 @@ from scapy.layers.inet import IP, TCP
|
||||||
from scapy.layers.l2 import ARP, Ether
|
from scapy.layers.l2 import ARP, Ether
|
||||||
from scapy.sendrecv import srp, sr1
|
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:
|
||||||
def __init__(self, interface: str):
|
def __init__(self, interface: str):
|
||||||
self.interface = interface
|
self.interface = interface
|
||||||
|
|
@ -38,6 +41,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
|
||||||
|
|
@ -51,7 +55,7 @@ class PortScanner:
|
||||||
IP(dst=ip) / TCP(dport=port, flags="S"),
|
IP(dst=ip) / TCP(dport=port, flags="S"),
|
||||||
timeout=2,
|
timeout=2,
|
||||||
verbose=False,
|
verbose=False,
|
||||||
iface=self.interface
|
iface=self.interface,
|
||||||
)
|
)
|
||||||
if pkt and pkt.haslayer(TCP) and pkt[TCP].flags == 0x12:
|
if pkt and pkt.haslayer(TCP) and pkt[TCP].flags == 0x12:
|
||||||
return port_config.get_port_name(port)
|
return port_config.get_port_name(port)
|
||||||
|
|
@ -71,6 +75,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()
|
||||||
|
|
@ -80,7 +85,9 @@ class DeviceInfoCollector:
|
||||||
try:
|
try:
|
||||||
return self.mac_lookup.lookup(mac)
|
return self.mac_lookup.lookup(mac)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"Не удалось определить производителя для MAC {mac}: {str(e)}")
|
logger.warning(
|
||||||
|
f"Не удалось определить производителя для MAC {mac}: {str(e)}"
|
||||||
|
)
|
||||||
return "Unknown"
|
return "Unknown"
|
||||||
|
|
||||||
def get_hostname(self, ip: str) -> Optional[str]:
|
def get_hostname(self, ip: str) -> Optional[str]:
|
||||||
|
|
@ -89,6 +96,7 @@ class DeviceInfoCollector:
|
||||||
except:
|
except:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
class DeviceStorage:
|
class DeviceStorage:
|
||||||
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)
|
||||||
|
|
@ -137,6 +145,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()
|
||||||
|
|
@ -187,12 +196,14 @@ class SubnetService:
|
||||||
return Device(
|
return Device(
|
||||||
ip=ip,
|
ip=ip,
|
||||||
mac=mac,
|
mac=mac,
|
||||||
hostname=self.device_info.get_hostname(ip) or existing_device.hostname,
|
hostname=self.device_info.get_hostname(ip)
|
||||||
|
or existing_device.hostname,
|
||||||
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
|
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)
|
||||||
|
|
@ -205,7 +216,7 @@ class SubnetService:
|
||||||
open_ports=open_ports,
|
open_ports=open_ports,
|
||||||
status="online",
|
status="online",
|
||||||
last_seen=now,
|
last_seen=now,
|
||||||
note=None
|
note=None,
|
||||||
)
|
)
|
||||||
|
|
||||||
with ThreadPoolExecutor(max_workers=10) as executor:
|
with ThreadPoolExecutor(max_workers=10) as executor:
|
||||||
|
|
@ -251,7 +262,9 @@ class SubnetService:
|
||||||
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)
|
||||||
open_ports = self.port_scanner.scan(ip)
|
open_ports = self.port_scanner.scan(ip)
|
||||||
logger.debug(f"Локальное устройство: IP={ip}, MAC={mac}, Ports={open_ports}")
|
logger.debug(
|
||||||
|
f"Локальное устройство: IP={ip}, MAC={mac}, Ports={open_ports}"
|
||||||
|
)
|
||||||
return Device(
|
return Device(
|
||||||
ip=ip,
|
ip=ip,
|
||||||
mac=mac,
|
mac=mac,
|
||||||
|
|
@ -260,7 +273,7 @@ class SubnetService:
|
||||||
open_ports=open_ports,
|
open_ports=open_ports,
|
||||||
status="online",
|
status="online",
|
||||||
last_seen=now,
|
last_seen=now,
|
||||||
note=None
|
note=None,
|
||||||
)
|
)
|
||||||
logger.warning(f"Локальное устройство не найдено в подсети {subnet}")
|
logger.warning(f"Локальное устройство не найдено в подсети {subnet}")
|
||||||
return None
|
return None
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,12 @@ import pywifi
|
||||||
from pywifi import Profile
|
from pywifi import Profile
|
||||||
from models.wifi import WifiAccessPoint, WifiScanResult
|
from models.wifi import WifiAccessPoint, WifiScanResult
|
||||||
|
|
||||||
logging.basicConfig(level=logging.DEBUG, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s")
|
logging.basicConfig(
|
||||||
|
level=logging.DEBUG, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
|
||||||
|
)
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class WifiService:
|
class WifiService:
|
||||||
"""Сервис для сканирования текущих Wi-Fi точек доступа"""
|
"""Сервис для сканирования текущих Wi-Fi точек доступа"""
|
||||||
|
|
||||||
|
|
@ -28,11 +31,15 @@ class WifiService:
|
||||||
logger.error("Не найдено сетевых интерфейсов")
|
logger.error("Не найдено сетевых интерфейсов")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
logger.debug(f"Доступные интерфейсы: {[iface.name() for iface in interfaces]}")
|
logger.debug(
|
||||||
|
f"Доступные интерфейсы: {[iface.name() for iface in interfaces]}"
|
||||||
|
)
|
||||||
|
|
||||||
for iface in interfaces:
|
for iface in interfaces:
|
||||||
iface_name = iface.name()
|
iface_name = iface.name()
|
||||||
if 'p2p' in iface_name.lower(): # Исключаю сетевые интерфейсы типа p2p из выбора, возможно сделать решение получше нежели исключать интерфейсы
|
if (
|
||||||
|
"p2p" in iface_name.lower()
|
||||||
|
): # Исключаю сетевые интерфейсы типа p2p из выбора, возможно сделать решение получше нежели исключать интерфейсы
|
||||||
logger.debug(f"Пропущен интерфейс {iface_name} (P2P)")
|
logger.debug(f"Пропущен интерфейс {iface_name} (P2P)")
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
|
|
@ -42,7 +49,9 @@ class WifiService:
|
||||||
if results:
|
if results:
|
||||||
logger.info(f"Выбран Wi-Fi-интерфейс: {iface_name}")
|
logger.info(f"Выбран Wi-Fi-интерфейс: {iface_name}")
|
||||||
return iface
|
return iface
|
||||||
logger.debug(f"Интерфейс {iface_name} не вернул результатов сканирования")
|
logger.debug(
|
||||||
|
f"Интерфейс {iface_name} не вернул результатов сканирования"
|
||||||
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.debug(f"Интерфейс {iface_name} неактивен: {str(e)}")
|
logger.debug(f"Интерфейс {iface_name} неактивен: {str(e)}")
|
||||||
|
|
||||||
|
|
@ -57,7 +66,9 @@ class WifiService:
|
||||||
try:
|
try:
|
||||||
return self.mac_lookup.lookup(mac)
|
return self.mac_lookup.lookup(mac)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"Не удалось определить производителя для MAC {mac}: {str(e)}")
|
logger.warning(
|
||||||
|
f"Не удалось определить производителя для MAC {mac}: {str(e)}"
|
||||||
|
)
|
||||||
return "Unknown"
|
return "Unknown"
|
||||||
|
|
||||||
def _get_encryption_from_iw(self, ap_info: Dict) -> str:
|
def _get_encryption_from_iw(self, ap_info: Dict) -> str:
|
||||||
|
|
@ -86,7 +97,9 @@ class WifiService:
|
||||||
logger.debug(f"Частота {frequency} вне диапазона 2.4/5 GHz")
|
logger.debug(f"Частота {frequency} вне диапазона 2.4/5 GHz")
|
||||||
return None
|
return None
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.debug(f"Ошибка при определении канала для частоты {frequency}: {str(e)}")
|
logger.debug(
|
||||||
|
f"Ошибка при определении канала для частоты {frequency}: {str(e)}"
|
||||||
|
)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _get_frequency(self, frequency: Optional[int]) -> Optional[str]:
|
def _get_frequency(self, frequency: Optional[int]) -> Optional[str]:
|
||||||
|
|
@ -101,7 +114,9 @@ class WifiService:
|
||||||
logger.debug(f"Частота {frequency} вне диапазона 2.4/5 GHz")
|
logger.debug(f"Частота {frequency} вне диапазона 2.4/5 GHz")
|
||||||
return None
|
return None
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.debug(f"Ошибка при определении диапазона для частоты {frequency}: {str(e)}")
|
logger.debug(
|
||||||
|
f"Ошибка при определении диапазона для частоты {frequency}: {str(e)}"
|
||||||
|
)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _get_iw_scan_data(self) -> List[Dict]:
|
def _get_iw_scan_data(self) -> List[Dict]:
|
||||||
|
|
@ -137,7 +152,9 @@ class WifiService:
|
||||||
current_ap = {"bssid": bssid_match.group(1).lower(), "security": ""}
|
current_ap = {"bssid": bssid_match.group(1).lower(), "security": ""}
|
||||||
elif current_ap:
|
elif current_ap:
|
||||||
if ssid_match := ssid_pattern.match(line):
|
if ssid_match := ssid_pattern.match(line):
|
||||||
current_ap["ssid"] = ssid_match.group(1) if ssid_match.group(1).strip() else ""
|
current_ap["ssid"] = (
|
||||||
|
ssid_match.group(1) if ssid_match.group(1).strip() else ""
|
||||||
|
)
|
||||||
elif freq_match := freq_pattern.match(line):
|
elif freq_match := freq_pattern.match(line):
|
||||||
current_ap["frequency"] = int(freq_match.group(1))
|
current_ap["frequency"] = int(freq_match.group(1))
|
||||||
elif signal_match := signal_pattern.match(line):
|
elif signal_match := signal_pattern.match(line):
|
||||||
|
|
@ -152,7 +169,9 @@ class WifiService:
|
||||||
if current_ap:
|
if current_ap:
|
||||||
aps.append(current_ap)
|
aps.append(current_ap)
|
||||||
|
|
||||||
logger.debug(f"Найдено {len(aps)} точек через iw: {[ap['bssid'] for ap in aps]}")
|
logger.debug(
|
||||||
|
f"Найдено {len(aps)} точек через iw: {[ap['bssid'] for ap in aps]}"
|
||||||
|
)
|
||||||
for ap in aps:
|
for ap in aps:
|
||||||
logger.debug(f"Данные точки {ap['bssid']}: {ap}")
|
logger.debug(f"Данные точки {ap['bssid']}: {ap}")
|
||||||
return aps
|
return aps
|
||||||
|
|
@ -176,16 +195,22 @@ class WifiService:
|
||||||
return WifiScanResult(access_points=aps)
|
return WifiScanResult(access_points=aps)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
logger.info(f"Начало сканирования Wi-Fi на интерфейсе {self.interface.name()}")
|
logger.info(
|
||||||
|
f"Начало сканирования Wi-Fi на интерфейсе {self.interface.name()}"
|
||||||
|
)
|
||||||
self.interface.scan()
|
self.interface.scan()
|
||||||
time.sleep(10) # Нативное значение для wifi интерфейса предназначенное на сканирование и сбор результата. Оптимальное решение от 5 до 15 секунд
|
time.sleep(
|
||||||
|
10
|
||||||
|
) # Нативное значение для wifi интерфейса предназначенное на сканирование и сбор результата. Оптимальное решение от 5 до 15 секунд
|
||||||
py_results = self.interface.scan_results()
|
py_results = self.interface.scan_results()
|
||||||
logger.debug(f"Найдено {len(py_results)} точек через pywifi")
|
logger.debug(f"Найдено {len(py_results)} точек через pywifi")
|
||||||
|
|
||||||
iw_results = self._get_iw_scan_data()
|
iw_results = self._get_iw_scan_data()
|
||||||
iw_by_bssid = {ap['bssid']: ap for ap in iw_results if ap.get('bssid')}
|
iw_by_bssid = {ap["bssid"]: ap for ap in iw_results if ap.get("bssid")}
|
||||||
|
|
||||||
best_aps_by_ssid: Dict[str, WifiAccessPoint] = {} # Сделал словарь, чтобы агрегировать точки доступа у сетей для выявления лучшего
|
best_aps_by_ssid: Dict[str, WifiAccessPoint] = (
|
||||||
|
{}
|
||||||
|
) # Сделал словарь, чтобы агрегировать точки доступа у сетей для выявления лучшего
|
||||||
for ap in py_results:
|
for ap in py_results:
|
||||||
bssid = ap.bssid.lower() if ap.bssid else None
|
bssid = ap.bssid.lower() if ap.bssid else None
|
||||||
if not bssid:
|
if not bssid:
|
||||||
|
|
@ -203,7 +228,8 @@ class WifiService:
|
||||||
vendor = self.get_vendor(bssid)
|
vendor = self.get_vendor(bssid)
|
||||||
|
|
||||||
logger.debug(
|
logger.debug(
|
||||||
f"Точка {bssid}: freq={frequency}, channel={channel}, encryption={encryption}, signal={signal}")
|
f"Точка {bssid}: freq={frequency}, channel={channel}, encryption={encryption}, signal={signal}"
|
||||||
|
)
|
||||||
|
|
||||||
current_ap = WifiAccessPoint(
|
current_ap = WifiAccessPoint(
|
||||||
ssid=ssid,
|
ssid=ssid,
|
||||||
|
|
@ -213,10 +239,14 @@ class WifiService:
|
||||||
encryption=encryption,
|
encryption=encryption,
|
||||||
frequency=freq_band,
|
frequency=freq_band,
|
||||||
vendor=vendor,
|
vendor=vendor,
|
||||||
hidden=hidden
|
hidden=hidden,
|
||||||
)
|
)
|
||||||
|
|
||||||
if ssid not in best_aps_by_ssid or (current_ap.signal and best_aps_by_ssid[ssid].signal and current_ap.signal > best_aps_by_ssid[ssid].signal):
|
if ssid not in best_aps_by_ssid or (
|
||||||
|
current_ap.signal
|
||||||
|
and best_aps_by_ssid[ssid].signal
|
||||||
|
and current_ap.signal > best_aps_by_ssid[ssid].signal
|
||||||
|
):
|
||||||
best_aps_by_ssid[ssid] = current_ap
|
best_aps_by_ssid[ssid] = current_ap
|
||||||
|
|
||||||
aps = list(best_aps_by_ssid.values())
|
aps = list(best_aps_by_ssid.values())
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ from models.auth import UserCreate, UserResponse
|
||||||
logging.basicConfig(level=logging.INFO)
|
logging.basicConfig(level=logging.INFO)
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class JsonStorage:
|
class JsonStorage:
|
||||||
"""Хранилище пользователей в JSON-файле"""
|
"""Хранилище пользователей в JSON-файле"""
|
||||||
|
|
||||||
|
|
@ -32,7 +33,9 @@ class JsonStorage:
|
||||||
else:
|
else:
|
||||||
logger.info(f"Файл {self.file_path} уже существует")
|
logger.info(f"Файл {self.file_path} уже существует")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Ошибка при создании файла {self.file_path}: {type(e).__name__} - {str(e)}")
|
logger.error(
|
||||||
|
f"Ошибка при создании файла {self.file_path}: {type(e).__name__} - {str(e)}"
|
||||||
|
)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def _read_data(self) -> dict:
|
def _read_data(self) -> dict:
|
||||||
|
|
@ -41,10 +44,14 @@ class JsonStorage:
|
||||||
with open(self.file_path, "r") as f:
|
with open(self.file_path, "r") as f:
|
||||||
return json.load(f)
|
return json.load(f)
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
logger.info(f"Файл {self.file_path} не существует, возвращаем пустую структуру")
|
logger.info(
|
||||||
|
f"Файл {self.file_path} не существует, возвращаем пустую структуру"
|
||||||
|
)
|
||||||
return {"users": [], "next_id": 1}
|
return {"users": [], "next_id": 1}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Ошибка при чтении файла {self.file_path}: {type(e).__name__} - {str(e)}")
|
logger.error(
|
||||||
|
f"Ошибка при чтении файла {self.file_path}: {type(e).__name__} - {str(e)}"
|
||||||
|
)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def _write_data(self, data: dict):
|
def _write_data(self, data: dict):
|
||||||
|
|
@ -53,7 +60,9 @@ class JsonStorage:
|
||||||
with open(self.file_path, "w") as f:
|
with open(self.file_path, "w") as f:
|
||||||
json.dump(data, f, indent=2)
|
json.dump(data, f, indent=2)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Ошибка при записи файла {self.file_path}: {type(e).__name__} - {str(e)}")
|
logger.error(
|
||||||
|
f"Ошибка при записи файла {self.file_path}: {type(e).__name__} - {str(e)}"
|
||||||
|
)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def create_user(self, user: UserCreate) -> UserResponse:
|
def create_user(self, user: UserCreate) -> UserResponse:
|
||||||
|
|
@ -70,22 +79,30 @@ class JsonStorage:
|
||||||
"id": next_id,
|
"id": next_id,
|
||||||
"username": user.username,
|
"username": user.username,
|
||||||
"password_hash": password_hash,
|
"password_hash": password_hash,
|
||||||
"is_admin": user.is_admin
|
"is_admin": user.is_admin,
|
||||||
}
|
}
|
||||||
users.append(new_user)
|
users.append(new_user)
|
||||||
data["next_id"] = next_id + 1
|
data["next_id"] = next_id + 1
|
||||||
self._write_data(data)
|
self._write_data(data)
|
||||||
logger.info(f"Пользователь {user.username} успешно зарегистрирован")
|
logger.info(f"Пользователь {user.username} успешно зарегистрирован")
|
||||||
return UserResponse(id=new_user["id"], username=new_user["username"], is_admin=new_user["is_admin"])
|
return UserResponse(
|
||||||
|
id=new_user["id"],
|
||||||
|
username=new_user["username"],
|
||||||
|
is_admin=new_user["is_admin"],
|
||||||
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Ошибка при создании пользователя {user.username}: {type(e).__name__} - {str(e)}")
|
logger.error(
|
||||||
|
f"Ошибка при создании пользователя {user.username}: {type(e).__name__} - {str(e)}"
|
||||||
|
)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def get_user_by_username(self, username: str) -> Optional[dict]:
|
def get_user_by_username(self, username: str) -> Optional[dict]:
|
||||||
"""Получает пользователя по имени"""
|
"""Получает пользователя по имени"""
|
||||||
try:
|
try:
|
||||||
if not os.path.exists(self.file_path):
|
if not os.path.exists(self.file_path):
|
||||||
logger.info(f"Файл {self.file_path} не существует, пользователь {username} не найден")
|
logger.info(
|
||||||
|
f"Файл {self.file_path} не существует, пользователь {username} не найден"
|
||||||
|
)
|
||||||
return None
|
return None
|
||||||
data = self._read_data()
|
data = self._read_data()
|
||||||
for user in data["users"]:
|
for user in data["users"]:
|
||||||
|
|
@ -93,7 +110,9 @@ class JsonStorage:
|
||||||
return user
|
return user
|
||||||
return None
|
return None
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Ошибка при получении пользователя {username}: {type(e).__name__} - {str(e)}")
|
logger.error(
|
||||||
|
f"Ошибка при получении пользователя {username}: {type(e).__name__} - {str(e)}"
|
||||||
|
)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def verify_password(self, plain_password: str, hashed_password: str) -> bool:
|
def verify_password(self, plain_password: str, hashed_password: str) -> bool:
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ from datetime import datetime, timedelta
|
||||||
logging.basicConfig(level=logging.INFO)
|
logging.basicConfig(level=logging.INFO)
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class SessionStorage:
|
class SessionStorage:
|
||||||
def __init__(self, file_path: str = "/etc/gonets/sessions.json"):
|
def __init__(self, file_path: str = "/etc/gonets/sessions.json"):
|
||||||
self.file_path = file_path
|
self.file_path = file_path
|
||||||
|
|
@ -28,7 +29,9 @@ class SessionStorage:
|
||||||
else:
|
else:
|
||||||
logger.info(f"Файл {self.file_path} уже существует")
|
logger.info(f"Файл {self.file_path} уже существует")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Ошибка при создании файла {self.file_path}: {type(e).__name__} - {str(e)}")
|
logger.error(
|
||||||
|
f"Ошибка при создании файла {self.file_path}: {type(e).__name__} - {str(e)}"
|
||||||
|
)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def _read_data(self) -> Dict:
|
def _read_data(self) -> Dict:
|
||||||
|
|
@ -36,7 +39,9 @@ class SessionStorage:
|
||||||
with open(self.file_path, "r") as f:
|
with open(self.file_path, "r") as f:
|
||||||
return json.load(f)
|
return json.load(f)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Ошибка при чтении файла {self.file_path}: {type(e).__name__} - {str(e)}")
|
logger.error(
|
||||||
|
f"Ошибка при чтении файла {self.file_path}: {type(e).__name__} - {str(e)}"
|
||||||
|
)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def _write_data(self, data: Dict):
|
def _write_data(self, data: Dict):
|
||||||
|
|
@ -44,7 +49,9 @@ class SessionStorage:
|
||||||
with open(self.file_path, "w") as f:
|
with open(self.file_path, "w") as f:
|
||||||
json.dump(data, f, indent=2)
|
json.dump(data, f, indent=2)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Ошибка при записи файла {self.file_path}: {type(e).__name__} - {str(e)}")
|
logger.error(
|
||||||
|
f"Ошибка при записи файла {self.file_path}: {type(e).__name__} - {str(e)}"
|
||||||
|
)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def create_session(self, username: str, token: str, client_id: str) -> str:
|
def create_session(self, username: str, token: str, client_id: str) -> str:
|
||||||
|
|
@ -60,13 +67,17 @@ class SessionStorage:
|
||||||
"token": token,
|
"token": token,
|
||||||
"client_id": client_id,
|
"client_id": client_id,
|
||||||
"created_at": created_at,
|
"created_at": created_at,
|
||||||
"expiry": expiry
|
"expiry": expiry,
|
||||||
}
|
}
|
||||||
self._write_data(data)
|
self._write_data(data)
|
||||||
logger.info(f"Сессия создана для пользователя {username} с session_id {session_id}")
|
logger.info(
|
||||||
|
f"Сессия создана для пользователя {username} с session_id {session_id}"
|
||||||
|
)
|
||||||
return session_id
|
return session_id
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Ошибка при создании сессии для {username}: {type(e).__name__} - {str(e)}")
|
logger.error(
|
||||||
|
f"Ошибка при создании сессии для {username}: {type(e).__name__} - {str(e)}"
|
||||||
|
)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def get_latest_session(self, client_id: str) -> Optional[Dict]:
|
def get_latest_session(self, client_id: str) -> Optional[Dict]:
|
||||||
|
|
@ -95,7 +106,9 @@ class SessionStorage:
|
||||||
self._write_data(data)
|
self._write_data(data)
|
||||||
return None
|
return None
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Ошибка при получении сессии для client_id {client_id}: {type(e).__name__} - {str(e)}")
|
logger.error(
|
||||||
|
f"Ошибка при получении сессии для client_id {client_id}: {type(e).__name__} - {str(e)}"
|
||||||
|
)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def delete_session(self, session_id: str):
|
def delete_session(self, session_id: str):
|
||||||
|
|
@ -108,7 +121,9 @@ class SessionStorage:
|
||||||
self._write_data(data)
|
self._write_data(data)
|
||||||
logger.info(f"Сессия с session_id {session_id} удалена")
|
logger.info(f"Сессия с session_id {session_id} удалена")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Ошибка при удалении сессии {session_id}: {type(e).__name__} - {str(e)}")
|
logger.error(
|
||||||
|
f"Ошибка при удалении сессии {session_id}: {type(e).__name__} - {str(e)}"
|
||||||
|
)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def clean_expired_sessions(self):
|
def clean_expired_sessions(self):
|
||||||
|
|
@ -137,5 +152,7 @@ class SessionStorage:
|
||||||
logger.debug("Истекшие сессии не найдены")
|
logger.debug("Истекшие сессии не найдены")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Ошибка при очистке истекших сессий: {type(e).__name__} - {str(e)}")
|
logger.error(
|
||||||
|
f"Ошибка при очистке истекших сессий: {type(e).__name__} - {str(e)}"
|
||||||
|
)
|
||||||
raise
|
raise
|
||||||
Loading…
Add table
Add a link
Reference in a new issue