Детальная обработка ответов и ошибок для работы с подсетью
This commit is contained in:
parent
1b0d18b681
commit
ea56d17ea7
5 changed files with 86 additions and 502 deletions
|
|
@ -33,9 +33,15 @@ def add_note(mac: str, request: NoteRequest):
|
||||||
"""Добавляет заметку для устройства по MAC-адресу"""
|
"""Добавляет заметку для устройства по MAC-адресу"""
|
||||||
try:
|
try:
|
||||||
subnet_service = SubnetService()
|
subnet_service = SubnetService()
|
||||||
if not subnet_service.add_note(mac, request.note):
|
success, error_message = subnet_service.add_note(mac, request.note)
|
||||||
raise HTTPException(status_code=404, detail=f"Устройство с MAC {mac} не найдено")
|
if not success:
|
||||||
|
if "не найдено" in error_message:
|
||||||
|
raise HTTPException(status_code=404, detail=error_message)
|
||||||
|
elif "уже существует" in error_message:
|
||||||
|
raise HTTPException(status_code=400, detail=error_message)
|
||||||
return {"message": f"Заметка '{request.note}' добавлена для устройства {mac}"}
|
return {"message": f"Заметка '{request.note}' добавлена для устройства {mac}"}
|
||||||
|
except HTTPException as 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)}")
|
||||||
|
|
||||||
|
|
@ -44,9 +50,15 @@ def update_note(mac: str, request: NoteRequest):
|
||||||
"""Обновляет заметку для устройства по MAC-адресу"""
|
"""Обновляет заметку для устройства по MAC-адресу"""
|
||||||
try:
|
try:
|
||||||
subnet_service = SubnetService()
|
subnet_service = SubnetService()
|
||||||
if not subnet_service.update_note(mac, request.note):
|
success, error_message = subnet_service.update_note(mac, request.note)
|
||||||
raise HTTPException(status_code=404, detail=f"Устройство с MAC {mac} не найдено")
|
if not success:
|
||||||
|
if "не найдено" in error_message:
|
||||||
|
raise HTTPException(status_code=404, detail=error_message)
|
||||||
|
elif "не существует" in error_message:
|
||||||
|
raise HTTPException(status_code=400, detail=error_message)
|
||||||
return {"message": f"Заметка '{request.note}' обновлена для устройства {mac}"}
|
return {"message": f"Заметка '{request.note}' обновлена для устройства {mac}"}
|
||||||
|
except HTTPException as 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)}")
|
||||||
|
|
||||||
|
|
@ -55,8 +67,14 @@ def delete_note(mac: str):
|
||||||
"""Удаляет заметку для устройства по MAC-адресу"""
|
"""Удаляет заметку для устройства по MAC-адресу"""
|
||||||
try:
|
try:
|
||||||
subnet_service = SubnetService()
|
subnet_service = SubnetService()
|
||||||
if not subnet_service.delete_note(mac):
|
success, error_message = subnet_service.delete_note(mac)
|
||||||
raise HTTPException(status_code=404, detail=f"Устройство с MAC {mac} не найдено")
|
if not success:
|
||||||
|
if "не найдено" in error_message:
|
||||||
|
raise HTTPException(status_code=404, detail=error_message)
|
||||||
|
elif "не существует" in error_message:
|
||||||
|
raise HTTPException(status_code=400, detail=error_message)
|
||||||
return {"message": f"Заметка удалена для устройства {mac}"}
|
return {"message": f"Заметка удалена для устройства {mac}"}
|
||||||
|
except HTTPException as 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)}")
|
||||||
|
|
@ -1,276 +0,0 @@
|
||||||
# Документация API FirewallD
|
|
||||||
|
|
||||||
## Общее описание
|
|
||||||
API предоставляет интерфейс для управления межсетевым экраном FirewallD в Linux системах. API построено на основе взаимодействия с FirewallD через D-Bus интерфейс.
|
|
||||||
|
|
||||||
## Внутренняя архитектура
|
|
||||||
|
|
||||||
### 1. Уровни взаимодействия
|
|
||||||
```
|
|
||||||
[HTTP API] -> [FastAPI Router] -> [FirewallD Service] -> [D-Bus] -> [FirewallD]
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. Компоненты системы
|
|
||||||
|
|
||||||
#### 2.1 D-Bus интерфейс
|
|
||||||
FirewallD предоставляет D-Bus интерфейс `org.fedoraproject.FirewallD1` для управления. Наш сервис подключается к системной шине D-Bus и взаимодействует с FirewallD через этот интерфейс.
|
|
||||||
|
|
||||||
Основные методы D-Bus:
|
|
||||||
- `getZones()` - получение списка зон
|
|
||||||
- `getDefaultZone()` - получение зоны по умолчанию
|
|
||||||
- `addService()` - добавление сервиса
|
|
||||||
- `removeService()` - удаление сервиса
|
|
||||||
и т.д.
|
|
||||||
|
|
||||||
#### 2.2 Модели данных
|
|
||||||
В `models/firewalld.py` определены Pydantic модели для валидации данных:
|
|
||||||
|
|
||||||
##### FirewallZone
|
|
||||||
Используется для представления зоны файрвола:
|
|
||||||
```python
|
|
||||||
class FirewallZone(BaseModel):
|
|
||||||
name: str
|
|
||||||
target: Optional[str] = None
|
|
||||||
services: List[str] = Field(default_factory=list)
|
|
||||||
ports: List[str] = Field(default_factory=list)
|
|
||||||
sources: List[str] = Field(default_factory=list)
|
|
||||||
interfaces: List[str] = Field(default_factory=list)
|
|
||||||
```
|
|
||||||
Применяется при получении настроек зоны через API.
|
|
||||||
|
|
||||||
##### FirewallService
|
|
||||||
Модель для конфигурации сервиса:
|
|
||||||
```python
|
|
||||||
class FirewallService(BaseModel):
|
|
||||||
name: str
|
|
||||||
ports: List[str] = Field(default_factory=list)
|
|
||||||
protocols: List[str] = Field(default_factory=list)
|
|
||||||
modules: List[str] = Field(default_factory=list)
|
|
||||||
```
|
|
||||||
Используется при добавлении новых сервисов через API.
|
|
||||||
|
|
||||||
##### FirewallPort
|
|
||||||
Модель для конфигурации порта с валидацией протокола:
|
|
||||||
```python
|
|
||||||
class FirewallPort(BaseModel):
|
|
||||||
port: int
|
|
||||||
protocol: Optional[str] = Field(default=None)
|
|
||||||
timeout: Optional[int] = 0
|
|
||||||
```
|
|
||||||
Применяется в API endpoint POST /api/v1/firewalld/zones/{zone}/ports для валидации входных данных.
|
|
||||||
|
|
||||||
##### FirewallSource
|
|
||||||
Модель для конфигурации источника с валидацией IP-адреса:
|
|
||||||
```python
|
|
||||||
class FirewallSource(BaseModel):
|
|
||||||
address: str = Field(..., pattern=r'^(?:[0-9]{1,3}\.){3}[0-9]{1,3}(?:/[0-9]{1,2})?$')
|
|
||||||
zone: Optional[str] = None
|
|
||||||
```
|
|
||||||
Используется в API endpoint POST /api/v1/firewalld/zones/{zone}/sources для валидации IP-адресов.
|
|
||||||
|
|
||||||
##### FirewallRule
|
|
||||||
Обобщенная модель правила файрвола:
|
|
||||||
```python
|
|
||||||
class FirewallRule(BaseModel):
|
|
||||||
zone: str
|
|
||||||
source: Optional[FirewallSource] = None
|
|
||||||
port: Optional[FirewallPort] = None
|
|
||||||
service: Optional[str] = None
|
|
||||||
```
|
|
||||||
Используется для комплексных операций с правилами.
|
|
||||||
|
|
||||||
В API (`api/v1/firewalld.py`) определены дополнительные модели для запросов:
|
|
||||||
|
|
||||||
##### PortRule
|
|
||||||
```python
|
|
||||||
class PortRule(BaseModel):
|
|
||||||
port: int
|
|
||||||
protocol: str
|
|
||||||
timeout: Optional[int] = 0
|
|
||||||
```
|
|
||||||
Используется для валидации входных данных при добавлении портов.
|
|
||||||
|
|
||||||
##### ServiceRule
|
|
||||||
```python
|
|
||||||
class ServiceRule(BaseModel):
|
|
||||||
service: str
|
|
||||||
timeout: Optional[int] = 0
|
|
||||||
```
|
|
||||||
Используется для валидации входных данных при добавлении сервисов.
|
|
||||||
|
|
||||||
##### SourceRule
|
|
||||||
```python
|
|
||||||
class SourceRule(BaseModel):
|
|
||||||
source: str
|
|
||||||
```
|
|
||||||
Используется для валидации входных данных при добавлении источников.
|
|
||||||
|
|
||||||
Все модели используют Pydantic для:
|
|
||||||
- Валидации входных данных
|
|
||||||
- Автоматической конвертации типов
|
|
||||||
- Генерации OpenAPI документации
|
|
||||||
- Проверки бизнес-правил (например, валидация протокола в FirewallPort)
|
|
||||||
|
|
||||||
#### 2.3 Сервисный слой
|
|
||||||
`FirewallService` в `services/firewalld_service.py` обеспечивает:
|
|
||||||
- Подключение к D-Bus
|
|
||||||
- Обработку ошибок
|
|
||||||
- Преобразование данных между API и D-Bus форматами
|
|
||||||
|
|
||||||
## API Endpoints
|
|
||||||
|
|
||||||
### Управление зонами
|
|
||||||
|
|
||||||
#### GET /api/v1/firewalld/zones
|
|
||||||
Получение списка всех доступных зон.
|
|
||||||
|
|
||||||
**Ответ:**
|
|
||||||
```json
|
|
||||||
[
|
|
||||||
"public",
|
|
||||||
"internal",
|
|
||||||
"external",
|
|
||||||
...
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
**Внутренняя работа:**
|
|
||||||
1. Запрос поступает в FastAPI router
|
|
||||||
2. Router вызывает `FirewallService.get_zones()`
|
|
||||||
3. Сервис делает вызов `getZones()` через D-Bus
|
|
||||||
4. Результат преобразуется в JSON и возвращается клиенту
|
|
||||||
|
|
||||||
#### GET /api/v1/firewalld/zones/default
|
|
||||||
Получение зоны по умолчанию.
|
|
||||||
|
|
||||||
**Ответ:**
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"name": "public"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Внутренняя работа:**
|
|
||||||
1. Вызов `FirewallService.get_default_zone()`
|
|
||||||
2. D-Bus запрос `getDefaultZone()`
|
|
||||||
3. Преобразование результата
|
|
||||||
|
|
||||||
#### PUT /api/v1/firewalld/zones/default/{zone}
|
|
||||||
Установка зоны по умолчанию.
|
|
||||||
|
|
||||||
**Параметры:**
|
|
||||||
- `zone`: имя зоны
|
|
||||||
|
|
||||||
**Внутренняя работа:**
|
|
||||||
1. Валидация имени зоны
|
|
||||||
2. Вызов `FirewallService.set_default_zone()`
|
|
||||||
3. D-Bus запрос `setDefaultZone()`
|
|
||||||
|
|
||||||
### Управление сервисами
|
|
||||||
|
|
||||||
#### GET /api/v1/firewalld/zones/{zone}/services
|
|
||||||
Получение списка сервисов в зоне.
|
|
||||||
|
|
||||||
**Ответ:**
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"services": ["http", "https", "ssh"]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Внутренняя работа:**
|
|
||||||
1. Проверка существования зоны
|
|
||||||
2. Вызов `FirewallService.get_services()`
|
|
||||||
3. D-Bus запрос `getServices()`
|
|
||||||
|
|
||||||
#### POST /api/v1/firewalld/zones/{zone}/services
|
|
||||||
Добавление сервиса в зону.
|
|
||||||
|
|
||||||
**Тело запроса:**
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"service": "http",
|
|
||||||
"timeout": 0
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Внутренняя работа:**
|
|
||||||
1. Валидация данных через Pydantic модель
|
|
||||||
2. Вызов `FirewallService.add_service()`
|
|
||||||
3. D-Bus запрос `addService()`
|
|
||||||
|
|
||||||
#### DELETE /api/v1/firewalld/zones/{zone}/services/{service}
|
|
||||||
Удаление сервиса из зоны.
|
|
||||||
|
|
||||||
**Внутренняя работа:**
|
|
||||||
1. Проверка существования сервиса
|
|
||||||
2. Вызов `FirewallService.remove_service()`
|
|
||||||
3. D-Bus запрос `removeService()`
|
|
||||||
|
|
||||||
### Управление портами
|
|
||||||
|
|
||||||
#### GET /api/v1/firewalld/zones/{zone}/ports
|
|
||||||
Получение списка портов в зоне.
|
|
||||||
|
|
||||||
**Ответ:**
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"ports": ["80/tcp", "443/tcp"]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### POST /api/v1/firewalld/zones/{zone}/ports
|
|
||||||
Добавление порта в зону.
|
|
||||||
|
|
||||||
**Тело запроса:**
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"port": 80,
|
|
||||||
"protocol": "tcp",
|
|
||||||
"timeout": 0
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### DELETE /api/v1/firewalld/zones/{zone}/ports
|
|
||||||
Удаление порта из зоны.
|
|
||||||
|
|
||||||
### Управление источниками
|
|
||||||
|
|
||||||
#### GET /api/v1/firewalld/zones/{zone}/sources
|
|
||||||
Получение списка источников в зоне.
|
|
||||||
|
|
||||||
#### POST /api/v1/firewalld/zones/{zone}/sources
|
|
||||||
Добавление источника в зону.
|
|
||||||
|
|
||||||
#### DELETE /api/v1/firewalld/zones/{zone}/sources/{source}
|
|
||||||
Удаление источника из зоны.
|
|
||||||
|
|
||||||
## Обработка ошибок
|
|
||||||
|
|
||||||
API возвращает следующие коды ошибок:
|
|
||||||
- 400: Неверный запрос (например, неверный формат данных)
|
|
||||||
- 404: Ресурс не найден (зона, сервис и т.д.)
|
|
||||||
- 500: Внутренняя ошибка сервера
|
|
||||||
|
|
||||||
При ошибке D-Bus или FirewallD, ошибка логируется и возвращается соответствующий HTTP код.
|
|
||||||
|
|
||||||
## Возможные улучшения
|
|
||||||
|
|
||||||
1. **Безопасность:**
|
|
||||||
- Добавить аутентификацию
|
|
||||||
- Добавить rate limiting
|
|
||||||
- Логирование действий
|
|
||||||
|
|
||||||
2. **Функциональность:**
|
|
||||||
- Добавить поддержку rich rules
|
|
||||||
- Управление ICMP правилами
|
|
||||||
- Управление forward портами
|
|
||||||
|
|
||||||
3. **Производительность:**
|
|
||||||
- Кэширование часто запрашиваемых данных
|
|
||||||
- Batch операции для множественных изменений
|
|
||||||
|
|
||||||
4. **Надёжность:**
|
|
||||||
- Retry механизм для D-Bus операций
|
|
||||||
- Более детальная обработка ошибок
|
|
||||||
- Валидация состояния FirewallD
|
|
||||||
208
SRV/docs/ufw.md
208
SRV/docs/ufw.md
|
|
@ -1,208 +0,0 @@
|
||||||
# UFW API Documentation
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
API для управления файрволом UFW (Uncomplicated Firewall) в Linux. Позволяет добавлять, удалять и просматривать правила межсетевого экрана.
|
|
||||||
|
|
||||||
## Архитектура
|
|
||||||
|
|
||||||
### Модели данных
|
|
||||||
|
|
||||||
#### Protocol (Протокол)
|
|
||||||
```python
|
|
||||||
class Protocol(str, Enum):
|
|
||||||
TCP = "tcp" # Только TCP протокол
|
|
||||||
UDP = "udp" # Только UDP протокол
|
|
||||||
BOTH = "both" # Оба протокола (TCP и UDP)
|
|
||||||
```
|
|
||||||
|
|
||||||
При указании `BOTH`, система автоматически создает правила для обоих протоколов, используя `proto tcp/udp` в команде UFW.
|
|
||||||
|
|
||||||
#### Action (Действие)
|
|
||||||
```python
|
|
||||||
class Action(str, Enum):
|
|
||||||
ALLOW = "allow" # Разрешить трафик
|
|
||||||
DENY = "deny" # Запретить трафик без уведомления
|
|
||||||
REJECT = "reject" # Запретить трафик с уведомлением отправителю
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Direction (Направление)
|
|
||||||
```python
|
|
||||||
class Direction(str, Enum):
|
|
||||||
IN = "in" # Входящий трафик
|
|
||||||
OUT = "out" # Исходящий трафик
|
|
||||||
```
|
|
||||||
|
|
||||||
#### UFWRule (Правило UFW)
|
|
||||||
```python
|
|
||||||
class UFWRule(BaseModel):
|
|
||||||
action: Action # Действие (allow/deny/reject)
|
|
||||||
direction: Direction # Направление трафика (in/out)
|
|
||||||
from_ip: str # IP-адрес источника
|
|
||||||
to_ip: str # IP-адрес назначения
|
|
||||||
port: Union[int, str] # Порт или диапазон портов
|
|
||||||
protocol: Protocol # Протокол
|
|
||||||
comment: Optional[str] # Комментарий к правилу
|
|
||||||
```
|
|
||||||
|
|
||||||
Модель выполняет следующие проверки:
|
|
||||||
1. Валидация IP адресов:
|
|
||||||
- Проверяет корректность IPv4/IPv6 адресов
|
|
||||||
- Поддерживает CIDR нотацию
|
|
||||||
- Проверяет совместимость версий IP (IPv4/IPv6)
|
|
||||||
|
|
||||||
2. Валидация портов:
|
|
||||||
- Одиночный порт: число от 1 до 65535
|
|
||||||
- Диапазон портов: строка вида "3000:4000"
|
|
||||||
- Проверяет корректность диапазона (start <= end)
|
|
||||||
|
|
||||||
## API Endpoints
|
|
||||||
|
|
||||||
### Получение правил
|
|
||||||
|
|
||||||
#### GET /ufw/rules
|
|
||||||
Получает список всех правил UFW.
|
|
||||||
|
|
||||||
**Response:**
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"enabled": true,
|
|
||||||
"rules": [
|
|
||||||
{
|
|
||||||
"action": "allow",
|
|
||||||
"direction": "in",
|
|
||||||
"from_ip": "0.0.0.0/0",
|
|
||||||
"to_ip": "0.0.0.0/0",
|
|
||||||
"port": 80,
|
|
||||||
"protocol": "tcp",
|
|
||||||
"comment": "Allow HTTP"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Добавление правил
|
|
||||||
|
|
||||||
#### POST /ufw/rules/in
|
|
||||||
Добавляет входящие правила UFW.
|
|
||||||
|
|
||||||
**Request:**
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"rules": [
|
|
||||||
{
|
|
||||||
"action": "allow",
|
|
||||||
"from_ip": "0.0.0.0/0",
|
|
||||||
"to_ip": "0.0.0.0/0",
|
|
||||||
"port": 80,
|
|
||||||
"protocol": "tcp",
|
|
||||||
"comment": "Allow HTTP"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Response:**
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"status": "success",
|
|
||||||
"message": "Входящие правила UFW успешно добавлены"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### POST /ufw/rules/out
|
|
||||||
Добавляет исходящие правила UFW.
|
|
||||||
|
|
||||||
**Request:**
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"rules": [
|
|
||||||
{
|
|
||||||
"action": "allow",
|
|
||||||
"from_ip": "192.168.1.0/24",
|
|
||||||
"to_ip": "0.0.0.0/0",
|
|
||||||
"port": 443,
|
|
||||||
"protocol": "tcp",
|
|
||||||
"comment": "Allow HTTPS outbound"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Response:**
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"status": "success",
|
|
||||||
"message": "Исходящие правила UFW успешно добавлены"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Удаление правил
|
|
||||||
|
|
||||||
#### DELETE /ufw/rules
|
|
||||||
Удаляет правила UFW по их номерам.
|
|
||||||
|
|
||||||
**Request:**
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"numbers": [1, 2, 3]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Response:**
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"status": "success",
|
|
||||||
"message": "Правила UFW успешно удалены"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Управление состоянием
|
|
||||||
|
|
||||||
#### POST /ufw/enable
|
|
||||||
Включает UFW.
|
|
||||||
|
|
||||||
**Response:**
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"status": "success",
|
|
||||||
"message": "UFW успешно включен"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### POST /ufw/disable
|
|
||||||
Выключает UFW.
|
|
||||||
|
|
||||||
**Response:**
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"status": "success",
|
|
||||||
"message": "UFW успешно выключен"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Обработка ошибок
|
|
||||||
|
|
||||||
### HTTP Status Codes
|
|
||||||
|
|
||||||
1. Успешные операции (200):
|
|
||||||
- Получение списка правил
|
|
||||||
- Успешное добавление/удаление правил
|
|
||||||
- Включение/выключение UFW
|
|
||||||
|
|
||||||
2. Ошибки клиента (400):
|
|
||||||
- UFW отключен при попытке добавления правил
|
|
||||||
- Некорректный IP адрес или CIDR
|
|
||||||
- Неверный номер порта или диапазон
|
|
||||||
- Несовместимые версии IP (IPv4/IPv6)
|
|
||||||
- Неверные значения enum (action/protocol/direction)
|
|
||||||
|
|
||||||
3. Ошибки сервера (500):
|
|
||||||
- Ошибки выполнения команд UFW
|
|
||||||
- Внутренние ошибки сервера
|
|
||||||
|
|
||||||
### Формат ошибок
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"detail": "Описание ошибки"
|
|
||||||
}
|
|
||||||
|
|
@ -268,44 +268,94 @@ class SubnetService:
|
||||||
logger.warning(f"Не удалось получить локальное устройство: {str(e)}")
|
logger.warning(f"Не удалось получить локальное устройство: {str(e)}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def add_note(self, mac: str, note: str) -> bool:
|
def add_note(self, mac: str, note: str) -> Tuple[bool, Optional[str]]:
|
||||||
|
"""Добавляет заметку к устройству по MAC-адресу.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
mac (str): MAC-адрес устройства.
|
||||||
|
note (str): Текст заметки.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Tuple[bool, Optional[str]]: Кортеж (успех, сообщение об ошибке).
|
||||||
|
- Успех: (True, None).
|
||||||
|
- Ошибка: (False, сообщение об ошибке).
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
Exception: Если произошла ошибка при сохранении.
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
saved_devices = self.storage.load_devices()
|
saved_devices = self.storage.load_devices()
|
||||||
if mac not in saved_devices:
|
if mac not in saved_devices:
|
||||||
logger.warning(f"Устройство с MAC {mac} не найдено")
|
logger.warning(f"Устройство с MAC {mac} не найдено")
|
||||||
return False
|
return False, f"Устройство с MAC {mac} не найдено"
|
||||||
|
if saved_devices[mac].note is not None:
|
||||||
|
logger.error(f"Заметка для устройства с MAC {mac} уже существует")
|
||||||
|
return False, f"Заметка для устройства с MAC {mac} уже существует"
|
||||||
saved_devices[mac].note = note
|
saved_devices[mac].note = note
|
||||||
self.storage.save_devices(list(saved_devices.values()))
|
self.storage.save_devices(list(saved_devices.values()))
|
||||||
logger.info(f"Добавлена заметка '{note}' для устройства с MAC {mac}")
|
logger.info(f"Добавлена заметка '{note}' для устройства с MAC {mac}")
|
||||||
return True
|
return True, None
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Ошибка при добавлении заметки для MAC {mac}: {str(e)}")
|
logger.error(f"Ошибка при добавлении заметки для MAC {mac}: {str(e)}")
|
||||||
return False
|
return False, f"Внутренняя ошибка сервера: {str(e)}"
|
||||||
|
|
||||||
def update_note(self, mac: str, note: str) -> bool:
|
def update_note(self, mac: str, note: str) -> Tuple[bool, Optional[str]]:
|
||||||
|
"""Обновляет заметку устройства по MAC-адресу.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
mac (str): MAC-адрес устройства.
|
||||||
|
note (str): Новый текст заметки.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Tuple[bool, Optional[str]]: Кортеж (успех, сообщение об ошибке).
|
||||||
|
- Успех: (True, None).
|
||||||
|
- Ошибка: (False, сообщение об ошибке).
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
Exception: Если произошла ошибка при сохранении.
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
saved_devices = self.storage.load_devices()
|
saved_devices = self.storage.load_devices()
|
||||||
if mac not in saved_devices:
|
if mac not in saved_devices:
|
||||||
logger.warning(f"Устройство с MAC {mac} не найдено")
|
logger.warning(f"Устройство с MAC {mac} не найдено")
|
||||||
return False
|
return False, f"Устройство с MAC {mac} не найдено"
|
||||||
|
if saved_devices[mac].note is None:
|
||||||
|
logger.error(f"Заметка для устройства с MAC {mac} не существует")
|
||||||
|
return False, f"Заметка для устройства с MAC {mac} не существует"
|
||||||
saved_devices[mac].note = note
|
saved_devices[mac].note = note
|
||||||
self.storage.save_devices(list(saved_devices.values()))
|
self.storage.save_devices(list(saved_devices.values()))
|
||||||
logger.info(f"Обновлена заметка '{note}' для устройства с MAC {mac}")
|
logger.info(f"Обновлена заметка '{note}' для устройства с MAC {mac}")
|
||||||
return True
|
return True, None
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Ошибка при обновлении заметки для MAC {mac}: {str(e)}")
|
logger.error(f"Ошибка при обновлении заметки для MAC {mac}: {str(e)}")
|
||||||
return False
|
return False, f"Внутренняя ошибка сервера: {str(e)}"
|
||||||
|
|
||||||
def delete_note(self, mac: str) -> bool:
|
def delete_note(self, mac: str) -> Tuple[bool, Optional[str]]:
|
||||||
|
"""Удаляет заметку устройства по MAC-адресу.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
mac (str): MAC-адрес устройства.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Tuple[bool, Optional[str]]: Кортеж (успех, сообщение об ошибке).
|
||||||
|
- Успех: (True, None).
|
||||||
|
- Ошибка: (False, сообщение об ошибке).
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
Exception: Если произошла ошибка при сохранении.
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
saved_devices = self.storage.load_devices()
|
saved_devices = self.storage.load_devices()
|
||||||
if mac not in saved_devices:
|
if mac not in saved_devices:
|
||||||
logger.warning(f"Устройство с MAC {mac} не найдено")
|
logger.warning(f"Устройство с MAC {mac} не найдено")
|
||||||
return False
|
return False, f"Устройство с MAC {mac} не найдено"
|
||||||
|
if saved_devices[mac].note is None:
|
||||||
|
logger.error(f"Заметка для устройства с MAC {mac} не существует")
|
||||||
|
return False, f"Заметка для устройства с MAC {mac} не существует"
|
||||||
saved_devices[mac].note = None
|
saved_devices[mac].note = None
|
||||||
self.storage.save_devices(list(saved_devices.values()))
|
self.storage.save_devices(list(saved_devices.values()))
|
||||||
logger.info(f"Удалена заметка для устройства с MAC {mac}")
|
logger.info(f"Удалена заметка для устройства с MAC {mac}")
|
||||||
return True
|
return True, None
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Ошибка при удалении заметки для MAC {mac}: {str(e)}")
|
logger.error(f"Ошибка при удалении заметки для MAC {mac}: {str(e)}")
|
||||||
return False
|
return False, f"Внутренняя ошибка сервера: {str(e)}"
|
||||||
Loading…
Add table
Add a link
Reference in a new issue