Разделение сканирования на разные подсети. Хранение каждой из просканированных подсетей. Исправление багов с заметками и обновленные локали

This commit is contained in:
parent c326edfb9a
commit b1306ff66f
10 changed files with 1124 additions and 285 deletions

View file

@ -1,5 +1,6 @@
import os
import logging
from typing import Optional
from fastapi import APIRouter, HTTPException, Depends, Response, Request
from starlette.responses import FileResponse
from services.settings_service import SettingsService
@ -13,12 +14,14 @@ router = APIRouter()
settings_service = SettingsService()
@router.post("/clear_subnet")
def clear_subnet(translations: Translations = Depends(get_translations())):
def clear_subnet(subnet: Optional[str] = None, translations: Translations = Depends(get_translations())):
"""Очищает данные узлов подсети, сохранённые в devices.json.
Удаляет все записи об узлах подсети, оставляя файл пустым.
Args:
subnet (Optional[str]): Подсеть в формате CIDR (например, "10.1.1.0/24").
Если None, очищает все подсети.
translations: Объект переводов для текущего языка.
Returns:
@ -28,9 +31,12 @@ def clear_subnet(translations: Translations = Depends(get_translations())):
HTTPException: Если произошла ошибка очистки (status_code=500).
"""
try:
success = settings_service.clear_subnet()
success = settings_service.clear_subnet(subnet)
if success:
return {"message": translations.gettext("Данные узлов подсети успешно очищены")}
if subnet:
return {"message": translations.gettext("Данные узлов подсети {subnet} успешно очищены").format(subnet=subnet)}
else:
return {"message": translations.gettext("Данные всех подсетей успешно очищены")}
else:
raise HTTPException(status_code=500, detail=translations.gettext("Не удалось очистить данные узлов подсети"))
except Exception as e:
@ -91,26 +97,28 @@ DEVICE_PATH = "storage/devices/devices.json"
WIFI_PATH = "storage/wifi/wifi.json"
@router.get("/export_devices", response_class=FileResponse)
async def export_devices(translations: Translations = Depends(get_translations())):
async def export_devices(subnet: str, translations: Translations = Depends(get_translations())):
"""
Экспортирует файл с данными устройств в формате JSON.
Экспортирует файл с данными устройств в формате JSON для указанной подсети.
Args:
subnet (str): Подсеть в формате CIDR (например, "10.1.1.0/24").
translations: Объект переводов для текущего языка.
Returns:
FileResponse: Файл devices.json для скачивания.
FileResponse: Файл <subnet>.json для скачивания.
Raises:
HTTPException: Если файл не найден (status_code=404).
"""
if not os.path.exists(DEVICE_PATH):
logger.warning(f"Export file not found: {DEVICE_PATH}")
file_path = f"storage/subnets/{subnet.replace('/', '_')}.json"
if not os.path.exists(file_path):
logger.warning(f"Export file not found: {file_path}")
raise HTTPException(status_code=404, detail=translations.gettext("Файл не найден"))
logger.info(f"Exporting file: {DEVICE_PATH}")
logger.info(f"Exporting file: {file_path}")
return FileResponse(
path=DEVICE_PATH,
filename="devices.json",
path=file_path,
filename=f"{subnet.replace('/', '_')}.json",
media_type="application/json"
)

View file

@ -2,7 +2,7 @@ from fastapi import APIRouter, HTTPException, Body, Depends
from models.subnet import SubnetScanResult, SubnetScanRequest
from models.wifi import WifiScanResult
from models.request import NoteRequest, WifiConnectRequest, WifiDisconnectRequest
from services.subnet_service import SubnetService
from services.subnet_service import SubnetService, get_subnet_service
from services.wifi_service import WifiService
from services.connect_service import WifiConnectionService
from utils.i18n import get_translations, _
@ -14,15 +14,15 @@ router = APIRouter()
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
@router.post("/devices", response_model=SubnetScanResult)
def scan_subnet(request: SubnetScanRequest = Body(...), translations: Translations = Depends(get_translations())):
def scan_subnet(request: SubnetScanRequest = Body(...), subnet_service: SubnetService = Depends(get_subnet_service), translations: Translations = Depends(get_translations())):
"""Сканирует локальную подсеть и возвращает данные об устройствах.
Выполняет сканирование указанной подсети для обнаружения активных устройств.
Args:
request (SubnetScanRequest): Объект с полем subnet.
subnet_service: Сервис сканирования подсети.
translations: Объект переводов для текущего языка.
Returns:
@ -32,17 +32,16 @@ def scan_subnet(request: SubnetScanRequest = Body(...), translations: Translatio
HTTPException: Если произошла ошибка сканирования (status_code=500).
"""
try:
subnet_service = SubnetService()
# Если subnet пустая строка, определяем автоматически
subnet = request.subnet.strip() if request.subnet else None
logger.info(f"Requested subnet scan for: {subnet or 'auto-detected'}")
result = subnet_service.scan_subnet(subnet)
if not result.devices:
if not result.devices and result.error:
logger.warning(f"No devices found in subnet {subnet or 'auto-detected'}")
return result
except Exception as e:
logger.error(f"Error in scan_subnet: {str(e)}")
raise HTTPException(status_code=500, detail=translations.gettext("Ошибка сканирования: {error}").format(error=str(e)))
raise HTTPException(status_code=500, detail=translations.gettext("Ошибка сканирования подсети: {error}").format(error=str(e)))
@router.get("/wifi", response_model=WifiScanResult)
def scan_wifi(timeout: int = 30, translations: Translations = Depends(get_translations())):
@ -68,7 +67,7 @@ def scan_wifi(timeout: int = 30, translations: Translations = Depends(get_transl
raise HTTPException(status_code=500, detail=translations.gettext("Ошибка сканирования Wi-Fi: {error}").format(error=str(e)))
@router.post("/devices/{mac}/note")
def add_note(mac: str, request: NoteRequest, translations: Translations = Depends(get_translations())):
def add_note(mac: str, request: NoteRequest, subnet_service: SubnetService = Depends(get_subnet_service), translations: Translations = Depends(get_translations())):
"""Добавляет заметку для устройства по MAC-адресу.
Привязывает заметку к устройству, идентифицируемому по MAC-адресу.
@ -76,6 +75,7 @@ def add_note(mac: str, request: NoteRequest, translations: Translations = Depend
Args:
mac (str): MAC-адрес устройства.
request (NoteRequest): Объект с полем "note" для текста заметки.
subnet_service: Сервис сканирования подсети.
translations: Объект переводов для текущего языка.
Returns:
@ -86,13 +86,14 @@ def add_note(mac: str, request: NoteRequest, translations: Translations = Depend
существует (status_code=400), или произошла внутренняя ошибка (status_code=500).
"""
try:
subnet_service = SubnetService()
success, error_message = subnet_service.add_note(mac, request.note)
success, result = subnet_service.add_note(mac, request.note, translations)
if not success:
if "не найдено" in error_message.lower():
raise HTTPException(status_code=404, detail=translations.gettext(error_message))
elif "уже существует" in error_message.lower():
raise HTTPException(status_code=400, detail=translations.gettext(error_message))
if result[0] == "not_found":
raise HTTPException(status_code=404, detail=translations.gettext("Устройство с MAC {mac} не найдено").format(mac=mac))
elif result[0] == "note_exists":
raise HTTPException(status_code=400, detail=translations.gettext("Заметка для устройства с MAC {mac} уже существует").format(mac=mac))
else:
raise HTTPException(status_code=500, detail=translations.gettext("Внутренняя ошибка сервера: {error}").format(error=result[1]["error"]))
return {"message": translations.gettext("Заметка '{note}' добавлена для устройства {mac}").format(note=request.note, mac=mac)}
except HTTPException as e:
raise e
@ -100,7 +101,7 @@ def add_note(mac: str, request: NoteRequest, translations: Translations = Depend
raise HTTPException(status_code=500, detail=translations.gettext("Ошибка добавления заметки: {error}").format(error=str(e)))
@router.put("/devices/{mac}/note")
def update_note(mac: str, request: NoteRequest, translations: Translations = Depends(get_translations())):
def update_note(mac: str, request: NoteRequest, subnet_service: SubnetService = Depends(get_subnet_service), translations: Translations = Depends(get_translations())):
"""Обновляет заметку для устройства по MAC-адресу.
Изменяет существующую заметку, привязанную к устройству.
@ -108,6 +109,7 @@ def update_note(mac: str, request: NoteRequest, translations: Translations = Dep
Args:
mac (str): MAC-адрес устройства.
request (NoteRequest): Объект с полем "note" для нового текста заметки.
subnet_service: Сервис сканирования подсети.
translations: Объект переводов для текущего языка.
Returns:
@ -118,13 +120,14 @@ def update_note(mac: str, request: NoteRequest, translations: Translations = Dep
существует (status_code=400), или произошла внутренняя ошибка (status_code=500).
"""
try:
subnet_service = SubnetService()
success, error_message = subnet_service.update_note(mac, request.note)
success, result = subnet_service.update_note(mac, request.note, translations)
if not success:
if "не найдено" in error_message.lower():
raise HTTPException(status_code=404, detail=translations.gettext(error_message))
elif "не существует" in error_message.lower():
raise HTTPException(status_code=400, detail=translations.gettext(error_message))
if result[0] == "not_found":
raise HTTPException(status_code=404, detail=translations.gettext("Устройство с MAC {mac} не найдено").format(mac=mac))
elif result[0] == "note_not_exists":
raise HTTPException(status_code=400, detail=translations.gettext("Заметка для устройства с MAC {mac} не существует").format(mac=mac))
else:
raise HTTPException(status_code=500, detail=translations.gettext("Внутренняя ошибка сервера: {error}").format(error=result[1]["error"]))
return {"message": translations.gettext("Заметка '{note}' обновлена для устройства {mac}").format(note=request.note, mac=mac)}
except HTTPException as e:
raise e
@ -132,13 +135,14 @@ def update_note(mac: str, request: NoteRequest, translations: Translations = Dep
raise HTTPException(status_code=500, detail=translations.gettext("Ошибка обновления заметки: {error}").format(error=str(e)))
@router.delete("/devices/{mac}/note")
def delete_note(mac: str, translations: Translations = Depends(get_translations())):
def delete_note(mac: str, subnet_service: SubnetService = Depends(get_subnet_service), translations: Translations = Depends(get_translations())):
"""Удаляет заметку для устройства по MAC-адресу.
Удаляет привязанную заметку для указанного устройства.
Args:
mac (str): MAC-адрес устройства.
subnet_service: Сервис сканирования подсети.
translations: Объект переводов для текущего языка.
Returns:
@ -149,13 +153,14 @@ def delete_note(mac: str, translations: Translations = Depends(get_translations(
существует (status_code=400), или произошла внутренняя ошибка (status_code=500).
"""
try:
subnet_service = SubnetService()
success, error_message = subnet_service.delete_note(mac)
success, result = subnet_service.delete_note(mac, translations)
if not success:
if "не найдено" in error_message.lower():
raise HTTPException(status_code=404, detail=translations.gettext(error_message))
elif "не существует" in error_message.lower():
raise HTTPException(status_code=400, detail=translations.gettext(error_message))
if result[0] == "not_found":
raise HTTPException(status_code=404, detail=translations.gettext("Устройство с MAC {mac} не найдено").format(mac=mac))
elif result[0] == "note_not_exists":
raise HTTPException(status_code=400, detail=translations.gettext("Заметка для устройства с MAC {mac} не существует").format(mac=mac))
else:
raise HTTPException(status_code=500, detail=translations.gettext("Внутренняя ошибка сервера: {error}").format(error=result[1]["error"]))
return {"message": translations.gettext("Заметка удалена для устройства {mac}").format(mac=mac)}
except HTTPException as e:
raise e
@ -182,12 +187,14 @@ def add_wifi_note(ssid: str, request: NoteRequest, translations: Translations =
"""
try:
wifi_service = WifiService()
success, error_message = wifi_service.add_note(ssid, request.note)
success, result = wifi_service.add_note(ssid, request.note)
if not success:
if "не найдена" in error_message.lower():
raise HTTPException(status_code=404, detail=translations.gettext(error_message))
elif "уже существует" in error_message.lower():
raise HTTPException(status_code=400, detail=translations.gettext(error_message))
if result[0] == "not_found":
raise HTTPException(status_code=404, detail=translations.gettext("Сеть с SSID {ssid} не найдена").format(ssid=ssid))
elif result[0] == "note_exists":
raise HTTPException(status_code=400, detail=translations.gettext("Заметка для сети с SSID {ssid} уже существует").format(ssid=ssid))
else:
raise HTTPException(status_code=500, detail=translations.gettext("Внутренняя ошибка сервера: {error}").format(error=result[1]["error"]))
return {"message": translations.gettext("Заметка '{note}' добавлена для сети с SSID {ssid}").format(note=request.note, ssid=ssid)}
except HTTPException as e:
raise e
@ -214,12 +221,14 @@ def update_wifi_note(ssid: str, request: NoteRequest, translations: Translations
"""
try:
wifi_service = WifiService()
success, error_message = wifi_service.update_note(ssid, request.note)
success, result = wifi_service.update_note(ssid, request.note)
if not success:
if "не найдена" in error_message.lower():
raise HTTPException(status_code=404, detail=translations.gettext(error_message))
elif "не существует" in error_message.lower():
raise HTTPException(status_code=400, detail=translations.gettext(error_message))
if result[0] == "not_found":
raise HTTPException(status_code=404, detail=translations.gettext("Сеть с SSID {ssid} не найдена").format(ssid=ssid))
elif result[0] == "note_not_exists":
raise HTTPException(status_code=400, detail=translations.gettext("Заметка для сети с SSID {ssid} не существует").format(ssid=ssid))
else:
raise HTTPException(status_code=500, detail=translations.gettext("Внутренняя ошибка сервера: {error}").format(error=result[1]["error"]))
return {"message": translations.gettext("Заметка '{note}' обновлена для сети с SSID {ssid}").format(note=request.note, ssid=ssid)}
except HTTPException as e:
raise e
@ -245,12 +254,14 @@ def delete_wifi_note(ssid: str, translations: Translations = Depends(get_transla
"""
try:
wifi_service = WifiService()
success, error_message = wifi_service.delete_note(ssid)
success, result = wifi_service.delete_note(ssid)
if not success:
if "не найдена" in error_message.lower():
raise HTTPException(status_code=404, detail=translations.gettext(error_message))
elif "не существует" in error_message.lower():
raise HTTPException(status_code=400, detail=translations.gettext(error_message))
if result[0] == "not_found":
raise HTTPException(status_code=404, detail=translations.gettext("Сеть с SSID {ssid} не найдена").format(ssid=ssid))
elif result[0] == "note_not_exists":
raise HTTPException(status_code=400, detail=translations.gettext("Заметка для сети с SSID {ssid} не существует").format(ssid=ssid))
else:
raise HTTPException(status_code=500, detail=translations.gettext("Внутренняя ошибка сервера: {error}").format(error=result[1]["error"]))
return {"message": translations.gettext("Заметка удалена для сети с SSID {ssid}").format(ssid=ssid)}
except HTTPException as e:
raise e

View file

@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2025-07-15 15:01+0500\n"
"PO-Revision-Date: 2025-07-15 15:02+0500\n"
"POT-Creation-Date: 2025-07-16 13:15+0500\n"
"PO-Revision-Date: 2025-07-16 13:15+0500\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: en\n"
"Language-Team: en <LL@li.org>\n"
@ -48,11 +48,11 @@ msgstr "The users.json file exists and contains users"
#: api/v1/auth.py:179
msgid "Файл users.json существует, но не содержит пользователей"
msgstr "The users.json file exists but does not contain users"
msgstr "The users.json file exists but contains no users"
#: api/v1/auth.py:183
msgid "Ошибка при проверке файла"
msgstr "Error checking the file"
msgstr "Error checking file"
#: api/v1/ip.py:36 api/v1/ip.py:73 api/v1/ip.py:110
#, python-brace-format
@ -96,145 +96,190 @@ msgstr "Failed to update container position: {error}"
msgid "Не удалось получить имя хоста"
msgstr "Failed to retrieve hostname"
#: api/v1/settings.py:33
msgid "Данные узлов подсети успешно очищены"
msgstr "Subnet node data successfully cleared"
#: api/v1/settings.py:37
#, python-brace-format
msgid "Данные узлов подсети {subnet} успешно очищены"
msgstr "Subnet node data for {subnet} successfully cleared"
#: api/v1/settings.py:35
#: api/v1/settings.py:39
msgid "Данные всех подсетей успешно очищены"
msgstr "All subnet data successfully cleared"
#: api/v1/settings.py:41
msgid "Не удалось очистить данные узлов подсети"
msgstr "Failed to clear subnet node data"
#: api/v1/settings.py:38 api/v1/settings.py:63 api/v1/settings.py:88
#: api/v1/settings.py:44 api/v1/settings.py:69 api/v1/settings.py:94
#, python-brace-format
msgid "Ошибка очистки: {error}"
msgstr "Clear error: {error}"
#: api/v1/settings.py:58
#: api/v1/settings.py:64
msgid "Данные точек доступа Wi-Fi успешно очищены"
msgstr "Wi-Fi access point data successfully cleared"
#: api/v1/settings.py:60
#: api/v1/settings.py:66
msgid "Не удалось очистить данные точек доступа Wi-Fi"
msgstr "Failed to clear Wi-Fi access point data"
#: api/v1/settings.py:83
#: api/v1/settings.py:89
msgid "Все сетевые данные успешно очищены"
msgstr "All network data successfully cleared"
#: api/v1/settings.py:85
#: api/v1/settings.py:91
msgid "Не удалось очистить все сетевые данные"
msgstr "Failed to clear all network data"
#: api/v1/settings.py:109 api/v1/settings.py:133
#: api/v1/settings.py:117 api/v1/settings.py:141
msgid "Файл не найден"
msgstr "File not found"
#: api/v1/settings.py:158
#: api/v1/settings.py:166
#, python-brace-format
msgid "Язык '{locale}' не поддерживается."
msgstr "Language '{locale}' is not supported."
#: api/v1/settings.py:164
#: api/v1/settings.py:172
#, python-brace-format
msgid "Язык '{locale}' не поддерживается (нет файлов переводов)."
msgstr "Language '{locale}' is not supported (no translation files)."
#: api/v1/settings.py:167
#: api/v1/settings.py:175
#, python-brace-format
msgid "Язык успешно установлен: {locale}"
msgstr "Language successfully set: {locale}"
#: api/v1/subnet.py:45
#: api/v1/subnet.py:44
#, python-brace-format
msgid "Ошибка сканирования: {error}"
msgstr "Scan error: {error}"
msgid "Ошибка сканирования подсети: {error}"
msgstr "Subnet scan error: {error}"
#: api/v1/subnet.py:68
#: api/v1/subnet.py:67
#, python-brace-format
msgid "Ошибка сканирования Wi-Fi: {error}"
msgstr "Wi-Fi scan error: {error}"
#: api/v1/subnet.py:96
#: api/v1/subnet.py:92 api/v1/subnet.py:126 api/v1/subnet.py:159
#, python-brace-format
msgid "Устройство с MAC {mac} не найдено"
msgstr "Device with MAC {mac} not found"
#: api/v1/subnet.py:94
#, python-brace-format
msgid "Заметка для устройства с MAC {mac} уже существует"
msgstr "Note for device with MAC {mac} already exists"
#: api/v1/subnet.py:96 api/v1/subnet.py:130 api/v1/subnet.py:163 \
#: api/v1/subnet.py:197 api/v1/subnet.py:231 api/v1/subnet.py:264 \
#: services/wifi_service.py:319 services/wifi_service.py:337 \
#: services/wifi_service.py:355
#, python-brace-format
msgid "Внутренняя ошибка сервера: {error}"
msgstr "Internal server error: {error}"
#: api/v1/subnet.py:97
#, python-brace-format
msgid "Заметка '{note}' добавлена для устройства {mac}"
msgstr "Note '{note}' added for device {mac}"
#: api/v1/subnet.py:100 api/v1/subnet.py:195
#: api/v1/subnet.py:101 api/v1/subnet.py:202
#, python-brace-format
msgid "Ошибка добавления заметки: {error}"
msgstr "Error adding note: {error}"
#: api/v1/subnet.py:128
#: api/v1/subnet.py:128 api/v1/subnet.py:161
#, python-brace-format
msgid "Заметка для устройства с MAC {mac} не существует"
msgstr "Note for device with MAC {mac} does not exist"
#: api/v1/subnet.py:131
#, python-brace-format
msgid "Заметка '{note}' обновлена для устройства {mac}"
msgstr "Note '{note}' updated for device {mac}"
#: api/v1/subnet.py:132 api/v1/subnet.py:227
#: api/v1/subnet.py:135 api/v1/subnet.py:236
#, python-brace-format
msgid "Ошибка обновления заметки: {error}"
msgstr "Error updating note: {error}"
#: api/v1/subnet.py:159
#: api/v1/subnet.py:164
#, python-brace-format
msgid "Заметка удалена для устройства {mac}"
msgstr "Note deleted for device {mac}"
#: api/v1/subnet.py:163 api/v1/subnet.py:258
#: api/v1/subnet.py:168 api/v1/subnet.py:269
#, python-brace-format
msgid "Ошибка удаления заметки: {error}"
msgstr "Error deleting note: {error}"
#: api/v1/subnet.py:191
#: api/v1/subnet.py:193 api/v1/subnet.py:227 api/v1/subnet.py:260 \
#: services/wifi_service.py:327 services/wifi_service.py:345
#, python-brace-format
msgid "Сеть с SSID {ssid} не найдена"
msgstr "Network with SSID {ssid} not found"
#: api/v1/subnet.py:195 services/wifi_service.py:312
#, python-brace-format
msgid "Заметка для сети с SSID {ssid} уже существует"
msgstr "Note for network with SSID {ssid} already exists"
#: api/v1/subnet.py:198
#, python-brace-format
msgid "Заметка '{note}' добавлена для сети с SSID {ssid}"
msgstr "Note '{note}' added for network with SSID {ssid}"
#: api/v1/subnet.py:223
#: api/v1/subnet.py:229 api/v1/subnet.py:262 services/wifi_service.py:330 \
#: services/wifi_service.py:348
#, python-brace-format
msgid "Заметка для сети с SSID {ssid} не существует"
msgstr "Note for network with SSID {ssid} does not exist"
#: api/v1/subnet.py:232
#, python-brace-format
msgid "Заметка '{note}' обновлена для сети с SSID {ssid}"
msgstr "Note '{note}' updated for network with SSID {ssid}"
#: api/v1/subnet.py:254
#: api/v1/subnet.py:265
#, python-brace-format
msgid "Заметка удалена для сети с SSID {ssid}"
msgstr "Note deleted for network with SSID {ssid}"
#: api/v1/subnet.py:282
#: api/v1/subnet.py:293
#, python-brace-format
msgid "Успешно подключено к {ssid}"
msgstr "Successfully connected to {ssid}"
#: api/v1/subnet.py:286
#: api/v1/subnet.py:297
#, python-brace-format
msgid "Ошибка подключения к Wi-Fi: {error}"
msgstr "Wi-Fi connection error: {error}"
#: api/v1/subnet.py:309
#: api/v1/subnet.py:320
#, python-brace-format
msgid "Успешно отключено от {ssid}"
msgstr "Successfully disconnected from {ssid}"
#: api/v1/subnet.py:313
#: api/v1/subnet.py:324
#, python-brace-format
msgid "Ошибка отключения от Wi-Fi: {error}"
msgstr "Wi-Fi disconnection error: {error}"
#: api/v1/subnet.py:337
#: api/v1/subnet.py:348
msgid "connected"
msgstr "connected"
#: api/v1/subnet.py:341
#: api/v1/subnet.py:352
#, python-brace-format
msgid "Ошибка проверки статуса Wi-Fi: {error}"
msgstr "Wi-Fi status check error: {error}"
#: api/v1/websocket.py:145
msgid "Ошибка: Ключ 'activation' отсутствует в полученных данных сети"
msgstr "Error: The 'activation' key is missing in the received network data"
msgstr "Error: 'activation' key is missing in received network data"
#: services/arp_service.py:102
msgid "Доступные устройства в сети:"
msgstr "Available devices in the network:"
msgstr "Available devices on the network:"
#: services/arp_service.py:103
msgid "IP"
@ -276,10 +321,10 @@ msgstr "Successfully logged out"
msgid "Ошибка при выходе из системы"
msgstr "Error during logout"
#: services/connect_service.py:70 services/connect_service.py:117
#: services/connect_service.py:70 services/connect_service.py:117 \
#: services/connect_service.py:154 services/wifi_service.py:227
msgid "Wi-Fi-интерфейс не доступен"
msgstr "Wi-Fi interface is not available"
msgstr "Wi-Fi interface not available"
#: services/connect_service.py:74
#, python-brace-format
@ -289,7 +334,7 @@ msgstr "Network with SSID {ssid} not found in the list of available networks"
#: services/connect_service.py:81 services/connect_service.py:92
#, python-brace-format
msgid "Ошибка подключения к {ssid}: Неверный пароль"
msgstr "Connection error to {ssid}: Invalid password"
msgstr "Connection error to {ssid}: Incorrect password"
#: services/connect_service.py:83 services/connect_service.py:94
#, python-brace-format
@ -361,52 +406,42 @@ msgstr "Failed to execute hostname command: {error}"
msgid "Ошибка обработки вывода hostname: {error}"
msgstr "Error processing hostname output: {error}"
#: services/settings_service.py:37
#: services/settings_service.py:55
#, python-brace-format
msgid "Ошибка очистки узлов подсети: {error}"
msgstr "Error clearing subnet nodes: {error}"
msgstr "Subnet node clear error: {error}"
#: services/settings_service.py:58
#: services/settings_service.py:76
#, python-brace-format
msgid "Ошибка очистки точек доступа Wi-Fi: {error}"
msgstr "Error clearing Wi-Fi access points: {error}"
msgstr "Wi-Fi access point clear error: {error}"
#: services/settings_service.py:84
#: services/settings_service.py:104
#, python-brace-format
msgid "Ошибка очистки всех сетевых данных: {error}"
msgstr "Error clearing all network data: {error}"
msgstr "All network data clear error: {error}"
#: services/subnet_service.py:75 services/wifi_service.py:115
msgid "Неизвестный"
msgstr "Unknown"
#: services/subnet_service.py:179
#: services/subnet_service.py:185
#, python-brace-format
msgid "Ошибка при сохранении устройств: {error}"
msgstr "Error saving devices: {error}"
#: services/subnet_service.py:374 services/subnet_service.py:391
#: services/subnet_service.py:408
#, python-brace-format
msgid "Устройство с MAC {mac} не найдено"
msgstr "Device with MAC {mac} not found"
#: services/subnet_service.py:268
msgid "Недопустимая подсеть по умолчанию"
msgstr "Invalid default subnet"
#: services/subnet_service.py:377
#, python-brace-format
msgid "Заметка для устройства с MAC {mac} уже существует"
msgstr "Note for device with MAC {mac} already exists"
#: services/subnet_service.py:288
msgid "В данной подсети не обнаружено устройств"
msgstr "No devices found in this subnet"
#: services/subnet_service.py:384 services/subnet_service.py:401
#: services/subnet_service.py:418 services/wifi_service.py:319
#: services/wifi_service.py:337 services/wifi_service.py:355
#: services/subnet_service.py:345
#, python-brace-format
msgid "Внутренняя ошибка сервера: {error}"
msgstr "Internal server error: {error}"
#: services/subnet_service.py:394 services/subnet_service.py:411
#, python-brace-format
msgid "Заметка для устройства с MAC {mac} не существует"
msgstr "Note for device with MAC {mac} does not exist"
msgid "Ошибка сканирования: {error}"
msgstr "Scan error: {error}"
#: services/wifi_service.py:61
#, python-brace-format
@ -451,23 +486,8 @@ msgstr "5 GHz"
#: services/wifi_service.py:165
msgid "Интерфейс не определён"
msgstr "Interface not defined"
msgstr "Interface not specified"
#: services/wifi_service.py:215
msgid "Таймаут при выполнении iw scan"
msgstr "Timeout during iw scan"
#: services/wifi_service.py:312
#, python-brace-format
msgid "Заметка для сети с SSID {ssid} уже существует"
msgstr "Note for network with SSID {ssid} already exists"
#: services/wifi_service.py:327 services/wifi_service.py:345
#, python-brace-format
msgid "Сеть с SSID {ssid} не найдена"
msgstr "Network with SSID {ssid} not found"
#: services/wifi_service.py:330 services/wifi_service.py:348
#, python-brace-format
msgid "Заметка для сети с SSID {ssid} не существует"
msgstr "Note for network with SSID {ssid} does not exist"
msgstr "Timeout during iw scan"

493
SRV/locales/messages.pot Normal file
View file

@ -0,0 +1,493 @@
# Translations template for PROJECT.
# Copyright (C) 2025 ORGANIZATION
# This file is distributed under the same license as the PROJECT project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2025.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2025-07-16 13:15+0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.17.0\n"
#: api/v1/auth.py:44
msgid "Не удалось зарегистрировать пользователя"
msgstr ""
#: api/v1/auth.py:75
msgid "Не удалось авторизовать пользователя"
msgstr ""
#: api/v1/auth.py:103
msgid "bearer"
msgstr ""
#: api/v1/auth.py:109
msgid "Не удалось получить данные пользователя"
msgstr ""
#: api/v1/auth.py:136
msgid "Не удалось выйти из системы"
msgstr ""
#: api/v1/auth.py:162
msgid "Файл users.json не существует"
msgstr ""
#: api/v1/auth.py:172
msgid "Файл users.json существует и содержит пользователей"
msgstr ""
#: api/v1/auth.py:179
msgid "Файл users.json существует, но не содержит пользователей"
msgstr ""
#: api/v1/auth.py:183
msgid "Ошибка при проверке файла"
msgstr ""
#: api/v1/ip.py:36 api/v1/ip.py:73 api/v1/ip.py:110
#, python-brace-format
msgid "Ошибка проверки IP конфигурации: {error}"
msgstr ""
#: api/v1/ip.py:64 api/v1/ip.py:101
#, python-brace-format
msgid "Настройки сетевого профиля для {connection_id} применены"
msgstr ""
#: api/v1/network.py:62
#, python-brace-format
msgid "Неверное имя контейнера: {container_name}"
msgstr ""
#: api/v1/network.py:129
msgid "Не удалось получить сетевые данные"
msgstr ""
#: api/v1/network.py:182
msgid ""
"Отсутствуют обязательные поля: container_id, container_x, container_y, "
"isOpen"
msgstr ""
#: api/v1/network.py:194
msgid "Неверный тип данных в запросе"
msgstr ""
#: api/v1/network.py:199
msgid "Позиция и состояние контейнера обновлены"
msgstr ""
#: api/v1/network.py:203
#, python-brace-format
msgid "Не удалось обновить положение контейнера: {error}"
msgstr ""
#: api/v1/network.py:225
msgid "Не удалось получить имя хоста"
msgstr ""
#: api/v1/settings.py:37
#, python-brace-format
msgid "Данные узлов подсети {subnet} успешно очищены"
msgstr ""
#: api/v1/settings.py:39
msgid "Данные всех подсетей успешно очищены"
msgstr ""
#: api/v1/settings.py:41
msgid "Не удалось очистить данные узлов подсети"
msgstr ""
#: api/v1/settings.py:44 api/v1/settings.py:69 api/v1/settings.py:94
#, python-brace-format
msgid "Ошибка очистки: {error}"
msgstr ""
#: api/v1/settings.py:64
msgid "Данные точек доступа Wi-Fi успешно очищены"
msgstr ""
#: api/v1/settings.py:66
msgid "Не удалось очистить данные точек доступа Wi-Fi"
msgstr ""
#: api/v1/settings.py:89
msgid "Все сетевые данные успешно очищены"
msgstr ""
#: api/v1/settings.py:91
msgid "Не удалось очистить все сетевые данные"
msgstr ""
#: api/v1/settings.py:117 api/v1/settings.py:141
msgid "Файл не найден"
msgstr ""
#: api/v1/settings.py:166
#, python-brace-format
msgid "Язык '{locale}' не поддерживается."
msgstr ""
#: api/v1/settings.py:172
#, python-brace-format
msgid "Язык '{locale}' не поддерживается (нет файлов переводов)."
msgstr ""
#: api/v1/settings.py:175
#, python-brace-format
msgid "Язык успешно установлен: {locale}"
msgstr ""
#: api/v1/subnet.py:44
#, python-brace-format
msgid "Ошибка сканирования подсети: {error}"
msgstr ""
#: api/v1/subnet.py:67
#, python-brace-format
msgid "Ошибка сканирования Wi-Fi: {error}"
msgstr ""
#: api/v1/subnet.py:92 api/v1/subnet.py:126 api/v1/subnet.py:159
#, python-brace-format
msgid "Устройство с MAC {mac} не найдено"
msgstr ""
#: api/v1/subnet.py:94
#, python-brace-format
msgid "Заметка для устройства с MAC {mac} уже существует"
msgstr ""
#: api/v1/subnet.py:96 api/v1/subnet.py:130 api/v1/subnet.py:163
#: api/v1/subnet.py:197 api/v1/subnet.py:231 api/v1/subnet.py:264
#: services/wifi_service.py:319 services/wifi_service.py:337
#: services/wifi_service.py:355
#, python-brace-format
msgid "Внутренняя ошибка сервера: {error}"
msgstr ""
#: api/v1/subnet.py:97
#, python-brace-format
msgid "Заметка '{note}' добавлена для устройства {mac}"
msgstr ""
#: api/v1/subnet.py:101 api/v1/subnet.py:202
#, python-brace-format
msgid "Ошибка добавления заметки: {error}"
msgstr ""
#: api/v1/subnet.py:128 api/v1/subnet.py:161
#, python-brace-format
msgid "Заметка для устройства с MAC {mac} не существует"
msgstr ""
#: api/v1/subnet.py:131
#, python-brace-format
msgid "Заметка '{note}' обновлена для устройства {mac}"
msgstr ""
#: api/v1/subnet.py:135 api/v1/subnet.py:236
#, python-brace-format
msgid "Ошибка обновления заметки: {error}"
msgstr ""
#: api/v1/subnet.py:164
#, python-brace-format
msgid "Заметка удалена для устройства {mac}"
msgstr ""
#: api/v1/subnet.py:168 api/v1/subnet.py:269
#, python-brace-format
msgid "Ошибка удаления заметки: {error}"
msgstr ""
#: api/v1/subnet.py:193 api/v1/subnet.py:227 api/v1/subnet.py:260
#: services/wifi_service.py:327 services/wifi_service.py:345
#, python-brace-format
msgid "Сеть с SSID {ssid} не найдена"
msgstr ""
#: api/v1/subnet.py:195 services/wifi_service.py:312
#, python-brace-format
msgid "Заметка для сети с SSID {ssid} уже существует"
msgstr ""
#: api/v1/subnet.py:198
#, python-brace-format
msgid "Заметка '{note}' добавлена для сети с SSID {ssid}"
msgstr ""
#: api/v1/subnet.py:229 api/v1/subnet.py:262 services/wifi_service.py:330
#: services/wifi_service.py:348
#, python-brace-format
msgid "Заметка для сети с SSID {ssid} не существует"
msgstr ""
#: api/v1/subnet.py:232
#, python-brace-format
msgid "Заметка '{note}' обновлена для сети с SSID {ssid}"
msgstr ""
#: api/v1/subnet.py:265
#, python-brace-format
msgid "Заметка удалена для сети с SSID {ssid}"
msgstr ""
#: api/v1/subnet.py:293
#, python-brace-format
msgid "Успешно подключено к {ssid}"
msgstr ""
#: api/v1/subnet.py:297
#, python-brace-format
msgid "Ошибка подключения к Wi-Fi: {error}"
msgstr ""
#: api/v1/subnet.py:320
#, python-brace-format
msgid "Успешно отключено от {ssid}"
msgstr ""
#: api/v1/subnet.py:324
#, python-brace-format
msgid "Ошибка отключения от Wi-Fi: {error}"
msgstr ""
#: api/v1/subnet.py:348
msgid "connected"
msgstr ""
#: api/v1/subnet.py:352
#, python-brace-format
msgid "Ошибка проверки статуса Wi-Fi: {error}"
msgstr ""
#: api/v1/websocket.py:145
msgid "Ошибка: Ключ 'activation' отсутствует в полученных данных сети"
msgstr ""
#: services/arp_service.py:102
msgid "Доступные устройства в сети:"
msgstr ""
#: services/arp_service.py:103
msgid "IP"
msgstr ""
#: services/arp_service.py:103
msgid "MAC"
msgstr ""
#: services/auth_service.py:72
msgid "Ошибка регистрации"
msgstr ""
#: services/auth_service.py:80 services/auth_service.py:84
msgid "Неверный логин или пароль"
msgstr ""
#: services/auth_service.py:100
msgid "Ошибка авторизации"
msgstr ""
#: services/auth_service.py:108
msgid "Неверный токен"
msgstr ""
#: services/auth_service.py:113 services/auth_service.py:128
msgid "Пользователь не найден"
msgstr ""
#: services/auth_service.py:122
msgid "Сессия недействительна"
msgstr ""
#: services/auth_service.py:144
msgid "Успешный выход из системы"
msgstr ""
#: services/auth_service.py:148
msgid "Ошибка при выходе из системы"
msgstr ""
#: services/connect_service.py:70 services/connect_service.py:117
#: services/connect_service.py:154 services/wifi_service.py:227
msgid "Wi-Fi-интерфейс не доступен"
msgstr ""
#: services/connect_service.py:74
#, python-brace-format
msgid "Сеть с SSID {ssid} не найдена в списке доступных сетей"
msgstr ""
#: services/connect_service.py:81 services/connect_service.py:92
#, python-brace-format
msgid "Ошибка подключения к {ssid}: Неверный пароль"
msgstr ""
#: services/connect_service.py:83 services/connect_service.py:94
#, python-brace-format
msgid ""
"Ошибка подключения к {ssid}: Сеть с SSID {ssid} недоступна или неверно "
"указана"
msgstr ""
#: services/connect_service.py:85 services/connect_service.py:95
#, python-brace-format
msgid "Ошибка подключения к {ssid}: {error}"
msgstr ""
#: services/connect_service.py:98
#, python-brace-format
msgid "Ошибка подключения к {ssid}: Неизвестная ошибка - {error}"
msgstr ""
#: services/connect_service.py:122
#, python-brace-format
msgid "Нет активного подключения для SSID {ssid}"
msgstr ""
#: services/connect_service.py:133
#, python-brace-format
msgid "Активное подключение с SSID {ssid} не найдено"
msgstr ""
#: services/connect_service.py:136 services/connect_service.py:139
#, python-brace-format
msgid "Ошибка отключения от {ssid}: {error}"
msgstr ""
#: services/connect_service.py:160
msgid "Не подключено к Wi-Fi"
msgstr ""
#: services/connect_service.py:163
#, python-brace-format
msgid "Ошибка проверки статуса: {error}"
msgstr ""
#: services/ip_service.py:69
#, python-brace-format
msgid "IP-адрес {ip} уже занят."
msgstr ""
#: services/ip_service.py:73
msgid "Настройки сетевого профиля доступны для использования."
msgstr ""
#: services/ip_service.py:102
#, python-brace-format
msgid "Подключение {connection_id} не найдено"
msgstr ""
#: services/ip_service.py:130
#, python-brace-format
msgid "Устройство для интерфейса {interface} не найдено"
msgstr ""
#: services/network_service.py:232
#, python-brace-format
msgid "Не удалось выполнить команду hostname: {error}"
msgstr ""
#: services/network_service.py:234
#, python-brace-format
msgid "Ошибка обработки вывода hostname: {error}"
msgstr ""
#: services/settings_service.py:55
#, python-brace-format
msgid "Ошибка очистки узлов подсети: {error}"
msgstr ""
#: services/settings_service.py:76
#, python-brace-format
msgid "Ошибка очистки точек доступа Wi-Fi: {error}"
msgstr ""
#: services/settings_service.py:104
#, python-brace-format
msgid "Ошибка очистки всех сетевых данных: {error}"
msgstr ""
#: services/subnet_service.py:75 services/wifi_service.py:115
msgid "Неизвестный"
msgstr ""
#: services/subnet_service.py:185
#, python-brace-format
msgid "Ошибка при сохранении устройств: {error}"
msgstr ""
#: services/subnet_service.py:268
msgid "Недопустимая подсеть по умолчанию"
msgstr ""
#: services/subnet_service.py:288
msgid "В данной подсети не обнаружено устройств"
msgstr ""
#: services/subnet_service.py:345
#, python-brace-format
msgid "Ошибка сканирования: {error}"
msgstr ""
#: services/wifi_service.py:61
#, python-brace-format
msgid "Ошибка при сохранении заметок: {error}"
msgstr ""
#: services/wifi_service.py:81
msgid "Не найдено сетевых интерфейсов"
msgstr ""
#: services/wifi_service.py:102
msgid "Не найден активный Wi-Fi-интерфейс"
msgstr ""
#: services/wifi_service.py:122
msgid "WPA3"
msgstr ""
#: services/wifi_service.py:124
msgid "WPA2"
msgstr ""
#: services/wifi_service.py:126
msgid "WPA"
msgstr ""
#: services/wifi_service.py:128
msgid "WEP"
msgstr ""
#: services/wifi_service.py:129
msgid "Открытая"
msgstr ""
#: services/wifi_service.py:152
msgid "2.4 ГГц"
msgstr ""
#: services/wifi_service.py:154
msgid "5 ГГц"
msgstr ""
#: services/wifi_service.py:165
msgid "Интерфейс не определён"
msgstr ""
#: services/wifi_service.py:215
msgid "Таймаут при выполнении iw scan"
msgstr ""

View file

@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2025-07-15 15:01+0500\n"
"PO-Revision-Date: 2025-07-15 15:02+0500\n"
"POT-Creation-Date: 2025-07-16 13:15+0500\n"
"PO-Revision-Date: 2025-07-16 13:15+0500\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: ru\n"
"Language-Team: ru <LL@li.org>\n"
@ -99,134 +99,179 @@ msgstr "Не удалось обновить положение контейне
msgid "Не удалось получить имя хоста"
msgstr "Не удалось получить имя хоста"
#: api/v1/settings.py:33
msgid "Данные узлов подсети успешно очищены"
msgstr "Данные узлов подсети успешно очищены"
#: api/v1/settings.py:37
#, python-brace-format
msgid "Данные узлов подсети {subnet} успешно очищены"
msgstr "Данные узлов подсети {subnet} успешно очищены"
#: api/v1/settings.py:35
#: api/v1/settings.py:39
msgid "Данные всех подсетей успешно очищены"
msgstr "Данные всех подсетей успешно очищены"
#: api/v1/settings.py:41
msgid "Не удалось очистить данные узлов подсети"
msgstr "Не удалось очистить данные узлов подсети"
#: api/v1/settings.py:38 api/v1/settings.py:63 api/v1/settings.py:88
#: api/v1/settings.py:44 api/v1/settings.py:69 api/v1/settings.py:94
#, python-brace-format
msgid "Ошибка очистки: {error}"
msgstr "Ошибка очистки: {error}"
#: api/v1/settings.py:58
#: api/v1/settings.py:64
msgid "Данные точек доступа Wi-Fi успешно очищены"
msgstr "Данные точек доступа Wi-Fi успешно очищены"
#: api/v1/settings.py:60
#: api/v1/settings.py:66
msgid "Не удалось очистить данные точек доступа Wi-Fi"
msgstr "Не удалось очистить данные точек доступа Wi-Fi"
#: api/v1/settings.py:83
#: api/v1/settings.py:89
msgid "Все сетевые данные успешно очищены"
msgstr "Все сетевые данные успешно очищены"
#: api/v1/settings.py:85
#: api/v1/settings.py:91
msgid "Не удалось очистить все сетевые данные"
msgstr "Не удалось очистить все сетевые данные"
#: api/v1/settings.py:109 api/v1/settings.py:133
#: api/v1/settings.py:117 api/v1/settings.py:141
msgid "Файл не найден"
msgstr "Файл не найден"
#: api/v1/settings.py:158
#: api/v1/settings.py:166
#, python-brace-format
msgid "Язык '{locale}' не поддерживается."
msgstr "Язык '{locale}' не поддерживается."
#: api/v1/settings.py:164
#: api/v1/settings.py:172
#, python-brace-format
msgid "Язык '{locale}' не поддерживается (нет файлов переводов)."
msgstr "Язык '{locale}' не поддерживается (нет файлов переводов)."
#: api/v1/settings.py:167
#: api/v1/settings.py:175
#, python-brace-format
msgid "Язык успешно установлен: {locale}"
msgstr "Язык успешно установлен: {locale}"
#: api/v1/subnet.py:45
#: api/v1/subnet.py:44
#, python-brace-format
msgid "Ошибка сканирования: {error}"
msgstr "Ошибка сканирования: {error}"
msgid "Ошибка сканирования подсети: {error}"
msgstr "Ошибка сканирования подсети: {error}"
#: api/v1/subnet.py:68
#: api/v1/subnet.py:67
#, python-brace-format
msgid "Ошибка сканирования Wi-Fi: {error}"
msgstr "Ошибка сканирования Wi-Fi: {error}"
#: api/v1/subnet.py:96
#: api/v1/subnet.py:92 api/v1/subnet.py:126 api/v1/subnet.py:159
#, python-brace-format
msgid "Устройство с MAC {mac} не найдено"
msgstr "Устройство с MAC {mac} не найдено"
#: api/v1/subnet.py:94
#, python-brace-format
msgid "Заметка для устройства с MAC {mac} уже существует"
msgstr "Заметка для устройства с MAC {mac} уже существует"
#: api/v1/subnet.py:96 api/v1/subnet.py:130 api/v1/subnet.py:163
#: api/v1/subnet.py:197 api/v1/subnet.py:231 api/v1/subnet.py:264
#: services/wifi_service.py:319 services/wifi_service.py:337
#: services/wifi_service.py:355
#, python-brace-format
msgid "Внутренняя ошибка сервера: {error}"
msgstr "Внутренняя ошибка сервера: {error}"
#: api/v1/subnet.py:97
#, python-brace-format
msgid "Заметка '{note}' добавлена для устройства {mac}"
msgstr "Заметка '{note}' добавлена для устройства {mac}"
#: api/v1/subnet.py:100 api/v1/subnet.py:195
#: api/v1/subnet.py:101 api/v1/subnet.py:202
#, python-brace-format
msgid "Ошибка добавления заметки: {error}"
msgstr "Ошибка добавления заметки: {error}"
#: api/v1/subnet.py:128
#: api/v1/subnet.py:128 api/v1/subnet.py:161
#, python-brace-format
msgid "Заметка для устройства с MAC {mac} не существует"
msgstr "Заметка для устройства с MAC {mac} не существует"
#: api/v1/subnet.py:131
#, python-brace-format
msgid "Заметка '{note}' обновлена для устройства {mac}"
msgstr "Заметка '{note}' обновлена для устройства {mac}"
#: api/v1/subnet.py:132 api/v1/subnet.py:227
#: api/v1/subnet.py:135 api/v1/subnet.py:236
#, python-brace-format
msgid "Ошибка обновления заметки: {error}"
msgstr "Ошибка обновления заметки: {error}"
#: api/v1/subnet.py:159
#: api/v1/subnet.py:164
#, python-brace-format
msgid "Заметка удалена для устройства {mac}"
msgstr "Заметка удалена для устройства {mac}"
#: api/v1/subnet.py:163 api/v1/subnet.py:258
#: api/v1/subnet.py:168 api/v1/subnet.py:269
#, python-brace-format
msgid "Ошибка удаления заметки: {error}"
msgstr "Ошибка удаления заметки: {error}"
#: api/v1/subnet.py:191
#: api/v1/subnet.py:193 api/v1/subnet.py:227 api/v1/subnet.py:260
#: services/wifi_service.py:327 services/wifi_service.py:345
#, python-brace-format
msgid "Сеть с SSID {ssid} не найдена"
msgstr "Сеть с SSID {ssid} не найдена"
#: api/v1/subnet.py:195 services/wifi_service.py:312
#, python-brace-format
msgid "Заметка для сети с SSID {ssid} уже существует"
msgstr "Заметка для сети с SSID {ssid} уже существует"
#: api/v1/subnet.py:198
#, python-brace-format
msgid "Заметка '{note}' добавлена для сети с SSID {ssid}"
msgstr "Заметка '{note}' добавлена для сети с SSID {ssid}"
#: api/v1/subnet.py:223
#: api/v1/subnet.py:229 api/v1/subnet.py:262 services/wifi_service.py:330
#: services/wifi_service.py:348
#, python-brace-format
msgid "Заметка для сети с SSID {ssid} не существует"
msgstr "Заметка для сети с SSID {ssid} не существует"
#: api/v1/subnet.py:232
#, python-brace-format
msgid "Заметка '{note}' обновлена для сети с SSID {ssid}"
msgstr "Заметка '{note}' обновлена для сети с SSID {ssid}"
#: api/v1/subnet.py:254
#: api/v1/subnet.py:265
#, python-brace-format
msgid "Заметка удалена для сети с SSID {ssid}"
msgstr "Заметка удалена для сети с SSID {ssid}"
#: api/v1/subnet.py:282
#: api/v1/subnet.py:293
#, python-brace-format
msgid "Успешно подключено к {ssid}"
msgstr "Успешно подключено к {ssid}"
#: api/v1/subnet.py:286
#: api/v1/subnet.py:297
#, python-brace-format
msgid "Ошибка подключения к Wi-Fi: {error}"
msgstr "Ошибка подключения к Wi-Fi: {error}"
#: api/v1/subnet.py:309
#: api/v1/subnet.py:320
#, python-brace-format
msgid "Успешно отключено от {ssid}"
msgstr "Успешно отключено от {ssid}"
#: api/v1/subnet.py:313
#: api/v1/subnet.py:324
#, python-brace-format
msgid "Ошибка отключения от Wi-Fi: {error}"
msgstr "Ошибка отключения от Wi-Fi: {error}"
#: api/v1/subnet.py:337
#: api/v1/subnet.py:348
msgid "connected"
msgstr "connected"
#: api/v1/subnet.py:341
#: api/v1/subnet.py:352
#, python-brace-format
msgid "Ошибка проверки статуса Wi-Fi: {error}"
msgstr "Ошибка проверки статуса Wi-Fi: {error}"
@ -366,17 +411,17 @@ msgstr "Не удалось выполнить команду hostname: {error}"
msgid "Ошибка обработки вывода hostname: {error}"
msgstr "Ошибка обработки вывода hostname: {error}"
#: services/settings_service.py:37
#: services/settings_service.py:55
#, python-brace-format
msgid "Ошибка очистки узлов подсети: {error}"
msgstr "Ошибка очистки узлов подсети: {error}"
#: services/settings_service.py:58
#: services/settings_service.py:76
#, python-brace-format
msgid "Ошибка очистки точек доступа Wi-Fi: {error}"
msgstr "Ошибка очистки точек доступа Wi-Fi: {error}"
#: services/settings_service.py:84
#: services/settings_service.py:104
#, python-brace-format
msgid "Ошибка очистки всех сетевых данных: {error}"
msgstr "Ошибка очистки всех сетевых данных: {error}"
@ -385,33 +430,23 @@ msgstr "Ошибка очистки всех сетевых данных: {error
msgid "Неизвестный"
msgstr "Неизвестный"
#: services/subnet_service.py:179
#: services/subnet_service.py:185
#, python-brace-format
msgid "Ошибка при сохранении устройств: {error}"
msgstr "Ошибка при сохранении устройств: {error}"
#: services/subnet_service.py:374 services/subnet_service.py:391
#: services/subnet_service.py:408
#, python-brace-format
msgid "Устройство с MAC {mac} не найдено"
msgstr "Устройство с MAC {mac} не найдено"
#: services/subnet_service.py:268
msgid "Недопустимая подсеть по умолчанию"
msgstr "Недопустимая подсеть по умолчанию"
#: services/subnet_service.py:377
#, python-brace-format
msgid "Заметка для устройства с MAC {mac} уже существует"
msgstr "Заметка для устройства с MAC {mac} уже существует"
#: services/subnet_service.py:288
msgid "В данной подсети не обнаружено устройств"
msgstr "В данной подсети не обнаружено устройств"
#: services/subnet_service.py:384 services/subnet_service.py:401
#: services/subnet_service.py:418 services/wifi_service.py:319
#: services/wifi_service.py:337 services/wifi_service.py:355
#: services/subnet_service.py:345
#, python-brace-format
msgid "Внутренняя ошибка сервера: {error}"
msgstr "Внутренняя ошибка сервера: {error}"
#: services/subnet_service.py:394 services/subnet_service.py:411
#, python-brace-format
msgid "Заметка для устройства с MAC {mac} не существует"
msgstr "Заметка для устройства с MAC {mac} не существует"
msgid "Ошибка сканирования: {error}"
msgstr "Ошибка сканирования: {error}"
#: services/wifi_service.py:61
#, python-brace-format
@ -460,19 +495,4 @@ msgstr "Интерфейс не определён"
#: services/wifi_service.py:215
msgid "Таймаут при выполнении iw scan"
msgstr "Таймаут при выполнении iw scan"
#: services/wifi_service.py:312
#, python-brace-format
msgid "Заметка для сети с SSID {ssid} уже существует"
msgstr "Заметка для сети с SSID {ssid} уже существует"
#: services/wifi_service.py:327 services/wifi_service.py:345
#, python-brace-format
msgid "Сеть с SSID {ssid} не найдена"
msgstr "Сеть с SSID {ssid} не найдена"
#: services/wifi_service.py:330 services/wifi_service.py:348
#, python-brace-format
msgid "Заметка для сети с SSID {ssid} не существует"
msgstr "Заметка для сети с SSID {ssid} не существует"
msgstr "Таймаут при выполнении iw scan"

View file

@ -1,7 +1,6 @@
from pydantic import BaseModel
from typing import Optional, List
class Device(BaseModel):
"""
Сканируемое устройство в подсети
@ -26,17 +25,17 @@ class Device(BaseModel):
last_seen: Optional[str] = None
note: Optional[str] = None
class SubnetScanResult(BaseModel):
"""
Результат сканирования устройств в подсети
Атрибуты:
devices (List[Device]): Список просканированных устройств
error (Optional[str]): Сообщение об ошибке, если сканирование не удалось
"""
devices: List[Device] = []
error: Optional[str] = None
class SubnetScanRequest(BaseModel):
"""

View file

@ -1,8 +1,11 @@
import json
import logging
from utils.i18n import get_translations, _
from typing import Optional
from utils.i18n import get_translations
from babel.support import Translations
from fastapi import Depends
from pathlib import Path
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
@ -11,12 +14,16 @@ class SettingsService:
"""Сервис для управления настройками и очистки данных сети."""
def __init__(self, translations: Translations = Depends(get_translations())):
self.subnet_file = "storage/devices/devices.json"
self.subnet_base_path = "storage/subnets"
self.wifi_file = "storage/wifi/wifi.json"
self.translations = translations
def clear_subnet(self) -> bool:
"""Очищает данные узлов подсети в файле devices.json.
def clear_subnet(self, subnet: Optional[str] = None) -> bool:
"""Очищает данные узлов подсети в указанном файле или во всех файлах.
Args:
subnet (Optional[str]): Подсеть в формате CIDR (например, "10.1.1.0/24").
Если None, очищает все подсети.
Returns:
bool: True, если очистка успешна.
@ -25,12 +32,23 @@ class SettingsService:
Exception: Если произошла ошибка при записи в файл.
"""
try:
with open(self.subnet_file, "w") as f:
json.dump([], f)
logger.info("Subnet nodes data successfully cleared")
return True
if subnet:
file_path = Path(self.subnet_base_path) / f"{subnet.replace('/', '_')}.json"
if file_path.exists():
with open(file_path, "w") as f:
json.dump([], f)
logger.info(f"Subnet nodes data cleared for {subnet}")
else:
logger.info(f"Subnet file not found for {subnet}, clearing considered successful")
return True
else:
for file_path in Path(self.subnet_base_path).glob("*.json"):
with open(file_path, "w") as f:
json.dump([], f)
logger.info(f"Cleared subnet data in {file_path}")
return True
except FileNotFoundError:
logger.info("Devices file not found, clearing considered successful")
logger.info("No subnet files found, clearing considered successful")
return True
except Exception as e:
logger.error(f"Error clearing subnet nodes: {str(e)}")
@ -58,7 +76,7 @@ class SettingsService:
raise Exception(self.translations.gettext("Ошибка очистки точек доступа Wi-Fi: {error}").format(error=str(e)))
def clear_all_data(self) -> bool:
"""Очищает данные узлов подсети и точек доступа Wi-Fi в обоих файлах.
"""Очищает данные узлов всех подсетей и точек доступа Wi-Fi.
Returns:
bool: True, если очистка успешна.
@ -68,10 +86,12 @@ class SettingsService:
"""
try:
try:
with open(self.subnet_file, "w") as f:
json.dump([], f)
for file_path in Path(self.subnet_base_path).glob("*.json"):
with open(file_path, "w") as f:
json.dump([], f)
logger.info(f"Cleared subnet data in {file_path}")
except FileNotFoundError:
logger.info("Devices file not found, clearing considered successful")
logger.info("No subnet files found, clearing considered successful")
try:
with open(self.wifi_file, "w") as f:
json.dump([], f)

View file

@ -27,14 +27,53 @@ logging.basicConfig(
)
logger = logging.getLogger(__name__)
def get_subnet_service(translations: Translations = Depends(get_translations())):
"""Фабричная функция для создания экземпляра SubnetService.
Использует зависимости FastAPI для получения объекта переводов и возвращает
экземпляр SubnetService с текущей локализацией.
Args:
translations (Translations): Объект переводов для текущего языка.
Returns:
SubnetService: Экземпляр сервиса для работы с подсетями.
"""
return SubnetService(translations)
class NetworkScanner:
"""Класс для сканирования подсети с использованием ARP-запросов."""
def __init__(self, interface: str, translations: Translations = Depends(get_translations())):
"""Класс для сканирования подсети с использованием ARP-запросов.
Выполняет отправку ARP-запросов для обнаружения устройств в указанной подсети.
"""
def __init__(self, interface: str, translations: Translations):
"""Инициализирует сканер сети.
Устанавливает интерфейс для сканирования и объект переводов.
Args:
interface (str): Имя сетевого интерфейса (например, "wlp0s20f3").
translations (Translations): Объект переводов для текущего языка.
"""
self.interface = interface
conf.iface = interface
self.translations = translations
def scan(self, subnet: str) -> List[Tuple[str, str]]:
"""Сканирует подсеть с использованием ARP-запросов.
Отправляет ARP-пакеты для обнаружения активных устройств и возвращает
список кортежей (IP, MAC).
Args:
subnet (str): Подсеть в формате CIDR (например, "10.1.1.0/24").
Returns:
List[Tuple[str, str]]: Список кортежей (IP-адрес, MAC-адрес) обнаруженных устройств.
Raises:
Exception: Если произошла ошибка сканирования.
"""
try:
arp = ARP(pdst=subnet)
ether = Ether(dst="ff:ff:ff:ff:ff:ff")
@ -46,17 +85,48 @@ class NetworkScanner:
return []
class PortScanner:
"""Класс для сканирования открытых портов на устройствах."""
def __init__(self, interface: str, translations: Translations = Depends(get_translations())):
"""Класс для сканирования открытых портов на устройствах.
Предоставляет функциональность для проверки доступности портов, пока не реализована.
"""
def __init__(self, interface: str, translations: Translations):
"""Инициализирует сканер портов.
Устанавливает интерфейс для сканирования и объект переводов.
Args:
interface (str): Имя сетевого интерфейса.
translations (Translations): Объект переводов для текущего языка.
"""
self.interface = interface
self.translations = translations
def scan(self, ip: str) -> List[str]:
"""Сканирует открытые порты на указанном IP-адресе.
Пока возвращает пустой список, так как функциональность не реализована.
Args:
ip (str): IP-адрес устройства для сканирования.
Returns:
List[str]: Список открытых портов (пока пустой).
"""
return []
class DeviceInfoCollector:
"""Класс для сбора информации об устройствах, включая вендоров и хостнеймы."""
def __init__(self, translations: Translations = Depends(get_translations())):
"""Класс для сбора информации об устройствах, включая вендоров и хостнеймы.
Использует внешние сервисы (MacLookup, NBTSCAN, DNS) для получения данных.
"""
def __init__(self, translations: Translations):
"""Инициализирует коллектор информации об устройствах.
Настраивает кэширование и объект переводов.
Args:
translations (Translations): Объект переводов для текущего языка.
"""
self.mac_lookup = MacLookup()
self.nbtscan_cache = {}
self.nbtscan_results = {}
@ -64,6 +134,16 @@ class DeviceInfoCollector:
@lru_cache(maxsize=1000)
def get_vendor(self, mac: str) -> str:
"""Получает вендора устройства по MAC-адресу.
Использует кэширование для повышения производительности.
Args:
mac (str): MAC-адрес устройства.
Returns:
str: Название вендора или "Неизвестный" в случае ошибки.
"""
try:
return self.mac_lookup.lookup(mac)
except Exception as e:
@ -71,6 +151,13 @@ class DeviceInfoCollector:
return self.translations.gettext("Неизвестный")
def scan_nbtscan(self, subnet: str) -> None:
"""Сканирует подсеть с помощью nbtscan для получения хостнеймов.
Сохраняет результаты в кэш и логирует процесс.
Args:
subnet (str): Подсеть в формате CIDR.
"""
if subnet not in self.nbtscan_results:
try:
logger.debug(f"Starting nbtscan for subnet {subnet}")
@ -97,6 +184,16 @@ class DeviceInfoCollector:
logger.error(f"NetBIOS: Error for subnet {subnet}: {str(e)}")
def get_hostname(self, ip: str) -> Optional[str]:
"""Получает хостнейм для указанного IP-адреса.
Пробует сначала DNS, затем NetBIOS.
Args:
ip (str): IP-адрес устройства.
Returns:
Optional[str]: Хостнейм или None, если не удалось определить.
"""
try:
socket.setdefaulttimeout(2)
return socket.gethostbyaddr(ip)[0]
@ -114,20 +211,56 @@ class DeviceInfoCollector:
return None
class DeviceStorage:
"""Класс для хранения и управления данными об устройствах."""
def __init__(self, storage_path: str = "storage/devices/devices.json", translations: Translations = Depends(get_translations())):
self.storage_file = Path(storage_path)
self.storage_file.parent.mkdir(parents=True, exist_ok=True)
self.offline_timeout = timedelta(days=7)
self.translations = translations
"""Класс для хранения и управления данными об устройствах.
def load_devices(self) -> Dict[str, Device]:
Обеспечивает загрузку и сохранение данных в JSON-файлы.
"""
def __init__(self, base_path: str = "storage/subnets", translations: Translations = None):
"""Инициализирует хранилище устройств.
Создаёт директорию, если её нет, и задаёт параметры очистки.
Args:
base_path (str): Путь к директории хранения данных.
translations (Translations, optional): Объект переводов.
"""
self.base_path = Path(base_path)
self.base_path.mkdir(parents=True, exist_ok=True)
self.offline_timeout = timedelta(days=7)
self.translations = translations or Translations.load("locales", ["ru"], "messages") # Fallback на русский
def get_subnet_file(self, subnet: str) -> Path:
"""Возвращает путь к файлу для конкретной подсети.
Заменяет символы '/' на '_' для корректного имени файла.
Args:
subnet (str): Подсеть в формате CIDR.
Returns:
Path: Путь к файлу.
"""
sanitized_subnet = subnet.replace("/", "_")
return self.base_path / f"{sanitized_subnet}.json"
def load_devices(self, subnet: str) -> Dict[str, Device]:
"""Загружает устройства для указанной подсети.
Читает данные из JSON-файла и фильтрует устаревшие устройства.
Args:
subnet (str): Подсеть в формате CIDR.
Returns:
Dict[str, Device]: Словарь устройств с MAC как ключом.
"""
file_path = self.get_subnet_file(subnet)
try:
if self.storage_file.exists():
with open(self.storage_file, "r", encoding="utf-8") as f:
if file_path.exists():
with open(file_path, "r", encoding="utf-8") as f:
data = json.load(f)
if not isinstance(data, dict) or "devices" not in data:
logger.warning(f"Invalid JSON structure in {self.storage_file}, expected {'devices': [...]}, got {data}")
logger.warning(f"Invalid JSON structure in {file_path}, expected {'devices': [...]}, got {data}")
return {}
devices = {}
for device_data in data.get("devices", []):
@ -137,13 +270,23 @@ class DeviceStorage:
return self._clean_old_devices(devices)
return {}
except json.JSONDecodeError as e:
logger.error(f"JSON decode error in {self.storage_file}: {str(e)}")
logger.error(f"JSON decode error in {file_path}: {str(e)}")
return {}
except Exception as e:
logger.error(f"Error loading devices: {str(e)}")
logger.error(f"Error loading devices for subnet {subnet}: {str(e)}")
return {}
def _clean_old_devices(self, devices: Dict[str, Device]) -> Dict[str, Device]:
"""Очищает список устройств от устаревших оффлайн-устройств.
Удаляет устройства, которые оффлайн более 7 дней.
Args:
devices (Dict[str, Device]): Словарь устройств.
Returns:
Dict[str, Device]: Отфильтрованный словарь устройств.
"""
now = datetime.now()
filtered = {}
for mac, device in devices.items():
@ -160,28 +303,63 @@ class DeviceStorage:
filtered[mac] = device
return filtered
def save_devices(self, devices: List[Device]) -> None:
def save_devices(self, subnet: str, devices: List[Device]) -> None:
"""Сохраняет устройства для указанной подсети.
Записывает данные в JSON-файл.
Args:
subnet (str): Подсеть в формате CIDR.
devices (List[Device]): Список устройств для сохранения.
Raises:
Exception: Если произошла ошибка при сохранении.
"""
file_path = self.get_subnet_file(subnet)
try:
data = SubnetScanResult(devices=devices).model_dump()
with open(self.storage_file, "w", encoding="utf-8") as f:
with open(file_path, "w", encoding="utf-8") as f:
json.dump(data, f, indent=2, ensure_ascii=False)
logger.info(f"Saved {len(devices)} devices to {self.storage_file}")
logger.info(f"Saved {len(devices)} devices to {file_path}")
except Exception as e:
logger.error(f"Error saving devices: {str(e)}")
raise Exception(self.translations.gettext("Ошибка при сохранении устройств: {error}").format(error=str(e)))
logger.error(f"Error saving devices for subnet {subnet}: {str(e)}")
if self.translations:
raise Exception(self.translations.gettext("Ошибка при сохранении устройств: {error}").format(error=str(e)))
else:
raise Exception(f"Ошибка при сохранении устройств: {str(e)}")
class SubnetService:
"""Сервис для сканирования подсетей и управления устройствами."""
def __init__(self, translations: Translations = Depends(get_translations())):
"""Сервис для сканирования подсетей и управления устройствами.
Объединяет функциональность сканирования, сбора информации и хранения данных.
"""
def __init__(self, translations: Translations):
"""Инициализирует сервис подсети.
Настраивает интерфейс, сканеры и хранилище с текущей локализацией.
Args:
translations (Translations): Объект переводов для текущего языка.
"""
self.interface = self._get_default_interface()
self.network_scanner = NetworkScanner(self.interface)
self.port_scanner = PortScanner(self.interface)
self.device_info = DeviceInfoCollector()
self.storage = DeviceStorage()
self.network_scanner = NetworkScanner(self.interface, translations)
self.port_scanner = PortScanner(self.interface, translations)
self.device_info = DeviceInfoCollector(translations)
self.storage = DeviceStorage(translations=translations)
self.translations = translations
logger.info(f"Initialized SubnetService with interface: {self.interface}")
def _get_default_interface(self) -> str:
"""Определяет интерфейс по умолчанию.
Использует информацию о шлюзе или первый доступный интерфейс.
Returns:
str: Имя интерфейса (например, "wlp0s20f3" или "eth0").
Raises:
Exception: Если не удалось определить интерфейс.
"""
try:
gateways = netifaces.gateways()
default_gateway = gateways.get("default", {}).get(netifaces.AF_INET)
@ -203,7 +381,16 @@ class SubnetService:
return "eth0"
def _get_default_subnet(self) -> str:
"""Автоматически определяет подсеть локального устройства."""
"""Автоматически определяет подсеть локального устройства.
Использует информацию о текущем интерфейсе для определения подсети.
Returns:
str: Подсеть в формате CIDR (например, "10.1.1.0/24").
Raises:
Exception: Если не удалось определить подсеть.
"""
try:
interfaces = netifaces.interfaces()
for iface in interfaces:
@ -228,13 +415,18 @@ class SubnetService:
def scan_subnet(self, subnet: Optional[str] = None) -> SubnetScanResult:
"""Сканирует указанную подсеть и возвращает результаты.
Выполняет ARP-сканирование, собирает информацию о устройствах и сохраняет данные.
Args:
subnet (Optional[str]): Подсеть в формате CIDR (например, "10.1.1.0/24").
Если None или пустая строка, определяется автоматически.
Returns:
SubnetScanResult: Объект с результатами сканирования, содержащий список
устройств.
устройств или сообщение об ошибке.
Raises:
Exception: Если произошла ошибка сканирования.
"""
start_time = time.time()
devices = []
@ -246,9 +438,9 @@ class SubnetService:
subnet = self._get_default_subnet()
if subnet == "0.0.0.0/0":
logger.warning("Default subnet is invalid, scan aborted")
return SubnetScanResult(devices=devices)
return SubnetScanResult(devices=devices, error=self.translations.gettext("Недопустимая подсеть по умолчанию"))
saved_devices = self.storage.load_devices()
saved_devices = self.storage.load_devices(subnet)
local_device = self._get_local_device(subnet)
if local_device:
@ -264,6 +456,10 @@ class SubnetService:
logger.info(f"Discovered devices: {len(discovered_devices)}")
self.device_info.scan_nbtscan(subnet)
if not discovered_devices and not local_device:
logger.warning(f"No devices found in subnet {subnet}")
return SubnetScanResult(devices=[], error=self.translations.gettext("В данной подсети не обнаружено устройств"))
def process_device(ip_mac: Tuple[str, str]) -> Optional[Device]:
ip, mac = ip_mac
if local_device and ip == local_device.ip:
@ -309,7 +505,7 @@ class SubnetService:
for device in devices:
if device.mac:
saved_devices[device.mac] = device
self.storage.save_devices(list(saved_devices.values()))
self.storage.save_devices(subnet, list(saved_devices.values()))
logger.info(
f"Subnet scan {subnet} completed in {time.time() - start_time:.2f} seconds, "
@ -319,9 +515,19 @@ class SubnetService:
except Exception as e:
logger.error(f"Error scanning subnet {subnet}: {str(e)}")
return SubnetScanResult(devices=devices)
return SubnetScanResult(devices=devices, error=self.translations.gettext("Ошибка сканирования: {error}").format(error=str(e)))
def _get_local_device(self, subnet: str) -> Optional[Device]:
"""Определяет локальное устройство в указанной подсети.
Использует информацию об интерфейсе для обнаружения.
Args:
subnet (str): Подсеть в формате CIDR.
Returns:
Optional[Device]: Объект устройства или None, если не найдено.
"""
now = datetime.now().astimezone()
formatted_now = now.strftime("%d-%m-%Y, %H:%M")
try:
@ -354,53 +560,115 @@ class SubnetService:
logger.warning(f"Failed to get local device: {str(e)}")
return None
def add_note(self, mac: str, note: str) -> Tuple[bool, Optional[str]]:
def add_note(self, mac: str, note: str, translations: Translations) -> Tuple[bool, Optional[Tuple[str, Dict]]]:
"""Добавляет заметку для устройства по MAC-адресу.
Привязывает заметку к устройству, если оно существует.
Args:
mac (str): MAC-адрес устройства.
note (str): Текст заметки.
translations (Translations): Объект переводов для текущего языка.
Returns:
Tuple[bool, Optional[Tuple[str, Dict]]]: Кортеж (успех, результат),
где результат содержит код ошибки/успеха и данные.
"""
try:
saved_devices = self.storage.load_devices()
saved_devices = {}
for subnet in self.storage.base_path.glob("*.json"):
devices = self.storage.load_devices(subnet.stem.replace("_", "/"))
saved_devices.update(devices)
if mac not in saved_devices:
logger.warning(f"Device with MAC {mac} not found")
return False, self.translations.gettext("Устройство с MAC {mac} не найдено").format(mac=mac)
return False, ("not_found", {"mac": mac})
if saved_devices[mac].note is not None:
logger.error(f"Note for device with MAC {mac} already exists")
return False, self.translations.gettext("Заметка для устройства с MAC {mac} уже существует").format(mac=mac)
saved_devices[mac].note = note
self.storage.save_devices(list(saved_devices.values()))
return False, ("note_exists", {"mac": mac})
for subnet_file in self.storage.base_path.glob("*.json"):
subnet = subnet_file.stem.replace("_", "/")
devices = self.storage.load_devices(subnet)
if mac in devices:
devices[mac].note = note
self.storage.save_devices(subnet, list(devices.values()))
break
logger.info(f"Added note '{note}' for device with MAC {mac}")
return True, None
return True, ("success", {"mac": mac, "note": note})
except Exception as e:
logger.error(f"Error adding note for MAC {mac}: {str(e)}")
return False, self.translations.gettext("Внутренняя ошибка сервера: {error}").format(error=str(e))
return False, ("internal_error", {"error": str(e)})
def update_note(self, mac: str, note: str) -> Tuple[bool, Optional[str]]:
def update_note(self, mac: str, note: str, translations: Translations) -> Tuple[bool, Optional[Tuple[str, Dict]]]:
"""Обновляет заметку для устройства по MAC-адресу.
Изменяет существующую заметку, если она есть.
Args:
mac (str): MAC-адрес устройства.
note (str): Новый текст заметки.
translations (Translations): Объект переводов для текущего языка.
Returns:
Tuple[bool, Optional[Tuple[str, Dict]]]: Кортеж (успех, результат),
где результат содержит код ошибки/успеха и данные.
"""
try:
saved_devices = self.storage.load_devices()
saved_devices = {}
for subnet in self.storage.base_path.glob("*.json"):
devices = self.storage.load_devices(subnet.stem.replace("_", "/"))
saved_devices.update(devices)
if mac not in saved_devices:
logger.warning(f"Device with MAC {mac} not found")
return False, self.translations.gettext("Устройство с MAC {mac} не найдено").format(mac=mac)
return False, ("not_found", {"mac": mac})
if saved_devices[mac].note is None:
logger.error(f"Note for device with MAC {mac} does not exist")
return False, self.translations.gettext("Заметка для устройства с MAC {mac} не существует").format(mac=mac)
saved_devices[mac].note = note
self.storage.save_devices(list(saved_devices.values()))
return False, ("note_not_exists", {"mac": mac})
for subnet_file in self.storage.base_path.glob("*.json"):
subnet = subnet_file.stem.replace("_", "/")
devices = self.storage.load_devices(subnet)
if mac in devices:
devices[mac].note = note
self.storage.save_devices(subnet, list(devices.values()))
break
logger.info(f"Updated note '{note}' for device with MAC {mac}")
return True, None
return True, ("success", {"mac": mac, "note": note})
except Exception as e:
logger.error(f"Error updating note for MAC {mac}: {str(e)}")
return False, self.translations.gettext("Внутренняя ошибка сервера: {error}").format(error=str(e))
return False, ("internal_error", {"error": str(e)})
def delete_note(self, mac: str) -> Tuple[bool, Optional[str]]:
def delete_note(self, mac: str, translations: Translations) -> Tuple[bool, Optional[Tuple[str, Dict]]]:
"""Удаляет заметку для устройства по MAC-адресу.
Удаляет привязанную заметку, если она существует.
Args:
mac (str): MAC-адрес устройства.
translations (Translations): Объект переводов для текущего языка.
Returns:
Tuple[bool, Optional[Tuple[str, Dict]]]: Кортеж (успех, результат),
где результат содержит код ошибки/успеха и данные.
"""
try:
saved_devices = self.storage.load_devices()
saved_devices = {}
for subnet in self.storage.base_path.glob("*.json"):
devices = self.storage.load_devices(subnet.stem.replace("_", "/"))
saved_devices.update(devices)
if mac not in saved_devices:
logger.warning(f"Device with MAC {mac} not found")
return False, self.translations.gettext("Устройство с MAC {mac} не найдено").format(mac=mac)
return False, ("not_found", {"mac": mac})
if saved_devices[mac].note is None:
logger.error(f"Note for device with MAC {mac} does not exist")
return False, self.translations.gettext("Заметка для устройства с MAC {mac} не существует").format(mac=mac)
saved_devices[mac].note = None
self.storage.save_devices(list(saved_devices.values()))
return False, ("note_not_exists", {"mac": mac})
for subnet_file in self.storage.base_path.glob("*.json"):
subnet = subnet_file.stem.replace("_", "/")
devices = self.storage.load_devices(subnet)
if mac in devices:
devices[mac].note = None
self.storage.save_devices(subnet, list(devices.values()))
break
logger.info(f"Deleted note for device with MAC {mac}")
return True, None
return True, ("success", {"mac": mac})
except Exception as e:
logger.error(f"Error deleting note for MAC {mac}: {str(e)}")
return False, self.translations.gettext("Внутренняя ошибка сервера: {error}").format(error=str(e))
return False, ("internal_error", {"error": str(e)})